From 33d3aa3226c2ca18c5118a19d70fe23011c2838d Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Tue, 10 Aug 2021 10:54:04 -0400 Subject: [PATCH 001/517] Set version to 93.0.0 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index cc28093c7f..2cc16c2a99 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -92.0.0-beta.1 +93.0.0-beta.1 From 6a1efacf3af087d3a8f140f8604bb779d46273f1 Mon Sep 17 00:00:00 2001 From: "codrut.topliceanu" Date: Mon, 9 Aug 2021 16:01:30 +0300 Subject: [PATCH 002/517] For #17917: Use View binding in collections --- .../collections/CollectionCreationFragment.kt | 18 +++-- .../CollectionCreationTabListAdapter.kt | 40 +++++----- .../collections/CollectionCreationView.kt | 76 ++++++++++--------- .../collections/SaveCollectionListAdapter.kt | 20 ++--- 4 files changed, 86 insertions(+), 68 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt index 012a73c886..ce8caddec8 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt @@ -12,11 +12,11 @@ import android.view.ViewGroup import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs -import kotlinx.android.synthetic.main.fragment_create_collection.view.* import kotlinx.coroutines.ExperimentalCoroutinesApi import mozilla.components.lib.state.ext.consumeFrom import org.mozilla.fenix.R import org.mozilla.fenix.components.StoreProvider +import org.mozilla.fenix.databinding.FragmentCreateCollectionBinding import org.mozilla.fenix.ext.requireComponents @ExperimentalCoroutinesApi @@ -25,6 +25,9 @@ class CollectionCreationFragment : DialogFragment() { private lateinit var collectionCreationStore: CollectionCreationStore private lateinit var collectionCreationInteractor: CollectionCreationInteractor + private var _binding: FragmentCreateCollectionBinding? = null + private val binding get() = _binding!! + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) isCancelable = false @@ -35,8 +38,8 @@ class CollectionCreationFragment : DialogFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - val view = inflater.inflate(R.layout.fragment_create_collection, container, false) + ): View { + _binding = FragmentCreateCollectionBinding.inflate(inflater, container, false) val args: CollectionCreationFragmentArgs by navArgs() collectionCreationStore = StoreProvider.get(this) { @@ -63,11 +66,11 @@ class CollectionCreationFragment : DialogFragment() { ) ) collectionCreationView = CollectionCreationView( - view.createCollectionWrapper, + binding.createCollectionWrapper, collectionCreationInteractor ) - return view + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -78,6 +81,11 @@ class CollectionCreationFragment : DialogFragment() { } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun onResume() { super.onResume() collectionCreationView.onResumed() diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationTabListAdapter.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationTabListAdapter.kt index e74cc340e8..f5842b3464 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationTabListAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationTabListAdapter.kt @@ -5,14 +5,13 @@ package org.mozilla.fenix.collections import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.core.view.isGone import androidx.core.view.isInvisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.collection_tab_list_row.* import org.mozilla.fenix.R +import org.mozilla.fenix.databinding.CollectionTabListRowBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.home.Tab @@ -26,11 +25,16 @@ class CollectionCreationTabListAdapter( private var selectedTabs: MutableSet = mutableSetOf() private var hideCheckboxes = false + private lateinit var binding: CollectionTabListRowBinding + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TabViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(TabViewHolder.LAYOUT_ID, parent, false) + binding = CollectionTabListRowBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) - return TabViewHolder(view) + return TabViewHolder(binding) } override fun onBindViewHolder(holder: TabViewHolder, position: Int, payloads: MutableList) { @@ -41,11 +45,11 @@ class CollectionCreationTabListAdapter( is CheckChanged -> { val checkChanged = payloads[0] as CheckChanged if (checkChanged.shouldBeChecked) { - holder.tab_selected_checkbox.isChecked = true + binding.tabSelectedCheckbox.isChecked = true } else if (checkChanged.shouldBeUnchecked) { - holder.tab_selected_checkbox.isChecked = false + binding.tabSelectedCheckbox.isChecked = false } - holder.tab_selected_checkbox.isGone = checkChanged.shouldHideCheckBox + binding.tabSelectedCheckbox.isGone = checkChanged.shouldHideCheckBox } } } @@ -54,7 +58,7 @@ class CollectionCreationTabListAdapter( override fun onBindViewHolder(holder: TabViewHolder, position: Int) { val tab = tabs[position] val isSelected = selectedTabs.contains(tab) - holder.tab_selected_checkbox.setOnCheckedChangeListener { _, isChecked -> + binding.tabSelectedCheckbox.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { selectedTabs.add(tab) interactor.addTabToSelection(tab) @@ -88,24 +92,24 @@ class CollectionCreationTabListAdapter( } } -class TabViewHolder(view: View) : ViewHolder(view) { +class TabViewHolder(private val binding: CollectionTabListRowBinding) : ViewHolder(binding.root) { init { - collection_item_tab.setOnClickListener { - tab_selected_checkbox.isChecked = !tab_selected_checkbox.isChecked + binding.collectionItemTab.setOnClickListener { + binding.tabSelectedCheckbox.isChecked = !binding.tabSelectedCheckbox.isChecked } } fun bind(tab: Tab, isSelected: Boolean, shouldHideCheckBox: Boolean) { - hostname.text = tab.hostname - tab_title.text = tab.title - tab_selected_checkbox.isInvisible = shouldHideCheckBox + binding.hostname.text = tab.hostname + binding.tabTitle.text = tab.title + binding.tabSelectedCheckbox.isInvisible = shouldHideCheckBox itemView.isClickable = !shouldHideCheckBox - if (tab_selected_checkbox.isChecked != isSelected) { - tab_selected_checkbox.isChecked = isSelected + if (binding.tabSelectedCheckbox.isChecked != isSelected) { + binding.tabSelectedCheckbox.isChecked = isSelected } - itemView.context.components.core.icons.loadIntoView(favicon_image, tab.url) + itemView.context.components.core.icons.loadIntoView(binding.faviconImage, tab.url) } companion object { diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationView.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationView.kt index 34db0f881f..ffeeb0e786 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationView.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationView.kt @@ -19,12 +19,12 @@ import androidx.transition.AutoTransition import androidx.transition.Transition import androidx.transition.TransitionManager import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.component_collection_creation.* import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.components.support.ktx.android.view.showKeyboard import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.databinding.ComponentCollectionCreationBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.home.Tab @@ -34,15 +34,19 @@ class CollectionCreationView( private val interactor: CollectionCreationInteractor ) : LayoutContainer { - override val containerView: View = LayoutInflater.from(container.context) - .inflate(R.layout.component_collection_creation, container, true) + private val binding = ComponentCollectionCreationBinding.inflate( + LayoutInflater.from(container.context), + container, + true + ) + override val containerView: View = binding.root private val bottomBarView = CollectionCreationBottomBarView( interactor = interactor, - layout = bottom_button_bar_layout, - iconButton = bottom_bar_icon_button, - textView = bottom_bar_text, - saveButton = save_button + layout = binding.bottomButtonBarLayout, + iconButton = binding.bottomBarIconButton, + textView = binding.bottomBarText, + saveButton = binding.saveButton ) private val collectionCreationTabListAdapter = CollectionCreationTabListAdapter(interactor) private val collectionSaveListAdapter = SaveCollectionListAdapter(interactor) @@ -58,10 +62,10 @@ class CollectionCreationView( init { transition.duration = TRANSITION_DURATION - transition.excludeTarget(back_button, true) + transition.excludeTarget(binding.backButton, true) - name_collection_edittext.filters += InputFilter.LengthFilter(COLLECTION_NAME_MAX_LENGTH) - name_collection_edittext.setOnEditorActionListener { view, actionId, _ -> + binding.nameCollectionEdittext.filters += InputFilter.LengthFilter(COLLECTION_NAME_MAX_LENGTH) + binding.nameCollectionEdittext.setOnEditorActionListener { view, actionId, _ -> val text = view.text.toString() if (actionId == EditorInfo.IME_ACTION_DONE && text.isNotBlank()) { when (step) { @@ -75,13 +79,13 @@ class CollectionCreationView( false } - tab_list.run { + binding.tabList.run { adapter = collectionCreationTabListAdapter itemAnimator = null layoutManager = LinearLayoutManager(containerView.context, RecyclerView.VERTICAL, true) } - collections_list.run { + binding.collectionsList.run { adapter = collectionSaveListAdapter layoutManager = LinearLayoutManager(containerView.context, RecyclerView.VERTICAL, true) } @@ -111,16 +115,16 @@ class CollectionCreationView( private fun updateForSelectTabs(state: CollectionCreationState) { containerView.context.components.analytics.metrics.track(Event.CollectionTabSelectOpened) - tab_list.isClickable = true + binding.tabList.isClickable = true - back_button.apply { + binding.backButton.apply { text = context.getString(R.string.create_collection_select_tabs) setOnClickListener { interactor.onBackPressed(SaveCollectionStep.SelectTabs) } } - select_all_button.apply { + binding.selectAllButton.apply { val allSelected = state.selectedTabs.size == state.tabs.size text = if (allSelected) context.getString(R.string.create_collection_deselect_all) @@ -136,39 +140,39 @@ class CollectionCreationView( R.layout.component_collection_creation ) collectionCreationTabListAdapter.updateData(state.tabs, state.selectedTabs) - selectTabsConstraints.applyTo(collection_constraint_layout) + selectTabsConstraints.applyTo(binding.collectionConstraintLayout) } private fun updateForSelectCollection() { - tab_list.isClickable = false + binding.tabList.isClickable = false selectCollectionConstraints.clone( containerView.context, R.layout.component_collection_creation_select_collection ) - selectCollectionConstraints.applyTo(collection_constraint_layout) + selectCollectionConstraints.applyTo(binding.collectionConstraintLayout) - back_button.apply { + binding.backButton.apply { text = context.getString(R.string.create_collection_select_collection) setOnClickListener { interactor.onBackPressed(SaveCollectionStep.SelectCollection) } } - TransitionManager.beginDelayedTransition(collection_constraint_layout, transition) + TransitionManager.beginDelayedTransition(binding.collectionConstraintLayout, transition) } private fun updateForNameCollection(state: CollectionCreationState) { - tab_list.isClickable = false + binding.tabList.isClickable = false nameCollectionConstraints.clone( containerView.context, R.layout.component_collection_creation_name_collection ) - nameCollectionConstraints.applyTo(collection_constraint_layout) + nameCollectionConstraints.applyTo(binding.collectionConstraintLayout) collectionCreationTabListAdapter.updateData(state.selectedTabs.toList(), state.selectedTabs, true) - back_button.apply { + binding.backButton.apply { text = context.getString(R.string.create_collection_name_collection) setOnClickListener { - name_collection_edittext.hideKeyboard() + binding.nameCollectionEdittext.hideKeyboard() val handler = Handler(Looper.getMainLooper()) handler.postDelayed( { @@ -179,19 +183,19 @@ class CollectionCreationView( } } - name_collection_edittext.showKeyboard() + binding.nameCollectionEdittext.showKeyboard() - name_collection_edittext.setText( + binding.nameCollectionEdittext.setText( containerView.context.getString( R.string.create_collection_default_name, state.defaultCollectionNumber ) ) - name_collection_edittext.setSelection(0, name_collection_edittext.text.length) + binding.nameCollectionEdittext.setSelection(0, binding.nameCollectionEdittext.text.length) } private fun updateForRenameCollection(state: CollectionCreationState) { - tab_list.isClickable = false + binding.tabList.isClickable = false state.selectedTabCollection?.let { tabCollection -> val publicSuffixList = containerView.context.components.publicSuffixList @@ -210,14 +214,14 @@ class CollectionCreationView( containerView.context, R.layout.component_collection_creation_name_collection ) - nameCollectionConstraints.applyTo(collection_constraint_layout) - name_collection_edittext.setText(state.selectedTabCollection?.title) - name_collection_edittext.setSelection(0, name_collection_edittext.text.length) + nameCollectionConstraints.applyTo(binding.collectionConstraintLayout) + binding.nameCollectionEdittext.setText(state.selectedTabCollection?.title) + binding.nameCollectionEdittext.setSelection(0, binding.nameCollectionEdittext.text.length) - back_button.apply { + binding.backButton.apply { text = context.getString(R.string.collection_rename) setOnClickListener { - name_collection_edittext.hideKeyboard() + binding.nameCollectionEdittext.hideKeyboard() val handler = Handler(Looper.getMainLooper()) handler.postDelayed( { @@ -231,7 +235,7 @@ class CollectionCreationView( override fun onTransitionStart(transition: Transition) { /* noop */ } override fun onTransitionEnd(transition: Transition) { - name_collection_edittext.showKeyboard() + binding.nameCollectionEdittext.showKeyboard() transition.removeListener(this) } @@ -239,12 +243,12 @@ class CollectionCreationView( override fun onTransitionPause(transition: Transition) { /* noop */ } override fun onTransitionResume(transition: Transition) { /* noop */ } }) - TransitionManager.beginDelayedTransition(collection_constraint_layout, transition) + TransitionManager.beginDelayedTransition(binding.collectionConstraintLayout, transition) } fun onResumed() { if (step == SaveCollectionStep.NameCollection || step == SaveCollectionStep.RenameCollection) { - name_collection_edittext.showKeyboard() + binding.nameCollectionEdittext.showKeyboard() } } diff --git a/app/src/main/java/org/mozilla/fenix/collections/SaveCollectionListAdapter.kt b/app/src/main/java/org/mozilla/fenix/collections/SaveCollectionListAdapter.kt index 0ee663b53d..c7dcc0c9a8 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/SaveCollectionListAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/SaveCollectionListAdapter.kt @@ -5,15 +5,14 @@ package org.mozilla.fenix.collections import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.core.graphics.BlendModeColorFilterCompat.createBlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat.SRC_IN import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.collections_list_item.* import mozilla.components.feature.tab.collections.TabCollection import org.mozilla.fenix.R import org.mozilla.fenix.components.description +import org.mozilla.fenix.databinding.CollectionsListItemBinding import org.mozilla.fenix.ext.getIconColor import org.mozilla.fenix.home.Tab import org.mozilla.fenix.utils.view.ViewHolder @@ -26,10 +25,13 @@ class SaveCollectionListAdapter( private var selectedTabs: Set = setOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CollectionViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(CollectionViewHolder.LAYOUT_ID, parent, false) + val binding = CollectionsListItemBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) - return CollectionViewHolder(view) + return CollectionViewHolder(binding) } override fun onBindViewHolder(holder: CollectionViewHolder, position: Int) { @@ -49,12 +51,12 @@ class SaveCollectionListAdapter( } } -class CollectionViewHolder(view: View) : ViewHolder(view) { +class CollectionViewHolder(private val binding: CollectionsListItemBinding) : ViewHolder(binding.root) { fun bind(collection: TabCollection) { - collection_item.text = collection.title - collection_description.text = collection.description(itemView.context) - collection_icon.colorFilter = + binding.collectionItem.text = collection.title + binding.collectionDescription.text = collection.description(itemView.context) + binding.collectionIcon.colorFilter = createBlendModeColorFilterCompat(collection.getIconColor(itemView.context), SRC_IN) } From 3112c977fc9265b6ab892214d28f593362348408 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Tue, 10 Aug 2021 16:56:43 +0000 Subject: [PATCH 003/517] Update Android Components version to 92.0.20210809190134. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 3831baa3e0..0523d5c21a 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "92.0.20210809143330" + const val VERSION = "92.0.20210809190134" } From 94cf03a1cac7b43194717ed41b346e86ec183120 Mon Sep 17 00:00:00 2001 From: "codrut.topliceanu" Date: Tue, 3 Aug 2021 16:28:22 +0300 Subject: [PATCH 004/517] For #17917: Use View binding in history screens --- .../fenix/library/history/HistoryAdapter.kt | 6 ++- .../fenix/library/history/HistoryFragment.kt | 12 +++-- .../fenix/library/history/HistoryView.kt | 42 ++++++++--------- .../viewholders/HistoryListItemViewHolder.kt | 46 +++++++++---------- app/src/main/res/layout/history_list_item.xml | 4 +- 5 files changed, 57 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryAdapter.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryAdapter.kt index 32b43b2210..7b111cda5a 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryAdapter.kt @@ -28,8 +28,9 @@ enum class HistoryItemTimeGroup { } } -class HistoryAdapter(private val historyInteractor: HistoryInteractor) : - PagedListAdapter(historyDiffCallback), +class HistoryAdapter( + private val historyInteractor: HistoryInteractor, +) : PagedListAdapter(historyDiffCallback), SelectionHolder { private var mode: HistoryFragmentState.Mode = HistoryFragmentState.Mode.Normal @@ -41,6 +42,7 @@ class HistoryAdapter(private val historyInteractor: HistoryInteractor) : override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HistoryListItemViewHolder { val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) + return HistoryListItemViewHolder(view, historyInteractor, this) } diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt index 4181a14c85..7cbba31ded 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt @@ -20,7 +20,6 @@ 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 @@ -43,6 +42,7 @@ 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.databinding.FragmentHistoryBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents @@ -62,13 +62,16 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandl private var _historyView: HistoryView? = null protected val historyView: HistoryView get() = _historyView!! + private var _binding: FragmentHistoryBinding? = null + private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - val view = inflater.inflate(R.layout.fragment_history, container, false) + ): View { + _binding = FragmentHistoryBinding.inflate(inflater, container, false) + val view = binding.root historyStore = StoreProvider.get(this) { HistoryFragmentStore( HistoryFragmentState( @@ -102,7 +105,7 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandl historyController ) _historyView = HistoryView( - view.historyLayout, + binding.historyLayout, historyInteractor ) @@ -254,6 +257,7 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandl override fun onDestroyView() { super.onDestroyView() _historyView = null + _binding = null } private fun openItem(item: HistoryItem) { diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt index b13f4a8133..ad4fdf8392 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt @@ -5,16 +5,13 @@ package org.mozilla.fenix.library.history import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.SimpleItemAnimator -import kotlinx.android.synthetic.main.component_history.* -import kotlinx.android.synthetic.main.component_history.view.* -import kotlinx.android.synthetic.main.recently_closed_nav_item.* import mozilla.components.support.base.feature.UserInteractionHandler import org.mozilla.fenix.R +import org.mozilla.fenix.databinding.ComponentHistoryBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.library.LibraryPageView import org.mozilla.fenix.selection.SelectionInteractor @@ -94,8 +91,9 @@ class HistoryView( val interactor: HistoryInteractor ) : LibraryPageView(container), UserInteractionHandler { - val view: View = LayoutInflater.from(container.context) - .inflate(R.layout.component_history, container, true) + val binding = ComponentHistoryBinding.inflate( + LayoutInflater.from(container.context), container, true + ) var mode: HistoryFragmentState.Mode = HistoryFragmentState.Mode.Normal private set @@ -104,7 +102,7 @@ class HistoryView( private val layoutManager = LinearLayoutManager(container.context) init { - view.history_list.apply { + binding.historyList.apply { layoutManager = this@HistoryView.layoutManager adapter = historyAdapter (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false @@ -112,19 +110,19 @@ class HistoryView( val primaryTextColor = ThemeManager.resolveAttribute(R.attr.primaryText, context) - view.swipe_refresh.setColorSchemeColors(primaryTextColor) - view.swipe_refresh.setOnRefreshListener { + binding.swipeRefresh.setColorSchemeColors(primaryTextColor) + binding.swipeRefresh.setOnRefreshListener { interactor.onRequestSync() - view.history_list.scrollToPosition(0) + binding.historyList.scrollToPosition(0) } } fun update(state: HistoryFragmentState) { val oldMode = mode - view.progress_bar.isVisible = state.isDeletingItems - view.swipe_refresh.isRefreshing = state.mode === HistoryFragmentState.Mode.Syncing - view.swipe_refresh.isEnabled = + binding.progressBar.isVisible = state.isDeletingItems + binding.swipeRefresh.isRefreshing = state.mode === HistoryFragmentState.Mode.Syncing + binding.swipeRefresh.isEnabled = state.mode === HistoryFragmentState.Mode.Normal || state.mode === HistoryFragmentState.Mode.Syncing mode = state.mode @@ -164,24 +162,24 @@ class HistoryView( } fun updateEmptyState(userHasHistory: Boolean) { - history_list.isVisible = userHasHistory - history_empty_view.isVisible = !userHasHistory - recently_closed_nav_empty.apply { - setOnClickListener { + binding.historyList.isVisible = userHasHistory + binding.historyEmptyView.isVisible = !userHasHistory + with(binding.recentlyClosedNavEmpty) { + recentlyClosedNav.setOnClickListener { interactor.onRecentlyClosedClicked() } - val numRecentTabs = view.context.components.core.store.state.closedTabs.size - recently_closed_tabs_description.text = String.format( - view.context.getString( + val numRecentTabs = recentlyClosedNav.context.components.core.store.state.closedTabs.size + recentlyClosedTabsDescription.text = String.format( + context.getString( if (numRecentTabs == 1) R.string.recently_closed_tab else R.string.recently_closed_tabs ), numRecentTabs ) - isVisible = !userHasHistory + recentlyClosedNav.isVisible = !userHasHistory } if (!userHasHistory) { - history_empty_view.announceForAccessibility(context.getString(R.string.history_empty_message)) + binding.historyEmptyView.announceForAccessibility(context.getString(R.string.history_empty_message)) } } diff --git a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt index e7810ac1bb..9de385d57b 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt @@ -5,13 +5,10 @@ package org.mozilla.fenix.library.history.viewholders import android.view.View -import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.history_list_item.view.* -import kotlinx.android.synthetic.main.library_site_item.view.* -import kotlinx.android.synthetic.main.recently_closed_nav_item.view.* import org.mozilla.fenix.R +import org.mozilla.fenix.databinding.HistoryListItemBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.hideAndDisable import org.mozilla.fenix.ext.showAndEnable @@ -30,11 +27,12 @@ class HistoryListItemViewHolder( ) : RecyclerView.ViewHolder(view) { private var item: HistoryItem? = null + private val binding = HistoryListItemBinding.bind(view) init { setupMenu() - itemView.delete_button.setOnClickListener { + binding.deleteButton.setOnClickListener { val selected = selectionHolder.selectedItems if (selected.isEmpty()) { historyInteractor.onDeleteAll() @@ -43,7 +41,7 @@ class HistoryListItemViewHolder( } } - itemView.findViewById(R.id.recently_closed_nav).setOnClickListener { + binding.recentlyClosedNavEmpty.recentlyClosedNav.setOnClickListener { historyInteractor.onRecentlyClosedClicked() } } @@ -56,30 +54,30 @@ class HistoryListItemViewHolder( isPendingDeletion: Boolean = false ) { if (isPendingDeletion) { - itemView.history_layout.visibility = View.GONE + binding.historyLayout.visibility = View.GONE } else { - itemView.history_layout.visibility = View.VISIBLE + binding.historyLayout.visibility = View.VISIBLE } - itemView.history_layout.titleView.text = item.title - itemView.history_layout.urlView.text = item.url + binding.historyLayout.titleView.text = item.title + binding.historyLayout.urlView.text = item.url toggleTopContent(showDeleteButton, mode === HistoryFragmentState.Mode.Normal) val headerText = timeGroup?.humanReadable(itemView.context) toggleHeader(headerText) - itemView.history_layout.setSelectionInteractor(item, selectionHolder, historyInteractor) - itemView.history_layout.changeSelected(item in selectionHolder.selectedItems) + binding.historyLayout.setSelectionInteractor(item, selectionHolder, historyInteractor) + binding.historyLayout.changeSelected(item in selectionHolder.selectedItems) if (this.item?.url != item.url) { - itemView.history_layout.loadFavicon(item.url) + binding.historyLayout.loadFavicon(item.url) } if (mode is HistoryFragmentState.Mode.Editing) { - itemView.overflow_menu.hideAndDisable() + binding.historyLayout.overflowView.hideAndDisable() } else { - itemView.overflow_menu.showAndEnable() + binding.historyLayout.overflowView.showAndEnable() } this.item = item @@ -87,10 +85,10 @@ class HistoryListItemViewHolder( private fun toggleHeader(headerText: String?) { if (headerText != null) { - itemView.header_title.visibility = View.VISIBLE - itemView.header_title.text = headerText + binding.headerTitle.visibility = View.VISIBLE + binding.headerTitle.text = headerText } else { - itemView.header_title.visibility = View.GONE + binding.headerTitle.visibility = View.GONE } } @@ -98,11 +96,11 @@ class HistoryListItemViewHolder( showTopContent: Boolean, isNormalMode: Boolean ) { - itemView.delete_button.isVisible = showTopContent - itemView.findViewById(R.id.recently_closed_nav).isVisible = showTopContent + binding.deleteButton.isVisible = showTopContent + binding.recentlyClosedNavEmpty.recentlyClosedNav.isVisible = showTopContent if (showTopContent) { - itemView.delete_button.run { + binding.deleteButton.run { if (isNormalMode) { isEnabled = true alpha = 1f @@ -112,14 +110,14 @@ class HistoryListItemViewHolder( } } val numRecentTabs = itemView.context.components.core.store.state.closedTabs.size - itemView.recently_closed_tabs_description.text = String.format( + binding.recentlyClosedNavEmpty.recentlyClosedTabsDescription.text = String.format( itemView.context.getString( if (numRecentTabs == 1) R.string.recently_closed_tab else R.string.recently_closed_tabs ), numRecentTabs ) - itemView.findViewById(R.id.recently_closed_nav).run { + binding.recentlyClosedNavEmpty.recentlyClosedNav.run { if (isNormalMode) { isEnabled = true alpha = 1f @@ -143,7 +141,7 @@ class HistoryListItemViewHolder( } } - itemView.history_layout.attachMenu(historyMenu.menuController) + binding.historyLayout.attachMenu(historyMenu.menuController) } companion object { diff --git a/app/src/main/res/layout/history_list_item.xml b/app/src/main/res/layout/history_list_item.xml index e0dcfd4919..bc8bac5300 100644 --- a/app/src/main/res/layout/history_list_item.xml +++ b/app/src/main/res/layout/history_list_item.xml @@ -18,7 +18,9 @@ android:visibility="gone" tools:visibility="visible" /> - + Date: Wed, 11 Aug 2021 11:32:43 -0400 Subject: [PATCH 005/517] For #20672 crash when attempting to disconnect from Mozilla account --- .../java/org/mozilla/fenix/settings/account/SignOutFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/settings/account/SignOutFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/account/SignOutFragment.kt index 100e9a7b2c..7039c74058 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/account/SignOutFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/account/SignOutFragment.kt @@ -57,7 +57,7 @@ class SignOutFragment : AppCompatDialogFragment() { ), binding.root.context.getString(R.string.app_name) ) - return view + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { From 71f1f6b88b4132b5d2d7d182d486a0d5f2688460 Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Wed, 11 Aug 2021 14:09:18 -0400 Subject: [PATCH 006/517] Disable intermittent tests. --- .../TrackingProtectionExceptionsInteractorTest.kt | 3 ++- app/src/test/java/org/mozilla/fenix/home/HomeFragmentTest.kt | 2 ++ .../fenix/search/awesomebar/ShortcutsSuggestionProviderTest.kt | 2 ++ .../settings/quicksettings/QuickSettingsFragmentReducerTest.kt | 2 ++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/test/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsInteractorTest.kt index a37c3c888d..a4a7747287 100644 --- a/app/src/test/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsInteractorTest.kt @@ -13,6 +13,7 @@ import io.mockk.verifySequence import mozilla.components.concept.engine.content.blocking.TrackingProtectionException import mozilla.components.feature.session.TrackingProtectionUseCases import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity @@ -56,7 +57,7 @@ class TrackingProtectionExceptionsInteractorTest { ) } } - + @Ignore("See https://github.com/mozilla-mobile/fenix/issues/20796") @Test fun onDeleteAll() { interactor.onDeleteAll() diff --git a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentTest.kt b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentTest.kt index bca3e73528..8a99d0bcfe 100644 --- a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentTest.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import mozilla.components.browser.menu.view.MenuButton import org.junit.Assert import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.mozilla.fenix.ext.settings import org.mozilla.fenix.utils.Settings @@ -45,6 +46,7 @@ class HomeFragmentTest { Assert.assertNull(topSitesConfig.frecencyConfig) } + @Ignore("See https://github.com/mozilla-mobile/fenix/issues/20795") @Test fun `GIVEN showTopFrecentSites is true WHEN getTopSitesConfig is called THEN it returns TopSitesConfig with non-null frecencyConfig`() { every { context.settings().showTopFrecentSites } returns true diff --git a/app/src/test/java/org/mozilla/fenix/search/awesomebar/ShortcutsSuggestionProviderTest.kt b/app/src/test/java/org/mozilla/fenix/search/awesomebar/ShortcutsSuggestionProviderTest.kt index 571fe0cb60..b57ed541be 100644 --- a/app/src/test/java/org/mozilla/fenix/search/awesomebar/ShortcutsSuggestionProviderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/search/awesomebar/ShortcutsSuggestionProviderTest.kt @@ -20,6 +20,7 @@ import mozilla.components.browser.state.store.BrowserStore import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.mozilla.fenix.R @@ -82,6 +83,7 @@ class ShortcutsSuggestionProviderTest { assertEquals("Search engine settings", suggestions[2].title) } + @Ignore("See https://github.com/mozilla-mobile/fenix/issues/20797") @Test fun `callbacks are triggered when suggestions are clicked`() = runBlockingTest { val engineOne = mockk(relaxed = true) diff --git a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt index 95963693fc..14e4cfc525 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt @@ -7,11 +7,13 @@ package org.mozilla.fenix.settings.quicksettings import mozilla.components.support.test.mock import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue +import org.junit.Ignore import org.junit.Test import org.mozilla.fenix.settings.PhoneFeature class QuickSettingsFragmentReducerTest { + @Ignore("See https://github.com/mozilla-mobile/fenix/issues/20792") @Test fun `WebsitePermissionAction - TogglePermission`() { val toggleablePermission = WebsitePermission.Toggleable( From 463728e007142a285046e644e35241539ea6d381 Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Mon, 9 Aug 2021 14:43:44 -0400 Subject: [PATCH 007/517] For #20764 add screen for opting out of experiments --- .../org/mozilla/fenix/BrowserDirection.kt | 1 + .../java/org/mozilla/fenix/HomeActivity.kt | 4 + .../fenix/settings/DataChoicesFragment.kt | 25 ++- .../mozilla/fenix/settings/SupportUtils.kt | 1 + .../settings/studies/CustomViewHolder.kt | 34 ++++ .../fenix/settings/studies/StudiesAdapter.kt | 164 ++++++++++++++++++ .../studies/StudiesAdapterDelegate.kt | 19 ++ .../fenix/settings/studies/StudiesFragment.kt | 54 ++++++ .../settings/studies/StudiesInteractor.kt | 39 +++++ .../fenix/settings/studies/StudiesView.kt | 132 ++++++++++++++ app/src/main/res/layout/settings_studies.xml | 61 +++++++ .../main/res/layout/studies_section_item.xml | 34 ++++ app/src/main/res/layout/study_item.xml | 54 ++++++ app/src/main/res/navigation/nav_graph.xml | 15 +- app/src/main/res/values/preference_keys.xml | 1 + app/src/main/res/values/strings.xml | 12 ++ .../main/res/xml/data_choices_preferences.xml | 6 +- .../studies/DefaultStudiesInteractorTest.kt | 58 +++++++ .../settings/studies/StudiesAdapterTest.kt | 134 ++++++++++++++ .../fenix/settings/studies/StudiesViewTest.kt | 118 +++++++++++++ 20 files changed, 956 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/settings/studies/CustomViewHolder.kt create mode 100644 app/src/main/java/org/mozilla/fenix/settings/studies/StudiesAdapter.kt create mode 100644 app/src/main/java/org/mozilla/fenix/settings/studies/StudiesAdapterDelegate.kt create mode 100644 app/src/main/java/org/mozilla/fenix/settings/studies/StudiesFragment.kt create mode 100644 app/src/main/java/org/mozilla/fenix/settings/studies/StudiesInteractor.kt create mode 100644 app/src/main/java/org/mozilla/fenix/settings/studies/StudiesView.kt create mode 100644 app/src/main/res/layout/settings_studies.xml create mode 100644 app/src/main/res/layout/studies_section_item.xml create mode 100644 app/src/main/res/layout/study_item.xml create mode 100644 app/src/test/java/org/mozilla/fenix/settings/studies/DefaultStudiesInteractorTest.kt create mode 100644 app/src/test/java/org/mozilla/fenix/settings/studies/StudiesAdapterTest.kt create mode 100644 app/src/test/java/org/mozilla/fenix/settings/studies/StudiesViewTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt b/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt index 113001daaa..bd50ba9fd1 100644 --- a/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt +++ b/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt @@ -28,6 +28,7 @@ enum class BrowserDirection(@IdRes val fragmentId: Int) { FromAddSearchEngineFragment(R.id.addSearchEngineFragment), FromEditCustomSearchEngineFragment(R.id.editCustomSearchEngineFragment), FromAddonDetailsFragment(R.id.addonDetailsFragment), + FromStudiesFragment(R.id.studiesFragment), FromAddonPermissionsDetailsFragment(R.id.addonPermissionsDetailFragment), FromLoginDetailFragment(R.id.loginDetailFragment), FromTabsTray(R.id.tabsTrayFragment), diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index af01860f0d..019ee6cfcc 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -111,6 +111,7 @@ import org.mozilla.fenix.settings.logins.fragment.LoginDetailFragmentDirections import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections import org.mozilla.fenix.settings.search.AddSearchEngineFragmentDirections import org.mozilla.fenix.settings.search.EditCustomSearchEngineFragmentDirections +import org.mozilla.fenix.settings.studies.StudiesFragmentDirections import org.mozilla.fenix.share.AddNewDeviceFragmentDirections import org.mozilla.fenix.tabstray.TabsTrayFragment import org.mozilla.fenix.tabstray.TabsTrayFragmentDirections @@ -764,6 +765,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { TabsTrayFragmentDirections.actionGlobalBrowser(customTabSessionId) BrowserDirection.FromRecentlyClosed -> RecentlyClosedFragmentDirections.actionGlobalBrowser(customTabSessionId) + BrowserDirection.FromStudiesFragment -> StudiesFragmentDirections.actionGlobalBrowser( + customTabSessionId + ) } /** diff --git a/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt index ceb6b2ab6a..6aa832f475 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/DataChoicesFragment.kt @@ -5,12 +5,15 @@ package org.mozilla.fenix.settings import android.os.Bundle +import androidx.navigation.findNavController +import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.MetricServiceType import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getPreferenceKey +import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar @@ -40,9 +43,6 @@ class DataChoicesFragment : PreferenceFragmentCompat() { } else { context.components.analytics.metrics.stop(MetricServiceType.Marketing) } - } else if (key == getPreferenceKey(R.string.pref_key_experimentation)) { - val enabled = context.settings().isExperimentationEnabled - context.components.analytics.experiments.globalUserParticipation = enabled } } } @@ -50,6 +50,7 @@ class DataChoicesFragment : PreferenceFragmentCompat() { override fun onResume() { super.onResume() showToolbar(getString(R.string.preferences_data_collection)) + updateStudiesSection() } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { @@ -68,10 +69,22 @@ class DataChoicesFragment : PreferenceFragmentCompat() { isChecked = context.settings().isMarketingTelemetryEnabled onPreferenceChangeListener = SharedPreferenceUpdater() } + } - requirePreference(R.string.pref_key_experimentation).apply { - isChecked = context.settings().isExperimentationEnabled - onPreferenceChangeListener = SharedPreferenceUpdater() + private fun updateStudiesSection() { + val studiesPreference = requirePreference(R.string.pref_key_studies_section) + val settings = requireContext().settings() + val stringId = if (settings.isExperimentationEnabled) { + R.string.studies_on + } else { + R.string.studies_off + } + studiesPreference.summary = getString(stringId) + + studiesPreference.setOnPreferenceClickListener { + val action = DataChoicesFragmentDirections.actionDataChoicesFragmentToStudiesFragment() + view?.findNavController()?.nav(R.id.dataChoicesFragment, action) + true } } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt index 6f510a30a9..b0ca33f43f 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt @@ -44,6 +44,7 @@ object SupportUtils { YOUR_RIGHTS("your-rights"), TRACKING_PROTECTION("tracking-protection-firefox-android"), WHATS_NEW("whats-new-firefox-preview"), + OPT_OUT_STUDIES("how-opt-out-studies-firefox-android"), SEND_TABS("send-tab-preview"), SET_AS_DEFAULT_BROWSER("set-firefox-preview-default"), SEARCH_SUGGESTION("how-search-firefox-preview"), diff --git a/app/src/main/java/org/mozilla/fenix/settings/studies/CustomViewHolder.kt b/app/src/main/java/org/mozilla/fenix/settings/studies/CustomViewHolder.kt new file mode 100644 index 0000000000..6c7a2b00a4 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/studies/CustomViewHolder.kt @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.studies + +import android.view.View +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.button.MaterialButton + +/** + * A base view holder for Studies. + */ +sealed class CustomViewHolder(view: View) : RecyclerView.ViewHolder(view) { + /** + * A view holder for displaying section items. + */ + class SectionViewHolder( + view: View, + val titleView: TextView, + val divider: View + ) : CustomViewHolder(view) + + /** + * A view holder for displaying study items. + */ + class StudyViewHolder( + view: View, + val titleView: TextView, + val summaryView: TextView, + val deleteButton: MaterialButton, + ) : CustomViewHolder(view) +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesAdapter.kt b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesAdapter.kt new file mode 100644 index 0000000000..5fe74443d9 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesAdapter.kt @@ -0,0 +1,164 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.studies + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.annotation.StringRes +import androidx.annotation.VisibleForTesting +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import com.google.android.material.button.MaterialButton +import org.mozilla.experiments.nimbus.internal.EnrolledExperiment +import org.mozilla.fenix.R +import org.mozilla.fenix.settings.studies.CustomViewHolder.SectionViewHolder +import org.mozilla.fenix.settings.studies.CustomViewHolder.StudyViewHolder + +private const val VIEW_HOLDER_TYPE_SECTION = 0 +private const val VIEW_HOLDER_TYPE_STUDY = 1 + +/** + * An adapter for displaying studies items. This will display information related to the state of + * a study such as active. In addition, it will perform actions such as removing a study. + * + * @property studiesDelegate Delegate that will provides method for handling + * the studies actions items. + * @param studies The list of studies. + * * @property studiesDelegate Delegate that will provides method for handling + * the studies actions items. + * @param shouldSubmitOnInit The sole purpose of this property is to prevent the submitList function + * to run on init, it should only be used from tests. + */ +@Suppress("LargeClass") +class StudiesAdapter( + private val studiesDelegate: StudiesAdapterDelegate, + studies: List, + @VisibleForTesting + internal val shouldSubmitOnInit: Boolean = true +) : ListAdapter(DifferCallback) { + /** + * Represents all the studies that will be distributed in multiple headers like + * active, and completed, this helps to have the data source of the items, + * displayed in the UI. + */ + @VisibleForTesting + internal var studiesMap: MutableMap = + studies.associateBy({ it.slug }, { it }).toMutableMap() + + init { + if (shouldSubmitOnInit) { + submitList(createListWithSections(studies)) + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder { + return when (viewType) { + VIEW_HOLDER_TYPE_STUDY -> createStudiesViewHolder(parent) + VIEW_HOLDER_TYPE_SECTION -> createSectionViewHolder(parent) + else -> throw IllegalArgumentException("Unrecognized viewType") + } + } + + private fun createSectionViewHolder(parent: ViewGroup): CustomViewHolder { + val context = parent.context + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.studies_section_item, parent, false) + val titleView = view.findViewById(R.id.title) + val divider = view.findViewById(R.id.divider) + return SectionViewHolder(view, titleView, divider) + } + + private fun createStudiesViewHolder(parent: ViewGroup): StudyViewHolder { + val context = parent.context + val view = LayoutInflater.from(context).inflate(R.layout.study_item, parent, false) + val titleView = view.findViewById(R.id.studyTitle) + val summaryView = view.findViewById(R.id.study_description) + val removeButton = view.findViewById(R.id.remove_button) + return StudyViewHolder( + view, + titleView, + summaryView, + removeButton + ) + } + + override fun getItemViewType(position: Int): Int { + return when (getItem(position)) { + is EnrolledExperiment -> VIEW_HOLDER_TYPE_STUDY + is Section -> VIEW_HOLDER_TYPE_SECTION + else -> throw IllegalArgumentException("items[position] has unrecognized type") + } + } + + override fun onBindViewHolder(holder: CustomViewHolder, position: Int) { + val item = getItem(position) + + when (holder) { + is SectionViewHolder -> bindSection(holder, item as Section) + is StudyViewHolder -> bindStudy(holder, item as EnrolledExperiment) + } + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun bindSection(holder: SectionViewHolder, section: Section) { + holder.titleView.setText(section.title) + holder.divider.isVisible = section.visibleDivider + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun bindStudy(holder: StudyViewHolder, study: EnrolledExperiment) { + holder.titleView.text = study.userFacingName + holder.summaryView.text = study.userFacingDescription + + holder.deleteButton.setOnClickListener { + studiesDelegate.onRemoveButtonClicked(study) + } + } + + internal fun createListWithSections(studies: List): List { + val itemsWithSections = ArrayList() + val activeStudies = ArrayList() + + activeStudies.addAll(studies) + + if (activeStudies.isNotEmpty()) { + itemsWithSections.add(Section(R.string.studies_active, true)) + itemsWithSections.addAll(activeStudies) + } + + return itemsWithSections + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal data class Section(@StringRes val title: Int, val visibleDivider: Boolean = true) + + /** + * Removes the portion of the list that contains the provided [study]. + * @property study The study to be removed. + */ + fun removeStudy(study: EnrolledExperiment) { + studiesMap.remove(study.slug) + submitList(createListWithSections(studiesMap.values.toList())) + } + + internal object DifferCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Any, newItem: Any): Boolean { + return when { + oldItem is EnrolledExperiment && newItem is EnrolledExperiment -> oldItem.slug == newItem.slug + oldItem is Section && newItem is Section -> oldItem.title == newItem.title + else -> false + } + } + + @SuppressLint("DiffUtilEquals") + override fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean { + return oldItem == newItem + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesAdapterDelegate.kt b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesAdapterDelegate.kt new file mode 100644 index 0000000000..86ef6a4948 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesAdapterDelegate.kt @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.studies + +import org.mozilla.experiments.nimbus.internal.EnrolledExperiment + +/** + * Provides methods for handling the studies items. + */ +interface StudiesAdapterDelegate { + /** + * Handler for when the remove button is clicked. + * + * @param experiment The [EnrolledExperiment] to remove. + */ + fun onRemoveButtonClicked(experiment: EnrolledExperiment) = Unit +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesFragment.kt new file mode 100644 index 0000000000..f45c0344b3 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesFragment.kt @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.studies + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.databinding.SettingsStudiesBinding +import org.mozilla.fenix.ext.requireComponents +import org.mozilla.fenix.ext.settings + +/** + * Lets the users control studies settings. + */ +class StudiesFragment : Fragment() { + private var _binding: SettingsStudiesBinding? = null + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val experiments = requireComponents.analytics.experiments + _binding = SettingsStudiesBinding.inflate(inflater, container, false) + val interactor = DefaultStudiesInteractor((activity as HomeActivity), experiments) + StudiesView( + lifecycleScope, + requireContext(), + binding, + interactor, + requireContext().settings(), + experiments, + ::isAttached + ).bind() + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + private fun isAttached(): Boolean = context != null +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesInteractor.kt b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesInteractor.kt new file mode 100644 index 0000000000..c859f4875d --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesInteractor.kt @@ -0,0 +1,39 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.studies + +import mozilla.components.service.nimbus.NimbusApi +import org.mozilla.experiments.nimbus.internal.EnrolledExperiment +import org.mozilla.fenix.BrowserDirection +import org.mozilla.fenix.HomeActivity + +interface StudiesInteractor { + /** + * Open the given [url] in the browser. + */ + fun openWebsite(url: String) + + /** + * Remove a study by the given [experiment]. + */ + fun removeStudy(experiment: EnrolledExperiment) +} + +class DefaultStudiesInteractor( + private val homeActivity: HomeActivity, + private val experiments: NimbusApi, +) : StudiesInteractor { + override fun openWebsite(url: String) { + homeActivity.openToBrowserAndLoad( + searchTermOrURL = url, + newTab = true, + from = BrowserDirection.FromStudiesFragment + ) + } + + override fun removeStudy(experiment: EnrolledExperiment) { + experiments.optOut(experiment.slug) + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesView.kt b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesView.kt new file mode 100644 index 0000000000..92723d7340 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesView.kt @@ -0,0 +1,132 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.studies + +import android.content.Context +import android.text.SpannableStringBuilder +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.text.style.URLSpan +import android.view.View +import android.widget.TextView +import androidx.annotation.VisibleForTesting +import androidx.appcompat.widget.SwitchCompat +import androidx.core.text.HtmlCompat +import androidx.core.text.getSpans +import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import mozilla.components.service.nimbus.NimbusApi +import mozilla.components.support.base.log.logger.Logger +import org.mozilla.experiments.nimbus.internal.EnrolledExperiment +import org.mozilla.fenix.R +import org.mozilla.fenix.databinding.SettingsStudiesBinding +import org.mozilla.fenix.settings.SupportUtils +import org.mozilla.fenix.settings.SupportUtils.SumoTopic.OPT_OUT_STUDIES +import org.mozilla.fenix.utils.Settings + +@Suppress("LongParameterList") +class StudiesView( + private val scope: CoroutineScope, + private val context: Context, + private val binding: SettingsStudiesBinding, + private val interactor: StudiesInteractor, + private val settings: Settings, + private val experiments: NimbusApi, + private val isAttached: () -> Boolean +) : StudiesAdapterDelegate { + private val logger = Logger("StudiesView") + + @VisibleForTesting + internal lateinit var adapter: StudiesAdapter + + @Suppress("TooGenericExceptionCaught") + fun bind() { + provideStudiesTitle().text = getSwitchTitle() + provideStudiesSwitch().isChecked = settings.isExperimentationEnabled + provideStudiesSwitch().setOnCheckedChangeListener { _, isChecked -> + settings.isExperimentationEnabled = isChecked + experiments.globalUserParticipation = isChecked + provideStudiesTitle().text = getSwitchTitle() + } + bindDescription() + + scope.launch(Dispatchers.IO) { + try { + val experiments = experiments.getActiveExperiments() + scope.launch(Dispatchers.Main) { + if (isAttached()) { + adapter = StudiesAdapter( + this@StudiesView, + experiments + ) + provideStudiesList().adapter = adapter + } + } + } catch (e: Throwable) { + logger.error("Failed to getActiveExperiments()", e) + } + } + } + + override fun onRemoveButtonClicked(experiment: EnrolledExperiment) { + interactor.removeStudy(experiment) + adapter.removeStudy(experiment) + } + + @VisibleForTesting + internal fun bindDescription() { + val sumoUrl = SupportUtils.getSumoURLForTopic(context, OPT_OUT_STUDIES) + val rawText = + context.getString(R.string.studies_description, sumoUrl) + val text = HtmlCompat.fromHtml(rawText, HtmlCompat.FROM_HTML_MODE_COMPACT) + + val spannableStringBuilder = SpannableStringBuilder(text) + val links = spannableStringBuilder.getSpans() + for (link in links) { + addActionToLinks(spannableStringBuilder, link) + } + binding.studiesDescription.text = spannableStringBuilder + binding.studiesDescription.movementMethod = LinkMovementMethod.getInstance() + } + + private fun addActionToLinks( + spannableStringBuilder: SpannableStringBuilder, + link: URLSpan + ) { + val start = spannableStringBuilder.getSpanStart(link) + val end = spannableStringBuilder.getSpanEnd(link) + val flags = spannableStringBuilder.getSpanFlags(link) + val clickable: ClickableSpan = object : ClickableSpan() { + override fun onClick(view: View) { + view.setOnClickListener { + interactor.openWebsite(link.url) + } + } + } + spannableStringBuilder.setSpan(clickable, start, end, flags) + spannableStringBuilder.removeSpan(link) + } + + @VisibleForTesting + internal fun getSwitchTitle(): String { + val stringId = if (settings.isExperimentationEnabled) { + R.string.studies_on + } else { + R.string.studies_off + } + return context.getString(stringId) + } + + @VisibleForTesting + internal fun provideStudiesTitle(): TextView = binding.studiesTitle + + @VisibleForTesting + internal fun provideStudiesSwitch(): SwitchCompat = binding.studiesSwitch + + @VisibleForTesting + internal fun provideStudiesList(): RecyclerView = binding.studiesList +} diff --git a/app/src/main/res/layout/settings_studies.xml b/app/src/main/res/layout/settings_studies.xml new file mode 100644 index 0000000000..b978849226 --- /dev/null +++ b/app/src/main/res/layout/settings_studies.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/studies_section_item.xml b/app/src/main/res/layout/studies_section_item.xml new file mode 100644 index 0000000000..ef2b6d2de7 --- /dev/null +++ b/app/src/main/res/layout/studies_section_item.xml @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/study_item.xml b/app/src/main/res/layout/study_item.xml new file mode 100644 index 0000000000..5cff292788 --- /dev/null +++ b/app/src/main/res/layout/study_item.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 71b28b4a1f..d780417aa4 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -553,7 +553,20 @@ + android:label="@string/preferences_data_choices"> + + + pref_key_private_browsing pref_key_leakcanary pref_key_remote_debugging + pref_key_studies_section pref_key_experimentation pref_key_showed_private_mode_cfr pref_key_private_mode_opened diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ac4ef65cc8..29204116f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -614,6 +614,14 @@ Close after one month + + + Remove + + Active + + Learn more]]> + Open tabs @@ -929,6 +937,10 @@ On Off + + On + + Off diff --git a/app/src/main/res/xml/data_choices_preferences.xml b/app/src/main/res/xml/data_choices_preferences.xml index d2dcc9d061..2aa2e39091 100644 --- a/app/src/main/res/xml/data_choices_preferences.xml +++ b/app/src/main/res/xml/data_choices_preferences.xml @@ -11,8 +11,8 @@ android:key="@string/pref_key_marketing_telemetry" android:summary="@string/preferences_marketing_data_description2" android:title="@string/preferences_marketing_data" /> - diff --git a/app/src/test/java/org/mozilla/fenix/settings/studies/DefaultStudiesInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/settings/studies/DefaultStudiesInteractorTest.kt new file mode 100644 index 0000000000..5384e80255 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/studies/DefaultStudiesInteractorTest.kt @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.studies + +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import mozilla.components.service.nimbus.NimbusApi +import org.junit.Before +import org.junit.Test +import org.mozilla.experiments.nimbus.internal.EnrolledExperiment +import org.mozilla.fenix.BrowserDirection +import org.mozilla.fenix.HomeActivity + +@ExperimentalCoroutinesApi +class DefaultStudiesInteractorTest { + @RelaxedMockK + private lateinit var activity: HomeActivity + + @RelaxedMockK + private lateinit var experiments: NimbusApi + + private lateinit var interactor: DefaultStudiesInteractor + + @Before + fun setup() { + MockKAnnotations.init(this) + interactor = DefaultStudiesInteractor(activity, experiments) + } + + @Test + fun `WHEN calling openWebsite THEN delegate to the homeActivity`() { + val url = "" + interactor.openWebsite(url) + + verify { + activity.openToBrowserAndLoad(url, true, BrowserDirection.FromStudiesFragment) + } + } + + @Test + fun `WHEN calling removeStudy THEN delegate to the NimbusApi`() { + val experiment = mockk(relaxed = true) + + every { experiment.slug } returns "slug" + + interactor.removeStudy(experiment) + + verify { + experiments.optOut("slug") + } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/settings/studies/StudiesAdapterTest.kt b/app/src/test/java/org/mozilla/fenix/settings/studies/StudiesAdapterTest.kt new file mode 100644 index 0000000000..33b190cb71 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/studies/StudiesAdapterTest.kt @@ -0,0 +1,134 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.studies + +import android.view.View +import android.widget.TextView +import androidx.core.view.isVisible +import com.google.android.material.button.MaterialButton +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.just +import io.mockk.mockk +import io.mockk.runs +import io.mockk.spyk +import io.mockk.verify +import junit.framework.TestCase.assertTrue +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import mozilla.components.support.test.robolectric.testContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.experiments.nimbus.internal.EnrolledExperiment +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.settings.studies.CustomViewHolder.SectionViewHolder +import org.mozilla.fenix.settings.studies.CustomViewHolder.StudyViewHolder +import org.mozilla.fenix.settings.studies.StudiesAdapter.Section + +@ExperimentalCoroutinesApi +@RunWith(FenixRobolectricTestRunner::class) +class StudiesAdapterTest { + @RelaxedMockK + private lateinit var delegate: StudiesAdapterDelegate + + private lateinit var adapter: StudiesAdapter + private lateinit var studies: List + + @Before + fun setup() { + MockKAnnotations.init(this) + studies = emptyList() + adapter = spyk(StudiesAdapter(delegate, studies, false)) + } + + @Test + fun `WHEN bindSection THEN bind the section information`() { + val holder = mockk() + val section = Section(R.string.studies_active, true) + val titleView = mockk(relaxed = true) + val divider = mockk(relaxed = true) + + every { holder.titleView } returns titleView + every { holder.divider } returns divider + + adapter.bindSection(holder, section) + + verify { + titleView.setText(section.title) + divider.isVisible = section.visibleDivider + } + } + + @Test + fun `WHEN bindStudy THEN bind the study information`() { + val holder = mockk() + val study = mockk() + val titleView = mockk(relaxed = true) + val summaryView = mockk(relaxed = true) + val deleteButton = spyk(MaterialButton(testContext)) + + every { study.slug } returns "slug" + every { study.userFacingName } returns "userFacingName" + every { study.userFacingDescription } returns "userFacingDescription" + every { holder.titleView } returns titleView + every { holder.summaryView } returns summaryView + every { holder.deleteButton } returns deleteButton + + adapter = spyk(StudiesAdapter(delegate, listOf(study), false)) + + adapter.bindStudy(holder, study) + + verify { + titleView.text = any() + summaryView.text = any() + } + + deleteButton.performClick() + + verify { + delegate.onRemoveButtonClicked(study) + } + } + + @Test + fun `WHEN removeStudy THEN the study should be removed`() { + val study = mockk() + + every { study.slug } returns "slug" + + adapter = spyk(StudiesAdapter(delegate, listOf(study), false)) + + every { adapter.submitList(any()) } just runs + + assertFalse(adapter.studiesMap.isEmpty()) + + adapter.removeStudy(study) + + assertTrue(adapter.studiesMap.isEmpty()) + + verify { + adapter.submitList(any()) + } + } + + @Test + fun `WHEN calling createListWithSections THEN returns the section + experiments`() { + val study = mockk() + + every { study.slug } returns "slug" + + adapter = spyk(StudiesAdapter(delegate, listOf(study), false)) + + val list = adapter.createListWithSections(listOf(study)) + + assertEquals(2, list.size) + assertTrue(list[0] is Section) + assertTrue(list[1] is EnrolledExperiment) + } +} diff --git a/app/src/test/java/org/mozilla/fenix/settings/studies/StudiesViewTest.kt b/app/src/test/java/org/mozilla/fenix/settings/studies/StudiesViewTest.kt new file mode 100644 index 0000000000..9deca91567 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/studies/StudiesViewTest.kt @@ -0,0 +1,118 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.studies + +import android.widget.TextView +import androidx.appcompat.widget.SwitchCompat +import androidx.recyclerview.widget.RecyclerView +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.just +import io.mockk.mockk +import io.mockk.runs +import io.mockk.spyk +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.TestCoroutineScope +import mozilla.components.service.nimbus.NimbusApi +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.support.test.rule.MainCoroutineRule +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.experiments.nimbus.internal.EnrolledExperiment +import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.databinding.SettingsStudiesBinding +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.utils.Settings + +@ExperimentalCoroutinesApi +@RunWith(FenixRobolectricTestRunner::class) +class StudiesViewTest { + @RelaxedMockK + private lateinit var activity: HomeActivity + + @RelaxedMockK + private lateinit var experiments: NimbusApi + + @RelaxedMockK + private lateinit var binding: SettingsStudiesBinding + + @RelaxedMockK + private lateinit var interactor: StudiesInteractor + + @RelaxedMockK + private lateinit var settings: Settings + + private val testCoroutineScope = TestCoroutineScope() + private val testDispatcher = TestCoroutineDispatcher() + private lateinit var view: StudiesView + + @get:Rule + val coroutinesTestRule = MainCoroutineRule(testDispatcher) + + @Before + fun setup() { + MockKAnnotations.init(this) + view = spyk( + StudiesView( + testCoroutineScope, + testContext, + binding, + interactor, + settings, + experiments + ) { true } + ) + } + + @After + fun cleanUp() { + testCoroutineScope.cleanupTestCoroutines() + testDispatcher.cleanupTestCoroutines() + } + + @Test + fun `WHEN calling bind THEN bind all the related information`() { + val studiesTitle = mockk(relaxed = true) + val studiesSwitch = mockk(relaxed = true) + val studiesList = mockk(relaxed = true) + + every { settings.isExperimentationEnabled } returns true + every { view.provideStudiesTitle() } returns studiesTitle + every { view.provideStudiesSwitch() } returns studiesSwitch + every { view.provideStudiesList() } returns studiesList + every { view.bindDescription() } just runs + every { view.getSwitchTitle() } returns "Title" + + view.bind() + + verify { + studiesTitle.text = "Title" + studiesSwitch.isChecked = true + view.bindDescription() + studiesList.adapter = any() + } + } + + @Test + fun `WHEN calling onRemoveButtonClicked THEN delegate to the interactor`() { + val experiment = mockk() + val adapter = mockk(relaxed = true) + + every { view.adapter } returns adapter + + view.onRemoveButtonClicked(experiment) + + verify { + interactor.removeStudy(experiment) + adapter.removeStudy(experiment) + } + } +} From 70a90a02914cadcedca83df72b7b95d8b26569d6 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Wed, 11 Aug 2021 18:20:12 +0000 Subject: [PATCH 008/517] Update Android Components version to 93.0.20210811143137. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 0523d5c21a..9eec9f2371 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "92.0.20210809190134" + const val VERSION = "93.0.20210811143137" } From c45703db073c8e0a40528d08eb20fe83443f282c Mon Sep 17 00:00:00 2001 From: Rohan Maity Date: Sat, 7 Aug 2021 18:15:13 +0530 Subject: [PATCH 009/517] For #20596 remove perf.startup probes --- app/metrics.yaml | 159 ------------------ .../org/mozilla/fenix/FenixApplication.kt | 76 ++++----- .../java/org/mozilla/fenix/HomeActivity.kt | 6 +- .../fenix/browser/BaseBrowserFragment.kt | 37 ++-- .../main/java/org/mozilla/fenix/ext/Glean.kt | 31 ---- .../org/mozilla/fenix/home/HomeFragment.kt | 137 ++++++++------- .../org/mozilla/fenix/HomeActivityTest.kt | 20 --- 7 files changed, 120 insertions(+), 346 deletions(-) delete mode 100644 app/src/main/java/org/mozilla/fenix/ext/Glean.kt diff --git a/app/metrics.yaml b/app/metrics.yaml index 6a9049609b..1d98d6dda0 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -4697,165 +4697,6 @@ perf.startup: - perf-android-fe@mozilla.com - mcomella@mozilla.com expires: "2022-02-01" - app_on_create_to_glean_init: - disabled: true - type: timing_distribution - time_unit: millisecond - description: | - A subsection of the duration of `FenixApplication.onCreate` and thus the - `application_on_create` probe from the start of the method through when - `initializeGlean` is called. Note: `initializeGlean` is a no-op for Beta - and Release builds which instead initialize it during - `MigratingFenixApplication`, which we don't currently measure. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18426 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18525#issue-594961170 - data_sensitivity: - - technical - notification_emails: - - perf-android-fe@mozilla.com - - mcomella@mozilla.com - expires: "2021-08-11" - app_on_create_to_megazord_init: - disabled: true - type: timing_distribution - time_unit: millisecond - description: | - A subsection of the duration of `FenixApplication.onCreate` and thus the - `application_on_create` probe from after the `app_on_create_to_glean_init` - probe until we block for the megazord to complete set up. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18426 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18525#issue-594961170 - data_sensitivity: - - technical - notification_emails: - - perf-android-fe@mozilla.com - - mcomella@mozilla.com - expires: "2021-08-11" - app_on_create_to_setup_in_main: - disabled: true - type: timing_distribution - time_unit: millisecond - description: | - A subsection of the duration of `FenixApplication.onCreate` and thus the - `application_on_create` probe from after the - `app_on_create_to_megazord_init` probe until the end of - `setupInMainProcessOnly`, which is expected to be the end of the - `onCreate` call (unless the implementation later changes). - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18426 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18525#issue-594961170 - data_sensitivity: - - technical - notification_emails: - - perf-android-fe@mozilla.com - - mcomella@mozilla.com - expires: "2021-08-11" - home_activity_on_create: - disabled: true - type: timing_distribution - time_unit: millisecond - description: | - The duration of `HomeActivity.onCreate`. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/17969 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/17973#issue-572183889 - data_sensitivity: - - technical - notification_emails: - - perf-android-fe@mozilla.com - - mcomella@mozilla.com - expires: "2021-08-11" - home_activity_on_start: - disabled: true - type: timing_distribution - time_unit: millisecond - description: | - The duration of `HomeActivity.onStart`. This may encapsulate - `HomeFragment` or `BrowserFragment` creation, depending on the code path, - so we expect this to take varying amounts of time. As such, this probe may - not be easy to interpret directly but we believe collecting it may give us - more information about different patterns we might see in performance - data. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18426 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18558#issue-596791848 - data_sensitivity: - - technical - notification_emails: - - perf-android-fe@mozilla.com - - mcomella@mozilla.com - expires: "2021-08-11" - home_fragment_on_create_view: - disabled: true - type: timing_distribution - time_unit: millisecond - description: | - The duration of `HomeFragment.onCreateView`. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18426 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18558#issue-596791848 - data_sensitivity: - - technical - notification_emails: - - perf-android-fe@mozilla.com - - mcomella@mozilla.com - expires: "2021-08-11" - home_fragment_on_view_created: - disabled: true - type: timing_distribution - time_unit: millisecond - description: | - The duration of `HomeFragment.onViewCreated`. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18426 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18558#issue-596791848 - data_sensitivity: - - technical - notification_emails: - - perf-android-fe@mozilla.com - - mcomella@mozilla.com - expires: "2021-08-11" - base_bfragment_on_create_view: - disabled: true - type: timing_distribution - time_unit: millisecond - description: | - The duration of `BaseBrowserFragment.onCreateView`. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18426 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18558#issue-596791848 - data_sensitivity: - - technical - notification_emails: - - perf-android-fe@mozilla.com - - mcomella@mozilla.com - expires: "2021-08-11" - base_bfragment_on_view_created: - disabled: true - type: timing_distribution - time_unit: millisecond - description: | - The duration of `BaseBrowserFragment.onViewCreated`. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18426 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18558#issue-596791848 - data_sensitivity: - - technical - notification_emails: - - perf-android-fe@mozilla.com - - mcomella@mozilla.com - expires: "2021-08-11" startup_type: type: labeled_counter description: | diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 8e071a06cd..f93f72054b 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -51,7 +51,6 @@ import org.mozilla.fenix.GleanMetrics.PerfStartup import org.mozilla.fenix.components.Components import org.mozilla.fenix.components.metrics.MetricServiceType import org.mozilla.fenix.components.metrics.SecurePrefsTelemetry -import org.mozilla.fenix.ext.measureNoInline import org.mozilla.fenix.ext.settings import org.mozilla.fenix.perf.ProfilerMarkerFactProcessor import org.mozilla.fenix.perf.StartupTimeline @@ -98,7 +97,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider { override fun onCreate() { // We use start/stop instead of measure so we don't measure outside the main process. val completeMethodDurationTimerId = PerfStartup.applicationOnCreate.start() // DO NOT MOVE ANYTHING ABOVE HERE. - val subsectionThroughGleanTimerId = PerfStartup.appOnCreateToGleanInit.start() super.onCreate() @@ -120,8 +118,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider { initializeGlean() } - PerfStartup.appOnCreateToGleanInit.stopAndAccumulate(subsectionThroughGleanTimerId) - setupInMainProcessOnly() // DO NOT MOVE ANYTHING BELOW THIS stop CALL. @@ -163,54 +159,50 @@ open class FenixApplication : LocaleAwareApplication(), Provider { @CallSuper open fun setupInMainProcessOnly() { - PerfStartup.appOnCreateToMegazordInit.measureNoInline { - ProfilerMarkerFactProcessor.create { components.core.engine.profiler }.register() + ProfilerMarkerFactProcessor.create { components.core.engine.profiler }.register() - run { - // Attention: Do not invoke any code from a-s in this scope. - val megazordSetup = setupMegazord() + run { + // Attention: Do not invoke any code from a-s in this scope. + val megazordSetup = setupMegazord() - setDayNightTheme() - components.strictMode.enableStrictMode(true) - warmBrowsersCache() + setDayNightTheme() + components.strictMode.enableStrictMode(true) + warmBrowsersCache() - // Make sure the engine is initialized and ready to use. - components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) { - components.core.engine.warmUp() - } - initializeWebExtensionSupport() - restoreBrowserState() - restoreDownloads() - - // Just to make sure it is impossible for any application-services pieces - // to invoke parts of itself that require complete megazord initialization - // before that process completes, we wait here, if necessary. - if (!megazordSetup.isCompleted) { - runBlockingIncrement { megazordSetup.await() } - } + // Make sure the engine is initialized and ready to use. + components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) { + components.core.engine.warmUp() + } + initializeWebExtensionSupport() + restoreBrowserState() + restoreDownloads() + + // Just to make sure it is impossible for any application-services pieces + // to invoke parts of itself that require complete megazord initialization + // before that process completes, we wait here, if necessary. + if (!megazordSetup.isCompleted) { + runBlockingIncrement { megazordSetup.await() } } } - PerfStartup.appOnCreateToSetupInMain.measureNoInline { - setupLeakCanary() - startMetricsIfEnabled() - setupPush() + setupLeakCanary() + startMetricsIfEnabled() + setupPush() - visibilityLifecycleCallback = VisibilityLifecycleCallback(getSystemService()) - registerActivityLifecycleCallbacks(visibilityLifecycleCallback) + visibilityLifecycleCallback = VisibilityLifecycleCallback(getSystemService()) + registerActivityLifecycleCallbacks(visibilityLifecycleCallback) - // Storage maintenance disabled, for now, as it was interfering with background migrations. - // See https://github.com/mozilla-mobile/fenix/issues/7227 for context. - // if ((System.currentTimeMillis() - settings().lastPlacesStorageMaintenance) > ONE_DAY_MILLIS) { - // runStorageMaintenance() - // } + // Storage maintenance disabled, for now, as it was interfering with background migrations. + // See https://github.com/mozilla-mobile/fenix/issues/7227 for context. + // if ((System.currentTimeMillis() - settings().lastPlacesStorageMaintenance) > ONE_DAY_MILLIS) { + // runStorageMaintenance() + // } - components.appStartReasonProvider.registerInAppOnCreate(this) - components.startupActivityLog.registerInAppOnCreate(this) - initVisualCompletenessQueueAndQueueTasks() + components.appStartReasonProvider.registerInAppOnCreate(this) + components.startupActivityLog.registerInAppOnCreate(this) + initVisualCompletenessQueueAndQueueTasks() - ProcessLifecycleOwner.get().lifecycle.addObserver(TelemetryLifecycleObserver(components.core.store)) - } + ProcessLifecycleOwner.get().lifecycle.addObserver(TelemetryLifecycleObserver(components.core.store)) } @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 019ee6cfcc..3ec6bd978d 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -67,7 +67,6 @@ import mozilla.components.support.utils.SafeIntent import mozilla.components.support.utils.toSafeIntent import mozilla.components.support.webextensions.WebExtensionPopupFeature import org.mozilla.fenix.GleanMetrics.Metrics -import org.mozilla.fenix.GleanMetrics.PerfStartup import org.mozilla.fenix.addons.AddonDetailsFragmentDirections import org.mozilla.fenix.addons.AddonPermissionsDetailsFragmentDirections import org.mozilla.fenix.browser.browsingmode.BrowsingMode @@ -79,7 +78,6 @@ import org.mozilla.fenix.exceptions.trackingprotection.TrackingProtectionExcepti import org.mozilla.fenix.ext.alreadyOnDestination import org.mozilla.fenix.ext.breadcrumb import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.measureNoInline import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.setNavigationIcon @@ -177,7 +175,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { private val startupPathProvider = StartupPathProvider() private lateinit var startupTypeTelemetry: StartupTypeTelemetry - final override fun onCreate(savedInstanceState: Bundle?): Unit = PerfStartup.homeActivityOnCreate.measureNoInline { + final override fun onCreate(savedInstanceState: Bundle?) { // DO NOT MOVE ANYTHING ABOVE THIS addMarker CALL. components.core.engine.profiler?.addMarker("Activity.onCreate", "HomeActivity") @@ -311,7 +309,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { isFenixTheDefaultBrowser() } - override fun onStart() = PerfStartup.homeActivityOnStart.measureNoInline { + override fun onStart() { super.onStart() // Diagnostic breadcrumb for "Display already aquired" crash: diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index c1f09e9e19..8486e1595b 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -133,10 +133,8 @@ import mozilla.components.feature.webauthn.WebAuthnFeature import mozilla.components.support.base.feature.ActivityResultHandler import mozilla.components.support.ktx.android.view.enterToImmersiveMode import mozilla.components.support.ktx.kotlin.getOrigin -import org.mozilla.fenix.GleanMetrics.PerfStartup import org.mozilla.fenix.components.toolbar.interactor.BrowserToolbarInteractor import org.mozilla.fenix.components.toolbar.interactor.DefaultBrowserToolbarInteractor -import org.mozilla.fenix.ext.measureNoInline import org.mozilla.fenix.ext.secure import org.mozilla.fenix.settings.biometric.BiometricPromptFeature import mozilla.components.feature.session.behavior.ToolbarPosition as MozacToolbarPosition @@ -212,7 +210,7 @@ abstract class BaseBrowserFragment : inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View = PerfStartup.baseBfragmentOnCreateView.measureNoInline { + ): View { customTabSessionId = requireArguments().getString(EXTRA_SESSION_ID) // Diagnostic breadcrumb for "Display already aquired" crash: @@ -235,30 +233,29 @@ abstract class BaseBrowserFragment : ) } - view + return view } - final override fun onViewCreated(view: View, savedInstanceState: Bundle?) = - PerfStartup.baseBfragmentOnViewCreated.measureNoInline { // weird indentation to avoid breaking blame. - initializeUI(view) + final override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + // weird indentation to avoid breaking blame. + initializeUI(view) - if (customTabSessionId == null) { - // We currently only need this observer to navigate to home - // in case all tabs have been removed on startup. No need to - // this if we have a known session to display. - observeRestoreComplete(requireComponents.core.store, findNavController()) - } - - observeTabSelection(requireComponents.core.store) + if (customTabSessionId == null) { + // We currently only need this observer to navigate to home + // in case all tabs have been removed on startup. No need to + // this if we have a known session to display. + observeRestoreComplete(requireComponents.core.store, findNavController()) + } - if (!onboarding.userHasBeenOnboarded()) { - observeTabSource(requireComponents.core.store) - } + observeTabSelection(requireComponents.core.store) - requireContext().accessibilityManager.addAccessibilityStateChangeListener(this) - Unit + if (!onboarding.userHasBeenOnboarded()) { + observeTabSource(requireComponents.core.store) } + requireContext().accessibilityManager.addAccessibilityStateChangeListener(this) + } + private fun initializeUI(view: View) { val tab = getCurrentTab() browserInitialized = if (tab != null) { diff --git a/app/src/main/java/org/mozilla/fenix/ext/Glean.kt b/app/src/main/java/org/mozilla/fenix/ext/Glean.kt deleted file mode 100644 index 9964aec421..0000000000 --- a/app/src/main/java/org/mozilla/fenix/ext/Glean.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.ext - -import mozilla.telemetry.glean.private.TimingDistributionMetricType - -/** - * A reimplementation of [TimingDistributionMetricType.measure] that address unintuitive - * issues around non-local returns: see https://bugzilla.mozilla.org/show_bug.cgi?id=1699505. - * This should be removed once that bug is resolved. That method's kdoc is as follows: - * - * Convenience method to simplify measuring a function or block of code. - * - * If the measured function throws, the measurement is canceled and the exception rethrown. - */ -@Suppress("TooGenericExceptionCaught") -fun TimingDistributionMetricType.measureNoInline(funcToMeasure: () -> U): U { - val timerId = start() - - val returnValue = try { - funcToMeasure() - } catch (e: Exception) { - cancel(timerId) - throw e - } - - stopAndAccumulate(timerId) - return returnValue -} diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 20318c219a..0fe0bef6d1 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -78,7 +78,6 @@ import mozilla.components.ui.tabcounter.TabCounterMenu import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.Config import org.mozilla.fenix.FeatureFlags -import org.mozilla.fenix.GleanMetrics.PerfStartup import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.browser.BrowserAnimator.Companion.getToolbarNavOptions @@ -97,7 +96,6 @@ import org.mozilla.fenix.components.toolbar.FenixTabCounterMenu import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.hideToolbar -import org.mozilla.fenix.ext.measureNoInline import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents @@ -200,7 +198,7 @@ class HomeFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? = PerfStartup.homeFragmentOnCreateView.measureNoInline { + ): View? { val view = inflater.inflate(R.layout.fragment_home, container, false) val activity = activity as HomeActivity val components = requireComponents @@ -339,7 +337,7 @@ class HomeFragment : Fragment() { appBarLayout = view.homeAppBar activity.themeManager.applyStatusBarTheme(activity) - view + return view } override fun onConfigurationChanged(newConfig: Configuration) { @@ -421,87 +419,86 @@ class HomeFragment : Fragment() { } @Suppress("LongMethod", "ComplexMethod") - override fun onViewCreated(view: View, savedInstanceState: Bundle?) = - PerfStartup.homeFragmentOnViewCreated.measureNoInline { - super.onViewCreated(view, savedInstanceState) - context?.metrics?.track(Event.HomeScreenDisplayed) - - observeSearchEngineChanges() - createHomeMenu(requireContext(), WeakReference(view.menuButton)) - createTabCounterMenu(view) - - view.menuButton.setColorFilter( - ContextCompat.getColor( - requireContext(), - ThemeManager.resolveAttribute(R.attr.primaryText, requireContext()) - ) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + context?.metrics?.track(Event.HomeScreenDisplayed) + + observeSearchEngineChanges() + createHomeMenu(requireContext(), WeakReference(view.menuButton)) + createTabCounterMenu(view) + + view.menuButton.setColorFilter( + ContextCompat.getColor( + requireContext(), + ThemeManager.resolveAttribute(R.attr.primaryText, requireContext()) ) + ) - view.toolbar.compoundDrawablePadding = - view.resources.getDimensionPixelSize(R.dimen.search_bar_search_engine_icon_padding) - view.toolbar_wrapper.setOnClickListener { - navigateToSearch() - requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME)) - } + view.toolbar.compoundDrawablePadding = + view.resources.getDimensionPixelSize(R.dimen.search_bar_search_engine_icon_padding) + view.toolbar_wrapper.setOnClickListener { + navigateToSearch() + requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME)) + } - view.toolbar_wrapper.setOnLongClickListener { - ToolbarPopupWindow.show( - WeakReference(it), - handlePasteAndGo = sessionControlInteractor::onPasteAndGo, - handlePaste = sessionControlInteractor::onPaste, - copyVisible = false - ) - true - } + view.toolbar_wrapper.setOnLongClickListener { + ToolbarPopupWindow.show( + WeakReference(it), + handlePasteAndGo = sessionControlInteractor::onPasteAndGo, + handlePaste = sessionControlInteractor::onPaste, + copyVisible = false + ) + true + } - view.tab_button.setOnClickListener { - if (FeatureFlags.showStartOnHomeSettings) { - requireComponents.analytics.metrics.track(Event.StartOnHomeOpenTabsTray) - } - openTabsTray() + view.tab_button.setOnClickListener { + if (FeatureFlags.showStartOnHomeSettings) { + requireComponents.analytics.metrics.track(Event.StartOnHomeOpenTabsTray) } + openTabsTray() + } - PrivateBrowsingButtonView( - privateBrowsingButton, - browsingModeManager - ) { newMode -> - if (newMode == BrowsingMode.Private) { - requireContext().settings().incrementNumTimesPrivateModeOpened() - } - - if (onboarding.userHasBeenOnboarded()) { - homeFragmentStore.dispatch( - HomeFragmentAction.ModeChange(Mode.fromBrowsingMode(newMode)) - ) - } + PrivateBrowsingButtonView( + privateBrowsingButton, + browsingModeManager + ) { newMode -> + if (newMode == BrowsingMode.Private) { + requireContext().settings().incrementNumTimesPrivateModeOpened() } - consumeFrom(requireComponents.core.store) { - updateTabCounter(it) + if (onboarding.userHasBeenOnboarded()) { + homeFragmentStore.dispatch( + HomeFragmentAction.ModeChange(Mode.fromBrowsingMode(newMode)) + ) } + } - homeViewModel.sessionToDelete?.also { - if (it == ALL_NORMAL_TABS || it == ALL_PRIVATE_TABS) { - removeAllTabsAndShowSnackbar(it) - } else { - removeTabAndShowSnackbar(it) - } + consumeFrom(requireComponents.core.store) { + updateTabCounter(it) + } + + homeViewModel.sessionToDelete?.also { + if (it == ALL_NORMAL_TABS || it == ALL_PRIVATE_TABS) { + removeAllTabsAndShowSnackbar(it) + } else { + removeTabAndShowSnackbar(it) } + } - homeViewModel.sessionToDelete = null + homeViewModel.sessionToDelete = null - updateTabCounter(requireComponents.core.store.state) + updateTabCounter(requireComponents.core.store.state) - if (bundleArgs.getBoolean(FOCUS_ON_ADDRESS_BAR)) { - navigateToSearch() - } else if (bundleArgs.getLong(FOCUS_ON_COLLECTION, -1) >= 0) { - // No need to scroll to async'd loaded TopSites if we want to scroll to collections. - homeViewModel.shouldScrollToTopSites = false - /* Triggered when the user has added a tab to a collection and has tapped - * the View action on the [TabsTrayDialogFragment] snackbar.*/ - scrollAndAnimateCollection(bundleArgs.getLong(FOCUS_ON_COLLECTION, -1)) - } + if (bundleArgs.getBoolean(FOCUS_ON_ADDRESS_BAR)) { + navigateToSearch() + } else if (bundleArgs.getLong(FOCUS_ON_COLLECTION, -1) >= 0) { + // No need to scroll to async'd loaded TopSites if we want to scroll to collections. + homeViewModel.shouldScrollToTopSites = false + /* Triggered when the user has added a tab to a collection and has tapped + * the View action on the [TabsTrayDialogFragment] snackbar.*/ + scrollAndAnimateCollection(bundleArgs.getLong(FOCUS_ON_COLLECTION, -1)) } + } private fun observeSearchEngineChanges() { consumeFlow(store) { flow -> diff --git a/app/src/test/java/org/mozilla/fenix/HomeActivityTest.kt b/app/src/test/java/org/mozilla/fenix/HomeActivityTest.kt index 8e335ddc48..1f02b75621 100644 --- a/app/src/test/java/org/mozilla/fenix/HomeActivityTest.kt +++ b/app/src/test/java/org/mozilla/fenix/HomeActivityTest.kt @@ -20,11 +20,9 @@ import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mozilla.fenix.GleanMetrics.PerfStartup import org.mozilla.fenix.HomeActivity.Companion.PRIVATE_BROWSING_MODE import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager @@ -33,7 +31,6 @@ import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.utils.Settings -import org.robolectric.Robolectric @RunWith(FenixRobolectricTestRunner::class) class HomeActivityTest { @@ -166,21 +163,4 @@ class HomeActivityTest { assertFalse(activity.shouldStartOnHome(startingIntent)) } - - @Ignore("failed after library upgrade, see: https://github.com/mozilla-mobile/fenix/issues/19921") - @Test - fun `WHEN onCreate is called THEN the duration is measured`() { - assertFalse(PerfStartup.homeActivityOnCreate.testHasValue()) // sanity check. - - // For some reason, the androidx replacement for this method, ActivityScenario, fails so we - // use the old Robolectric version. Perhaps it's because it forces the Activity to the - // RESUMED state (unlike Robolectric where we can get to CREATED) so not enough code is - // mocked for that to work. - // - // There are various exceptions thrown on background threads when this test runs but it - // doesn't seem to impact correctness so we ignore them. - Robolectric.buildActivity(HomeActivity::class.java) - .create() - assertTrue(PerfStartup.homeActivityOnCreate.testHasValue()) - } } From f2862eef6a4fde016105560072883c513c96a850 Mon Sep 17 00:00:00 2001 From: "codrut.topliceanu" Date: Wed, 11 Aug 2021 11:33:00 +0300 Subject: [PATCH 010/517] For #20098: Allow PB PiP video screenshots ...when `Allow screenshots in private browsing` is enabled --- .../main/java/org/mozilla/fenix/HomeActivity.kt | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 3ec6bd978d..a72ca29e0e 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -278,14 +278,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { override fun onResume() { super.onResume() - // Even if screenshots are allowed, we hide private content in the recents screen in onPause - // only when we are in private mode, so in onResume we should go back to setting these flags - // with the user screenshot setting only when we are in private mode. - // See https://github.com/mozilla-mobile/fenix/issues/11153 - if (settings().lastKnownMode == BrowsingMode.Private) { - updateSecureWindowFlags(settings().lastKnownMode) - } - // Diagnostic breadcrumb for "Display already aquired" crash: // https://github.com/mozilla-mobile/android-components/issues/7960 breadcrumb( @@ -339,13 +331,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { settings().shouldReturnToBrowser = components.core.store.state.getNormalOrPrivateTabs(private = false).isNotEmpty() - // Even if screenshots are allowed, we want to hide private content in the recents screen - // only when we are in private mode - // See https://github.com/mozilla-mobile/fenix/issues/11153 - if (settings().lastKnownMode.isPrivate) { - window.addFlags(FLAG_SECURE) - } - lifecycleScope.launch(IO) { components.core.bookmarksStorage.getTree(BookmarkRoot.Root.id, true)?.let { val desktopRootNode = DesktopFolders( From 1f9b1fa0d5cddff956581c8dab5ed7ab7bf0448b Mon Sep 17 00:00:00 2001 From: Steven Knipe Date: Thu, 12 Aug 2021 09:24:07 -0400 Subject: [PATCH 011/517] For #4134 Add Forward Back Reload to Toolbar on Tablets --- .../mozilla/fenix/browser/BrowserFragment.kt | 94 +++++++++++++++++-- app/src/main/res/values-sw600dp/bools.xml | 7 ++ app/src/main/res/values/bools.xml | 1 + 3 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/values-sw600dp/bools.xml diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index d0e985cfc4..12dc527f1f 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -33,6 +33,7 @@ import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.TabCollectionStorage +import org.mozilla.fenix.components.toolbar.ToolbarMenu import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.nav @@ -82,10 +83,10 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { if (FeatureFlags.showHomeButtonFeature) { val homeAction = BrowserToolbar.Button( imageDrawable = AppCompatResources.getDrawable( - requireContext(), + context, R.drawable.mozac_ic_home )!!, - contentDescription = requireContext().getString(R.string.browser_toolbar_home), + contentDescription = context.getString(R.string.browser_toolbar_home), iconTintColorResource = ThemeManager.resolveAttribute(R.attr.primaryText, context), listener = browserToolbarInteractor::onHomeButtonClicked ) @@ -93,19 +94,100 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { browserToolbarView.view.addNavigationAction(homeAction) } + if (resources.getBoolean(R.bool.tablet)) { + val enableTint = ThemeManager.resolveAttribute(R.attr.primaryText, context) + val disableTint = ThemeManager.resolveAttribute(R.attr.disabled, context) + val backAction = BrowserToolbar.TwoStateButton( + primaryImage = AppCompatResources.getDrawable( + context, + R.drawable.mozac_ic_back + )!!, + primaryContentDescription = context.getString(R.string.browser_menu_back), + primaryImageTintResource = enableTint, + isInPrimaryState = { getCurrentTab()?.content?.canGoBack ?: false }, + secondaryImageTintResource = disableTint, + disableInSecondaryState = true, + longClickListener = { + browserToolbarInteractor.onBrowserToolbarMenuItemTapped( + ToolbarMenu.Item.Back(viewHistory = true) + ) + }, + listener = { + browserToolbarInteractor.onBrowserToolbarMenuItemTapped( + ToolbarMenu.Item.Back(viewHistory = false) + ) + } + ) + browserToolbarView.view.addNavigationAction(backAction) + val forwardAction = BrowserToolbar.TwoStateButton( + primaryImage = AppCompatResources.getDrawable( + context, + R.drawable.mozac_ic_forward + )!!, + primaryContentDescription = context.getString(R.string.browser_menu_forward), + primaryImageTintResource = enableTint, + isInPrimaryState = { getCurrentTab()?.content?.canGoForward ?: false }, + secondaryImageTintResource = disableTint, + disableInSecondaryState = true, + longClickListener = { + browserToolbarInteractor.onBrowserToolbarMenuItemTapped( + ToolbarMenu.Item.Forward(viewHistory = true) + ) + }, + listener = { + browserToolbarInteractor.onBrowserToolbarMenuItemTapped( + ToolbarMenu.Item.Forward(viewHistory = false) + ) + } + ) + browserToolbarView.view.addNavigationAction(forwardAction) + val refreshAction = BrowserToolbar.TwoStateButton( + primaryImage = AppCompatResources.getDrawable( + context, + R.drawable.mozac_ic_refresh + )!!, + primaryContentDescription = context.getString(R.string.browser_menu_refresh), + primaryImageTintResource = enableTint, + isInPrimaryState = { + getCurrentTab()?.content?.loading == false + }, + secondaryImage = AppCompatResources.getDrawable( + context, + R.drawable.mozac_ic_stop + )!!, + secondaryContentDescription = context.getString(R.string.browser_menu_stop), + disableInSecondaryState = false, + longClickListener = { + browserToolbarInteractor.onBrowserToolbarMenuItemTapped( + ToolbarMenu.Item.Reload(bypassCache = true) + ) + }, + listener = { + if (getCurrentTab()?.content?.loading == true) { + browserToolbarInteractor.onBrowserToolbarMenuItemTapped(ToolbarMenu.Item.Stop) + } else { + browserToolbarInteractor.onBrowserToolbarMenuItemTapped( + ToolbarMenu.Item.Reload(bypassCache = false) + ) + } + } + ) + browserToolbarView.view.addNavigationAction(refreshAction) + } + val readerModeAction = BrowserToolbar.ToggleButton( image = AppCompatResources.getDrawable( - requireContext(), + context, R.drawable.ic_readermode )!!, imageSelected = AppCompatResources.getDrawable( - requireContext(), + context, R.drawable.ic_readermode_selected )!!, - contentDescription = requireContext().getString(R.string.browser_menu_read), - contentDescriptionSelected = requireContext().getString(R.string.browser_menu_read_close), + contentDescription = context.getString(R.string.browser_menu_read), + contentDescriptionSelected = context.getString(R.string.browser_menu_read_close), visible = { readerModeAvailable }, diff --git a/app/src/main/res/values-sw600dp/bools.xml b/app/src/main/res/values-sw600dp/bools.xml new file mode 100644 index 0000000000..6d2bf3e16d --- /dev/null +++ b/app/src/main/res/values-sw600dp/bools.xml @@ -0,0 +1,7 @@ + + + + true + \ No newline at end of file diff --git a/app/src/main/res/values/bools.xml b/app/src/main/res/values/bools.xml index 91d4bf5728..aae7a9c9b2 100644 --- a/app/src/main/res/values/bools.xml +++ b/app/src/main/res/values/bools.xml @@ -6,4 +6,5 @@ true false true + false From 46d89ac863c5561fe6363bfb4c163cd319d5ef9e Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Thu, 12 Aug 2021 15:38:40 +0000 Subject: [PATCH 012/517] Update Android Components version to 93.0.20210812143121. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 9eec9f2371..1175e6e3aa 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "93.0.20210811143137" + const val VERSION = "93.0.20210812143121" } From 2426384a2eda66ff29a55a6a55ecdf422c3ee853 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Wed, 11 Aug 2021 23:39:04 -0400 Subject: [PATCH 013/517] Update the description for an inactive tabs string --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 29204116f1..d352c1f0b2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1791,7 +1791,7 @@ Inactive tabs - + Tabs are available here for %s. After that time, tabs will be automatically closed. 30 days From ea56dd216bc3527943c26e709d3d12e22a083104 Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Thu, 12 Aug 2021 14:32:38 -0500 Subject: [PATCH 014/517] Use header20 style on collections to match other home items. Add margin above collections. (#20633) --- app/src/main/res/layout/collection_header.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/collection_header.xml b/app/src/main/res/layout/collection_header.xml index 86f05b890f..c21157c9f3 100644 --- a/app/src/main/res/layout/collection_header.xml +++ b/app/src/main/res/layout/collection_header.xml @@ -9,4 +9,4 @@ android:gravity="center_vertical" android:layout_marginTop="40dp" android:text="@string/collections_header" - android:textAppearance="@style/HeaderTextStyle" /> + android:textAppearance="@style/Header20TextStyle" /> From 667ef89220a8b99b36b428d36adaeca96c023daf Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Wed, 11 Aug 2021 11:49:22 -0700 Subject: [PATCH 015/517] No issue: move kotlin-allopen outside appVariants loop. The kotlin-allopen plugin could be getting applied for each variant we have in the app. With the changed code, it will only get applied once. --- app/build.gradle | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 60210fd6c0..aebfb93463 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -226,19 +226,19 @@ android { } } -android.applicationVariants.all { variant -> - // ------------------------------------------------------------------------------------------------- // Set up kotlin-allopen plugin for writing tests // ------------------------------------------------------------------------------------------------- - boolean hasTest = gradle.startParameter.taskNames.find { it.contains("test") || it.contains("Test") } != null - if (hasTest) { - apply plugin: 'kotlin-allopen' - allOpen { - annotation("org.mozilla.fenix.utils.OpenClass") - } +boolean hasTest = gradle.startParameter.taskNames.find { it.contains("test") || it.contains("Test") } != null +if (hasTest) { + apply plugin: 'kotlin-allopen' + allOpen { + annotation("org.mozilla.fenix.utils.OpenClass") } +} + +android.applicationVariants.all { variant -> // ------------------------------------------------------------------------------------------------- // Generate version codes for builds From 5a8a7f549946fc8ad6ccf31f8c9c6bc2180aaed2 Mon Sep 17 00:00:00 2001 From: Ben Hearsum Date: Thu, 12 Aug 2021 11:16:44 -0400 Subject: [PATCH 016/517] Ship nightlies to Google Play twice a day This was moved to once per day in https://bugzilla.mozilla.org/show_bug.cgi?id=1628413 when Google Play reviews were very slow. That hasn't been a problem for awhile, and the less frequent nightlies are causing delays getting new Android Components bumps shipped out. --- .cron.yml | 8 -------- taskcluster/fenix_taskgraph/target_tasks.py | 20 +------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/.cron.yml b/.cron.yml index 3e9ae015f6..c51e01f7e0 100644 --- a/.cron.yml +++ b/.cron.yml @@ -11,14 +11,6 @@ jobs: target-tasks-method: nightly when: - {hour: 5, minute: 0} - # This is a temporary hook in order to not overload Google Play. - # See bug 1628413 for more context. - - name: nightly-on-google-play - job: - type: decision-task - treeherder-symbol: Nd-gp - target-tasks-method: nightly-on-google-play - when: - {hour: 17, minute: 0} - name: fennec-production job: diff --git a/taskcluster/fenix_taskgraph/target_tasks.py b/taskcluster/fenix_taskgraph/target_tasks.py index 71785428cc..9096c9ff4a 100644 --- a/taskcluster/fenix_taskgraph/target_tasks.py +++ b/taskcluster/fenix_taskgraph/target_tasks.py @@ -28,25 +28,7 @@ def target_tasks_nightly(full_task_graph, parameters, graph_config): """Select the set of tasks required for a nightly build.""" def filter(task, parameters): - # We don't want to ship nightly while Google Play is still behind manual review. - # See bug 1628413 for more context. - return task.attributes.get("nightly", False) and task.kind != "push-apk" - - return [l for l, t in full_task_graph.tasks.items() if filter(t, parameters)] - - -@_target_task("nightly-on-google-play") -def target_tasks_nightly_on_google_play(full_task_graph, parameters, graph_config): - """Select the set of tasks required for a nightly build that goes on Google Play.""" - - def filter(task, parameters): - return ( - task.attributes.get("nightly", False) and - # This target_task is temporary while Google Play processes APKs slower than usually - # (bug 1628413). So we want this target task to be only about shipping APKs to GP and - # not doing any other miscellaneous tasks like performance testing - task.kind not in ("browsertime", "visual-metrics", "raptor") - ) + return task.attributes.get("nightly", False) return [l for l, t in full_task_graph.tasks.items() if filter(t, parameters)] From 5ff2c2184952aeb878e9c715073385032190a962 Mon Sep 17 00:00:00 2001 From: Aaron Train Date: Fri, 13 Aug 2021 15:00:10 -0400 Subject: [PATCH 017/517] Filter out test target packages in Flank-x86 (#20841) * Filter out test target packages in Flank-x86 * Ignore verifyRunBlockingAndStrictModeSuppresionCount --- .../org/mozilla/fenix/perf/StartupExcessiveResourceUseTest.kt | 2 ++ automation/taskcluster/androidTest/flank-x86.yml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/perf/StartupExcessiveResourceUseTest.kt b/app/src/androidTest/java/org/mozilla/fenix/perf/StartupExcessiveResourceUseTest.kt index 5640d9166c..4e32f0de45 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/perf/StartupExcessiveResourceUseTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/perf/StartupExcessiveResourceUseTest.kt @@ -14,6 +14,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import kotlinx.android.synthetic.main.activity_home.* import org.junit.Assert.assertEquals +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.mozilla.fenix.ext.components @@ -81,6 +82,7 @@ class StartupExcessiveResourceUseTest { private val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + @Ignore("See: https://github.com/mozilla-mobile/fenix/pull/20841#issuecomment-898630241c") @Test fun verifyRunBlockingAndStrictModeSuppresionCount() { uiDevice.waitForIdle() // wait for async UI to load. diff --git a/automation/taskcluster/androidTest/flank-x86.yml b/automation/taskcluster/androidTest/flank-x86.yml index a2a9f9f488..70e63a0c7e 100644 --- a/automation/taskcluster/androidTest/flank-x86.yml +++ b/automation/taskcluster/androidTest/flank-x86.yml @@ -38,8 +38,8 @@ gcloud: performance-metrics: true test-targets: - - package org.mozilla.fenix.ui - - package org.mozilla.fenix.glean + - notPackage org.mozilla.fenix.screenshots + - notPackage org.mozilla.fenix.syncintegration device: - model: Pixel2 From 8b80cccd1ba5f8fec3f687b77061e0d3164d0abd Mon Sep 17 00:00:00 2001 From: Mickey Moz <33347735+MickeyMoz@users.noreply.github.com> Date: Sat, 14 Aug 2021 03:42:51 +0200 Subject: [PATCH 018/517] Update Android Components version to 93.0.20210813143343. (#20839) Co-authored-by: Ryan VanderMeulen --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 1175e6e3aa..63cd5b596b 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "93.0.20210812143121" + const val VERSION = "93.0.20210813143343" } From 545a59ac979df87e809b5bc3fccac1f8f077bd2c Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Wed, 11 Aug 2021 17:12:48 -0400 Subject: [PATCH 019/517] Close #20792: Fix intermittent test failures in QuickSettingsFragmentReducerTest --- .../QuickSettingsFragmentReducer.kt | 2 +- .../QuickSettingsFragmentReducerTest.kt | 27 ++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt index e26c7f3f0f..3260fee33a 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt @@ -7,7 +7,7 @@ package org.mozilla.fenix.settings.quicksettings /** * Parent Reducer for all [QuickSettingsFragmentState]s of all Views shown in this Fragment. */ -fun quickSettingsFragmentReducer( +internal fun quickSettingsFragmentReducer( state: QuickSettingsFragmentState, action: QuickSettingsFragmentAction ): QuickSettingsFragmentState { diff --git a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt index 14e4cfc525..82653d1805 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt @@ -4,16 +4,14 @@ package org.mozilla.fenix.settings.quicksettings -import mozilla.components.support.test.mock +import mozilla.components.feature.sitepermissions.SitePermissionsRules import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue -import org.junit.Ignore import org.junit.Test import org.mozilla.fenix.settings.PhoneFeature class QuickSettingsFragmentReducerTest { - @Ignore("See https://github.com/mozilla-mobile/fenix/issues/20792") @Test fun `WebsitePermissionAction - TogglePermission`() { val toggleablePermission = WebsitePermission.Toggleable( @@ -26,8 +24,8 @@ class QuickSettingsFragmentReducerTest { val map = mapOf(PhoneFeature.CAMERA to toggleablePermission) - - val state = QuickSettingsFragmentState(mock(), map) + val infoState = WebsiteInfoState("", "", WebsiteSecurityUiValues.SECURE, "") + val state = QuickSettingsFragmentState(infoState, map) val newState = quickSettingsFragmentReducer( state, WebsitePermissionAction.TogglePermission( @@ -46,7 +44,7 @@ class QuickSettingsFragmentReducerTest { val permissionPermission = WebsitePermission.Autoplay( autoplayValue = AutoplayValue.BlockAll( label = "label", - rules = mock(), + rules = createTestRule(), sitePermission = null ), options = emptyList(), @@ -55,11 +53,11 @@ class QuickSettingsFragmentReducerTest { val map = mapOf(PhoneFeature.AUTOPLAY to permissionPermission) - - val state = QuickSettingsFragmentState(mock(), map) + val infoState = WebsiteInfoState("", "", WebsiteSecurityUiValues.SECURE, "") + val state = QuickSettingsFragmentState(infoState, map) val autoplayValue = AutoplayValue.AllowAll( label = "newLabel", - rules = mock(), + rules = createTestRule(), sitePermission = null ) val newState = quickSettingsFragmentReducer( @@ -71,4 +69,15 @@ class QuickSettingsFragmentReducerTest { newState.websitePermissionsState[PhoneFeature.AUTOPLAY] as WebsitePermission.Autoplay assertEquals(autoplayValue, result.autoplayValue) } + + private fun createTestRule() = SitePermissionsRules( + SitePermissionsRules.Action.ALLOWED, + SitePermissionsRules.Action.ALLOWED, + SitePermissionsRules.Action.ALLOWED, + SitePermissionsRules.Action.ALLOWED, + SitePermissionsRules.AutoplayAction.ALLOWED, + SitePermissionsRules.AutoplayAction.ALLOWED, + SitePermissionsRules.Action.ALLOWED, + SitePermissionsRules.Action.ALLOWED, + ) } From 0556e6b1c47f8e0ffcaa3b099b86ea3958f16ac4 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Wed, 11 Aug 2021 17:30:41 -0400 Subject: [PATCH 020/517] Close #20797: Fix intermittent test failures in ShortcutsSuggestionProviderTest --- .../ShortcutsSuggestionProviderTest.kt | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/app/src/test/java/org/mozilla/fenix/search/awesomebar/ShortcutsSuggestionProviderTest.kt b/app/src/test/java/org/mozilla/fenix/search/awesomebar/ShortcutsSuggestionProviderTest.kt index b57ed541be..936e2bdad9 100644 --- a/app/src/test/java/org/mozilla/fenix/search/awesomebar/ShortcutsSuggestionProviderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/search/awesomebar/ShortcutsSuggestionProviderTest.kt @@ -10,7 +10,6 @@ import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.unmockkStatic -import io.mockk.verifySequence import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runBlockingTest import mozilla.components.browser.state.search.SearchEngine @@ -19,8 +18,8 @@ import mozilla.components.browser.state.state.SearchState import mozilla.components.browser.state.store.BrowserStore import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.mozilla.fenix.R @@ -63,9 +62,10 @@ class ShortcutsSuggestionProviderTest { ) ) ) - val provider = ShortcutsSuggestionProvider(store, context, mockk(), mockk()) + val provider = ShortcutsSuggestionProvider(store, context, {}, {}) val suggestions = provider.onInputChanged("") + assertEquals(3, suggestions.size) assertEquals(provider, suggestions[0].provider) @@ -83,7 +83,6 @@ class ShortcutsSuggestionProviderTest { assertEquals("Search engine settings", suggestions[2].title) } - @Ignore("See https://github.com/mozilla-mobile/fenix/issues/20797") @Test fun `callbacks are triggered when suggestions are clicked`() = runBlockingTest { val engineOne = mockk(relaxed = true) @@ -95,13 +94,13 @@ class ShortcutsSuggestionProviderTest { ) ) - val selectShortcutEngine = mockk<(SearchEngine) -> Unit>(relaxed = true) - val selectShortcutEngineSettings = mockk<() -> Unit>(relaxed = true) + var selectEngine: SearchEngine? = null + var selectShortcutEngineSettingsChanged = false val provider = ShortcutsSuggestionProvider( store, context, - selectShortcutEngine, - selectShortcutEngineSettings + { selectEngine = it }, + { selectShortcutEngineSettingsChanged = true } ) val suggestions = provider.onInputChanged("") @@ -110,9 +109,7 @@ class ShortcutsSuggestionProviderTest { suggestions[0].onSuggestionClicked?.invoke() suggestions[1].onSuggestionClicked?.invoke() - verifySequence { - selectShortcutEngine(engineOne) - selectShortcutEngineSettings() - } + assertEquals(engineOne, selectEngine) + assertTrue(selectShortcutEngineSettingsChanged) } } From 0f35700e00f3014a038a53eb33ad962804f1fca3 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Wed, 11 Aug 2021 18:07:22 -0400 Subject: [PATCH 021/517] Close #20795: Speculative fix for intermittent test failures in HomeFragmentTest The expectation is that replacing `return` with `answers` will compute the return value for the extension function again in order to avoid the error, "no answer found for: Settings". --- .../test/java/org/mozilla/fenix/home/HomeFragmentTest.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentTest.kt b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentTest.kt index 8a99d0bcfe..fbcbc6070a 100644 --- a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentTest.kt @@ -13,7 +13,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import mozilla.components.browser.menu.view.MenuButton import org.junit.Assert import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.mozilla.fenix.ext.settings import org.mozilla.fenix.utils.Settings @@ -32,8 +31,8 @@ class HomeFragmentTest { homeFragment = spyk(HomeFragment()) - every { homeFragment.context } returns context - every { context.settings() } returns settings + every { homeFragment.context } answers { context } + every { context.settings() } answers { settings } } @Test @@ -46,7 +45,6 @@ class HomeFragmentTest { Assert.assertNull(topSitesConfig.frecencyConfig) } - @Ignore("See https://github.com/mozilla-mobile/fenix/issues/20795") @Test fun `GIVEN showTopFrecentSites is true WHEN getTopSitesConfig is called THEN it returns TopSitesConfig with non-null frecencyConfig`() { every { context.settings().showTopFrecentSites } returns true From 708f024d078e4ba7bccc3761c2d8901021e1f49a Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Wed, 11 Aug 2021 20:02:18 -0400 Subject: [PATCH 022/517] Close #20796: Fix intermittent test failure in TrackingProtectionExceptionsInteractorTest --- .../ExceptionsFragmentStore.kt | 11 ++++- ...ckingProtectionExceptionsInteractorTest.kt | 45 +++++++++++++------ 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/trackingprotection/ExceptionsFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/exceptions/trackingprotection/ExceptionsFragmentStore.kt index 6fa287e6d1..6b8d7c14d8 100644 --- a/app/src/main/java/org/mozilla/fenix/exceptions/trackingprotection/ExceptionsFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/exceptions/trackingprotection/ExceptionsFragmentStore.kt @@ -6,14 +6,21 @@ package org.mozilla.fenix.exceptions.trackingprotection import mozilla.components.concept.engine.content.blocking.TrackingProtectionException import mozilla.components.lib.state.Action +import mozilla.components.lib.state.Middleware import mozilla.components.lib.state.State import mozilla.components.lib.state.Store /** * The [Store] for holding the [ExceptionsFragmentState] and applying [ExceptionsFragmentAction]s. */ -class ExceptionsFragmentStore(initialState: ExceptionsFragmentState) : - Store(initialState, ::exceptionsStateReducer) +class ExceptionsFragmentStore( + initialState: ExceptionsFragmentState = ExceptionsFragmentState(), + middlewares: List> = emptyList() +) : Store( + initialState, + ::exceptionsStateReducer, + middlewares +) /** * Actions to dispatch through the `ExceptionsStore` to modify `ExceptionsState` through the reducer. diff --git a/app/src/test/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsInteractorTest.kt index a4a7747287..7e1a69b5c7 100644 --- a/app/src/test/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsInteractorTest.kt @@ -10,10 +10,15 @@ import io.mockk.impl.annotations.MockK import io.mockk.mockk import io.mockk.verify import io.mockk.verifySequence +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.content.blocking.TrackingProtectionException +import mozilla.components.concept.engine.content.blocking.TrackingProtectionExceptionStorage import mozilla.components.feature.session.TrackingProtectionUseCases +import mozilla.components.support.test.libstate.ext.waitUntilIdle +import mozilla.components.support.test.middleware.CaptureActionsMiddleware +import org.junit.Assert.assertEquals import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity @@ -22,10 +27,15 @@ import org.mozilla.fenix.settings.SupportUtils class TrackingProtectionExceptionsInteractorTest { @MockK(relaxed = true) private lateinit var activity: HomeActivity - @MockK(relaxed = true) private lateinit var exceptionsStore: ExceptionsFragmentStore - @MockK(relaxed = true) private lateinit var trackingProtectionUseCases: TrackingProtectionUseCases private lateinit var interactor: TrackingProtectionExceptionsInteractor - private lateinit var results: List + + private val results: List = emptyList() + private val engine: Engine = mockk(relaxed = true) + private val store = BrowserStore() + private val capture = CaptureActionsMiddleware() + private val exceptionsStore = ExceptionsFragmentStore(middlewares = listOf(capture)) + private val trackingProtectionUseCases = TrackingProtectionUseCases(store, engine) + private val trackingStorage: TrackingProtectionExceptionStorage = mockk(relaxed = true) @Before fun setup() { @@ -36,8 +46,8 @@ class TrackingProtectionExceptionsInteractorTest { trackingProtectionUseCases = trackingProtectionUseCases ) - results = emptyList() - every { trackingProtectionUseCases.fetchExceptions(any()) } answers { + every { engine.trackingProtectionExceptionStore } returns trackingStorage + every { trackingStorage.fetchAll(any()) } answers { firstArg<(List) -> Unit>()(results) } } @@ -57,27 +67,36 @@ class TrackingProtectionExceptionsInteractorTest { ) } } - @Ignore("See https://github.com/mozilla-mobile/fenix/issues/20796") @Test fun onDeleteAll() { interactor.onDeleteAll() + verifySequence { - trackingProtectionUseCases.removeAllExceptions() - trackingProtectionUseCases.fetchExceptions(any()) + trackingStorage.removeAll(any()) + trackingStorage.fetchAll(any()) } - verify { exceptionsStore.dispatch(ExceptionsFragmentAction.Change(results)) } + exceptionsStore.waitUntilIdle() + + capture.assertLastAction(ExceptionsFragmentAction.Change::class) { + assertEquals(results, it.list) + } } @Test fun onDeleteOne() { val exceptionsItem = mockk() interactor.onDeleteOne(exceptionsItem) + verifySequence { - trackingProtectionUseCases.removeException(exceptionsItem) - trackingProtectionUseCases.fetchExceptions(any()) + trackingStorage.remove(exceptionsItem) + trackingStorage.fetchAll(any()) } - verify { exceptionsStore.dispatch(ExceptionsFragmentAction.Change(results)) } + exceptionsStore.waitUntilIdle() + + capture.assertLastAction(ExceptionsFragmentAction.Change::class) { + assertEquals(results, it.list) + } } } From 5c328f96168a25e727c8e40432f53bcb700d1816 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Wed, 11 Aug 2021 20:35:56 -0400 Subject: [PATCH 023/517] Close #15168: Remove intermittent failing test in ToolbarViewTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test seems to be hacking at the binding between Fenix and the BrowserToolbar to simulate toolbar events passing to the Fenix interactor. This is rather clumsy test that relies on the magic working of mockk instead of following a general unit testing strategy that would commonly require the class to be re-written to allow for better testing instead. It is far safer to remove this test since we are not guaranteeing anything in it and instead we see intermittent failures that make us lose more time. So therefore.. 🔥 --- .../fenix/search/toolbar/ToolbarViewTest.kt | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/app/src/test/java/org/mozilla/fenix/search/toolbar/ToolbarViewTest.kt b/app/src/test/java/org/mozilla/fenix/search/toolbar/ToolbarViewTest.kt index 414df3fc9a..03fa66a9ca 100644 --- a/app/src/test/java/org/mozilla/fenix/search/toolbar/ToolbarViewTest.kt +++ b/app/src/test/java/org/mozilla/fenix/search/toolbar/ToolbarViewTest.kt @@ -16,11 +16,9 @@ import io.mockk.verify import mozilla.components.browser.toolbar.BrowserToolbar import mozilla.components.browser.toolbar.edit.EditToolbar import mozilla.components.concept.engine.Engine -import mozilla.components.concept.toolbar.Toolbar import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse -import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -68,30 +66,6 @@ class ToolbarViewTest { toolbar = spyk(BrowserToolbar(context)) } - @Test - fun `sets up interactor listeners`() { - lateinit var urlCommitListener: ((String) -> Boolean) - var editListener: Toolbar.OnEditListener? = null - every { toolbar.setOnUrlCommitListener(any()) } answers { - urlCommitListener = firstArg() - } - every { toolbar.setOnEditListener(any()) } answers { - editListener = firstArg() - } - - buildToolbarView(isPrivate = false) - - assertFalse(urlCommitListener("test")) - verify { interactor.onUrlCommitted("test") } - - assertNotNull(editListener) - assertFalse(editListener!!.onCancelEditing()) - verify { interactor.onEditingCanceled() } - - editListener!!.onTextChanged("https://example.com") - verify { interactor.onTextChanged("https://example.com") } - } - @Test fun `sets toolbar to normal mode`() { buildToolbarView(isPrivate = false) From 3e2dae1c7336eac0615d53469c566d3fe77dc2ff Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Sat, 14 Aug 2021 15:36:35 +0000 Subject: [PATCH 024/517] Update Android Components version to 93.0.20210814143348. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 63cd5b596b..71c2bc94a6 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "93.0.20210813143343" + const val VERSION = "93.0.20210814143348" } From 76a8706e1f6e1f4ee010d4c853cbb210d2cd52ee Mon Sep 17 00:00:00 2001 From: Mickey Moz <33347735+MickeyMoz@users.noreply.github.com> Date: Mon, 16 Aug 2021 04:18:08 +0200 Subject: [PATCH 025/517] Update Android Components version to 93.0.20210815143147. (#20859) --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 71c2bc94a6..cc073e5733 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "93.0.20210814143348" + const val VERSION = "93.0.20210815143147" } From 2b53df2ed4357e1bc1b994f21e0698a8158cc467 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Thu, 29 Jul 2021 17:34:16 +0200 Subject: [PATCH 026/517] Use theming in debug screen. --- .../settings/SecretDebugSettingsFragment.kt | 16 ++++++++-- .../org/mozilla/fenix/theme/FirefoxTheme.kt | 30 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/theme/FirefoxTheme.kt diff --git a/app/src/main/java/org/mozilla/fenix/settings/SecretDebugSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SecretDebugSettingsFragment.kt index f9ea3d1a32..07eae25609 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SecretDebugSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SecretDebugSettingsFragment.kt @@ -21,6 +21,7 @@ import androidx.fragment.app.Fragment import org.mozilla.fenix.R import org.mozilla.fenix.components.components import org.mozilla.fenix.ext.showToolbar +import org.mozilla.fenix.theme.FirefoxTheme class SecretDebugSettingsFragment : Fragment() { @@ -36,7 +37,11 @@ class SecretDebugSettingsFragment : Fragment() { savedInstanceState: Bundle? ): View { return ComposeView(requireContext()).apply { - setContent { DebugInfo() } + setContent { + FirefoxTheme { + DebugInfo() + } + } } } } @@ -46,24 +51,29 @@ private fun DebugInfo() { val store = components.core.store Column( - modifier = Modifier.padding(8.dp) + modifier = Modifier + .padding(8.dp) ) { Text( text = stringResource(R.string.debug_info_region_home), style = MaterialTheme.typography.h6, - modifier = Modifier.padding(4.dp) + color = MaterialTheme.colors.onBackground, + modifier = Modifier.padding(4.dp), ) Text( text = store.state.search.region?.home ?: "Unknown", + color = MaterialTheme.colors.onBackground, modifier = Modifier.padding(4.dp) ) Text( text = stringResource(R.string.debug_info_region_current), style = MaterialTheme.typography.h6, + color = MaterialTheme.colors.onBackground, modifier = Modifier.padding(4.dp) ) Text( text = store.state.search.region?.current ?: "Unknown", + color = MaterialTheme.colors.onBackground, modifier = Modifier.padding(4.dp) ) } diff --git a/app/src/main/java/org/mozilla/fenix/theme/FirefoxTheme.kt b/app/src/main/java/org/mozilla/fenix/theme/FirefoxTheme.kt new file mode 100644 index 0000000000..d7cb9499f8 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/theme/FirefoxTheme.kt @@ -0,0 +1,30 @@ +/* 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.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Colors +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable + +/** + * The theme for Mozilla Firefox for Android (Fenix). + */ +@Composable +fun FirefoxTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + MaterialTheme( + content = content, + colors = if (darkTheme) darkColorPalette else lightColorPalette + ) +} + +private val darkColorPalette: Colors = darkColors() + +private val lightColorPalette: Colors = lightColors() From eb490a898c2303752bed7c7237267ff7684b562d Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Fri, 13 Aug 2021 11:17:58 -0400 Subject: [PATCH 027/517] Remove duplicated line in TabsSettingsFragment --- .../main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt index 0bd913116b..889b0fe2b9 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt @@ -60,7 +60,6 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { radioOneWeek = requirePreference(R.string.pref_key_close_tabs_after_one_week) radioOneMonth = requirePreference(R.string.pref_key_close_tabs_after_one_month) - startOnHomeRadioFourHours = requirePreference(R.string.pref_key_start_on_home_after_four_hours) startOnHomeRadioFourHours = requirePreference(R.string.pref_key_start_on_home_after_four_hours) startOnHomeRadioAlways = requirePreference(R.string.pref_key_start_on_home_always) startOnHomeRadioNever = requirePreference(R.string.pref_key_start_on_home_never) From b4bcaa0bd3e1a345bd36710483416bcc39c6091d Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Fri, 13 Aug 2021 16:26:17 +0200 Subject: [PATCH 028/517] Android Autofill: Use AppCompat theme for UnlockActivity. --- app/src/beta/AndroidManifest.xml | 2 +- app/src/debug/AndroidManifest.xml | 2 +- app/src/nightly/AndroidManifest.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/beta/AndroidManifest.xml b/app/src/beta/AndroidManifest.xml index ca3571f569..f98b6fa2f3 100644 --- a/app/src/beta/AndroidManifest.xml +++ b/app/src/beta/AndroidManifest.xml @@ -8,7 +8,7 @@ + android:theme="@style/Theme.AppCompat.Translucent" /> + android:theme="@style/Theme.AppCompat.Translucent" /> + android:theme="@style/Theme.AppCompat.Translucent" /> Date: Fri, 13 Aug 2021 11:50:40 -0400 Subject: [PATCH 029/517] For #20764 separate learn more string --- .../java/org/mozilla/fenix/settings/studies/StudiesView.kt | 5 +++-- app/src/main/res/values/strings.xml | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesView.kt b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesView.kt index 92723d7340..4331676df0 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesView.kt @@ -80,8 +80,9 @@ class StudiesView( @VisibleForTesting internal fun bindDescription() { val sumoUrl = SupportUtils.getSumoURLForTopic(context, OPT_OUT_STUDIES) - val rawText = - context.getString(R.string.studies_description, sumoUrl) + val description = context.getString(R.string.studies_description) + val learnMore = context.getString(R.string.studies_learn_more) + val rawText = "$description $learnMore" val text = HtmlCompat.fromHtml(rawText, HtmlCompat.FROM_HTML_MODE_COMPACT) val spannableStringBuilder = SpannableStringBuilder(text) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d352c1f0b2..e8385ff6ee 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -619,8 +619,10 @@ Remove Active - - Learn more]]> + + Firefox may install and run studies from time to time. + + Learn more From 29b6bab43573ce2a120763f66a67da2a198ba180 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Fri, 6 Aug 2021 23:54:25 -0400 Subject: [PATCH 030/517] Update TabSessionState.createdAt for inactive tabs debugging --- .../java/org/mozilla/fenix/tabstray/TabsTrayController.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt index d55d1cf2b4..28ba20c19d 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.tabstray import androidx.annotation.VisibleForTesting import androidx.navigation.NavController import kotlinx.coroutines.ExperimentalCoroutinesApi +import mozilla.components.browser.state.action.DebugAction import mozilla.components.browser.state.action.LastAccessAction import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.selector.getNormalOrPrivateTabs @@ -15,6 +16,7 @@ import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.base.profiler.Profiler import mozilla.components.concept.tabstray.Tab import mozilla.components.feature.tabs.TabsUseCases +import mozilla.components.lib.state.DelicateAction import org.mozilla.fenix.R import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager @@ -180,10 +182,14 @@ class DefaultTabsTrayController( * * ⚠️ DO NOT USE THIS OUTSIDE OF DEBUGGING/TESTING. */ + @OptIn(DelicateAction::class) override fun forceTabsAsInactive(tabs: Collection, numOfDays: Long) { tabs.forEach { tab -> val daysSince = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(numOfDays) - browserStore.dispatch(LastAccessAction.UpdateLastAccessAction(tab.id, daysSince)) + browserStore.apply { + dispatch(LastAccessAction.UpdateLastAccessAction(tab.id, daysSince)) + dispatch(DebugAction.UpdateCreatedAtAction(tab.id, daysSince)) + } } } From bd665e2f7e4cfd6cb1ab835c2bd70b2911dfcd94 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Sat, 14 Aug 2021 18:26:11 -0400 Subject: [PATCH 031/517] Close #20853: Fix intermittent failing test in HomeFragmentStoreTest --- .../java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt index 3c7886b357..3f89d96f1f 100644 --- a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt @@ -32,7 +32,6 @@ class HomeFragmentStoreTest { private lateinit var accountManager: FxaAccountManager private lateinit var onboarding: FenixOnboarding private lateinit var browsingModeManager: BrowsingModeManager - private lateinit var dispatchModeChanges: (mode: Mode) -> Unit private lateinit var currentMode: CurrentMode private lateinit var homeFragmentState: HomeFragmentState private lateinit var homeFragmentStore: HomeFragmentStore @@ -43,7 +42,6 @@ class HomeFragmentStoreTest { accountManager = mockk(relaxed = true) onboarding = mockk(relaxed = true) browsingModeManager = mockk(relaxed = true) - dispatchModeChanges = mockk(relaxed = true) every { context.components.backgroundServices.accountManager } returns accountManager every { onboarding.userHasBeenOnboarded() } returns true @@ -52,9 +50,8 @@ class HomeFragmentStoreTest { currentMode = CurrentMode( context, onboarding, - browsingModeManager, - dispatchModeChanges - ) + browsingModeManager + ) {} homeFragmentState = HomeFragmentState( collections = emptyList(), From fd1890090c82a9471e9b338799d6dde8993ff925 Mon Sep 17 00:00:00 2001 From: Mozilla L10n Automation Bot Date: Mon, 16 Aug 2021 15:32:26 +0000 Subject: [PATCH 032/517] Import l10n. --- app/src/main/res/values-ca/strings.xml | 2 +- app/src/main/res/values-fa/strings.xml | 20 ++++++------- app/src/main/res/values-my/strings.xml | 39 ++++++++++++++++++++++++- app/src/main/res/values-sat/strings.xml | 14 ++++----- app/src/main/res/values-sq/strings.xml | 35 ++++++++++++++++++++-- app/src/main/res/values-tl/strings.xml | 3 ++ 6 files changed, 90 insertions(+), 23 deletions(-) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 13fe21a865..97fa92b863 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -1009,7 +1009,7 @@ Usades recentment - Inicia la sessió per sincronitzar + Inicia sessió per sincronitzar Inicia la sessió al Sync diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index c957954db4..7a3e4540a6 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -4,6 +4,7 @@ خصوصی %s %s (خصوصی) + گزینه‌های بیشتر @@ -91,13 +92,6 @@ رد کردن - - طرح بندی برگه های باز را تغییر دهید. به تنظیمات بروید و شبکه را در زیر نمای برگه انتخاب کنید. - - برو به تنظیمات - - رد کردن - زبانه جدید @@ -166,7 +160,7 @@ The first parameter is the name of the app defined in app_name (for example: Fenix) --> قدرت گرفته توسط %1$s - نمای خواننده + نمای مطالعه بستن نمای مطالعه @@ -181,6 +175,10 @@ ویرایش + + + صفحه خانگی + اتصال امکان پذیر نیست. طرح آدرس غیرقابل تشخیص است. @@ -540,8 +538,6 @@ زبانهٔ جدید پیدا کردن در صفحه - - زبانه‌های همگام‌سازی شده فهرست خواندن @@ -1018,6 +1014,8 @@ حذف و باز کنید قدرت گرفته توسط + + مجموعه حذف شده @@ -1618,7 +1616,7 @@ رشته جست‌و‌جو برای استفاده - درخواست را با “%s” جایگزین کنید. مثال: \nhttps://www.google.com/search?q=%s + درخواست را با “%s” جایگزین کنید. مثال: \nhttps://www.google.com/search?q=%s بیشتر بدانید diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index d2d011f3e1..bfebd33a65 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -50,6 +50,11 @@ မကြာသေးမီက သိမ်းဆည်းခဲ့သည် + + လတ်တလောသိမ်းထားသောစာမျက်နှာများ + + အားလုံးပြပါ + %1$s ကို Mozilla မှ ထုတ်လုပ်သည်။ @@ -68,6 +73,9 @@ နေပါစေ + + အပြင်အဆင်များသို့ သွားပါ + တပ်ဗ် အသစ် @@ -142,6 +150,9 @@ ထည့်ပါ + + ပြင်ဆင် + ရွေးချယ်ထားသောဘာသာစကား @@ -352,6 +363,8 @@ စျေးကွက်ရှာဖွေရေးအချက်အလက် သင်ဘယ် လုပ်ဆောင်ချက်များ ကို %1$s တွင် သုံးသည် ကို ကျွန်တော်တို့၏ မိုဘိုင်း မာကတ်တာ Leanplum နှင့် မျှဝေပေးပါ + + လေ့လာရေး စမ်းသပ်ချက်များ @@ -399,6 +412,10 @@ ကိရိယာအခင်းအကျင်းကိုလိုက်နာပါ + + + ပြန်လည်စတင်ရန်ဆွဲပါ + Sessions @@ -491,9 +508,13 @@ စုဆောင်းမှုအမည်ပြောင်းပါ တပ်ဗ်များကို ဖွင့်ပါ - + + အမည်ပြောင်းပါ + ဖယ်ရှားပါ + + မှတ်တမ်းမှ ဖျက်ပါ သိမ်းရန် @@ -534,6 +555,12 @@ မှတ်တမ်းများ ဒီမှာ မရှိပါ + + ဖွင့်ပါ + + ဖယ်ရှားပါ + + တောင်းပန်ပါတယ်။ ဒီစာမျက်နှာကို %1$s မှမတင်နိုင်ပါ။ @@ -665,6 +692,8 @@ အသံ နှင့် ဗွီဒီယို ကိုဝိုင်ဖိုင်တွင် ဖွင့်နိုင်သည် အသံကိုသာပိတ်ပါ + + အသံကိုသာပိတ်ပါ အသံ နှင့် ဗီဒီယိုကို ခွင့်ပြုပါ။ @@ -770,6 +799,9 @@ မှပံ့ပိုးသည် + + စျေးကွက်ရှာဖွေရေး + စုဆောင်းမှုကိုဖျက်လိုက်သည် @@ -856,6 +888,8 @@ သိုလှောင်မှုနေရာလွတ်သည် ဆိုဒ်ခွင့်ပြုချက်များ + + ဆွဲယူထားသည့်ဖိုင်များ ကြည့်ရှုမှုအချက်အလက်များ ဖျက်ပါ @@ -880,6 +914,9 @@ ကြည့်ရှုမှုအချက်အလက်များ ဖျက်နေသည်… + + Nightly ကိုရယူပါ + diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml index 2ee91e2d1f..c01d8d2102 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -360,7 +360,7 @@ ᱠᱷᱟᱛᱟ ᱨᱮᱭᱟᱜ ᱥᱟᱡᱟᱣ ᱠᱚ - URLs ᱟᱪᱛᱮ ᱯᱩᱨᱟᱹᱣᱼᱜ ᱢᱟ + URLs ᱟᱡ ᱛᱮ ᱯᱩᱨᱟᱹᱣᱼᱜ ᱢᱟ ᱞᱤᱝᱠ ᱠᱚ ᱮᱯ ᱨᱮ ᱡᱷᱤᱡᱽ ᱢᱮ @@ -897,7 +897,7 @@ ᱥᱟᱱᱟᱢ ᱥᱟᱭᱤᱴ ᱨᱮ ᱪᱷᱟᱹᱲ ᱠᱚ ᱯᱷᱟᱨᱪᱟᱭ ᱢᱮ - ᱟᱪᱛᱮ ᱮᱱᱮᱡ + ᱟᱡ ᱛᱮ ᱮᱱᱮᱡ ᱠᱮᱢᱨᱟ @@ -1122,7 +1122,7 @@ ᱪᱤᱠᱤ ᱨᱮᱭᱟᱜ ᱢᱟᱯ - ᱟᱪᱛᱮ ᱪᱤᱠᱤ ᱥᱟᱡᱟᱣ + ᱟᱡ ᱛᱮ ᱪᱤᱠᱤ ᱥᱟᱡᱟᱣ ᱪᱤᱠᱤ ᱢᱟᱞ ᱫᱚ ᱮᱱᱰᱨᱚᱭᱮᱰ ᱥᱟᱡᱟᱣ ᱥᱟᱞᱟᱜ ᱥᱚᱢᱟᱱᱚᱜᱼᱟ ᱾ ᱪᱤᱠᱤ ᱢᱟᱯ ᱵᱚᱸᱫᱚ ᱞᱟᱹᱜᱤᱫ ᱱᱚᱰᱮ ᱢᱮᱱᱮᱡ ᱢᱮ ᱾ @@ -1160,9 +1160,9 @@ ᱵᱽᱨᱟᱣᱡᱤᱝ ᱰᱟᱴᱟ ᱢᱮᱴᱟᱣ ᱢᱮ ᱟᱲᱟᱜ ᱜᱷᱚᱲᱤ - ᱵᱨᱟᱩᱡᱤᱝ ᱰᱟᱴᱟ ᱟᱪᱛᱮ ᱢᱮᱴᱟᱣ ᱛᱟᱦᱮᱱᱟᱭ ᱡᱚᱠᱷᱚᱱ ᱟᱢ \"ᱟᱲᱟᱹᱜ\" ᱵᱟᱪᱷᱟᱣ ᱛᱟᱦᱮᱱᱟᱢ ᱢᱩᱞ ᱢᱮᱱᱩ ᱠᱷᱚᱱ + ᱵᱨᱟᱩᱡᱤᱝ ᱰᱟᱴᱟ ᱟᱡ ᱛᱮ ᱢᱮᱴᱟᱣ ᱛᱟᱦᱮᱱᱟᱭ ᱡᱚᱠᱷᱚᱱ ᱟᱢ \"ᱟᱲᱟᱹᱜ\" ᱵᱟᱪᱷᱟᱣ ᱛᱟᱦᱮᱱᱟᱢ ᱢᱩᱞ ᱢᱮᱱᱩ ᱠᱷᱚᱱ - ᱵᱨᱟᱩᱡᱤᱝ ᱰᱟᱴᱟ ᱟᱪᱛᱮ ᱢᱮᱴᱟᱣ ᱛᱟᱦᱮᱱᱟᱭ ᱡᱚᱠᱷᱚᱱ ᱟᱢ \"ᱟᱲᱟᱹᱜ\" ᱵᱟᱪᱷᱟᱣ ᱛᱟᱦᱮᱱᱟᱢ ᱢᱩᱞ ᱢᱮᱱᱩ ᱠᱷᱚᱱ + ᱵᱨᱟᱩᱡᱤᱝ ᱰᱟᱴᱟ ᱟᱡ ᱛᱮ ᱢᱮᱴᱟᱣ ᱛᱟᱦᱮᱱᱟᱭ ᱡᱚᱠᱷᱚᱱ ᱟᱢ \"ᱟᱲᱟᱹᱜ\" ᱵᱟᱪᱷᱟᱣ ᱛᱟᱦᱮᱱᱟᱢ ᱢᱩᱞ ᱢᱮᱱᱩ ᱠᱷᱚᱱ ᱟᱲᱟᱹᱜ ᱢᱮ @@ -1288,7 +1288,7 @@ ᱛᱤᱱᱟᱹᱜ ᱜᱟᱱ ᱵᱮᱴᱨᱭ ᱵᱚᱪᱚᱛ ᱢᱮ ᱟᱨ ᱟᱢᱟᱜ ᱢᱮᱫ ᱵᱟᱧᱪᱟᱣ ᱛᱟᱢ ᱧᱩᱛ ᱩᱭᱦᱟᱹᱨ ᱪᱟᱹᱞᱩ ᱠᱟᱛᱮ ᱾ - ᱟᱪᱛᱮ + ᱟᱡ ᱛᱮ ᱟᱢᱟᱜ ᱥᱟᱫᱷᱚᱱ ᱨᱮᱭᱟᱜ ᱥᱟᱡᱟᱣ ᱦᱟᱛᱟᱣ ᱟᱭ @@ -1485,7 +1485,7 @@ ᱛᱤᱥ ᱦᱚᱸ ᱟᱞᱳᱢ ᱥᱟᱺᱪᱟᱣᱼᱟ - ᱟᱪᱛᱮ ᱯᱮᱨᱮᱡᱽ + ᱟᱡ ᱛᱮ ᱯᱮᱨᱮᱡᱽ ᱞᱚᱜᱤᱱ ᱠᱚ ᱥᱤᱸᱠ ᱢᱮ diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 46f3f7cf7b..dd9e289e46 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -4,6 +4,7 @@ %s Privat %s (Privat) + Më tepër mundësi @@ -46,6 +47,17 @@ E përzgjedhur + + + Ruajtur së fundi + + Faqerojtës ruajtur së fundi + + Shfaqi krejt + + + Shfaq butonin e krejt faqerojtësve të ruajtur + %1$s prodhohet nga Mozilla. @@ -100,6 +112,12 @@ Sajte kryesues + + + Hidhu mbrapa + + Shfaqi krejt + Skeda të Hapura @@ -1024,6 +1042,17 @@ Fshije dhe Hape Bazuar Në + + + Marketing + + %1$s është i shpejtë dhe privat + + Bëjeni %1$s-in shfletuesin tuaj parazgjedhje + + Koleksioni u fshi @@ -1572,9 +1601,9 @@ Numër Karte Datë Skadimi - + Muaj Date Skadimi - + Vit Date Skadimi Emër në Kartë @@ -1633,7 +1662,7 @@ Varg kërkimi për t’u përdorur - Zëvendësoni kërkesën me “%s”. Shembull:\nhttps://www.google.com/search?q=%s + Zëvendësoni kërkesën me “%s”. Shembull:\nhttps://www.google.com/search?q=%s Mësoni Më Tepër diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 690915cec5..0775d2355e 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -111,6 +111,9 @@ Mga pangunahing site + + + Balikan ang naiwan Ipakita lahat From 465f553ea81f3b351f0ba36d43abbf9f4e1376b6 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Tue, 8 Jun 2021 12:07:02 -0400 Subject: [PATCH 033/517] For #19886 - Remove the tracking protection indicator --- .../fenix/browser/BaseBrowserFragment.kt | 15 ---- .../mozilla/fenix/browser/BrowserFragment.kt | 19 ----- .../components/toolbar/ToolbarIntegration.kt | 59 +++----------- .../customtabs/ExternalAppBrowserFragment.kt | 18 ----- app/src/main/res/drawable-v24/shield_dark.xml | 81 ------------------- .../main/res/drawable-v24/shield_light.xml | 81 ------------------- app/src/main/res/drawable/shield_dark.xml | 31 ------- app/src/main/res/drawable/shield_light.xml | 31 ------- .../fenix/browser/BaseBrowserFragmentTest.kt | 4 - 9 files changed, 9 insertions(+), 330 deletions(-) delete mode 100644 app/src/main/res/drawable-v24/shield_dark.xml delete mode 100644 app/src/main/res/drawable-v24/shield_light.xml delete mode 100644 app/src/main/res/drawable/shield_dark.xml delete mode 100644 app/src/main/res/drawable/shield_light.xml diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index 8486e1595b..b5f1bed798 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -115,7 +115,6 @@ import org.mozilla.fenix.ext.breadcrumb import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.ext.hideToolbar -import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings @@ -397,11 +396,6 @@ abstract class BaseBrowserFragment : showQuickSettingsDialog() } - browserToolbarView.view.display.setOnTrackingProtectionClickedListener { - context.metrics.track(Event.TrackingProtectionIconPressed) - showTrackingProtectionPanel() - } - contextMenuFeature.set( feature = ContextMenuFeature( fragmentManager = parentFragmentManager, @@ -1163,8 +1157,6 @@ abstract class BaseBrowserFragment : sitePermissions: SitePermissions? ) - protected abstract fun navToTrackingProtectionPanel(tab: SessionState) - /** * Returns the layout [android.view.Gravity] for the quick settings and ETP dialog. */ @@ -1200,13 +1192,6 @@ abstract class BaseBrowserFragment : } } - private fun showTrackingProtectionPanel() { - val tab = getCurrentTab() ?: return - view?.let { - navToTrackingProtectionPanel(tab) - } - } - /** * Set the activity normal/private theme to match the current session. */ diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index 12dc527f1f..0bb5c9331b 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -37,10 +37,8 @@ import org.mozilla.fenix.components.toolbar.ToolbarMenu import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.nav -import org.mozilla.fenix.ext.navigateSafe import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.ext.runIfFragmentIsAttached import org.mozilla.fenix.shortcut.PwaOnboardingObserver import org.mozilla.fenix.theme.ThemeManager import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay @@ -337,23 +335,6 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { nav(R.id.browserFragment, directions) } - override fun navToTrackingProtectionPanel(tab: SessionState) { - val navController = findNavController() - requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains -> - runIfFragmentIsAttached { - val isEnabled = tab.trackingProtection.enabled && !contains - val directions = - BrowserFragmentDirections.actionBrowserFragmentToTrackingProtectionPanelDialogFragment( - sessionId = tab.id, - url = tab.content.url, - trackingProtectionEnabled = isEnabled, - gravity = getAppropriateLayoutGravity() - ) - navController.navigateSafe(R.id.browserFragment, directions) - } - } - } - private val collectionStorageObserver = object : TabCollectionStorage.Observer { override fun onCollectionCreated( title: String, diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt index 5a248bf122..1d1658f2b5 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt @@ -5,8 +5,6 @@ package org.mozilla.fenix.components.toolbar import android.content.Context -import android.content.res.Configuration -import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -104,49 +102,10 @@ class DefaultToolbarIntegration( toolbar.display.menuBuilder = toolbarMenu.menuBuilder toolbar.private = isPrivate - val drawable = - if (isPrivate) AppCompatResources.getDrawable( - context, - R.drawable.shield_dark - ) else when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) { - Configuration.UI_MODE_NIGHT_UNDEFINED, // We assume light here per Android doc's recommendation - Configuration.UI_MODE_NIGHT_NO -> { - AppCompatResources.getDrawable(context, R.drawable.shield_light) - } - Configuration.UI_MODE_NIGHT_YES -> { - AppCompatResources.getDrawable(context, R.drawable.shield_dark) - } - else -> AppCompatResources.getDrawable(context, R.drawable.shield_light) - } - - toolbar.display.indicators = - if (context.settings().shouldUseTrackingProtection) { - listOf( - DisplayToolbar.Indicators.TRACKING_PROTECTION, - DisplayToolbar.Indicators.SECURITY, - DisplayToolbar.Indicators.EMPTY, - DisplayToolbar.Indicators.HIGHLIGHT - ) - } else { - listOf( - DisplayToolbar.Indicators.SECURITY, - DisplayToolbar.Indicators.EMPTY, - DisplayToolbar.Indicators.HIGHLIGHT - ) - } - context.settings().shouldUseTrackingProtection - - toolbar.display.icons = toolbar.display.icons.copy( - emptyIcon = null, - trackingProtectionTrackersBlocked = drawable!!, - trackingProtectionNothingBlocked = AppCompatResources.getDrawable( - context, - R.drawable.ic_tracking_protection_enabled - )!!, - trackingProtectionException = AppCompatResources.getDrawable( - context, - R.drawable.ic_tracking_protection_disabled - )!! + toolbar.display.indicators = listOf( + DisplayToolbar.Indicators.SECURITY, + DisplayToolbar.Indicators.EMPTY, + DisplayToolbar.Indicators.HIGHLIGHT ) val tabCounterMenu = FenixTabCounterMenu( @@ -155,11 +114,11 @@ class DefaultToolbarIntegration( interactor.onTabCounterMenuItemTapped(it) }, iconColor = - if (isPrivate) { - ContextCompat.getColor(context, R.color.primary_text_private_theme) - } else { - null - } + if (isPrivate) { + ContextCompat.getColor(context, R.color.primary_text_private_theme) + } else { + null + } ).also { it.updateMenu(context.settings().toolbarPosition) } diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt index a1d88d9c66..0bd6203b5c 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt @@ -37,7 +37,6 @@ import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.ext.runIfFragmentIsAttached /** * Fragment used for browsing the web within external apps. @@ -192,23 +191,6 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler nav(R.id.externalAppBrowserFragment, directions) } - override fun navToTrackingProtectionPanel(tab: SessionState) { - requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains -> - runIfFragmentIsAttached { - val isEnabled = tab.trackingProtection.enabled && !contains - val directions = - ExternalAppBrowserFragmentDirections - .actionGlobalTrackingProtectionPanelDialogFragment( - sessionId = tab.id, - url = tab.content.url, - trackingProtectionEnabled = isEnabled, - gravity = getAppropriateLayoutGravity() - ) - nav(R.id.externalAppBrowserFragment, directions) - } - } - } - override fun getContextMenuCandidates( context: Context, view: View diff --git a/app/src/main/res/drawable-v24/shield_dark.xml b/app/src/main/res/drawable-v24/shield_dark.xml deleted file mode 100644 index 0e322f5694..0000000000 --- a/app/src/main/res/drawable-v24/shield_dark.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable-v24/shield_light.xml b/app/src/main/res/drawable-v24/shield_light.xml deleted file mode 100644 index 0e322f5694..0000000000 --- a/app/src/main/res/drawable-v24/shield_light.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/shield_dark.xml b/app/src/main/res/drawable/shield_dark.xml deleted file mode 100644 index f97e24bdba..0000000000 --- a/app/src/main/res/drawable/shield_dark.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/shield_light.xml b/app/src/main/res/drawable/shield_light.xml deleted file mode 100644 index 34cbdc10e7..0000000000 --- a/app/src/main/res/drawable/shield_light.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/test/java/org/mozilla/fenix/browser/BaseBrowserFragmentTest.kt b/app/src/test/java/org/mozilla/fenix/browser/BaseBrowserFragmentTest.kt index a018419bba..614df23d35 100644 --- a/app/src/test/java/org/mozilla/fenix/browser/BaseBrowserFragmentTest.kt +++ b/app/src/test/java/org/mozilla/fenix/browser/BaseBrowserFragmentTest.kt @@ -210,8 +210,4 @@ private class TestBaseBrowserFragment : BaseBrowserFragment() { override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) { // no-op } - - override fun navToTrackingProtectionPanel(tab: SessionState) { - // no-op - } } From cad0e110dd4677355f3708ef52e38f36bc1b4595 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Tue, 15 Jun 2021 11:13:26 -0400 Subject: [PATCH 034/517] For #19886 - Add navigation from quick settings to tracking protection panel --- .../mozilla/fenix/browser/BrowserFragment.kt | 28 ++++---- .../customtabs/ExternalAppBrowserFragment.kt | 27 ++++---- .../quicksettings/QuickSettingsController.kt | 33 ++++++++- .../QuickSettingsFragmentState.kt | 4 +- .../QuickSettingsFragmentStore.kt | 45 ++++++++++++- .../quicksettings/QuickSettingsInteractor.kt | 10 ++- .../QuickSettingsSheetDialogFragment.kt | 9 ++- .../quicksettings/TrackingProtectionView.kt | 67 +++++++++++++++++++ .../settings/quicksettings/WebsiteInfoView.kt | 13 ---- .../TrackingProtectionOverlay.kt | 6 +- .../TrackingProtectionPanelDialogFragment.kt | 6 +- .../TrackingProtectionStore.kt | 6 +- .../fragment_quick_settings_dialog_sheet.xml | 17 ++++- .../quicksettings_tracking_protection.xml | 39 +++++++++++ .../res/layout/quicksettings_website_info.xml | 27 ++------ app/src/main/res/navigation/nav_graph.xml | 3 + app/src/main/res/values/strings.xml | 3 + 17 files changed, 267 insertions(+), 76 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt create mode 100644 app/src/main/res/layout/quicksettings_tracking_protection.xml diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index 0bb5c9331b..7e6a4dd0e1 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -321,18 +321,22 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { } override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) { - val directions = - BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment( - sessionId = tab.id, - url = tab.content.url, - title = tab.content.title, - isSecured = tab.content.securityInfo.secure, - sitePermissions = sitePermissions, - gravity = getAppropriateLayoutGravity(), - certificateName = tab.content.securityInfo.issuer, - permissionHighlights = tab.content.permissionHighlights - ) - nav(R.id.browserFragment, directions) + requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains -> + val isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains + val directions = + BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment( + sessionId = tab.id, + url = tab.content.url, + title = tab.content.title, + isSecured = tab.content.securityInfo.secure, + sitePermissions = sitePermissions, + gravity = getAppropriateLayoutGravity(), + certificateName = tab.content.securityInfo.issuer, + permissionHighlights = tab.content.permissionHighlights, + isTrackingProtectionEnabled = isTrackingProtectionEnabled + ) + nav(R.id.browserFragment, directions) + } } private val collectionStorageObserver = object : TabCollectionStorage.Observer { diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt index 0bd6203b5c..6bbe13e68a 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt @@ -177,18 +177,21 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler } override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) { - val directions = ExternalAppBrowserFragmentDirections - .actionGlobalQuickSettingsSheetDialogFragment( - sessionId = tab.id, - url = tab.content.url, - title = tab.content.title, - isSecured = tab.content.securityInfo.secure, - sitePermissions = sitePermissions, - gravity = getAppropriateLayoutGravity(), - certificateName = tab.content.securityInfo.issuer, - permissionHighlights = tab.content.permissionHighlights - ) - nav(R.id.externalAppBrowserFragment, directions) + requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains -> + val directions = ExternalAppBrowserFragmentDirections + .actionGlobalQuickSettingsSheetDialogFragment( + sessionId = tab.id, + url = tab.content.url, + title = tab.content.title, + isSecured = tab.content.securityInfo.secure, + sitePermissions = sitePermissions, + gravity = getAppropriateLayoutGravity(), + certificateName = tab.content.securityInfo.issuer, + permissionHighlights = tab.content.permissionHighlights, + isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains + ) + nav(R.id.externalAppBrowserFragment, directions) + } } override fun getContextMenuCandidates( diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt index 48ea3d23c9..0d7cdfe40a 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt @@ -11,12 +11,15 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import mozilla.components.browser.state.selector.findTabOrCustomTab import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.feature.session.SessionUseCases.ReloadUrlUseCase import mozilla.components.concept.engine.permission.SitePermissions +import mozilla.components.feature.session.SessionUseCases.ReloadUrlUseCase import mozilla.components.feature.tabs.TabsUseCases.AddNewTabUseCase import mozilla.components.support.base.feature.OnNeedToRequestPermissions import mozilla.components.support.ktx.kotlin.getOrigin +import org.mozilla.fenix.R import org.mozilla.fenix.components.PermissionStorage +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.nav import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled import org.mozilla.fenix.settings.toggle @@ -54,6 +57,16 @@ interface QuickSettingsController { * feature [PhoneFeature] which the user granted Android permission(s) for. */ fun handleAndroidPermissionGranted(feature: PhoneFeature) + + /** + * @see [TrackingProtectionInteractor.onTrackingProtectionToggled] + */ + fun handleTrackingProtectionToggled(isEnabled: Boolean) + + /** + * @see [TrackingProtectionInteractor.onBlockedItemsClicked] + */ + fun handleBlockedItemsClicked() } /** @@ -155,6 +168,24 @@ class DefaultQuickSettingsController( ) } + override fun handleTrackingProtectionToggled(isEnabled: Boolean) { + TODO("Not yet implemented") + } + + override fun handleBlockedItemsClicked() { + dismiss.invoke() + + val state = quickSettingsStore.state.trackingProtectionState + val directions = QuickSettingsSheetDialogFragmentDirections + .actionGlobalTrackingProtectionPanelDialogFragment( + sessionId = sessionId, + url = state.url, + trackingProtectionEnabled = state.isTrackingProtectionEnabled, + gravity = context.components.settings.toolbarPosition.androidGravity + ) + navController.nav(R.id.quickSettingsSheetDialogFragment, directions) + } + /** * Request a certain set of runtime Android permissions. * diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt index 0d9e8c75d9..bcf6ce1933 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentState.kt @@ -15,6 +15,7 @@ import mozilla.components.feature.sitepermissions.SitePermissionsRules.AutoplayA import mozilla.components.lib.state.State import org.mozilla.fenix.R import org.mozilla.fenix.settings.PhoneFeature +import org.mozilla.fenix.trackingprotection.TrackingProtectionState import org.mozilla.fenix.utils.Settings /** @@ -24,7 +25,8 @@ import org.mozilla.fenix.utils.Settings */ data class QuickSettingsFragmentState( val webInfoState: WebsiteInfoState, - val websitePermissionsState: WebsitePermissionsState + val websitePermissionsState: WebsitePermissionsState, + val trackingProtectionState: TrackingProtectionState ) : State /** diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt index cbf36b871e..45218a7634 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt @@ -6,16 +6,19 @@ package org.mozilla.fenix.settings.quicksettings import android.content.Context import androidx.annotation.VisibleForTesting +import mozilla.components.browser.state.selector.findTabOrCustomTab import mozilla.components.browser.state.state.content.PermissionHighlightsState import mozilla.components.concept.engine.permission.SitePermissions import mozilla.components.lib.state.Action import mozilla.components.lib.state.Reducer import mozilla.components.lib.state.State import mozilla.components.lib.state.Store +import org.mozilla.fenix.ext.components import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.createStore import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled import org.mozilla.fenix.settings.quicksettings.ext.shouldBeVisible +import org.mozilla.fenix.trackingprotection.TrackingProtectionState import org.mozilla.fenix.utils.Settings import java.util.EnumMap @@ -48,7 +51,10 @@ class QuickSettingsFragmentStore( * @param isSecured [Boolean] whether the connection is secured (TLS) or not. * @param permissions [SitePermissions]? list of website permissions and their status. * @param settings [Settings] application settings. - * @param certificateName [String] the certificate name of the current web page. + * @param certificateName [String] the certificate name of the current web page. + * @param sessionId [String] TODO + * @param isTrackingProtectionEnabled [Boolean] Current status of tracking protection + * for this session. */ @Suppress("LongParameterList") fun createStore( @@ -59,7 +65,9 @@ class QuickSettingsFragmentStore( isSecured: Boolean, permissions: SitePermissions?, permissionHighlights: PermissionHighlightsState, - settings: Settings + settings: Settings, + sessionId: String, + isTrackingProtectionEnabled: Boolean ) = QuickSettingsFragmentStore( QuickSettingsFragmentState( webInfoState = createWebsiteInfoState( @@ -73,6 +81,12 @@ class QuickSettingsFragmentStore( permissions, permissionHighlights, settings + ), + trackingProtectionState = createTrackingProtectionState( + context, + sessionId, + websiteUrl, + isTrackingProtectionEnabled ) ) ) @@ -127,6 +141,33 @@ class QuickSettingsFragmentStore( return state } + /** + * Construct an initial [TrackingProtectionState] to be rendered by + * [TrackingProtectionView]. + * + * @param context [Context] used for various Android interactions. + * @param sessionId [String] TODO + * @param websiteUrl [String] the URL of the current web page. + * @param isTrackingProtectionEnabled [Boolean] Current status of tracking protection + * for this session. + */ + @VisibleForTesting + fun createTrackingProtectionState( + context: Context, + sessionId: String, + websiteUrl: String, + isTrackingProtectionEnabled: Boolean + ): TrackingProtectionState { + return TrackingProtectionState( + tab = context.components.core.store.state.findTabOrCustomTab(sessionId), + url = websiteUrl, + isTrackingProtectionEnabled = isTrackingProtectionEnabled, + listTrackers = listOf(), + mode = TrackingProtectionState.Mode.Normal, + lastAccessedCategory = "" + ) + } + /** * [PhoneFeature] to a [WebsitePermission] mapper. */ diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt index 0b5ea9086e..08e3e02176 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt @@ -15,7 +15,7 @@ package org.mozilla.fenix.settings.quicksettings */ class QuickSettingsInteractor( private val controller: QuickSettingsController -) : WebsitePermissionInteractor { +) : WebsitePermissionInteractor, TrackingProtectionInteractor { override fun onPermissionsShown() { controller.handlePermissionsShown() } @@ -27,4 +27,12 @@ class QuickSettingsInteractor( override fun onAutoplayChanged(value: AutoplayValue) { controller.handleAutoplayChanged(value) } + + override fun onTrackingProtectionToggled(isEnabled: Boolean) { + controller.handleTrackingProtectionToggled(isEnabled) + } + + override fun onBlockedItemsClicked() { + controller.handleBlockedItemsClicked() + } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt index eb933655c5..9b3c12c209 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt @@ -50,7 +50,9 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { private lateinit var quickSettingsController: QuickSettingsController private lateinit var websiteInfoView: WebsiteInfoView private lateinit var websitePermissionsView: WebsitePermissionsView + private lateinit var trackingProtectionView: TrackingProtectionView private lateinit var interactor: QuickSettingsInteractor + private var tryToRequestPermissions: Boolean = false private val args by navArgs() @@ -77,7 +79,9 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { permissions = args.sitePermissions, settings = components.settings, certificateName = args.certificateName, - permissionHighlights = args.permissionHighlights + permissionHighlights = args.permissionHighlights, + sessionId = args.sessionId, + isTrackingProtectionEnabled = args.isTrackingProtectionEnabled ) quickSettingsController = DefaultQuickSettingsController( @@ -105,6 +109,8 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { websiteInfoView = WebsiteInfoView(binding.websiteInfoLayout) websitePermissionsView = WebsitePermissionsView(binding.websitePermissionsLayout, interactor) + trackingProtectionView = + TrackingProtectionView(rootView.trackingProtectionLayout, interactor) return rootView } @@ -143,6 +149,7 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { consumeFrom(quickSettingsStore) { websiteInfoView.update(it.webInfoState) websitePermissionsView.update(it.websitePermissionsState) + trackingProtectionView.update(it.trackingProtectionState) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt new file mode 100644 index 0000000000..158e9701a9 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.quicksettings + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.component_tracking_protection_panel.trackingProtectionSwitch +import kotlinx.android.synthetic.main.quicksettings_tracking_protection.* +import kotlinx.android.synthetic.main.switch_with_description.view.* +import org.mozilla.fenix.R +import org.mozilla.fenix.trackingprotection.TrackingProtectionState + +/** + * Contract declaring all possible user interactions with [TrackingProtectionView] + */ +interface TrackingProtectionInteractor { + + /** + * Called whenever the tracking protection toggle for this site is toggled + * + * @param isEnabled new status of session tracking protection + */ + fun onTrackingProtectionToggled(isEnabled: Boolean) + + /** + * Navigates to the tracking protection preferences. Called when a user clicks on the + * "Blocked items" button. + */ + fun onBlockedItemsClicked() +} + +/** + * TODO + * + * @param containerView [ViewGroup] in which this View will inflate itself. + * @param interactor [TrackingProtectionInteractor] which will have delegated to all user interactions. + */ +class TrackingProtectionView( + override val containerView: ViewGroup, + val interactor: TrackingProtectionInteractor +) : LayoutContainer { + + private val context = containerView.context + val view: View = LayoutInflater.from(context) + .inflate(R.layout.quicksettings_tracking_protection, containerView, true) + + fun update(state: TrackingProtectionState) { + bindTrackingProtectionInfo(state.isTrackingProtectionEnabled) + + trackingProtectionBlockedItems.setOnClickListener { + interactor.onBlockedItemsClicked() + } + } + + private fun bindTrackingProtectionInfo(isTrackingProtectionEnabled: Boolean) { + trackingProtectionSwitch.switch_widget.isChecked = isTrackingProtectionEnabled + trackingProtectionSwitch.switch_widget.jumpDrawablesToCurrentState() + + trackingProtectionSwitch.switch_widget.setOnCheckedChangeListener { _, isChecked -> + interactor.onTrackingProtectionToggled(isChecked) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt index 4a50c37830..c7c50fabe8 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoView.kt @@ -35,26 +35,13 @@ class WebsiteInfoView( */ fun update(state: WebsiteInfoState) { bindUrl(state.websiteUrl) - bindTitle(state.websiteTitle) bindSecurityInfo(state.websiteSecurityUiValues) - bindCertificateName(state.certificateName) } private fun bindUrl(websiteUrl: String) { binding.url.text = websiteUrl } - private fun bindTitle(websiteTitle: String) { - binding.title.text = websiteTitle - } - - private fun bindCertificateName(cert: String) { - val certificateLabel = - binding.root.context.getString(R.string.certificate_info_verified_by, cert) - binding.certificateInfo.text = certificateLabel - binding.certificateInfo.isVisible = cert.isNotEmpty() - } - private fun bindSecurityInfo(uiValues: WebsiteSecurityUiValues) { val tint = getColor(binding.root.context, uiValues.iconTintRes) binding.securityInfo.setText(uiValues.securityInfoRes) diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt index 3e03dc54d3..85ef24084a 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt @@ -148,7 +148,7 @@ class TrackingProtectionOverlay( val toolbar = getToolbar() val trackingProtectionIcon: View = - toolbar.findViewById(R.id.mozac_browser_toolbar_tracking_protection_indicator) + toolbar.findViewById(R.id.mozac_browser_toolbar_security_indicator) val xOffset = triangleMarginStartPx + triangleWidthPx / 2 @@ -172,12 +172,10 @@ class TrackingProtectionOverlay( it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) } - val etpShield = - getToolbar().findViewById(R.id.mozac_browser_toolbar_tracking_protection_indicator) trackingOnboardingDialog.message.setOnClickListener { metrics.track(Event.ContextualHintETPInsideTap) trackingOnboardingDialog.dismiss() - etpShield.performClick() + trackingProtectionIcon.performClick() } metrics.track(Event.ContextualHintETPDisplayed) diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt index ec7ad0cdc4..445c8e4cb8 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt @@ -87,9 +87,9 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt trackingProtectionStore = StoreProvider.get(this) { TrackingProtectionStore( TrackingProtectionState( - tab, - args.url, - args.trackingProtectionEnabled, + tab = tab, + url = args.url, + isTrackingProtectionEnabled = args.trackingProtectionEnabled, listTrackers = listOf(), mode = TrackingProtectionState.Mode.Normal, lastAccessedCategory = "" diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt index 394587b9fe..087b7392f4 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt @@ -47,12 +47,14 @@ sealed class TrackingProtectionAction : Action { /** * The state for the Tracking Protection Panel + * @property tab TODO * @property url Current URL to display - * @property isTrackingProtectionEnabled Current status of tracking protection for this session (ie is an exception) + * @property isTrackingProtectionEnabled Current status of tracking protection for this session + * (ie is an exception) * @property listTrackers Current Tracker Log list of blocked and loaded tracker categories * @property mode Current Mode of TrackingProtection * @property lastAccessedCategory Remembers the last accessed details category, used to move - * accessibly focus after returning from details_moode + * accessibly focus after returning from details_mode */ data class TrackingProtectionState( val tab: SessionState?, diff --git a/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml b/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml index f5d321de7f..0a785afb15 100644 --- a/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml +++ b/app/src/main/res/layout/fragment_quick_settings_dialog_sheet.xml @@ -26,7 +26,7 @@ android:id="@+id/websitePermissionsLayout" android:layout_width="match_parent" android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="parent" /> + app:layout_constraintBottom_toTopOf="@id/trackingProtectionDivider" /> + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/quicksettings_website_info.xml b/app/src/main/res/layout/quicksettings_website_info.xml index 594138d944..815e0c0ac0 100644 --- a/app/src/main/res/layout/quicksettings_website_info.xml +++ b/app/src/main/res/layout/quicksettings_website_info.xml @@ -16,16 +16,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:minHeight="@dimen/tracking_protection_item_height"> - - + android:minHeight="@dimen/tracking_protection_item_height" + android:paddingTop="8dp" + android:paddingBottom="8dp"> - - + tools:text="Connection is secure" /> diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index d780417aa4..941860b0d9 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -780,6 +780,9 @@ + Cryptominers Fingerprinters + + Blocked items + Blocked Allowed From 61dfb40339a8b73995bc2c2cea0ba91f0d503976 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Wed, 16 Jun 2021 18:36:30 -0400 Subject: [PATCH 035/517] For #19886 - Handle toggling tracking protection in quick settings --- .../quicksettings/QuickSettingsController.kt | 21 +++- .../QuickSettingsFragmentAction.kt | 14 +++ .../QuickSettingsFragmentReducer.kt | 24 +++++ .../quicksettings/TrackingProtectionView.kt | 15 +-- .../TrackingProtectionPanelDialogFragment.kt | 21 ---- .../TrackingProtectionPanelInteractor.kt | 5 - .../TrackingProtectionPanelView.kt | 19 ---- .../TrackingProtectionStore.kt | 4 - .../component_tracking_protection_panel.xml | 14 +-- .../DefaultQuickSettingsControllerTest.kt | 102 +++++++++++++++++- .../QuickSettingsFragmentReducerTest.kt | 29 +++++ .../QuickSettingsFragmentStoreTest.kt | 55 ++++++++-- .../QuickSettingsInteractorTest.kt | 20 ++++ .../quicksettings/WebsiteInfoViewTest.kt | 7 -- .../TrackingProtectionPanelInteractorTest.kt | 12 --- .../TrackingProtectionStoreTest.kt | 17 --- 16 files changed, 265 insertions(+), 114 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt index 0d7cdfe40a..83a06fc43c 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt @@ -18,7 +18,9 @@ import mozilla.components.support.base.feature.OnNeedToRequestPermissions import mozilla.components.support.ktx.kotlin.getOrigin import org.mozilla.fenix.R import org.mozilla.fenix.components.PermissionStorage +import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled @@ -169,7 +171,24 @@ class DefaultQuickSettingsController( } override fun handleTrackingProtectionToggled(isEnabled: Boolean) { - TODO("Not yet implemented") + val components = context.components + val sessionState = components.core.store.state.findTabOrCustomTab(sessionId) + + sessionState?.let { session -> + val trackingProtectionUseCases = components.useCases.trackingProtectionUseCases + val sessionUseCases = components.useCases.sessionUseCases + + if (isEnabled) { + trackingProtectionUseCases.removeException(session.id) + } else { + context.metrics.track(Event.TrackingProtectionException) + trackingProtectionUseCases.addException(session.id) + } + + sessionUseCases.reload.invoke(session.id) + } + + quickSettingsStore.dispatch(TrackingProtectionAction.ToggleTrackingProtectionEnabled(isEnabled)) } override fun handleBlockedItemsClicked() { diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentAction.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentAction.kt index 48d785ca28..8d24dd7c75 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentAction.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentAction.kt @@ -6,6 +6,7 @@ package org.mozilla.fenix.settings.quicksettings import mozilla.components.lib.state.Action import org.mozilla.fenix.settings.PhoneFeature +import org.mozilla.fenix.trackingprotection.TrackingProtectionState /** * Parent [Action] for all the [QuickSettingsFragmentState] changes. @@ -46,3 +47,16 @@ sealed class WebsitePermissionAction(open val updatedFeature: PhoneFeature) : Qu val autoplayValue: AutoplayValue ) : WebsitePermissionAction(PhoneFeature.AUTOPLAY) } + +/** + * All possible [TrackingProtectionState] changes as a result oof user / system interactions. + */ +sealed class TrackingProtectionAction : QuickSettingsFragmentAction() { + /** + * Toggles the enabled state of tracking protection. + * + * @param isTrackingProtectionEnabled Whether or not tracking protection is enabled. + */ + data class ToggleTrackingProtectionEnabled(val isTrackingProtectionEnabled: Boolean) : + TrackingProtectionAction() +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt index 3260fee33a..0e3654389d 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducer.kt @@ -4,6 +4,8 @@ package org.mozilla.fenix.settings.quicksettings +import org.mozilla.fenix.trackingprotection.TrackingProtectionState + /** * Parent Reducer for all [QuickSettingsFragmentState]s of all Views shown in this Fragment. */ @@ -24,6 +26,12 @@ internal fun quickSettingsFragmentReducer( action ) ) + is TrackingProtectionAction -> state.copy( + trackingProtectionState = TrackingProtectionStateReducer.reduce( + state = state.trackingProtectionState, + action = action + ) + ) } } @@ -58,3 +66,19 @@ object WebsitePermissionsStateReducer { } } } + +object TrackingProtectionStateReducer { + /** + * Handles creating a new [TrackingProtectionState] based on the specific + * [TrackingProtectionAction]. + */ + fun reduce( + state: TrackingProtectionState, + action: TrackingProtectionAction + ): TrackingProtectionState { + return when (action) { + is TrackingProtectionAction.ToggleTrackingProtectionEnabled -> + state.copy(isTrackingProtectionEnabled = action.isTrackingProtectionEnabled) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt index 158e9701a9..1c8aa1380d 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt @@ -8,21 +8,20 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.component_tracking_protection_panel.trackingProtectionSwitch import kotlinx.android.synthetic.main.quicksettings_tracking_protection.* import kotlinx.android.synthetic.main.switch_with_description.view.* import org.mozilla.fenix.R import org.mozilla.fenix.trackingprotection.TrackingProtectionState /** - * Contract declaring all possible user interactions with [TrackingProtectionView] + * Contract declaring all possible user interactions with [TrackingProtectionView]. */ interface TrackingProtectionInteractor { /** - * Called whenever the tracking protection toggle for this site is toggled + * Called whenever the tracking protection toggle for this site is toggled. * - * @param isEnabled new status of session tracking protection + * @param isEnabled Whether or not tracking protection is enabled. */ fun onTrackingProtectionToggled(isEnabled: Boolean) @@ -34,10 +33,12 @@ interface TrackingProtectionInteractor { } /** - * TODO + * MVI View that displays the tracking protection toggle and navigation to additional tracking + * protection details. * * @param containerView [ViewGroup] in which this View will inflate itself. - * @param interactor [TrackingProtectionInteractor] which will have delegated to all user interactions. + * @param interactor [TrackingProtectionInteractor] which will have delegated to all user + * interactions. */ class TrackingProtectionView( override val containerView: ViewGroup, @@ -57,6 +58,8 @@ class TrackingProtectionView( } private fun bindTrackingProtectionInfo(isTrackingProtectionEnabled: Boolean) { + trackingProtectionSwitch.trackingProtectionCategoryItemDescription.text = + view.context.getString(if (isTrackingProtectionEnabled) R.string.etp_panel_on else R.string.etp_panel_off) trackingProtectionSwitch.switch_widget.isChecked = isTrackingProtectionEnabled trackingProtectionSwitch.switch_widget.jumpDrawablesToCurrentState() diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt index 445c8e4cb8..0c29fda19c 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt @@ -41,7 +41,6 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents @@ -98,7 +97,6 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt } trackingProtectionInteractor = TrackingProtectionPanelInteractor( trackingProtectionStore, - ::toggleTrackingProtection, ::openTrackingProtectionSettings ) trackingProtectionView = @@ -143,25 +141,6 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt ) } - private fun toggleTrackingProtection(isEnabled: Boolean) { - context?.let { context -> - val session = context.components.core.store.state.findTabOrCustomTab(args.sessionId) - session?.let { - if (isEnabled) { - trackingProtectionUseCases.removeException(it.id) - } else { - context.metrics.track(Event.TrackingProtectionException) - trackingProtectionUseCases.addException(it.id) - } - - with(context.components) { - useCases.sessionUseCases.reload.invoke(session.id) - } - } - } - trackingProtectionStore.dispatch(TrackingProtectionAction.TrackerBlockingChanged(isEnabled)) - } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return if (args.gravity == Gravity.BOTTOM) { object : BottomSheetDialog(requireContext(), this.theme) { diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelInteractor.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelInteractor.kt index d6e326b4a5..a7b65976ad 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelInteractor.kt @@ -10,7 +10,6 @@ package org.mozilla.fenix.trackingprotection */ class TrackingProtectionPanelInteractor( private val store: TrackingProtectionStore, - private val toggleTrackingProtection: (Boolean) -> Unit, private val openTrackingProtectionSettings: () -> Unit ) : TrackingProtectionPanelViewInteractor { override fun openDetails(category: TrackingProtectionCategory, categoryBlocked: Boolean) { @@ -21,10 +20,6 @@ class TrackingProtectionPanelInteractor( openTrackingProtectionSettings.invoke() } - override fun trackingProtectionToggled(isEnabled: Boolean) { - toggleTrackingProtection.invoke(isEnabled) - } - override fun onBackPressed() { store.dispatch(TrackingProtectionAction.ExitDetailsMode) } diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt index 7b86e88a00..437064f5a2 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt @@ -18,7 +18,6 @@ import androidx.core.view.isVisible import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.component_tracking_protection_panel.* import kotlinx.android.synthetic.main.component_tracking_protection_panel.details_blocking_header -import kotlinx.android.synthetic.main.switch_with_description.view.* import mozilla.components.browser.state.state.CustomTabSessionState import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes import org.mozilla.fenix.R @@ -41,12 +40,6 @@ interface TrackingProtectionPanelViewInteractor { */ fun selectTrackingProtectionSettings() - /** - * Called whenever the tracking protection toggle for this site is toggled - * @param isEnabled new status of session tracking protection - */ - fun trackingProtectionToggled(isEnabled: Boolean) - /** * Called whenever back is pressed */ @@ -111,7 +104,6 @@ class TrackingProtectionPanelView( not_blocking_header.isGone = bucketedTrackers.loadedIsEmpty() bindUrl(state.url) - bindTrackingProtectionInfo(state.isTrackingProtectionEnabled) blocking_header.isGone = bucketedTrackers.blockedIsEmpty() updateCategoryVisibility() @@ -225,17 +217,6 @@ class TrackingProtectionPanelView( this.url.text = url.toUri().hostWithoutCommonPrefixes } - private fun bindTrackingProtectionInfo(isTrackingProtectionOn: Boolean) { - trackingProtectionSwitch.trackingProtectionCategoryItemDescription.text = - view.context.getString(if (isTrackingProtectionOn) R.string.etp_panel_on else R.string.etp_panel_off) - trackingProtectionSwitch.switch_widget.isChecked = isTrackingProtectionOn - trackingProtectionSwitch.switch_widget.jumpDrawablesToCurrentState() - - trackingProtectionSwitch.switch_widget.setOnCheckedChangeListener { _, isChecked -> - interactor.trackingProtectionToggled(isChecked) - } - } - fun onBackPressed(): Boolean { return when (mode) { is TrackingProtectionState.Mode.Details -> { diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt index 087b7392f4..2b2ea06828 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt @@ -34,8 +34,6 @@ sealed class TrackingProtectionAction : Action { data class UrlChange(val url: String) : TrackingProtectionAction() data class TrackerLogChange(val listTrackers: List) : TrackingProtectionAction() - data class TrackerBlockingChanged(val isTrackingProtectionEnabled: Boolean) : - TrackingProtectionAction() object ExitDetailsMode : TrackingProtectionAction() data class EnterDetailsMode( @@ -134,7 +132,5 @@ fun trackingProtectionStateReducer( ), lastAccessedCategory = action.category.name ) - is TrackingProtectionAction.TrackerBlockingChanged -> - state.copy(isTrackingProtectionEnabled = action.isTrackingProtectionEnabled) } } diff --git a/app/src/main/res/layout/component_tracking_protection_panel.xml b/app/src/main/res/layout/component_tracking_protection_panel.xml index b58da52d59..b1d2e7d34c 100644 --- a/app/src/main/res/layout/component_tracking_protection_panel.xml +++ b/app/src/main/res/layout/component_tracking_protection_panel.xml @@ -28,18 +28,6 @@ app:layout_constraintTop_toTopOf="parent" tools:text="https://wikipedia.org" /> - - ) -> Unit + private lateinit var controller: DefaultQuickSettingsController @Before @@ -83,7 +93,25 @@ class DefaultQuickSettingsControllerTest { tab = createTab("https://mozilla.org") browserStore = BrowserStore(BrowserState(tabs = listOf(tab))) sitePermissions = SitePermissions(origin = "", savedAt = 123) - controller = spyk(createController()) + + controller = spyk( + DefaultQuickSettingsController( + context = context, + quickSettingsStore = store, + browserStore = browserStore, + sessionId = tab.id, + ioScope = coroutinesScope, + navController = navController, + sitePermissions = sitePermissions, + settings = appSettings, + permissionStorage = permissionStorage, + reload = reload, + addNewTab = addNewTab, + requestRuntimePermissions = requestPermissions, + displayPermissions = displayPermissions, + dismiss = dismiss + ) + ) } @After @@ -156,8 +184,9 @@ class DefaultQuickSettingsControllerTest { permissionStorage = permissionStorage, reload = reload, addNewTab = addNewTab, + requestRuntimePermissions = requestPermissions, displayPermissions = {}, - dismiss = {} + dismiss = dismiss ) every { websitePermission.phoneFeature } returns PhoneFeature.CAMERA @@ -292,4 +321,71 @@ class DefaultQuickSettingsControllerTest { dismiss = dismiss ) } + + @Test + fun `handleTrackingProtectionToggled should call the right use cases`() { + val trackingProtectionUseCases: TrackingProtectionUseCases = mockk(relaxed = true) + val sessionUseCases: SessionUseCases = mockk(relaxed = true) + val metrics: MetricController = mockk(relaxed = true) + + every { context.components.core.store } returns browserStore + every { context.components.useCases.trackingProtectionUseCases } returns trackingProtectionUseCases + every { context.components.useCases.sessionUseCases } returns sessionUseCases + every { context.metrics } returns metrics + every { store.dispatch(any()) } returns mockk() + + var isEnabled = true + + controller.handleTrackingProtectionToggled(isEnabled) + + verify { + trackingProtectionUseCases.removeException(tab.id) + sessionUseCases.reload.invoke(tab.id) + store.dispatch(TrackingProtectionAction.ToggleTrackingProtectionEnabled(isEnabled)) + } + + isEnabled = false + + controller.handleTrackingProtectionToggled(isEnabled) + + verify { + metrics.track(Event.TrackingProtectionException) + trackingProtectionUseCases.addException(tab.id) + sessionUseCases.reload.invoke(tab.id) + store.dispatch(TrackingProtectionAction.ToggleTrackingProtectionEnabled(isEnabled)) + } + } + + @Test + fun `handleBlockedItemsClicked should call dismiss and navigate to the tracking protection panel dialog`() { + every { context.components.core.store } returns browserStore + every { context.components.settings } returns appSettings + every { context.components.settings.toolbarPosition.androidGravity } returns mockk(relaxed = true) + + val isTrackingProtectionEnabled = true + val state = QuickSettingsFragmentStore.createTrackingProtectionState( + context = context, + websiteUrl = tab.content.url, + sessionId = tab.id, + isTrackingProtectionEnabled = isTrackingProtectionEnabled + ) + + every { store.state.trackingProtectionState } returns state + + controller.handleBlockedItemsClicked() + + verify { + dismiss.invoke() + + navController.nav( + R.id.quickSettingsSheetDialogFragment, + QuickSettingsSheetDialogFragmentDirections.actionGlobalTrackingProtectionPanelDialogFragment( + sessionId = tab.id, + url = state.url, + trackingProtectionEnabled = state.isTrackingProtectionEnabled, + gravity = context.components.settings.toolbarPosition.androidGravity + ) + ) + } + } } diff --git a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt index 82653d1805..7b355ee123 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentReducerTest.kt @@ -4,11 +4,16 @@ package org.mozilla.fenix.settings.quicksettings +import kotlinx.coroutines.runBlocking import mozilla.components.feature.sitepermissions.SitePermissionsRules +import mozilla.components.support.test.mock import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotSame import org.junit.Assert.assertTrue import org.junit.Test import org.mozilla.fenix.settings.PhoneFeature +import org.mozilla.fenix.trackingprotection.TrackingProtectionState class QuickSettingsFragmentReducerTest { @@ -70,6 +75,30 @@ class QuickSettingsFragmentReducerTest { assertEquals(autoplayValue, result.autoplayValue) } + @Test + fun `TrackingProtectionAction - ToggleTrackingProtectionEnabled`() = runBlocking { + val state = QuickSettingsFragmentState( + webInfoState = mock(), + websitePermissionsState = mock(), + trackingProtectionState = TrackingProtectionState( + tab = null, + url = "https://www.firefox.com", + isTrackingProtectionEnabled = true, + listTrackers = listOf(), + mode = TrackingProtectionState.Mode.Normal, + lastAccessedCategory = "" + ) + ) + + val newState = quickSettingsFragmentReducer( + state = state, + action = TrackingProtectionAction.ToggleTrackingProtectionEnabled(false) + ) + + assertNotSame(state, newState) + assertFalse(newState.trackingProtectionState.isTrackingProtectionEnabled) + } + private fun createTestRule() = SitePermissionsRules( SitePermissionsRules.Action.ALLOWED, SitePermissionsRules.Action.ALLOWED, diff --git a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStoreTest.kt index 7ff135006a..bf8994126e 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStoreTest.kt @@ -5,19 +5,23 @@ package org.mozilla.fenix.settings.quicksettings import android.content.pm.PackageManager +import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.spyk import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking +import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.content.PermissionHighlightsState +import mozilla.components.browser.state.state.createTab +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.permission.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissionsRules -import mozilla.components.feature.sitepermissions.SitePermissionsRules.AutoplayAction import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action +import mozilla.components.feature.sitepermissions.SitePermissionsRules.AutoplayAction +import mozilla.components.support.test.mock import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -29,12 +33,14 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.R +import org.mozilla.fenix.ext.components import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.toWebsitePermission import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled import org.mozilla.fenix.settings.quicksettings.ext.shouldBeVisible import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_ALL +import org.mozilla.fenix.trackingprotection.TrackingProtectionState import org.mozilla.fenix.utils.Settings @RunWith(FenixRobolectricTestRunner::class) @@ -59,21 +65,32 @@ class QuickSettingsFragmentStoreTest { @Test fun `createStore constructs a QuickSettingsFragmentState`() { + val tab = createTab( + url = "https://www.firefox.com", + title = "Firefox" + ) + val browserStore = BrowserStore(BrowserState(tabs = listOf(tab))) + + every { context.components.core.store } returns browserStore + val store = QuickSettingsFragmentStore.createStore( context = context, - websiteUrl = "url", - websiteTitle = "Hello", + websiteUrl = tab.content.url, + websiteTitle = tab.content.title, certificateName = "issuer", isSecured = true, permissions = permissions, permissionHighlights = permissionHighlights, - settings = appSettings + settings = appSettings, + sessionId = tab.id, + isTrackingProtectionEnabled = true ) assertNotNull(store) assertNotNull(store.state) assertNotNull(store.state.webInfoState) assertNotNull(store.state.websitePermissionsState) + assertNotNull(store.state.trackingProtectionState) } @Test @@ -264,7 +281,9 @@ class QuickSettingsFragmentStoreTest { ) ) val initialState = QuickSettingsFragmentState( - websiteInfoState, initialWebsitePermissionsState + webInfoState = websiteInfoState, + websitePermissionsState = initialWebsitePermissionsState, + trackingProtectionState = mock() ) val store = QuickSettingsFragmentStore(initialState) @@ -313,6 +332,30 @@ class QuickSettingsFragmentStoreTest { assertEquals(defaultBlockedByAndroidStatus, store.state.websitePermissionsState.getValue(PhoneFeature.LOCATION).isBlockedByAndroid) } + @Test + fun `createTrackingProtectionState constructs a TrackingProtectionState with the right values`() { + val tab = createTab("https://www.firefox.com") + val browserStore = BrowserStore(BrowserState(tabs = listOf(tab))) + val isTrackingProtectionEnabled = true + + every { context.components.core.store } returns browserStore + + val state = QuickSettingsFragmentStore.createTrackingProtectionState( + context = context, + websiteUrl = tab.content.url, + sessionId = tab.id, + isTrackingProtectionEnabled = isTrackingProtectionEnabled + ) + + assertNotNull(state) + assertEquals(tab, state.tab) + assertEquals(tab.content.url, state.url) + assertEquals(isTrackingProtectionEnabled, state.isTrackingProtectionEnabled) + assertEquals(0, state.listTrackers.size) + assertEquals(TrackingProtectionState.Mode.Normal, state.mode) + assertEquals("", state.lastAccessedCategory) + } + private fun getRules() = SitePermissionsRules( camera = Action.ASK_TO_ALLOW, location = Action.ASK_TO_ALLOW, diff --git a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractorTest.kt index 09e5b349e3..ed12816843 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractorTest.kt @@ -53,4 +53,24 @@ class QuickSettingsInteractorTest { assertTrue(permission.isCaptured) assertEquals(websitePermission, permission.captured) } + + @Test + fun `onTrackingProtectionToggled should delegate the controller`() { + val isEnabled = true + + interactor.onTrackingProtectionToggled(isEnabled) + + verify { + controller.handleTrackingProtectionToggled(isEnabled) + } + } + + @Test + fun `onBlockedItemsClicked should delegate the controller`() { + interactor.onBlockedItemsClicked() + + verify { + controller.handleBlockedItemsClicked() + } + } } diff --git a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoViewTest.kt b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoViewTest.kt index 7fe563a3ba..75d7357a54 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoViewTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/WebsiteInfoViewTest.kt @@ -5,11 +5,8 @@ package org.mozilla.fenix.settings.quicksettings import android.widget.FrameLayout -import androidx.core.view.isVisible import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -40,9 +37,7 @@ class WebsiteInfoViewTest { ) assertEquals("https://mozilla.org", binding.url.text) - assertEquals("Mozilla", binding.title.text) assertEquals("Secure Connection", binding.securityInfo.text) - assertFalse(binding.certificateInfo.isVisible) } @Test @@ -57,7 +52,5 @@ class WebsiteInfoViewTest { ) assertEquals("Insecure Connection", binding.securityInfo.text) - assertEquals("Verified By: Certificate", binding.certificateInfo.text) - assertTrue(binding.certificateInfo.isVisible) } } diff --git a/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelInteractorTest.kt index 5df05bf3bd..14c123cdb5 100644 --- a/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelInteractorTest.kt @@ -55,18 +55,6 @@ class TrackingProtectionPanelInteractorTest { assertEquals(true, openSettings) } - @Test - fun trackingProtectionToggled() { - var trackingProtectionNewValue: Boolean? = null - val interactor = TrackingProtectionPanelInteractor( - mockk(), - { trackingProtectionNewValue = it }, - { } - ) - interactor.trackingProtectionToggled(true) - assertEquals(true, trackingProtectionNewValue) - } - @Test fun onBackPressed() { val store: TrackingProtectionStore = mockk(relaxed = true) diff --git a/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStoreTest.kt b/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStoreTest.kt index 2083960295..c1bbd0f6c5 100644 --- a/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStoreTest.kt @@ -50,23 +50,6 @@ class TrackingProtectionStoreTest { assertEquals(store.state.lastAccessedCategory, initialState.lastAccessedCategory) } - @Test - fun trackerBlockingChanged() = runBlocking { - val initialState = defaultState() - val store = TrackingProtectionStore(initialState) - - store.dispatch(TrackingProtectionAction.TrackerBlockingChanged(false)).join() - assertNotSame(initialState, store.state) - assertEquals( - store.state.mode, - TrackingProtectionState.Mode.Normal - ) - assertEquals( - false, - store.state.isTrackingProtectionEnabled - ) - } - @Test fun trackerListChanged() = runBlocking { val initialState = defaultState() From a535a8d03195c39ec8db2640e2b44d0aa359eb7f Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Mon, 21 Jun 2021 01:47:54 -0400 Subject: [PATCH 036/517] For #19886 - Add right arrowheader to tracking protection navigators --- app/src/main/res/layout/component_tracking_protection_panel.xml | 1 - app/src/main/res/values/styles.xml | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/component_tracking_protection_panel.xml b/app/src/main/res/layout/component_tracking_protection_panel.xml index b1d2e7d34c..7fa2e3590f 100644 --- a/app/src/main/res/layout/component_tracking_protection_panel.xml +++ b/app/src/main/res/layout/component_tracking_protection_panel.xml @@ -175,7 +175,6 @@ style="@style/QuickSettingsLargeText.Icon" android:layout_width="match_parent" android:layout_height="@dimen/tracking_protection_item_height" - android:paddingEnd="24dp" android:text="@string/etp_settings" app:drawableStartCompat="@drawable/ic_settings" app:layout_constraintTop_toBottomOf="@id/line_divider" /> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3398c0e122..59108b9bb3 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -495,6 +495,7 @@ - - - - - - - - - From da006c6cce58446c44b1c5a4335d1518390f5b59 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Tue, 28 Sep 2021 23:59:25 +0000 Subject: [PATCH 299/517] Update Android Components version to 94.0.20210928190108. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 35aad127ec..55b405fc26 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20210928143135" + const val VERSION = "94.0.20210928190108" } From 8193138015cb613c886834b4fa06acd65e4f6f3e Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Tue, 28 Sep 2021 22:10:47 -0400 Subject: [PATCH 300/517] Fix recent tabs intermittent tests (#21557) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../org/mozilla/fenix/ext/BrowserState.kt | 5 +---- .../fenix/home/recenttabs/view/RecentTabs.kt | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt index ed67aa676c..8b8851172f 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt @@ -9,7 +9,6 @@ import mozilla.components.browser.state.selector.selectedNormalTab import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.TabSessionState import mozilla.components.feature.tabs.ext.hasMediaPlayed -import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.home.recenttabs.RecentTab import org.mozilla.fenix.tabstray.browser.TabGroup import org.mozilla.fenix.tabstray.browser.maxActiveTime @@ -35,9 +34,7 @@ fun BrowserState.asRecentTabs(): List { inProgressMediaTab?.let { add(RecentTab.Tab(it)) } } - if (FeatureFlags.tabGroupFeature) { - lastSearchGroup?.let { add(it) } - } + lastSearchGroup?.let { add(it) } } } diff --git a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt index 2ea26bd90c..48ff303a16 100644 --- a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt +++ b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt @@ -43,6 +43,7 @@ import mozilla.components.browser.icons.compose.Placeholder import mozilla.components.browser.icons.compose.WithIcon import mozilla.components.support.ktx.kotlin.getRepresentativeSnippet import mozilla.components.ui.colors.PhotonColors +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.components.components import org.mozilla.fenix.home.recenttabs.RecentTab @@ -77,14 +78,16 @@ fun RecentTabs( ) } is RecentTab.SearchGroup -> { - RecentSearchGroupItem( - searchTerm = tab.searchTerm, - tabId = tab.tabId, - url = tab.url, - thumbnail = tab.thumbnail, - count = tab.count, - onSearchGroupClicked = onRecentSearchGroupClicked - ) + if (FeatureFlags.tabGroupFeature) { + RecentSearchGroupItem( + searchTerm = tab.searchTerm, + tabId = tab.tabId, + url = tab.url, + thumbnail = tab.thumbnail, + count = tab.count, + onSearchGroupClicked = onRecentSearchGroupClicked + ) + } } } } From 53206df9b6f8bc8429d21e93cf53bc788067b922 Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Tue, 28 Sep 2021 16:34:58 -0400 Subject: [PATCH 301/517] Close #21552: Remove Extra Subtitle In Jump Back In Item --- .../java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt index 48ff303a16..cbdfc3d619 100644 --- a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt +++ b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt @@ -141,8 +141,6 @@ private fun RecentTabItem( ) { RecentTabTitle(title = title) - RecentTabSubtitle(subtitle = url) - Row { RecentTabImage( url = url, From 1015072db68d37b668fe77cc1849ba30354c294c Mon Sep 17 00:00:00 2001 From: Oana Horvath Date: Tue, 28 Sep 2021 13:35:38 +0300 Subject: [PATCH 302/517] Fixed disabled UI test openExternalLinksInPrivateTest --- .../mozilla/fenix/ui/SettingsPrivacyTest.kt | 5 ++-- .../mozilla/fenix/ui/TabbedBrowsingTest.kt | 26 +++++-------------- .../mozilla/fenix/ui/robots/BrowserRobot.kt | 17 +++--------- 3 files changed, 14 insertions(+), 34 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt index ff7d8ed873..867b66c5ca 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt @@ -9,7 +9,6 @@ import androidx.test.uiautomator.UiDevice import okhttp3.mockwebserver.MockWebServer import org.junit.After import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.mozilla.fenix.ext.settings @@ -46,6 +45,9 @@ class SettingsPrivacyTest { dispatcher = AndroidAssetDispatcher() start() } + + val settings = activityTestRule.activity.applicationContext.settings() + settings.hasShownHomeOnboardingDialog = true } @After @@ -272,7 +274,6 @@ class SettingsPrivacyTest { } } - @Ignore("Disabled for failing with new Compose Awesomebar") @Test fun openExternalLinksInPrivateTest() { val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt index 73a3a49771..0cd7d39ef3 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt @@ -12,6 +12,7 @@ import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.TestAssetHelper @@ -51,6 +52,9 @@ class TabbedBrowsingTest { dispatcher = AndroidAssetDispatcher() start() } + + val settings = activityTestRule.activity.applicationContext.settings() + settings.hasShownHomeOnboardingDialog = true } @After @@ -281,7 +285,6 @@ class TabbedBrowsingTest { navigationToolbar { }.enterURLAndEnterToBrowser(defaultWebPage.url) { - // verifyPageContent(defaultWebPage.content) }.openTabDrawer { verifyExistingTabList() verifyNewTabButton() @@ -289,7 +292,9 @@ class TabbedBrowsingTest { verifyExistingOpenTabs(defaultWebPage.title) verifyCloseTabsButton(defaultWebPage.title) }.openNewTab { - }.dismissSearchBar { } + verifySearchBarEmpty() + verifyKeyboardVisibility() + } } @Test @@ -298,14 +303,6 @@ class TabbedBrowsingTest { navigationToolbar { }.enterURLAndEnterToBrowser(defaultWebPage.url) { - // verifyPageContent(defaultWebPage.content) - }.openTabDrawer { - verifyExistingTabList() - verifyNewTabButton() - verifyTabTrayOverflowMenu(true) - verifyExistingOpenTabs(defaultWebPage.title) - verifyCloseTabsButton(defaultWebPage.title) - }.closeTabDrawer { }.openTabButtonShortcutsMenu { verifyTabButtonShortcutMenuItems() }.closeTabFromShortcutsMenu { @@ -316,28 +313,19 @@ class TabbedBrowsingTest { verifyFocusedNavigationToolbar() // dismiss search dialog homeScreen { }.pressBack() - verifyHomePrivateBrowsingButton() - verifyHomeMenu() - verifyHomeWordmark() - verifyTabButton() verifyPrivateSessionMessage() verifyHomeToolbar() - verifyHomeComponent() } navigationToolbar { }.enterURLAndEnterToBrowser(defaultWebPage.url) { - }.openTabButtonShortcutsMenu { }.openTabFromShortcutsMenu { verifyKeyboardVisible() verifyFocusedNavigationToolbar() // dismiss search dialog homeScreen { }.pressBack() - verifyHomeMenu() verifyHomeWordmark() - verifyTabButton() verifyHomeToolbar() - verifyHomeComponent() } } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt index 8f347570ae..730536c81b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt @@ -497,25 +497,16 @@ class BrowserRobot { } fun openTabDrawer(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition { - mDevice.waitForIdle(waitingTime) - mDevice.findObject( - UiSelector() - .resourceId("$packageName:id/counter_box") - .descriptionContains("The tab counter toolbar button.") - ).waitForExists(waitingTime) + mDevice.waitNotNull(Until.findObject(By.desc("Tabs"))) tabsCounter().click() - mDevice.waitNotNull( - Until.findObject(By.res("$packageName:id/tab_layout")), - waitingTime - ) + mDevice.waitNotNull(Until.findObject(By.res("$packageName:id/tab_layout"))) TabDrawerRobot().interact() return TabDrawerRobot.Transition() } fun openTabButtonShortcutsMenu(interact: NavigationToolbarRobot.() -> Unit): NavigationToolbarRobot.Transition { - mDevice.waitForIdle(waitingTime) - + mDevice.waitNotNull(Until.findObject(By.desc("Tabs"))) tabsCounter().click(LONG_CLICK_DURATION) NavigationToolbarRobot().interact() @@ -594,7 +585,7 @@ private fun assertMenuButton() { .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } -private fun tabsCounter() = mDevice.findObject(By.res("$packageName:id/counter_box")) +private fun tabsCounter() = mDevice.findObject(By.desc("Tabs")) private fun mediaPlayerPlayButton() = mDevice.findObject( From 0ac23486c1faf47ee5a8c1438b9e6c2c382e9b04 Mon Sep 17 00:00:00 2001 From: Oana Horvath Date: Tue, 28 Sep 2021 16:00:29 +0300 Subject: [PATCH 303/517] Fixed verifyAboutFirefoxPreview test according with new steps --- .../ui/robots/SettingsSubMenuAboutRobot.kt | 40 +++---------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt index cfbaa1e966..76d57e6f38 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt @@ -24,24 +24,21 @@ import androidx.test.espresso.matcher.ViewMatchers.withSubstring import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice -import androidx.test.uiautomator.UiSelector +import java.text.SimpleDateFormat +import java.time.LocalDateTime +import java.time.format.DateTimeFormatterBuilder +import java.time.temporal.ChronoField +import java.util.Calendar +import java.util.Date import org.hamcrest.CoreMatchers import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.containsString import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.R -import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.TestHelper import org.mozilla.fenix.helpers.TestHelper.appName -import org.mozilla.fenix.helpers.TestHelper.packageName import org.mozilla.fenix.helpers.isVisibleForUser import org.mozilla.fenix.settings.SupportUtils -import java.text.SimpleDateFormat -import java.time.LocalDateTime -import java.time.format.DateTimeFormatterBuilder -import java.time.temporal.ChronoField -import java.util.Calendar -import java.util.Date /** * Implementation of Robot Pattern for the settings search sub menu. @@ -145,16 +142,6 @@ private fun assertWhatIsNewInFirefoxPreview() { onView(withText("What’s new in $appName")) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .perform(click()) - - // Commenting out since the Text to verify in the web site seems to be different now - /* - TestHelper.verifyUrl( - SupportUtils.SumoTopic.WHATS_NEW.topicStr, - "org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view", - R.id.mozac_browser_toolbar_url_view - )*/ - - Espresso.pressBack() } private fun assertSupport() { @@ -171,8 +158,6 @@ private fun assertSupport() { "org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view", R.id.mozac_browser_toolbar_url_view ) - - Espresso.pressBack() } private fun assertCrashes() { @@ -197,13 +182,6 @@ private fun assertCrashes() { for (i in 1..3) { Espresso.pressBack() } - - mDevice.findObject( - UiSelector().resourceId("$packageName:id/homeLayout") - ).waitForExists(waitingTime) - - onView(ViewMatchers.withResourceName("homeLayout")) - .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) } private fun assertPrivacyNotice() { @@ -220,8 +198,6 @@ private fun assertPrivacyNotice() { "org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view", R.id.mozac_browser_toolbar_url_view ) - - Espresso.pressBack() } private fun assertKnowYourRights() { @@ -238,8 +214,6 @@ private fun assertKnowYourRights() { "org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view", R.id.mozac_browser_toolbar_url_view ) - - Espresso.pressBack() } private fun assertLicensingInformation() { @@ -256,8 +230,6 @@ private fun assertLicensingInformation() { "org.mozilla.fenix.debug:id/mozac_browser_toolbar_url_view", R.id.mozac_browser_toolbar_url_view ) - - Espresso.pressBack() } private fun assertLibrariesUsed() { From 37e342fd923edf325ed4d1c77c8dce38d0bf0668 Mon Sep 17 00:00:00 2001 From: AndiAJ Date: Wed, 29 Sep 2021 12:11:15 +0300 Subject: [PATCH 304/517] For #21478 fix flaky verifyOpenTopSiteNormalTab UI test --- .../androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt | 8 ++------ .../java/org/mozilla/fenix/ui/robots/BrowserRobot.kt | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt index f27a8dfd47..a59d38ef59 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt @@ -83,16 +83,12 @@ class TopSitesTest { verifyAddToTopSitesButton() }.addToFirefoxHome { verifySnackBarText("Added to top sites!") - }.openTabDrawer { - }.openNewTab { - }.dismissSearchBar { + }.goToHomescreen { verifyExistingTopSitesList() verifyExistingTopSitesTabs(defaultWebPageTitle) }.openTopSiteTabWithTitle(title = defaultWebPageTitle) { verifyUrl(defaultWebPage.url.toString().replace("http://", "")) - }.openTabDrawer { - }.openNewTab { - }.dismissSearchBar { + }.goToHomescreen { verifyExistingTopSitesList() verifyExistingTopSitesTabs(defaultWebPageTitle) }.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt index 730536c81b..5be3b47238 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt @@ -524,6 +524,7 @@ class BrowserRobot { onView(withContentDescription("Home screen")) .check(matches(isDisplayed())) .click() + mDevice.waitForIdle() HomeScreenRobot().interact() return HomeScreenRobot.Transition() From ddd472d14370d8574c6980f2d75a0249d7c9fe80 Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Tue, 28 Sep 2021 19:06:25 -0400 Subject: [PATCH 305/517] No issue: Improve rendering of jump back in --- .../org/mozilla/fenix/ui/CollectionTest.kt | 12 ++--------- .../java/org/mozilla/fenix/ui/HistoryTest.kt | 5 +++-- .../java/org/mozilla/fenix/ui/SmokeTest.kt | 1 + .../mozilla/fenix/ui/TabbedBrowsingTest.kt | 1 + .../mozilla/fenix/ui/ThreeDotMenuMainTest.kt | 2 ++ .../org/mozilla/fenix/home/HomeFragment.kt | 21 +++++++++++++++++-- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt index 1fe27bba9c..926e937106 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt @@ -40,6 +40,8 @@ class CollectionTest { @Before fun setUp() { + activityTestRule.activity.applicationContext.settings().hasShownHomeOnboardingDialog = true + mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() start() @@ -54,8 +56,6 @@ class CollectionTest { @Test // open a webpage, and add currently opened tab to existing collection fun mainMenuSaveToExistingCollection() { - val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true val firstWebPage = getGenericAsset(mockWebServer, 1) val secondWebPage = getGenericAsset(mockWebServer, 2) @@ -83,8 +83,6 @@ class CollectionTest { @Test @Ignore("https://github.com/mozilla-mobile/fenix/issues/21397") fun verifyAddTabButtonOfCollectionMenu() { - val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true val firstWebPage = getGenericAsset(mockWebServer, 1) val secondWebPage = getGenericAsset(mockWebServer, 2) @@ -112,8 +110,6 @@ class CollectionTest { @Test @Ignore("https://github.com/mozilla-mobile/fenix/issues/21397") fun renameCollectionTest() { - val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true val webPage = getGenericAsset(mockWebServer, 1) navigationToolbar { @@ -135,8 +131,6 @@ class CollectionTest { @Test fun createSecondCollectionTest() { - val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true val webPage = getGenericAsset(mockWebServer, 1) navigationToolbar { @@ -216,8 +210,6 @@ class CollectionTest { @Test fun selectTabOnLongTapTest() { - val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true val firstWebPage = getGenericAsset(mockWebServer, 1) val secondWebPage = getGenericAsset(mockWebServer, 2) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt index 1d5e8de266..94a6c34f88 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt @@ -42,6 +42,9 @@ class HistoryTest { @Before fun setUp() { + InstrumentationRegistry.getInstrumentation().targetContext.settings() + .hasShownHomeOnboardingDialog = true + mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() start() @@ -221,8 +224,6 @@ class HistoryTest { fun deleteMultipleSelectionTest() { val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2) - val settings = InstrumentationRegistry.getInstrumentation().targetContext.settings() - settings.hasShownHomeOnboardingDialog = true navigationToolbar { }.enterURLAndEnterToBrowser(firstWebPage.url) { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt index af5b10bfa5..83c9756d96 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt @@ -108,6 +108,7 @@ class SmokeTest { // So we are initializing this here instead of in all related tests. browserStore = activityTestRule.activity.components.core.store + activityTestRule.activity.applicationContext.settings().hasShownHomeOnboardingDialog = true mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() start() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt index 0cd7d39ef3..685e3bc903 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt @@ -48,6 +48,7 @@ class TabbedBrowsingTest { @Before fun setUp() { + activityTestRule.activity.applicationContext.settings().hasShownHomeOnboardingDialog = true mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() start() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt index a992796f61..c1689eac7d 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt @@ -9,6 +9,7 @@ import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.ui.robots.homeScreen @@ -28,6 +29,7 @@ class ThreeDotMenuMainTest { @Before fun setUp() { + activityTestRule.activity.applicationContext.settings().hasShownHomeOnboardingDialog = true mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() start() diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index ea9be27efd..9175a0dc85 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -80,6 +80,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.browser.BrowserAnimator.Companion.getToolbarNavOptions import org.mozilla.fenix.browser.BrowserFragmentDirections import org.mozilla.fenix.browser.browsingmode.BrowsingMode +import org.mozilla.fenix.components.Components import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.PrivateShortcutCreateManager import org.mozilla.fenix.components.StoreProvider @@ -92,6 +93,7 @@ import org.mozilla.fenix.components.tips.providers.MasterPasswordTipProvider import org.mozilla.fenix.components.toolbar.FenixTabCounterMenu import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.databinding.FragmentHomeBinding +import org.mozilla.fenix.ext.asRecentTabs import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.hideToolbar import org.mozilla.fenix.ext.metrics @@ -104,6 +106,7 @@ import org.mozilla.fenix.historymetadata.controller.DefaultHistoryMetadataContro import org.mozilla.fenix.home.mozonline.showPrivacyPopWindow import org.mozilla.fenix.home.recentbookmarks.RecentBookmarksFeature import org.mozilla.fenix.home.recentbookmarks.controller.DefaultRecentBookmarksController +import org.mozilla.fenix.home.recenttabs.RecentTab import org.mozilla.fenix.home.recenttabs.RecentTabsListFeature import org.mozilla.fenix.home.recenttabs.controller.DefaultRecentTabsController import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController @@ -235,7 +238,10 @@ class HomeFragment : Fragment() { recentBookmarks = emptyList(), showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome, showSetAsDefaultBrowserCard = components.settings.shouldShowSetAsDefaultBrowserCard(), - recentTabs = emptyList(), + // Provide an initial state for recent tabs to prevent re-rendering on the home screen. + // This will otherwise cause a visual jump as the section gets rendered from no state + // to some state. + recentTabs = getRecentTabs(components), historyMetadata = emptyList() ), listOf( @@ -650,7 +656,10 @@ class HomeFragment : Fragment() { ).getTip() }, showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome, - recentTabs = emptyList(), + // Provide an initial state for recent tabs to prevent re-rendering on the home screen. + // This will otherwise cause a visual jump as the section gets rendered from no state + // to some state. + recentTabs = getRecentTabs(components), recentBookmarks = emptyList(), historyMetadata = emptyList() ) @@ -1136,6 +1145,14 @@ class HomeFragment : Fragment() { binding.sessionControlRecyclerView.adapter?.notifyDataSetChanged() } + private fun getRecentTabs(components: Components): List { + return if (components.settings.showRecentTabsFeature) { + components.core.store.state.asRecentTabs() + } else { + emptyList() + } + } + companion object { const val ALL_NORMAL_TABS = "all_normal" const val ALL_PRIVATE_TABS = "all_private" From d30583e0e700511bc2396decf6e955db499ad023 Mon Sep 17 00:00:00 2001 From: Andrei Joltan <51314259+AndiAJ@users.noreply.github.com> Date: Wed, 29 Sep 2021 20:45:08 +0300 Subject: [PATCH 306/517] For #21442 fix flaky deleteMultipleSelectionTest UI test (#21563) Co-authored-by: AndiAJ Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt | 8 +++++--- .../java/org/mozilla/fenix/ui/robots/HistoryRobot.kt | 9 +++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt index 94a6c34f88..448be81cee 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt @@ -227,16 +227,18 @@ class HistoryTest { navigationToolbar { }.enterURLAndEnterToBrowser(firstWebPage.url) { - }.openTabDrawer { - }.openNewTab { - }.submitQuery(secondWebPage.url.toString()) { + }.openNavigationToolbar { + }.enterURLAndEnterToBrowser(secondWebPage.url) { mDevice.waitForIdle() + verifyUrl(secondWebPage.url.toString()) }.openThreeDotMenu { }.openHistory { verifyHistoryListExists() historyListIdlingResource = RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 2) IdlingRegistry.getInstance().register(historyListIdlingResource!!) + verifyHistoryItemExists(firstWebPage.url.toString()) + verifyHistoryItemExists(secondWebPage.url.toString()) longTapSelectItem(firstWebPage.url) longTapSelectItem(secondWebPage.url) openActionBarOverflowOrOptionsMenu(activityTestRule.activity) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt index 45e1e3e990..a9731051fd 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt @@ -19,8 +19,10 @@ import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.Until import org.hamcrest.Matchers import org.hamcrest.Matchers.allOf +import org.junit.Assert.assertTrue import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestHelper.waitForObjects import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.ext.waitNotNull @@ -51,6 +53,8 @@ class HistoryRobot { assertVisitedTimeTitle() } + fun verifyHistoryItemExists(url: String) = assertHistoryItemExists(url) + fun verifyFirstTestPageTitle(title: String) = assertTestPageTitle(title) fun verifyTestPageUrl(expectedUrl: Uri) = assertPageUrl(expectedUrl) @@ -120,6 +124,11 @@ private fun assertEmptyHistoryView() = private fun assertHistoryListExists() = mDevice.findObject(UiSelector().resourceId("R.id.history_list")).waitForExists(waitingTime) +private fun assertHistoryItemExists(url: String) { + mDevice.waitForObjects(mDevice.findObject(UiSelector().textContains(url))) + assertTrue(mDevice.findObject(UiSelector().textContains(url)).waitForExists(waitingTime)) +} + private fun assertVisitedTimeTitle() = onView(withId(R.id.header_title)).check(matches(withText("Today"))) From 0c632dbbdbc78ed283a82a1696c302a974fefe37 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Tue, 28 Sep 2021 23:30:18 +0300 Subject: [PATCH 307/517] For #21391 - Final design composables Fonts are not exactly following the Figma design but do better suit the overall design since the other fonts are also not respecting the latest specs. --- .../fenix/compose/ClickableSubstringLink.kt | 97 ++++ .../java/org/mozilla/fenix/compose/Image.kt | 84 +++ .../fenix/compose/ImagesPlaceholder.kt | 87 ++++ .../mozilla/fenix/compose/ListItemTabLarge.kt | 160 ++++++ .../compose/ListItemTabLargePlaceholder.kt | 79 +++ .../mozilla/fenix/compose/SectionHeader.kt | 48 ++ .../mozilla/fenix/compose/SelectableChip.kt | 77 +++ .../fenix/compose/StaggeredHorizontalGrid.kt | 137 +++++ .../org/mozilla/fenix/compose/TabSubtitle.kt | 49 ++ .../fenix/compose/TabSubtitleWithInterdot.kt | 95 ++++ .../org/mozilla/fenix/compose/TabTitle.kt | 51 ++ .../mozilla/fenix/ext/HomeFragmentState.kt | 2 +- .../org/mozilla/fenix/home/HomeFragment.kt | 1 + .../mozilla/fenix/home/HomeFragmentStore.kt | 2 +- .../sessioncontrol/SessionControlAdapter.kt | 1 + .../SessionControlInteractor.kt | 4 + .../pocket/PocketStoriesComposables.kt | 479 ++++-------------- .../pocket/PocketStoriesController.kt | 15 + .../pocket/PocketStoriesInteractor.kt | 7 + .../pocket/PocketStoriesViewHolder.kt | 58 ++- app/src/main/res/drawable/pocket_vector.xml | 15 + app/src/main/res/values/strings.xml | 13 + .../fenix/ext/HomeFragmentStateTest.kt | 10 +- .../home/SessionControlInteractorTest.kt | 9 + .../DefaultPocketStoriesControllerTest.kt | 31 +- 25 files changed, 1212 insertions(+), 399 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/compose/ClickableSubstringLink.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/Image.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/ImagesPlaceholder.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/SelectableChip.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/StaggeredHorizontalGrid.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/TabSubtitle.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/TabSubtitleWithInterdot.kt create mode 100644 app/src/main/java/org/mozilla/fenix/compose/TabTitle.kt create mode 100644 app/src/main/res/drawable/pocket_vector.xml diff --git a/app/src/main/java/org/mozilla/fenix/compose/ClickableSubstringLink.kt b/app/src/main/java/org/mozilla/fenix/compose/ClickableSubstringLink.kt new file mode 100644 index 0000000000..f8bc4bc2bc --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/ClickableSubstringLink.kt @@ -0,0 +1,97 @@ +/* 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.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.text.ClickableText +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.tooling.preview.Preview +import mozilla.components.ui.colors.PhotonColors + +/** + * [Text] containing a substring styled as an URL informing when this is clicked. + * + * @param text Full text that will be displayed + * @param textColor [Color] of the normal text. The URL substring will have a default URL style applied. + * @param clickableStartIndex [text] index at which the URL substring starts. + * @param clickableEndIndex [text] index at which the URL substring ends. + * @param onClick Callback to be invoked only when the URL substring is clicked. + */ +@Composable +fun ClickableSubstringLink( + text: String, + textColor: Color, + clickableStartIndex: Int, + clickableEndIndex: Int, + onClick: () -> Unit +) { + val annotatedText = buildAnnotatedString { + append(text) + + addStyle( + SpanStyle(textColor), + start = 0, + end = clickableStartIndex + ) + + addStyle( + SpanStyle( + textDecoration = TextDecoration.Underline, + color = when (isSystemInDarkTheme()) { + true -> PhotonColors.Violet40 + false -> PhotonColors.Violet70 + } + ), + start = clickableStartIndex, + end = clickableEndIndex + ) + + addStyle( + SpanStyle(textColor), + start = clickableEndIndex, + end = text.length + ) + + addStringAnnotation( + tag = "link", + annotation = "", + start = clickableStartIndex, + end = clickableEndIndex + ) + } + + ClickableText( + text = annotatedText, + onClick = { + annotatedText + .getStringAnnotations("link", it, it) + .firstOrNull()?.let { + onClick() + } + } + ) +} + +@Composable +@Preview +private fun ClickableSubstringTextPreview() { + val text = "This text contains a link" + Box(modifier = Modifier.background(PhotonColors.White)) { + ClickableSubstringLink( + text, + PhotonColors.DarkGrey90, + text.indexOf("link"), + text.length + ) { } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/Image.kt b/app/src/main/java/org/mozilla/fenix/compose/Image.kt new file mode 100644 index 0000000000..46d53ec789 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/Image.kt @@ -0,0 +1,84 @@ +/* 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.compose + +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient +import mozilla.components.concept.fetch.Client +import mozilla.components.concept.fetch.MutableHeaders +import mozilla.components.concept.fetch.Request +import mozilla.components.concept.fetch.Response +import mozilla.components.support.images.compose.loader.ImageLoader +import mozilla.components.support.images.compose.loader.WithImage + +/** + * A composable that lays out and draws the image from a given URL while showing a default placeholder + * while that image is downloaded or a default fallback image when downloading failed. + * + * @param client [Client] instance to be used for downloading the image. + * When using [GeckoViewFetchClient] the image will automatically be cached if it has the right headers. + * @param url URL from where the to download the image to be shown. + * @param modifier [Modifier] to be applied to the layout. + * @param private Whether or not this is a private request. Like in private browsing mode, + * private requests will not cache anything on disk and not send any cookies shared with the browser. + * @param targetSize Image size (width and height) the loaded image should be scaled to. + * @param contentDescription Localized text used by accessibility services to describe what this image represents. + * This should always be provided unless this image is used for decorative purposes, and does not represent + * a meaningful action that a user can take. + */ +@Composable +@Suppress("LongParameterList") +fun Image( + client: Client, + url: String, + modifier: Modifier = Modifier, + private: Boolean = false, + targetSize: Dp = 100.dp, + contentDescription: String? = null +) { + ImageLoader( + url = url, + client = client, + private = private, + targetSize = targetSize + ) { + WithImage { painter -> + androidx.compose.foundation.Image( + painter = painter, + modifier = modifier, + contentDescription = contentDescription, + ) + } + + WithDefaultPlaceholder(modifier, contentDescription) + + WithDefaultFallback(modifier, contentDescription) + } +} + +@Composable +@Preview +private fun ImagePreview() { + Image( + FakeClient(), + "https://mozilla.com", + Modifier.height(100.dp).width(200.dp) + ) +} + +internal class FakeClient : Client() { + override fun fetch(request: Request) = Response( + url = request.url, + status = 200, + body = Response.Body.empty(), + headers = MutableHeaders() + ) +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/ImagesPlaceholder.kt b/app/src/main/java/org/mozilla/fenix/compose/ImagesPlaceholder.kt new file mode 100644 index 0000000000..e60fb4ef78 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/ImagesPlaceholder.kt @@ -0,0 +1,87 @@ +/* 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.compose + +import androidx.compose.foundation.Image +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.painter.ColorPainter +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import mozilla.components.support.images.compose.loader.Fallback +import mozilla.components.support.images.compose.loader.ImageLoaderScope +import mozilla.components.support.images.compose.loader.Placeholder +import mozilla.components.ui.colors.PhotonColors + +/** + * Renders the app default image placeholder while the image is still getting loaded. + * + * @param modifier [Modifier] allowing to control among others the dimensions and shape of the image. + * @param contentDescription Text provided to accessibility services to describe what this image represents. + * Defaults to [null] suited for an image used only for decorative purposes and not to be read by + * accessibility services. + */ +@Composable +internal fun ImageLoaderScope.WithDefaultPlaceholder( + modifier: Modifier, + contentDescription: String? = null +) { + Placeholder { + DefaultImagePlaceholder(modifier, contentDescription) + } +} + +/** + * Renders the app default image placeholder if loading the image failed. + * + * @param modifier [Modifier] allowing to control among others the dimensions and shape of the image. + * @param contentDescription Text provided to accessibility services to describe what this image represents. + * Defaults to [null] suited for an image used only for decorative purposes and not to be read by + * accessibility services. + */ +@Composable +internal fun ImageLoaderScope.WithDefaultFallback( + modifier: Modifier, + contentDescription: String? = null +) { + Fallback { + DefaultImagePlaceholder(modifier, contentDescription) + } +} + +/** + * Application default image placeholder. + * + * @param modifier [Modifier] allowing to control among others the dimensions and shape of the image. + * @param contentDescription Text provided to accessibility services to describe what this image represents. + * Defaults to [null] suited for an image used only for decorative purposes and not to be read by + * accessibility services. + */ +@Composable +internal fun DefaultImagePlaceholder( + modifier: Modifier, + contentDescription: String? = null +) { + val color = when (isSystemInDarkTheme()) { + true -> PhotonColors.DarkGrey30 + false -> PhotonColors.LightGrey30 + } + + Image(ColorPainter(color), contentDescription, modifier) +} + +@Composable +@Preview +private fun DefaultImagePlaceholderPreview() { + DefaultImagePlaceholder( + Modifier + .size(200.dp, 100.dp) + .clip(RoundedCornerShape(8.dp)) + ) +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt new file mode 100644 index 0000000000..c7836cde83 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt @@ -0,0 +1,160 @@ +/* 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.compose + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient +import mozilla.components.concept.fetch.Client +import org.mozilla.fenix.theme.FirefoxTheme + +/** + * Default layout of a large tab shown in a list taking String arguments for title and caption. + * Has the following structure: + * ``` + * --------------------------------------------- + * | -------------- Title | + * | | Image | wrapped on | + * | | from | three rows if needed | + * | | imageUrl | | + * | -------------- Optional caption | + * --------------------------------------------- + * ``` + * + * @param client [Client] instance to be used for downloading the image. + * When using [GeckoViewFetchClient] the image will automatically be cached if it has the right headers. + * @param imageUrl URL from where the to download a header image of the tab this composable renders. + * @param title Title off the tab this composable renders. + * @param caption Optional caption text. + * @param onClick Optional callback to be invoked when this composable is clicked. + */ +@Composable +fun ListItemTabLarge( + client: Client, + imageUrl: String, + title: String, + caption: String? = null, + onClick: (() -> Unit)? = null +) { + ListItemTabSurface(client, imageUrl, onClick) { + TabTitle(text = title, maxLines = 3) + + if (caption != null) { + TabSubtitle(text = caption) + } + } +} + +/** + * Default layout of a large tab shown in a list taking composable arguments for title and caption + * allowing as an exception to customize these elements. + * Has the following structure: + * ``` + * --------------------------------------------- + * | -------------- -------------------------- | + * | | | | Title | | + * | | Image | | composable | | + * | | from | -------------------------- | + * | | imageUrl | -------------------------- | + * | | | | Optional composable | | + * | -------------- -------------------------- | + * --------------------------------------------- + * ``` + * + * @param client [Client] instance to be used for downloading the image. + * When using [GeckoViewFetchClient] the image will automatically be cached if it has the right headers. + * @param imageUrl URL from where the to download a header image of the tab this composable renders. + * @param title Composable rendering the title of the tab this composable represents. + * @param subtitle Optional tab caption composable. + * @param onClick Optional callback to be invoked when this composable is clicked. + */ +@Composable +fun ListItemTabLarge( + client: Client, + imageUrl: String, + onClick: () -> Unit, + title: @Composable () -> Unit, + subtitle: @Composable (() -> Unit)? = null +) { + ListItemTabSurface(client, imageUrl, onClick) { + title() + + subtitle?.invoke() + } +} + +/** + * Shared default configuration of a ListItemTabLarge Composable. + * + * @param client [Client] instance to be used for downloading the image. + * When using [GeckoViewFetchClient] the image will automatically be cached if it has the right headers. + * @param imageUrl URL from where the to download a header image of the tab this composable renders. + * @param onClick Optional callback to be invoked when this composable is clicked. + * @param tabDetails [Composable] Displayed to the the end of the image. Allows for variation in the item text style. + */ +@Composable +private fun ListItemTabSurface( + client: Client, + imageUrl: String, + onClick: (() -> Unit)? = null, + tabDetails: @Composable () -> Unit +) { + var modifier = Modifier.size(328.dp, 116.dp) + if (onClick != null) modifier = modifier.then(Modifier.clickable { onClick() }) + + Card( + modifier = modifier, + shape = RoundedCornerShape(8.dp), + backgroundColor = FirefoxTheme.colors.surface, + elevation = 6.dp + ) { + Row( + modifier = Modifier.padding(16.dp) + ) { + val (imageWidth, imageHeight) = 116.dp to 84.dp + val imageModifier = Modifier + .size(imageWidth, imageHeight) + .clip(RoundedCornerShape(8.dp)) + + Image(client, imageUrl, imageModifier, false, imageWidth) + + Spacer(Modifier.width(16.dp)) + + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.SpaceBetween + ) { + tabDetails() + } + } + } +} + +@Composable +@Preview +private fun ListItemTabLargePreview() { + FirefoxTheme { + ListItemTabLarge( + client = FakeClient(), + imageUrl = "", + title = "This is a very long title for a tab but needs to be so for this preview", + caption = "And this is a caption" + ) { } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt new file mode 100644 index 0000000000..16b912a3b3 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt @@ -0,0 +1,79 @@ +/* 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.compose + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.mozilla.fenix.theme.FirefoxTheme + +/** + * Placeholder of a [ListItemTabLarge] with the same dimensions but only a centered text. + * Has the following structure: + * ``` + * --------------------------------------------- + * | | + * | | + * | Placeholder text | + * | | + * | | + * --------------------------------------------- + * ``` + * + * @param text The only [String] that this will display. + * @param onClick Optional callback to be invoked when this composable is clicked. + */ +@Composable +fun ListItemTabLargePlaceholder( + text: String, + onClick: () -> Unit = { } +) { + Card( + modifier = Modifier + .size(328.dp, 116.dp) + .clickable { onClick() }, + shape = RoundedCornerShape(8.dp), + backgroundColor = FirefoxTheme.colors.surface, + elevation = 6.dp, + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + verticalArrangement = Arrangement.Center + ) { + Text( + text = text, + color = FirefoxTheme.colors.textPrimary, + textAlign = TextAlign.Center, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = TextStyle(fontSize = 20.sp), + ) + } + } +} + +@Composable +@Preview +private fun ListItemTabLargePlaceholderPreview() { + FirefoxTheme { + ListItemTabLargePlaceholder(text = "Item placeholder") + } +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt b/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt new file mode 100644 index 0000000000..3c273f720a --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt @@ -0,0 +1,48 @@ +/* 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.compose + +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.sp +import org.mozilla.fenix.R +import org.mozilla.fenix.theme.FirefoxTheme + +/** + * Default layout for the header of a screen section. + * + * @param text [String] to be styled as header and displayed. + * @param modifier [Modifier] to be applied to the [Text]. + */ +@Composable +fun SectionHeader( + text: String, + modifier: Modifier = Modifier +) { + Text( + modifier = modifier, + text = text, + style = TextStyle( + fontFamily = FontFamily(Font(R.font.metropolis_semibold)), + fontSize = 20.sp, + lineHeight = 20.sp + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = FirefoxTheme.colors.textPrimary + ) +} + +@Composable +@Preview +private fun HeadingTextPreview() { + SectionHeader(text = "Section title") +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/SelectableChip.kt b/app/src/main/java/org/mozilla/fenix/compose/SelectableChip.kt new file mode 100644 index 0000000000..11876397a8 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/SelectableChip.kt @@ -0,0 +1,77 @@ +/* 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.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.selection.selectable +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.capitalize +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import mozilla.components.ui.colors.PhotonColors +import org.mozilla.fenix.theme.FirefoxTheme + +/** + * Default layout of a selectable chip. + * + * @param text [String] displayed in this chip. Ideally should only be one word. + * @param isSelected Whether this should be shown as selected. + * @param onClick Callback for when the user taps this. + */ +@Composable +fun SelectableChip( + text: String, + isSelected: Boolean, + onClick: () -> Unit +) { + val contentColor = when (isSystemInDarkTheme()) { + true -> PhotonColors.LightGrey10 + false -> if (isSelected) PhotonColors.LightGrey10 else PhotonColors.DarkGrey90 + } + + @Suppress("MagicNumber") + val backgroundColor = when (isSystemInDarkTheme()) { + true -> if (isSelected) PhotonColors.Violet50 else PhotonColors.DarkGrey50 + // Custom color codes matching the Figma design. + false -> if (isSelected) { Color(0xFF312A65) } else { Color(0x1420123A) } + } + + Box( + modifier = Modifier + .selectable(isSelected) { onClick() } + .clip(MaterialTheme.shapes.small) + .background(backgroundColor) + .padding(16.dp, 10.dp) + ) { + Text( + text = text.capitalize(Locale.current), + style = TextStyle(fontSize = 14.sp), + color = contentColor + ) + } +} + +@Composable +@Preview +private fun SelectableChipPreview() { + FirefoxTheme { + Box(Modifier.fillMaxSize().background(FirefoxTheme.colors.surface)) { + SelectableChip("Chirp", false) { } + SelectableChip(text = "Chirp", isSelected = true) { } + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/StaggeredHorizontalGrid.kt b/app/src/main/java/org/mozilla/fenix/compose/StaggeredHorizontalGrid.kt new file mode 100644 index 0000000000..aca7c8dbd7 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/StaggeredHorizontalGrid.kt @@ -0,0 +1,137 @@ +/* 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.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.Placeable +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import org.mozilla.fenix.theme.FirefoxTheme + +/** + * Displays a list of items as a staggered horizontal grid placing them on ltr rows and continuing + * on as many below rows as needed to place all items. + * + * In an effort to best utilize the available row space this can mix the items such that narrower ones + * are placed on the same row as wider ones if the otherwise next item doesn't fit. + * + * @param modifier [Modifier] to be applied to the layout. + * @param horizontalItemsSpacing Minimum horizontal space between items. Does not add spacing to layout bounds. + * @param verticalItemsSpacing Vertical space between items + * @param arrangement How the items will be horizontally aligned and spaced. + * @param content The children composables to be laid out. + */ +@Composable +fun StaggeredHorizontalGrid( + modifier: Modifier = Modifier, + horizontalItemsSpacing: Dp = 0.dp, + verticalItemsSpacing: Dp = 8.dp, + arrangement: Arrangement.Horizontal = Arrangement.Start, + content: @Composable () -> Unit +) { + val currentLayoutDirection = LocalLayoutDirection.current + + Layout(content, modifier) { items, constraints -> + val horizontalItemsSpacingPixels = horizontalItemsSpacing.roundToPx() + val verticalItemsSpacingPixels = verticalItemsSpacing.roundToPx() + var totalHeight = 0 + val itemsRows = mutableListOf>() + val notYetPlacedItems = items.map { + it.measure(constraints) + }.toMutableList() + + fun getIndexOfNextPlaceableThatFitsRow(available: List, currentWidth: Int): Int { + return available.indexOfFirst { + currentWidth + it.width <= constraints.maxWidth + } + } + + // Populate each row with as many items as possible combining wider with narrower items. + // This will change the order of shown categories. + var (currentRow, currentWidth) = mutableListOf() to 0 + while (notYetPlacedItems.isNotEmpty()) { + if (currentRow.isEmpty()) { + currentRow.add( + notYetPlacedItems[0].also { + currentWidth += it.width + horizontalItemsSpacingPixels + totalHeight += it.height + verticalItemsSpacingPixels + } + ) + notYetPlacedItems.removeAt(0) + } else { + val nextPlaceableThatFitsIndex = getIndexOfNextPlaceableThatFitsRow(notYetPlacedItems, currentWidth) + if (nextPlaceableThatFitsIndex >= 0) { + currentRow.add( + notYetPlacedItems[nextPlaceableThatFitsIndex].also { + currentWidth += it.width + horizontalItemsSpacingPixels + } + ) + notYetPlacedItems.removeAt(nextPlaceableThatFitsIndex) + } else { + itemsRows.add(currentRow) + currentRow = mutableListOf() + currentWidth = 0 + } + } + } + if (currentRow.isNotEmpty()) { + itemsRows.add(currentRow) + } + totalHeight -= verticalItemsSpacingPixels + + // Place each item from each row on screen. + layout(constraints.maxWidth, totalHeight) { + itemsRows.forEachIndexed { rowIndex, itemRow -> + val itemsSizes = IntArray(itemRow.size) { + itemRow[it].width + when (currentLayoutDirection == LayoutDirection.Ltr) { + true -> if (it < itemRow.lastIndex) horizontalItemsSpacingPixels else 0 + false -> if (it > 0) horizontalItemsSpacingPixels else 0 + } + } + val itemsPositions = IntArray(itemsSizes.size) { 0 } + with(arrangement) { + arrange(constraints.maxWidth, itemsSizes, currentLayoutDirection, itemsPositions) + } + + itemRow.forEachIndexed { itemIndex, item -> + item.place( + x = itemsPositions[itemIndex], + y = (rowIndex * item.height) + (rowIndex * verticalItemsSpacingPixels) + ) + } + } + } + } +} + +@Composable +@Preview +private fun StaggeredHorizontalGridPreview() { + FirefoxTheme { + Box(Modifier.background(FirefoxTheme.colors.surface)) { + StaggeredHorizontalGrid( + horizontalItemsSpacing = 8.dp, + arrangement = Arrangement.Center + ) { + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" + .split(" ") + .forEach { + Text(text = it, color = Color.Red, modifier = Modifier.border(3.dp, Color.Blue)) + } + } + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/TabSubtitle.kt b/app/src/main/java/org/mozilla/fenix/compose/TabSubtitle.kt new file mode 100644 index 0000000000..747c93e798 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/TabSubtitle.kt @@ -0,0 +1,49 @@ +/* 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.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.sp +import org.mozilla.fenix.theme.FirefoxTheme + +/** + * Default layout for a tab composable caption. + * + * @param text Tab caption. + * @param modifier Optional [Modifier] to be applied to the layout. + */ +@Composable +fun TabSubtitle( + text: String, + modifier: Modifier = Modifier +) { + Text( + modifier = modifier, + maxLines = 1, + text = text, + style = TextStyle(fontSize = 12.sp), + overflow = TextOverflow.Ellipsis, + color = FirefoxTheme.colors.textSecondary + ) +} + +@Composable +@Preview +private fun TabSubtitlePreview() { + FirefoxTheme { + Box(Modifier.background(FirefoxTheme.colors.surface)) { + TabSubtitle( + "Awesome tab subtitle", + ) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/TabSubtitleWithInterdot.kt b/app/src/main/java/org/mozilla/fenix/compose/TabSubtitleWithInterdot.kt new file mode 100644 index 0000000000..9d0de51a26 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/TabSubtitleWithInterdot.kt @@ -0,0 +1,95 @@ +/* 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.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.tooling.preview.Preview +import org.mozilla.fenix.theme.FirefoxTheme + +/** + * Special caption text for a tab layout shown on one line. + * + * This will combine [firstText] with a interdot and then [secondText] ensuring that the second text + * (which is assumed to be smaller) always fills as much space as needed with the [firstText] automatically + * being resized to be smaller with an added ellipsis characters if needed. + * + * Possible results: + * ``` + * - when both texts would fit the screen + * ------------------------------------------ + * |firstText · secondText | + * ------------------------------------------ + * + * - when both text do not fit, second is shown in entirety, first is ellipsised. + * ------------------------------------------ + * |longerFirstTextOrSmallSc... · secondText| + * ------------------------------------------ + * ``` + * + * @param firstText Text shown at the start of the row. + * @param secondText Text shown at the end of the row. + */ +@Composable +fun TabSubtitleWithInterdot( + firstText: String, + secondText: String, +) { + val currentLayoutDirection = LocalLayoutDirection.current + + Layout( + content = { + TabSubtitle(text = firstText) + TabSubtitle(text = " \u00b7 ") + TabSubtitle(text = secondText) + } + ) { items, constraints -> + + // We need to measure from the end to start to ensure the secondItem will always be on screen + // and depending on secondItem's width and interdot's width the firstItem is automatically resized. + val secondItem = items[2].measure(constraints) + val interdot = items[1].measure( + constraints.copy(maxWidth = constraints.maxWidth - secondItem.width) + ) + val firstItem = items[0].measure( + constraints.copy(maxWidth = constraints.maxWidth - secondItem.width - interdot.width) + ) + + layout(constraints.maxWidth, constraints.maxHeight) { + val itemsPositions = IntArray(items.size) + with(Arrangement.Start) { + arrange( + constraints.maxWidth, + intArrayOf(firstItem.width, interdot.width, secondItem.width), + currentLayoutDirection, + itemsPositions + ) + } + + val placementHeight = constraints.maxHeight - firstItem.height + listOf(firstItem, interdot, secondItem).forEachIndexed { index, item -> + item.place(itemsPositions[index], placementHeight) + } + } + } +} + +@Composable +@Preview +private fun TabSubtitleWithInterdotPreview() { + FirefoxTheme { + Box(Modifier.background(FirefoxTheme.colors.surface)) { + TabSubtitleWithInterdot( + firstText = "firstText", + secondText = "secondText", + ) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/compose/TabTitle.kt b/app/src/main/java/org/mozilla/fenix/compose/TabTitle.kt new file mode 100644 index 0000000000..bcca9ebf2f --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/TabTitle.kt @@ -0,0 +1,51 @@ +/* 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.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.sp +import org.mozilla.fenix.theme.FirefoxTheme + +/** + * Default layout for a tab composable title. + * + * @param text Tab title + * @param maxLines Maximum number of lines for [text] to span, wrapping if necessary. + * If the text exceeds the given number of lines it will be ellipsized. + * @param modifier Optional [Modifier] to be applied to the layout. + */ +@Composable +fun TabTitle( + text: String, + maxLines: Int, + modifier: Modifier = Modifier +) { + Text( + modifier = modifier, + maxLines = maxLines, + text = text, + style = TextStyle(fontSize = 14.sp), + overflow = TextOverflow.Ellipsis, + color = FirefoxTheme.colors.textPrimary + ) +} + +@Composable +private fun TabTitlePreview() { + FirefoxTheme { + Box(Modifier.background(FirefoxTheme.colors.surface)) { + TabTitle( + "Awesome tab title", + 2 + ) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt b/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt index ead2a8cf31..ff82c2d241 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt @@ -36,7 +36,7 @@ fun HomeFragmentState.getFilteredStories( } val oldestSortedCategories = currentlySelectedCategories - .sortedBy { it.lastInteractedWithTimestamp } + .sortedByDescending { it.lastInteractedWithTimestamp } val filteredStoriesCount = getFilteredStoriesCount( pocketStoriesCategories, oldestSortedCategories, neededStoriesCount diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 9175a0dc85..4b238230b2 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -344,6 +344,7 @@ class HomeFragment : Fragment() { navController = findNavController() ), pocketStoriesController = DefaultPocketStoriesController( + homeActivity = activity, homeStore = homeFragmentStore ) ) diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt index 21c8e36f66..8f825d96c8 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt @@ -53,7 +53,7 @@ data class Tab( * @property recentTabs The list of recent [RecentTab] in the [HomeFragment]. * @property recentBookmarks The list of recently saved [BookmarkNode]s to show on the [HomeFragment]. * @property historyMetadata The list of [HistoryMetadataGroup]. - * @property pocketStories Currently shown [PocketRecommendedStory]ies. + * @property pocketStories The list of currently shown [PocketRecommendedStory]s. * @property pocketStoriesCategories All [PocketRecommendedStory] categories. * Also serves as an in memory cache of all stories mapped by category allowing for quick stories filtering. */ diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt index 1a02ef459d..ddacd882d8 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt @@ -219,6 +219,7 @@ class AdapterItemDiffCallback : DiffUtil.ItemCallback() { } } +@Suppress("LongParameterList") class SessionControlAdapter( private val store: HomeFragmentStore, private val interactor: SessionControlInteractor, diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt index f931eaf4c3..62704793d3 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt @@ -393,4 +393,8 @@ class SessionControlInteractor( override fun onStoriesShown(storiesShown: List) { pocketStoriesController.handleStoriesShown(storiesShown) } + + override fun onExternalLinkClicked(link: String) { + pocketStoriesController.handleExternalLinkClick(link) + } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index e70e7586f6..2de2d24886 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -2,188 +2,116 @@ * 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/. */ -@file:OptIn(ExperimentalMaterialApi::class, ExperimentalAnimationApi::class) @file:Suppress("MagicNumber") package org.mozilla.fenix.home.sessioncontrol.viewholders.pocket -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.foundation.Image -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.ClickableText -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.Card -import androidx.compose.material.ContentAlpha -import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.LocalContentAlpha -import androidx.compose.material.MaterialTheme -import androidx.compose.material.OutlinedButton import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.layout.Layout -import androidx.compose.ui.layout.Placeable import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import mozilla.components.concept.fetch.Client -import mozilla.components.concept.fetch.MutableHeaders -import mozilla.components.concept.fetch.Request -import mozilla.components.concept.fetch.Response import mozilla.components.service.pocket.PocketRecommendedStory -import mozilla.components.support.images.compose.loader.Fallback -import mozilla.components.support.images.compose.loader.ImageLoader -import mozilla.components.support.images.compose.loader.Placeholder -import mozilla.components.support.images.compose.loader.WithImage import mozilla.components.ui.colors.PhotonColors import org.mozilla.fenix.R +import org.mozilla.fenix.compose.ClickableSubstringLink +import org.mozilla.fenix.compose.FakeClient +import org.mozilla.fenix.compose.ListItemTabLarge +import org.mozilla.fenix.compose.ListItemTabLargePlaceholder +import org.mozilla.fenix.compose.SelectableChip +import org.mozilla.fenix.compose.StaggeredHorizontalGrid +import org.mozilla.fenix.compose.TabSubtitleWithInterdot +import org.mozilla.fenix.compose.TabTitle +import org.mozilla.fenix.theme.FirefoxTheme import kotlin.math.roundToInt import kotlin.random.Random /** * Displays a single [PocketRecommendedStory]. + * + * @param story The [PocketRecommendedStory] to be displayed. + * @param client [Client] instance to be used for downloading the story header image. + * @param onStoryClick Callback for when the user taps on this story. */ @Composable fun PocketStory( @PreviewParameter(PocketStoryProvider::class) story: PocketRecommendedStory, client: Client, - modifier: Modifier = Modifier + onStoryClick: (PocketRecommendedStory) -> Unit, ) { - Column( - modifier - .size(160.dp, 191.dp) - .clip(RoundedCornerShape(4.dp)) - .clickable { /* no-op */ } - ) { - Card( - elevation = 6.dp, - shape = RoundedCornerShape(4.dp), - modifier = Modifier.size(160.dp, 87.dp) - ) { - ImageLoader( - client = client, - // The endpoint allows us to ask for the optimal resolution image. - url = story.imageUrl.replace( - "{wh}", - with(LocalDensity.current) { - "${160.dp.toPx().roundToInt()}x${87.dp.toPx().roundToInt()}" - } - ), - targetSize = 160.dp - ) { - WithImage { painter -> - Image( - painter, - modifier = Modifier.size(160.dp, 87.dp), - contentDescription = "${story.title} story image" - ) - } - - Placeholder { - Box( - Modifier.background( - when (isSystemInDarkTheme()) { - true -> Color(0xFF42414D) // DarkGrey30 - false -> PhotonColors.LightGrey30 - } - ) - ) - } - - Fallback { - Box( - Modifier.background( - when (isSystemInDarkTheme()) { - true -> Color(0xFF42414D) // DarkGrey30 - false -> PhotonColors.LightGrey30 - } - ) - ) - } - } - } - - Spacer(modifier = Modifier.height(8.dp)) - - CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { - Text( - modifier = Modifier.padding(bottom = 2.dp), - text = story.publisher, - style = MaterialTheme.typography.caption, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) + val imageUrl = story.imageUrl.replace( + "{wh}", + with(LocalDensity.current) { "${116.dp.toPx().roundToInt()}x${84.dp.toPx().roundToInt()}" } + ) + ListItemTabLarge( + client = client, + imageUrl = imageUrl, + onClick = { onStoryClick(story) }, + title = { + TabTitle(text = story.title, maxLines = 3) + }, + subtitle = { + TabSubtitleWithInterdot(story.publisher, "${story.timeToRead} min") } - Text( - text = story.title, - style = MaterialTheme.typography.subtitle1, - maxLines = 4, - overflow = TextOverflow.Ellipsis - ) - } + ) } /** - * Displays a list of [PocketRecommendedStory]es. + * Displays a list of [PocketRecommendedStory]es on 3 by 3 grid. + * If there aren't enough stories to fill all columns placeholders containing an external link + * to go to Pocket for more recommendations are added. + * + * @param stories The list of [PocketRecommendedStory]ies to be displayed. Expect a list with 8 items. + * @param client [Client] instance to be used for downloading the story header image. + * @param onExternalLinkClicked Callback for when the user taps an element which contains an + * external link for where user can go for more recommendations. */ @Composable fun PocketStories( @PreviewParameter(PocketStoryProvider::class) stories: List, - client: Client + client: Client, + onExternalLinkClicked: (String) -> Unit ) { - // Items will be shown on two rows. Ceil the divide result to show more items on the top row. - val halfStoriesIndex = (stories.size + 1) / 2 + // Show stories in a 3 by 3 grid + val gridLength = 3 LazyRow { - itemsIndexed(stories) { index, item -> - if (index < halfStoriesIndex) { - Column( - Modifier.padding(end = if (index == halfStoriesIndex) 0.dp else 8.dp) - ) { - PocketStory(item, client) - - Spacer(modifier = Modifier.height(24.dp)) + itemsIndexed(stories.chunked(gridLength)) { rowIndex, columnItems -> + Column(Modifier.padding(end = if (rowIndex < gridLength - 1) 8.dp else 0.dp)) { + for (index in 0 until gridLength) { + columnItems.getOrNull(index)?.let { story -> + PocketStory(story, client) { + onExternalLinkClicked(story.url) + } + } ?: ListItemTabLargePlaceholder(stringResource(R.string.pocket_stories_placeholder_text)) { + onExternalLinkClicked("http://getpocket.com/explore") + } - stories.getOrNull(halfStoriesIndex + index)?.let { - PocketStory(it, client) + // Add padding between all rows. Not also after the last. + if (index < gridLength - 1) { + Spacer(modifier = Modifier.height(8.dp)) } } } @@ -194,278 +122,104 @@ fun PocketStories( /** * Displays a list of [PocketRecommendedStoryCategory]. * - * @param categories the categories needed to be displayed. - * @param onCategoryClick callback for when the user taps a category. + * @param categories The categories needed to be displayed. + * @param onCategoryClick Callback for when the user taps a category. */ @Composable fun PocketStoriesCategories( categories: List, onCategoryClick: (PocketRecommendedStoryCategory) -> Unit ) { - StaggeredHorizontalGrid { + StaggeredHorizontalGrid( + horizontalItemsSpacing = 16.dp + ) { categories.forEach { category -> - PocketStoryCategory(category) { - onCategoryClick(it) + SelectableChip(category.name, category.isSelected) { + onCategoryClick(category) } } } } /** - * Displays an individual [PocketRecommendedStoryCategory]. + * Pocket feature section title. + * Shows a default text about Pocket and offers a external link to learn more. * - * @param category the categories needed to be displayed. - * @param onClick callback for when the user taps this category. + * @param onExternalLinkClicked Callback invoked when the user clicks the "Learn more" link. + * Contains the full URL for where the user should be navigated to. */ @Composable -fun PocketStoryCategory( - category: PocketRecommendedStoryCategory, - onClick: (PocketRecommendedStoryCategory) -> Unit +fun PoweredByPocketHeader( + onExternalLinkClicked: (String) -> Unit, ) { - val contentColor = when (category.isSelected) { - true -> Color.Blue - false -> Color.DarkGray + val color = when (isSystemInDarkTheme()) { + true -> PhotonColors.LightGrey30 + false -> PhotonColors.DarkGrey90 } - OutlinedButton( - onClick = { onClick(category) }, - shape = RoundedCornerShape(32.dp), - border = BorderStroke(1.dp, contentColor), - colors = ButtonDefaults.outlinedButtonColors( - contentColor = contentColor - ), - contentPadding = PaddingValues(8.dp, 7.dp) - ) { - Row { - Text( - text = category.name, - modifier = Modifier.alignByBaseline(), - ) - Icon( - painter = painterResource(id = R.drawable.mozac_ic_check), - contentDescription = "Expand or collapse Pocket recommended stories", - modifier = Modifier.alignByBaseline() - ) - } - } -} - -/** - * Displays a list of items as a staggered horizontal grid placing them on ltr rows and continuing - * on as many below rows as needed to place all items. - * - * In an effort to best utilize the available row space this can mix the items such that narrower ones - * are placed on the same row as wider ones if the otherwise next item doesn't fit. - * - * @param modifier to be applied to the layout. - * @param horizontalItemsSpacing minimum horizontal space between items. Does not add spacing to layout bounds. - * @param verticalItemsSpacing vertical space between items - * @param arrangement how the items will be horizontally aligned and spaced. - * @param content the children composables to be laid out. - */ -@Composable -fun StaggeredHorizontalGrid( - modifier: Modifier = Modifier, - horizontalItemsSpacing: Dp = 0.dp, - verticalItemsSpacing: Dp = 8.dp, - arrangement: Arrangement.Horizontal = Arrangement.SpaceEvenly, - content: @Composable () -> Unit -) { - Layout(content, modifier) { items, constraints -> - val horizontalItemsSpacingPixels = horizontalItemsSpacing.roundToPx() - val verticalItemsSpacingPixels = verticalItemsSpacing.roundToPx() - var totalHeight = 0 - val itemsRows = mutableListOf>() - val notYetPlacedItems = items.map { - it.measure(constraints) - }.toMutableList() - - fun getIndexOfNextPlaceableThatFitsRow(available: List, currentWidth: Int): Int { - return available.indexOfFirst { - currentWidth + it.width <= constraints.maxWidth - } - } - - // Populate each row with as many items as possible combining wider with narrower items. - // This will change the order of shown categories. - var (currentRow, currentWidth) = mutableListOf() to 0 - while (notYetPlacedItems.isNotEmpty()) { - if (currentRow.isEmpty()) { - currentRow.add( - notYetPlacedItems[0].also { - currentWidth += it.width - totalHeight += it.height + verticalItemsSpacingPixels - } - ) - notYetPlacedItems.removeAt(0) - } else { - val nextPlaceableThatFitsIndex = getIndexOfNextPlaceableThatFitsRow(notYetPlacedItems, currentWidth) - if (nextPlaceableThatFitsIndex >= 0) { - currentRow.add( - notYetPlacedItems[nextPlaceableThatFitsIndex].also { - currentWidth += it.width + horizontalItemsSpacingPixels - } - ) - notYetPlacedItems.removeAt(nextPlaceableThatFitsIndex) - } else { - itemsRows.add(currentRow) - currentRow = mutableListOf() - currentWidth = 0 - } - } - } - if (currentRow.isNotEmpty()) { - itemsRows.add(currentRow) - } - totalHeight -= verticalItemsSpacingPixels - - // Place each item from each row on screen. - layout(constraints.maxWidth, totalHeight) { - itemsRows.forEachIndexed { rowIndex, itemRow -> - val itemsSizes = IntArray(itemRow.size) { - itemRow[it].width + - if (it < itemRow.lastIndex) horizontalItemsSpacingPixels else 0 - } - val itemsPositions = IntArray(itemsSizes.size) { 0 } - with(arrangement) { - arrange(constraints.maxWidth, itemsSizes, LayoutDirection.Ltr, itemsPositions) - } - - itemRow.forEachIndexed { itemIndex, item -> - item.place( - x = itemsPositions[itemIndex], - y = (rowIndex * item.height) + (rowIndex * verticalItemsSpacingPixels) - ) - } - } - } - } -} - -/** - * Displays [content] in a layout which will have at the bottom more information about Pocket - * and also an external link for more up-to-date content. - */ -@Composable -fun PocketRecommendations( - content: @Composable (() -> Unit) -) { - val annotatedText = buildAnnotatedString { - val text = "Pocket is part of the Firefox family. " - val link = "Learn more." - val annotationStartIndex = text.length - val annotationEndIndex = annotationStartIndex + link.length - - append(text + link) - - addStyle( - SpanStyle(textDecoration = TextDecoration.Underline), - start = annotationStartIndex, - end = annotationEndIndex - ) - - addStringAnnotation( - tag = "link", - annotation = "https://www.mozilla.org/en-US/firefox/pocket/", - start = annotationStartIndex, - end = annotationEndIndex - ) - } + val link = stringResource(R.string.pocket_stories_feature_learn_more) + val text = stringResource(R.string.pocket_stories_feature_caption, link) + val linkStartIndex = text.indexOf(link) + val linkEndIndex = linkStartIndex + link.length Column( - modifier = Modifier.padding(vertical = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { - content() - - CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { - ClickableText( - text = annotatedText, - style = MaterialTheme.typography.caption, - onClick = { - annotatedText - .getStringAnnotations("link", it, it) - .firstOrNull()?.let { - println("Learn more clicked! Should now access ${it.item}") - } - } + Row( + Modifier + .fillMaxWidth() + .semantics(mergeDescendants = true) { }, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(id = R.drawable.pocket_vector), + contentDescription = null, + // Apply the red tint in code. Otherwise the image is black and white. + tint = Color(0xFFEF4056) ) - } - } -} -/** - * Displays [content] in an expandable card. - */ -@Composable -fun ExpandableCard( - modifier: Modifier = Modifier, - content: @Composable (() -> Unit) -) { - var isExpanded by remember { mutableStateOf(true) } - val chevronRotationState by animateFloatAsState(targetValue = if (isExpanded) 0f else 180f) + Spacer(modifier = Modifier.width(16.dp)) - Card( - modifier = modifier, - shape = RoundedCornerShape(4.dp), - onClick = { isExpanded = !isExpanded } - ) { - Column { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - modifier = Modifier.weight(10f), - text = "Trending stories from Pocket", - style = MaterialTheme.typography.h6, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) + Column { + Text(text = stringResource(R.string.pocket_stories_feature_title), color = color) - IconButton( - onClick = { isExpanded = !isExpanded }, - modifier = Modifier.rotate(chevronRotationState) - ) { - Icon( - modifier = Modifier.weight(1f), - painter = painterResource(id = R.drawable.ic_chevron_up), - contentDescription = "Expand or collapse Pocket recommended stories", - ) + ClickableSubstringLink(text, color, linkStartIndex, linkEndIndex) { + onExternalLinkClicked("https://www.mozilla.org/en-US/firefox/pocket/") } } - - AnimatedVisibility(visible = isExpanded) { - content() - } } } } @Composable @Preview -private fun FinalDesign() { - ExpandableCard { - PocketRecommendations { - PocketStories( - stories = getFakePocketStories(7), - client = FakeClient() - ) +private fun PocketStoriesComposablesPreview() { + FirefoxTheme { + Box(Modifier.background(FirefoxTheme.colors.surface)) { + Column { + PocketStories( + stories = getFakePocketStories(8), + client = FakeClient(), + onExternalLinkClicked = { } + ) + Spacer(Modifier.height(10.dp)) - Spacer(Modifier.height(8.dp)) + PocketStoriesCategories( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor".split(" ").map { + PocketRecommendedStoryCategory(it) + } + ) { } + Spacer(Modifier.height(10.dp)) - PocketStoriesCategories( - listOf("general", "health", "technology", "food", "career").map { - PocketRecommendedStoryCategory(it) - } - ) { } + PoweredByPocketHeader { } + } } } } private class PocketStoryProvider : PreviewParameterProvider { override val values = getFakePocketStories(7).asSequence() - override val count = 7 + override val count = 8 } private fun getFakePocketStories(limit: Int = 1): List { @@ -487,12 +241,3 @@ private fun getFakePocketStories(limit: Int = 1): List { } } } - -private class FakeClient : Client() { - override fun fetch(request: Request) = Response( - url = request.url, - status = 200, - body = Response.Body.empty(), - headers = MutableHeaders() - ) -} diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt index 0d2dee2ab1..ea6f25610e 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt @@ -8,6 +8,8 @@ import org.mozilla.fenix.home.HomeFragmentAction import org.mozilla.fenix.home.HomeFragmentStore import mozilla.components.lib.state.Store import mozilla.components.service.pocket.PocketRecommendedStory +import org.mozilla.fenix.BrowserDirection +import org.mozilla.fenix.HomeActivity /** * Contract for how all user interactions with the Pocket recommended stories feature are to be handled. @@ -26,14 +28,23 @@ interface PocketStoriesController { * @param storiesShown the new list of [PocketRecommendedStory]es shown to the user. */ fun handleStoriesShown(storiesShown: List) + + /** + * Callback for when the an external link is clicked. + * + * @param link URL clicked. + */ + fun handleExternalLinkClick(link: String) } /** * Default behavior for handling all user interactions with the Pocket recommended stories feature. * + * @param homeActivity [HomeActivity] used to open URLs in a new tab. * @param homeStore [Store] from which to read the current Pocket recommendations and dispatch new actions on. */ internal class DefaultPocketStoriesController( + val homeActivity: HomeActivity, val homeStore: HomeFragmentStore ) : PocketStoriesController { override fun handleCategoryClick(categoryClicked: PocketRecommendedStoryCategory) { @@ -74,4 +85,8 @@ internal class DefaultPocketStoriesController( override fun handleStoriesShown(storiesShown: List) { homeStore.dispatch(HomeFragmentAction.PocketStoriesShown(storiesShown)) } + + override fun handleExternalLinkClick(link: String) { + homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) + } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt index d0e0a0d148..7f4d18a591 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt @@ -23,4 +23,11 @@ interface PocketStoriesInteractor { * @param storiesShown the new list of [PocketRecommendedStory]es shown to the user. */ fun onStoriesShown(storiesShown: List) + + /** + * Callback for when the user clicks an external link. + * + * @param link URL clicked. + */ + fun onExternalLinkClicked(link: String) } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt index cb8393b5b8..e108f08de2 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt @@ -15,15 +15,19 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.recyclerview.widget.RecyclerView import mozilla.components.concept.fetch.Client import mozilla.components.lib.state.ext.observeAsComposableState import mozilla.components.service.pocket.PocketRecommendedStory +import org.mozilla.fenix.R +import org.mozilla.fenix.compose.SectionHeader import org.mozilla.fenix.home.HomeFragmentStore +import org.mozilla.fenix.theme.FirefoxTheme -internal const val POCKET_STORIES_TO_SHOW_COUNT = 7 -internal const val POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT = 7 +internal const val POCKET_STORIES_TO_SHOW_COUNT = 8 +internal const val POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT = 8 /** * [RecyclerView.ViewHolder] that will display a list of [PocketRecommendedStory]es @@ -46,7 +50,15 @@ class PocketStoriesViewHolder( ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed ) composeView.setContent { - PocketStories(store, client, interactor::onStoriesShown, interactor::onCategoryClick) + FirefoxTheme { + PocketStories( + store, + client, + interactor::onStoriesShown, + interactor::onCategoryClick, + interactor::onExternalLinkClicked + ) + } } } @@ -60,7 +72,8 @@ fun PocketStories( store: HomeFragmentStore, client: Client, onStoriesShown: (List) -> Unit, - onCategoryClick: (PocketRecommendedStoryCategory) -> Unit + onCategoryClick: (PocketRecommendedStoryCategory) -> Unit, + onExternalLinkClicked: (String) -> Unit ) { val stories = store .observeAsComposableState { state -> state.pocketStories }.value @@ -76,21 +89,32 @@ fun PocketStories( } } - ExpandableCard( - Modifier - .fillMaxWidth() - .padding(top = 40.dp) - ) { - PocketRecommendations { - Column { - PocketStories(stories ?: emptyList(), client) + Column(modifier = Modifier.padding(vertical = 48.dp)) { + SectionHeader( + text = stringResource(R.string.pocket_stories_header), + modifier = Modifier + .fillMaxWidth() + ) - Spacer(Modifier.height(8.dp)) + Spacer(Modifier.height(17.dp)) - PocketStoriesCategories(categories ?: emptyList()) { - onCategoryClick(it) - } - } + PocketStories(stories ?: emptyList(), client, onExternalLinkClicked) + + Spacer(Modifier.height(24.dp)) + + SectionHeader( + text = stringResource(R.string.pocket_stories_categories_header), + modifier = Modifier.fillMaxWidth() + ) + + Spacer(Modifier.height(17.dp)) + + PocketStoriesCategories(categories ?: emptyList()) { + onCategoryClick(it) } + + Spacer(Modifier.height(24.dp)) + + PoweredByPocketHeader(onExternalLinkClicked) } } diff --git a/app/src/main/res/drawable/pocket_vector.xml b/app/src/main/res/drawable/pocket_vector.xml new file mode 100644 index 0000000000..9c2ad19290 --- /dev/null +++ b/app/src/main/res/drawable/pocket_vector.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index edb80dc45d..7948f87ac4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1909,4 +1909,17 @@ Close + + + Thought provoking stories + + Stories by topic + + Discover more + + Powered by Pocket + + Part of the Firefox family. %s + + Learn more diff --git a/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt b/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt index 4e1c743c06..42a9199af3 100644 --- a/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt +++ b/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt @@ -141,7 +141,7 @@ class HomeFragmentStateTest { } @Test - fun `GIVEN two categories are selected WHEN getFilteredStories is called for an odd number of stories THEN there are more by one stories from the oldest category`() { + fun `GIVEN two categories are selected WHEN getFilteredStories is called for an odd number of stories THEN there are more by one stories from the newest category`() { val firstSelectedCategory = otherStoriesCategory.copy(lastInteractedWithTimestamp = 0, isSelected = true) val lastSelectedCategory = anotherStoriesCategory.copy(lastInteractedWithTimestamp = 1, isSelected = true) val homeState = HomeFragmentState( @@ -153,8 +153,8 @@ class HomeFragmentStateTest { val result = homeState.getFilteredStories(5) assertEquals(5, result.size) - assertEquals(3, result.filter { it.category == firstSelectedCategory.name }.size) - assertEquals(2, result.filter { it.category == lastSelectedCategory.name }.size) + assertEquals(2, result.filter { it.category == firstSelectedCategory.name }.size) + assertEquals(3, result.filter { it.category == lastSelectedCategory.name }.size) } @Test @@ -280,7 +280,7 @@ class HomeFragmentStateTest { @Test fun `GIVEN two categories selected with more than needed stories WHEN getFilteredStories is called THEN the results are sorted in the order of least shown`() { val firstCategory = PocketRecommendedStoryCategory( - "first", getFakePocketStories(3, "first"), true, 222 + "first", getFakePocketStories(3, "first"), true, 0 ).run { // Avoid the first item also being the oldest to eliminate a potential bug in code // that would still get the expected result. @@ -295,7 +295,7 @@ class HomeFragmentStateTest { ) } val secondCategory = PocketRecommendedStoryCategory( - "second", getFakePocketStories(3, "second"), true, 0 + "second", getFakePocketStories(3, "second"), true, 222 ).run { // Avoid the first item also being the oldest to eliminate a potential bug in code // that would still get the expected result. diff --git a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt index 96369c0c10..4d005c22d0 100644 --- a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt @@ -248,4 +248,13 @@ class SessionControlInteractorTest { verify { pocketStoriesController.handleCategoryClick(clickedCategory) } } + + @Test + fun `GIVEN a PocketStoriesInteractor WHEN an external link is clicked THEN handle it in a PocketStoriesController`() { + val link = "https://www.mozilla.org/en-US/firefox/pocket/" + + interactor.onExternalLinkClicked(link) + + verify { pocketStoriesController.handleExternalLinkClick(link) } + } } diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt index e16582efa9..b4105ac86c 100644 --- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt @@ -9,6 +9,8 @@ import io.mockk.spyk import io.mockk.verify import mozilla.components.service.pocket.PocketRecommendedStory import org.junit.Test +import org.mozilla.fenix.BrowserDirection +import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.home.HomeFragmentAction import org.mozilla.fenix.home.HomeFragmentState import org.mozilla.fenix.home.HomeFragmentStore @@ -23,7 +25,7 @@ class DefaultPocketStoriesControllerTest { HomeFragmentState(pocketStoriesCategories = listOf(category1, category2)) ) ) - val controller = DefaultPocketStoriesController(store) + val controller = DefaultPocketStoriesController(mockk(), store) controller.handleCategoryClick(category1) verify(exactly = 0) { store.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(category1.name)) } @@ -33,7 +35,7 @@ class DefaultPocketStoriesControllerTest { } @Test - fun `GIVEN 7 categories are selected WHEN when a new one is clicked THEN the oldest seleected is deselected before selecting the new one`() { + fun `GIVEN 8 categories are selected WHEN when a new one is clicked THEN the oldest selected is deselected before selecting the new one`() { val category1 = PocketRecommendedStoryCategory( "cat1", emptyList(), isSelected = true, lastInteractedWithTimestamp = 111 ) @@ -43,6 +45,7 @@ class DefaultPocketStoriesControllerTest { val category4 = category1.copy("cat4", lastInteractedWithTimestamp = 444) val category5 = category1.copy("cat5", lastInteractedWithTimestamp = 555) val category6 = category1.copy("cat6", lastInteractedWithTimestamp = 678) + val category7 = category1.copy("cat6", lastInteractedWithTimestamp = 890) val newSelectedCategory = category1.copy( "newSelectedCategory", isSelected = false, lastInteractedWithTimestamp = 654321 ) @@ -50,12 +53,12 @@ class DefaultPocketStoriesControllerTest { HomeFragmentStore( HomeFragmentState( pocketStoriesCategories = listOf( - category1, category2, category3, category4, category5, category6, oldestSelectedCategory + category1, category2, category3, category4, category5, category6, category7, oldestSelectedCategory ) ) ) ) - val controller = DefaultPocketStoriesController(store) + val controller = DefaultPocketStoriesController(mockk(), store) controller.handleCategoryClick(newSelectedCategory) @@ -64,7 +67,7 @@ class DefaultPocketStoriesControllerTest { } @Test - fun `GIVEN fewer than 7 categories are selected WHEN when a new one is clicked THEN don't deselect anything but select the newly clicked category`() { + fun `GIVEN fewer than 8 categories are selected WHEN when a new one is clicked THEN don't deselect anything but select the newly clicked category`() { val category1 = PocketRecommendedStoryCategory( "cat1", emptyList(), isSelected = true, lastInteractedWithTimestamp = 111 ) @@ -73,6 +76,7 @@ class DefaultPocketStoriesControllerTest { val oldestSelectedCategory = category1.copy("oldestSelectedCategory", lastInteractedWithTimestamp = 0) val category4 = category1.copy("cat4", lastInteractedWithTimestamp = 444) val category5 = category1.copy("cat5", lastInteractedWithTimestamp = 555) + val category6 = category1.copy("cat6", lastInteractedWithTimestamp = 678) val newSelectedCategory = category1.copy( "newSelectedCategory", isSelected = false, lastInteractedWithTimestamp = 654321 ) @@ -80,12 +84,12 @@ class DefaultPocketStoriesControllerTest { HomeFragmentStore( HomeFragmentState( pocketStoriesCategories = listOf( - category1, category2, category3, category4, category5, oldestSelectedCategory + category1, category2, category3, category4, category5, category6, oldestSelectedCategory ) ) ) ) - val controller = DefaultPocketStoriesController(store) + val controller = DefaultPocketStoriesController(mockk(), store) controller.handleCategoryClick(newSelectedCategory) @@ -96,11 +100,22 @@ class DefaultPocketStoriesControllerTest { @Test fun `WHEN new stories are shown THEN update the State`() { val store = spyk(HomeFragmentStore()) - val controller = DefaultPocketStoriesController(store) + val controller = DefaultPocketStoriesController(mockk(), store) val storiesShown: List = mockk() controller.handleStoriesShown(storiesShown) verify { store.dispatch(HomeFragmentAction.PocketStoriesShown(storiesShown)) } } + + @Test + fun `WHEN an external link is clicked then open that using HomeActivity`() { + val link = "https://www.mozilla.org/en-US/firefox/pocket/" + val homeActivity: HomeActivity = mockk(relaxed = true) + val controller = DefaultPocketStoriesController(homeActivity, mockk()) + + controller.handleExternalLinkClick(link) + + verify { homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) } + } } From e72b7f7cc8b5dabc0548be66f04663f60e68b05b Mon Sep 17 00:00:00 2001 From: Mugurell Date: Wed, 29 Sep 2021 11:56:52 +0300 Subject: [PATCH 308/517] For #21561 - Enable/Disable the feature from the customization menu --- .../java/org/mozilla/fenix/FeatureFlags.kt | 12 ++++++++++++ .../org/mozilla/fenix/home/HomeFragment.kt | 8 ++++++-- .../home/sessioncontrol/SessionControlView.kt | 9 +++------ .../fenix/settings/CustomizationFragment.kt | 2 +- .../fenix/settings/SecretSettingsFragment.kt | 19 ------------------- .../java/org/mozilla/fenix/utils/Settings.kt | 2 +- app/src/main/res/values/static_strings.xml | 2 -- .../res/xml/secret_settings_preferences.xml | 5 ----- .../sessioncontrol/SessionControlViewTest.kt | 17 ----------------- 9 files changed, 23 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index 789eba230f..d621137f94 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -4,6 +4,10 @@ package org.mozilla.fenix +import android.content.Context +import mozilla.components.support.locale.LocaleManager +import mozilla.components.support.locale.LocaleManager.getSystemDefault + /** * A single source for setting feature flags that are mostly based on build type. */ @@ -73,4 +77,12 @@ object FeatureFlags { * Enables showing search groupings in the History. */ val showHistorySearchGroups = Config.channel.isNightlyOrDebug + + /** + * Show Pocket recommended stories on home. + */ + fun isPocketRecommendationsFeatureEnabled(context: Context): Boolean { + return Config.channel.isNightlyOrDebug && + "en-US" == LocaleManager.getCurrentLocale(context)?.toLanguageTag() ?: getSystemDefault().toLanguageTag() + } } diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 4b238230b2..46b0cd6b70 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -252,13 +252,17 @@ class HomeFragment : Fragment() { ) } - if (requireContext().settings().pocketRecommendations) { - lifecycleScope.launch(IO) { + lifecycleScope.launch(IO) { + if (FeatureFlags.isPocketRecommendationsFeatureEnabled(requireContext()) && + requireContext().settings().pocketRecommendations + ) { val categories = components.core.pocketStoriesService.getStories() .groupBy { story -> story.category } .map { (category, stories) -> PocketRecommendedStoryCategory(category, stories) } homeFragmentStore.dispatch(HomeFragmentAction.PocketStoriesCategoriesChange(categories)) + } else { + homeFragmentStore.dispatch(HomeFragmentAction.PocketStoriesChange(emptyList())) } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt index 497c61ec61..0903563cf7 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt @@ -4,7 +4,6 @@ package org.mozilla.fenix.home.sessioncontrol -import android.content.Context import android.view.View import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner @@ -32,7 +31,6 @@ import org.mozilla.fenix.utils.Settings @Suppress("ComplexMethod", "LongParameterList") @VisibleForTesting internal fun normalModeAdapterItems( - context: Context, topSites: List, collections: List, expandedCollections: Set, @@ -82,7 +80,7 @@ internal fun normalModeAdapterItems( showCollections(collections, expandedCollections, items) } - if (context.settings().pocketRecommendations && pocketStories.isNotEmpty()) { + if (pocketStories.isNotEmpty()) { shouldShowCustomizeHome = true items.add(AdapterItem.PocketStoriesItem) } @@ -150,9 +148,8 @@ private fun onboardingAdapterItems(onboardingState: OnboardingState): List = when (mode) { +private fun HomeFragmentState.toAdapterList(): List = when (mode) { is Mode.Normal -> normalModeAdapterItems( - context, topSites, collections, expandedCollections, @@ -216,7 +213,7 @@ class SessionControlView( interactor.showOnboardingDialog() } - val stateAdapterList = state.toAdapterList(view.context) + val stateAdapterList = state.toAdapterList() if (homeScreenViewModel.shouldScrollToTopSites) { sessionControlAdapter.submitList(stateAdapterList) { diff --git a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt index c485c6ca02..607c59cc45 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt @@ -159,7 +159,7 @@ class CustomizationFragment : PreferenceFragmentCompat() { } requirePreference(R.string.pref_key_pocket_homescreen_recommendations).apply { - isVisible = false + isVisible = FeatureFlags.isPocketRecommendationsFeatureEnabled(context) isChecked = context.settings().pocketRecommendations onPreferenceChangeListener = CustomizeHomeMetricsUpdater() } diff --git a/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt index 8f50dfa773..2ace1c605b 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt @@ -11,7 +11,6 @@ import android.widget.Toast import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference -import org.mozilla.fenix.Config import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.ext.components @@ -77,24 +76,6 @@ class SecretSettingsFragment : PreferenceFragmentCompat() { isChecked = context.settings().nimbusUsePreview onPreferenceChangeListener = SharedPreferenceUpdater() } - - requirePreference(R.string.pref_key_pocket_homescreen_recommendations).apply { - isVisible = Config.channel.isDebug - isChecked = context.settings().pocketRecommendations - onPreferenceChangeListener = object : SharedPreferenceUpdater() { - override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean { - (newValue as? Boolean)?.let { - if (it) { - context.components.core.pocketStoriesService.startPeriodicStoriesRefresh() - } else { - context.components.core.pocketStoriesService.stopPeriodicStoriesRefresh() - } - } - - return super.onPreferenceChange(preference, newValue) - } - } - } } companion object { diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index f76639ab54..5c9c94d0fd 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -1175,6 +1175,6 @@ class Settings(private val appContext: Context) : PreferencesHolder { var pocketRecommendations by booleanPreference( appContext.getPreferenceKey(R.string.pref_key_pocket_homescreen_recommendations), - default = false + default = true ) } diff --git a/app/src/main/res/values/static_strings.xml b/app/src/main/res/values/static_strings.xml index 1135d467dc..21913fbd10 100644 --- a/app/src/main/res/values/static_strings.xml +++ b/app/src/main/res/values/static_strings.xml @@ -49,8 +49,6 @@ Nimbus Experiments Use Nimbus Preview Collection (requires restart) - - Debug only. Enable Pocket recommended articles on homescreen. Make inactive diff --git a/app/src/main/res/xml/secret_settings_preferences.xml b/app/src/main/res/xml/secret_settings_preferences.xml index 96eeeb71a3..e45009d1c8 100644 --- a/app/src/main/res/xml/secret_settings_preferences.xml +++ b/app/src/main/res/xml/secret_settings_preferences.xml @@ -25,9 +25,4 @@ android:key="@string/pref_key_nimbus_use_preview" android:title="@string/preferences_nimbus_use_preview_collection" app:iconSpaceReserved="false" /> - diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/SessionControlViewTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/SessionControlViewTest.kt index c1514572c9..11e9afd02a 100644 --- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/SessionControlViewTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/SessionControlViewTest.kt @@ -7,7 +7,6 @@ package org.mozilla.fenix.home.sessioncontrol import androidx.recyclerview.widget.RecyclerView import io.mockk.every import io.mockk.mockk -import io.mockk.spyk import io.mockk.verify import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNodeType @@ -19,7 +18,6 @@ import org.junit.Assert.assertTrue import org.junit.Assert.assertFalse import org.junit.Test import org.junit.runner.RunWith -import org.mozilla.fenix.ext.settings import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.historymetadata.HistoryMetadataGroup import org.mozilla.fenix.home.HomeFragmentState @@ -145,7 +143,6 @@ class SessionControlViewTest { val pocketArticles = emptyList() val results = normalModeAdapterItems( - testContext, topSites, collections, expandedCollections, @@ -173,7 +170,6 @@ class SessionControlViewTest { val pocketArticles = emptyList() val results = normalModeAdapterItems( - testContext, topSites, collections, expandedCollections, @@ -202,7 +198,6 @@ class SessionControlViewTest { val pocketArticles = emptyList() val results = normalModeAdapterItems( - testContext, topSites, collections, expandedCollections, @@ -229,14 +224,8 @@ class SessionControlViewTest { val recentTabs = emptyList() val historyMetadata = emptyList() val pocketArticles = listOf(PocketRecommendedStory("", "", "", "", "", 1, 1)) - val context = spyk(testContext) - - val settings: Settings = mockk() - every { settings.pocketRecommendations } returns true - every { context.settings() } returns settings val results = normalModeAdapterItems( - context, topSites, collections, expandedCollections, @@ -262,14 +251,8 @@ class SessionControlViewTest { val recentTabs = emptyList() val historyMetadata = emptyList() val pocketArticles = emptyList() - val context = spyk(testContext) - - val settings: Settings = mockk() - every { settings.pocketRecommendations } returns true - every { context.settings() } returns settings val results = normalModeAdapterItems( - context, topSites, collections, expandedCollections, From 02614eb8897255bccaeb03e63ec29dcceeee49a6 Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Wed, 29 Sep 2021 10:16:31 -0700 Subject: [PATCH 309/517] For #21574: Move the homescreen onboarding card behind a feature flag --- app/src/main/java/org/mozilla/fenix/FeatureFlags.kt | 5 +++++ .../home/sessioncontrol/SessionControlController.kt | 11 +++++++---- app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index d621137f94..b420fd9795 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -85,4 +85,9 @@ object FeatureFlags { return Config.channel.isNightlyOrDebug && "en-US" == LocaleManager.getCurrentLocale(context)?.toLanguageTag() ?: getSystemDefault().toLanguageTag() } + + /** + * Enables showing the homescreen onboarding card. + */ + val showHomeOnboarding = Config.channel.isDebug } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt index 078110cc4e..9b956a3ddc 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt @@ -27,6 +27,7 @@ import mozilla.components.feature.top.sites.TopSite import mozilla.components.support.ktx.android.view.showKeyboard import mozilla.components.support.ktx.kotlin.isUrl import org.mozilla.fenix.BrowserDirection +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.browser.BrowserFragmentDirections @@ -454,10 +455,12 @@ class DefaultSessionControlController( } override fun handleShowOnboardingDialog() { - navController.nav( - R.id.homeFragment, - HomeFragmentDirections.actionGlobalHomeOnboardingDialog() - ) + if (FeatureFlags.showHomeOnboarding) { + navController.nav( + R.id.homeFragment, + HomeFragmentDirections.actionGlobalHomeOnboardingDialog() + ) + } } override fun handleReadPrivacyNoticeClicked() { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7948f87ac4..23225f3d72 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -255,6 +255,8 @@ Search %s Search directly from the address bar + + What’s new in Firefox! From f8edee649af12d0934922de81afd847ea5d2cee8 Mon Sep 17 00:00:00 2001 From: Mozilla L10n Automation Bot Date: Thu, 30 Sep 2021 00:06:00 +0000 Subject: [PATCH 310/517] Import l10n. --- app/src/main/res/values-cs/strings.xml | 101 ++++++++-- app/src/main/res/values-cy/strings.xml | 4 +- app/src/main/res/values-da/strings.xml | 47 +++-- app/src/main/res/values-fy-rNL/strings.xml | 55 ++++-- app/src/main/res/values-hr/strings.xml | 49 +++-- app/src/main/res/values-ja/strings.xml | 40 ++-- app/src/main/res/values-kmr/strings.xml | 207 ++++++++++++++++++--- app/src/main/res/values-lo/strings.xml | 115 +++++++++--- app/src/main/res/values-nl/strings.xml | 3 + app/src/main/res/values-nn-rNO/strings.xml | 37 ++-- app/src/main/res/values-pt-rPT/strings.xml | 78 ++++++-- app/src/main/res/values-rm/strings.xml | 101 ++++++++-- app/src/main/res/values-tt/strings.xml | 76 +++++--- app/src/main/res/values-uk/strings.xml | 2 +- 14 files changed, 713 insertions(+), 202 deletions(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index e02f198c8f..d7bfe76e30 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -52,7 +52,9 @@ - Naposledy uložené + Naposledy uložené + + Naposledy přidané Naposledy uložené záložky @@ -123,7 +125,14 @@ - Předchozí průzkumy + Předchozí průzkumy + + + Nedávno navštívené + + Odstranit @@ -202,6 +211,8 @@ Upravit + + Přizpůsobit Domovská obrazovka @@ -396,6 +407,18 @@ Nastavení sbírky změněno. Pro aplikování změn se nyní aplikace ukončí… + + + Návrat zpět + + Naposledy uložené + + Naposledy přidané + + Nedávno navštívené + + Pocket + Doplněk není podporován @@ -585,6 +608,13 @@ Zavřít + + Jedna stránka + + Stránek: %d + Nedávno zavřené panely @@ -764,6 +794,9 @@ Uložit + + Ostatní + Vymazat historii @@ -776,15 +809,15 @@ Vymazat - Zkopírovat + Zkopírovat - Sdílet + Sdílet - Otevřít v novém panelu + Otevřít v novém panelu - Otevřít v anonymním panelu + Otevřít v anonymním panelu - Smazat + Smazat Vybráno položek: %1$d @@ -1261,12 +1294,12 @@ Už máte účet? - Co je nového + Co je nového - Zajímají vás novinky v přepracované aplikaci %s? Chcete vědět, co se změnilo? + Zajímají vás novinky v přepracované aplikaci %s? Chcete vědět, co se změnilo? - Zde najdete všechny odpovědi + Zde najdete všechny odpovědi Synchronizace Firefoxu mezi zařízeními @@ -1306,14 +1339,14 @@ Nastavte si umístění nástrojové lišty na obrazovce dole nebo nahoře, ať na ni dosáhnete. - Prohlížejte v soukromí + Prohlížejte v soukromí - Pro jednorázové otevření anonymního panelu stačí použít ikonku %s. + Pro jednorázové otevření anonymního panelu stačí použít ikonku %s. - Pokud chcete anonymní panel otevírat pokaždé, můžete změnit nastavení anonymního prohlížení. + Pokud chcete anonymní panel otevírat pokaždé, můžete změnit nastavení anonymního prohlížení. - Otevřít nastavení + Otevřít nastavení Vaše soukromí Maže cookies nastavené během přesměrování známými sledujícími servery. + + Některé níže označené sledovací prvky byly na této stránce částečně povoleny, protože jste s nimi interagovali *. + + Zjistit více + Podpora @@ -1544,6 +1583,9 @@ Vyplňování a ukládání uživatelských jmen a hesel v dalších aplikacích na vašem zařízení. + + Přidat přihlašovací údaje + Synchronizovat přihlašovací údaje @@ -1606,6 +1648,8 @@ Kopírovat uživatelské jméno Vymazat uživatelské jméno + + Vymazat adresu serveru Kopírovat server @@ -1778,9 +1822,13 @@ %1$s na ZAP.]]> - Zabezpečené spojení + Zabezpečené spojení - Nezabezpečené spojení + Spojení není zabezpečené + + Zabezpečené spojení + + Nezabezpečené spojení Opravdu chcete všem serverům odebrat všechna oprávnění? @@ -1820,8 +1868,14 @@ Zahodit změny Upravit - + + Přidat nové přihlašovací údaje + Je vyžadováno heslo + + Uživatelské je povinné + + Adresa serveru je povinná Hlasové vyhledávání @@ -1830,6 +1884,15 @@ Přihlašovací údaje s tímto uživatelským jménem už existují + + https://www.example.com + + Adresa serveru musí obsahovat „https://“ nebo „http://“ + + Adresa serveru musí obsahovat „https://“ nebo „http://“ + + Zadejte platnou adresu serveru + Připojte další zařízení. @@ -1856,8 +1919,10 @@ OK, rozumím + + Zobrazovat nejnavštěvovanější stránky - Zobrazovat nejnavštěvovanější stránky + Zobrazovat nejnavštěvovanější stránky Název diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index 5257066d6e..2a3255e064 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -778,7 +778,7 @@ Cadw - Arall + Eraill @@ -1745,7 +1745,7 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Dileu - Arall + Eraill Enw diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 78b8daf070..1c8a6909f9 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -52,7 +52,7 @@ - Gemt for nylig + Gemt for nylig Bogmærker gemt for nylig @@ -121,7 +121,11 @@ - Tidligere udforsket + Tidligere udforsket + + + Besøgt for nylig @@ -387,6 +391,11 @@ Tilføjelses-samling ændret. Afslutter applikationen for at anvende ændringerne… + + Gemt for nylig + + Besøgt for nylig + Tilføjelsen understøttes ikke @@ -753,15 +762,15 @@ Ryd - Kopier + Kopier - Del + Del - Åbn i nyt faneblad + Åbn i nyt faneblad - Åbn i privat faneblad + Åbn i privat faneblad - Slet + Slet %1$d valgt @@ -1026,7 +1035,7 @@ Alle handlinger - Brugt for nyligt + Brugt for nylig Log ind for at synkronisere @@ -1231,12 +1240,12 @@ Har du allerede en konto? - Se nyhederne + Se nyhederne - Har du spørgsmål om den nye version af %s? Vil du vide, hvad der er ændret? + Har du spørgsmål om den nye version af %s? Vil du vide, hvad der er ændret? - Få svar her + Få svar her Synkroniser Firefox mellem enheder @@ -1276,14 +1285,14 @@ Placer værktøjslinjen indenfor rækkevidde. Behold den nederst, eller flyt den til toppen. - Privat browsing + Privat browsing - Åbn et privat faneblad én gang - klik på %s-ikonet. + Åbn et privat faneblad én gang - klik på %s-ikonet. - Åbn private faneblad hver gang: Opdater dine indstillinger for privat browsing. + Åbn private faneblad hver gang: Opdater dine indstillinger for privat browsing. - Åbn indstillinger + Åbn indstillinger Bedre beskyttelse af dit privatliv - Sikker forbindelse + Sikker forbindelse - Usikker forbindelse + Usikker forbindelse Er du sikker på, at du vil rydde alle tilladelser for alle websteder? @@ -1785,7 +1794,7 @@ Fortryd ændringer Rediger - + Adgangskode påkrævet Stemme-søgning @@ -1820,7 +1829,7 @@ Ok, forstået - Vis mest besøgte websteder + Vis mest besøgte websteder Navn diff --git a/app/src/main/res/values-fy-rNL/strings.xml b/app/src/main/res/values-fy-rNL/strings.xml index 9e63269ec8..52b8949ccf 100644 --- a/app/src/main/res/values-fy-rNL/strings.xml +++ b/app/src/main/res/values-fy-rNL/strings.xml @@ -51,7 +51,9 @@ - Koartlyn bewarre + Koartlyn bewarre + + Resint oanmakke blêdwizers Resint bewarre blêdwizers @@ -124,7 +126,14 @@ - Eardere ferkenningen + Eardere ferkenningen + + + Koartlyn besocht + + Fuortsmite @@ -396,8 +405,13 @@ Add-onkolleksje oanpast. Tapassing wurdt ôfsluten om wizigingen ta te passen… + + + Tebekspringe - Koartlyn bewarre + Koartlyn bewarre + + Resint oanmakke blêdwizers Koartlyn besocht @@ -586,6 +600,13 @@ Slute + + %d website + + %d websites + Koartlyn sluten ljepblêden @@ -771,15 +792,15 @@ Wiskje - Kopiearje + Kopiearje - Diele + Diele - Iepenje yn nij ljepblêd + Iepenje yn nij ljepblêd - Iepenje yn priveeljepblêd + Iepenje yn priveeljepblêd - Fuortsmite + Fuortsmite %1$d selektearre @@ -1250,12 +1271,12 @@ Hawwe jo al in account? - Besjoch wat der nij is + Besjoch wat der nij is - Hawwe jo fragen oer it opnij ûntwurpen %s? Wolle jo witte wat der wizige is? + Hawwe jo fragen oer it opnij ûntwurpen %s? Wolle jo witte wat der wizige is? - Hjir fine jo antwurden + Hjir fine jo antwurden Syngronisearje Firefox tusken apparaten @@ -1295,14 +1316,14 @@ Pleats de arkbalke binnen hânberik. Hâld him ûnderoan of ferpleats him nei boppe. - Sneup privee + Sneup privee - Ien kear in priveeljepblêd iepenje: tik op it piktogram %s. + Ien kear in priveeljepblêd iepenje: tik op it piktogram %s. - Altyd priveeljepblêden iepenje: wurkje jo ynstellingen foar priveenavigaasje by. + Altyd priveeljepblêden iepenje: wurkje jo ynstellingen foar priveenavigaasje by. - Ynstellingen iepenje + Ynstellingen iepenje Jo privacy https://www.example.com - It webadres moat \‘https://\’ of \‘http://\’ befetsje + It webadres moat \‘https://\’ of \‘http://\’ befetsje + + It webadres moat ‘https://’ of ‘http://’ befetsje Jildige hostnamme fereaske diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 04bfbb8d41..4ce938b3dd 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -52,7 +52,9 @@ - Nedavno spremljeno + Nedavno spremljeno + + Nedavno zabilježeno Nedavno spremljene zabilješke @@ -121,7 +123,14 @@ - Prošla pregledavanja + Prošla pregledavanja + + + Nedavno posjećeno + + Ukloni @@ -396,7 +405,9 @@ Vrati se natrag - Nedavno spremljeno + Nedavno spremljeno + + Nedavno zabilježeno Nedavno posjećeno @@ -590,6 +601,10 @@ Zatvori + + %d stranica + Nedavno zatvorene kartice @@ -778,15 +793,15 @@ Izbriši - Kopiraj + Kopiraj - Dijeli + Dijeli - Otvori u novoj kartici + Otvori u novoj kartici - Otvori u privatnoj kartici + Otvori u privatnoj kartici - Izbriši + Izbriši @@ -1262,12 +1277,12 @@ Već imaš račun? - Pogledaj što je novo + Pogledaj što je novo - Imaš pitanja o redizajniranom %su? Želiš znati što se promijenilo? + Imaš pitanja o redizajniranom %su? Želiš znati što se promijenilo? - Potraži odgovore ovdje + Potraži odgovore ovdje Sinkroniziraj Firefox između uređaja @@ -1308,14 +1323,14 @@ Stavi alatnu traku na dohvat ruke. Zadrži ju na dnu ili premjesti na vrh. - Pregledaj privatno + Pregledaj privatno - Otvori privatnu karticu jednom: Dodirni ikonu %s. + Otvori privatnu karticu jednom: Dodirni ikonu %s. - Otvori privatne kartice svaki put: aktualiziraj postavke za privatno pregledavanje. + Otvori privatne kartice svaki put: aktualiziraj postavke za privatno pregledavanje. - Otvori postavke + Otvori postavke Tvoja privatnost @@ -1862,7 +1877,9 @@ https://www.example.com - Web-adresa mora sadržavati \“https://\“ ili \“http://\“ + Web-adresa mora sadržavati \“https://\“ ili \“http://\“ + + Web-adresa mora sadržavati "https://" ili "http://" Valjana domena je potrebna diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index a6b2ae4c61..2ab525e585 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -128,7 +128,14 @@ - 過去の調査 + 過去の調査 + + + 最近訪れたサイト + + 削除 @@ -602,6 +609,13 @@ 閉じる + + %d サイト + + %d サイト + 最近閉じたタブ @@ -790,15 +804,15 @@ 消去 - コピー + コピー - 共有 + 共有 - 新しいタブで開く + 新しいタブで開く - プライベートタブで開く + プライベートタブで開く - 削除 + 削除 %1$d 件選択 @@ -1280,12 +1294,12 @@ アカウントをお持ちですか? - 新機能の紹介 + 新機能の紹介 - 再設計された %s について、どこが新しくなったのか知りたいですか? + 再設計された %s について、どこが新しくなったのか知りたいですか? - その答えはこちらにあります + その答えはこちらにあります 端末間で Firefox を同期 @@ -1325,14 +1339,14 @@ ツールバーを簡単に手の届く位置に置きましょう。画面下または上に移動できます。 - プライベートなブラウジング + プライベートなブラウジング - プライベートタブを今回のみ開く: %s アイコンをタップします。 + プライベートタブを今回のみ開く: %s アイコンをタップします。 - プライベートタブを毎回開く: プライベートブラウジング設定を変更してください。 + プライベートタブを毎回開く: プライベートブラウジング設定を変更してください。 - 設定を開く + 設定を開く あなたのプライバシー %s’a veşartî @@ -22,7 +22,7 @@ 1 hilpekîna vekirî. Ji bo hilpekînê biguherînî, bitikîne. - %1$s hilpekînên vekirî. Ji bo hilpekînê biguherînî, bitikîne. + %1$s hilpekînên vekirî. Ji bo hilpekînan biguherînî, bitikîne. %1$d hilpekîn hatin bijartin @@ -48,6 +48,19 @@ Hate bijartin + + + Tomarbûnên dawî + + Bijareyên dawî + + Bijareyên vê dawiyê tomarbûyî + + Gişî nîşan bide + + + Bişkoka bijareyên tomarbûyî nîşan bide + %1$s berhemeke Mozillayê ye. @@ -103,6 +116,23 @@ Malperên sereke + + + vegere + + Gişî nîşan bide + + + + Lêgerînên berê + + Seredanên dawî + + Rake + Hilpekînên vekirî @@ -176,6 +206,12 @@ Serast bike + + Destpêkê taybet bike + + + Ekrana sereke + Nayê girêdan. Şemaya URLyê nayê nasîn. @@ -298,7 +334,7 @@ Rûkar - Serrûpel + Destpêk Hereketên tiliyan @@ -365,6 +401,18 @@ Koleksiyona pêvekan hat guhertin. Ji bo sepandina guhertinan ji sepanê derdikeve… + + + vegere + + Tomarbûnên dawî + + Bijareyên dawî + + Seredanên dawî + + Pocket + Pêvek nayê piştgirîkirin @@ -555,6 +603,13 @@ Bigire + + %d malper + + %d malper + Hilpekînên herî dawî hatine girtin @@ -590,6 +645,15 @@ Piştî mehekê + + + Ji destpêkê dest pê bike + + Piştî çar saetan + + Timî + + Qet Manûelê bigire @@ -599,6 +663,25 @@ Piştî mehekê bigire + + + Rake + + Çalak + + Carinan dibe ku Firefox lêkolînan saz bike û wan bixebitîne. + + Zêdetir bizane + + Sepan ew ê ji bo sepandina guhertinan were girtin + + Baş e + + Betal bike + + + Ji bo sepandina guhertinan ji sepanê tê derketin… + Hilpekînên vekirî @@ -626,6 +709,8 @@ Hemû hilpekînan parve bike Hilpekînên herî dawî hatine girtin + + Vê dawiyê hatine girtin Sazkariyên hesabî @@ -635,7 +720,7 @@ Hilpekîna nû - Here serrûpelê + Here destpêkê Moda hilpekînan biguherîne @@ -695,6 +780,9 @@ Tomar bike + + Yên din + Raboriyê jê bibe @@ -709,15 +797,15 @@ Paqij bike - Kopî bike + Kopî bike - Parve bike + Parve bike - Di hilpekîna nû de veke + Di hilpekîna nû de veke - Di hilpekîna veşartî de veke + Di hilpekîna veşartî de veke - Jê bibe + Jê bibe %1$d hate bijartin @@ -918,6 +1006,11 @@ Girtî + + Vekirî + + Girtî + Koleksiyon @@ -1019,6 +1112,17 @@ Jê bibe û veke Bihêzkirin ji aliyê + + + Bazarkirin + + %1$s him bilez him jî veşartî ye + + %1$s bila bibe geroka te ya sereke + + Koleksiyon hate jêbirin @@ -1069,7 +1173,7 @@ Jê bibe - Betal bike + Betal bike Têketina moda ekrana dagirtî @@ -1175,12 +1279,12 @@ Jixwe hesabekî te heye? - Tiştên nû bibîne + Tiştên nû bibîne - Pirsên te derbarê %s’ê ya nû de hene? Dixwazî bizanî ka çi guheriye? + Pirsên te derbarê %s’ê ya nû de hene? Dixwazî bizanî ka çi guheriye? - Bersiv li vir in + Bersiv li vir in Firefoxê di navbera amûran de senkronîze bike @@ -1220,14 +1324,14 @@ Darikê amûran dayne ciyekê bi hêsanî bigihîjiyê. Dikarî wê li xwarê bihêlî yan jî bibî jorê. - Bi nihênî bigere + Bi nihênî bigere - Ji bo carekê hilpekîneke veşartî veke: Li îkona %s’ê bitikîne. + Ji bo carekê hilpekîneke veşartî veke: Li îkona %s’ê bitikîne. - Her carê hilpekînên veşartî veke: Sazkariyên xwe yên gerîna veşartî nûve bike. + Her carê hilpekînên veşartî veke: Sazkariyên xwe yên gerîna veşartî nûve bike. - Sazkariyan veke + Sazkariyan veke Nihêniya te Agahdariya me ya nihêniyê bixwîne - Bigire + Bigire Dest bi gerînê bike @@ -1339,6 +1443,9 @@ Kankerên krîpto Berhevkarên şoptiliyan + + Hûrgilî + Yên astengbûyî Destûrgirtî @@ -1363,7 +1470,7 @@ Nahêle reklam, vîdyo û naverokên din ên tê de koda şopandinê hene bên barkirin. Dibe ku bandorê li taybetiyên hin malperan bike. - Gava mertal bû rengê mor, tê vê wateyê ku %s’ê şopîner asteng kirine. Ji bo agahiyên zêdetir, bitepîne. + Gava mertal bû rengê mor, tê vê wateyê ku %s’ê şopîner asteng kirine. Ji bo agahiyên zêdetir, bitepîne. Parastin ji bo vê malperê VEKIRÎ ne. @@ -1389,6 +1496,9 @@ Kûkiyên ji aliyê beralîkirinên malperên tên naskirin ve hatine bicîkirin paqij dike. + + Zêdetir bizane + Piştgirî @@ -1446,7 +1556,17 @@ Qet tomar neke - Bixweber dagire + Bixweber dagire + + + Di %1$s de bixweber dagire + + Di sepanên din de bixweber dagire + + Bila di sepanên din ên cîhaza te de navên bikarhêner û pêborîn bêne dagirtin. + + Hesêb tevlî bike + Hesaban senkronîze bike @@ -1509,6 +1629,8 @@ Navê bikarhêner kopî bike Navê bikarhêner paqij bike + + Hewangehê paqiij bike Malperê kopî bike @@ -1565,6 +1687,10 @@ Hejmara kartê Dema dawî ya bikaranînê + + Tarîxa dawî ya bikaranînê - Meh + + Tarîxa dawî ya bikaranînê - Sal Navê li ser kartê @@ -1584,6 +1710,8 @@ Tika ye hejmereke derbasdar ya karta krediyê binivîse + + Ji kerema xwe vê qadê dagirin Ji bo kartên xwe yên qeydkirî bibînî kilîdê rake @@ -1620,7 +1748,7 @@ Rêzika lêgerînê ya ji bo bikaranînê - Lêpirsînê bi “%s”ê pev biguherîne. Mînak:\nhttps://www.google.com/search?q=%s + Lêpirsînê bi “%s”ê pev biguherîne. Mînak:\nhttps://www.google.com/search?q=%s Zêdetir Bizane @@ -1669,9 +1797,13 @@ %1$s’yê bike VEKIRÎ]]> - Girêdana ewle + Girêdan ewle ye - Girêdana neewle + Girêdan ne ewle ye + + Girêdana ewle + + Girêdana neewle Tu ji dil dixwazî hemû destûrên li ser hemû malperan jê bibî? @@ -1712,8 +1844,14 @@ Guhertinan betal bike Sererast bike - + + Hesabê nû tevlî bike + Pêborîn hewce ye + + Navê bikarhêner pêwîst e + + Hewangeh pêwîst e Lêgerîna dengî @@ -1721,6 +1859,15 @@ Jixwe hesabek bi vî navê bikarhêneriyê heye + + https://www.example.com + + Di navnîşana webê de divê "https://" an jî "http://" hebe. + + Di navnîşana webê de divê "https://" an jî "http://" hebe. + + Hewangeha derbasdar pêwîst e + Cîhazeke din girê bide. @@ -1745,8 +1892,10 @@ Ji bo malpereke din a sereke lê zêde bikî, yekê jê bibe. Li malperê bitikîne û li ser bihêle, piştre rakirinê hilbijêre. Baş e + + Malperên sereke nîşan bide - Malperên herî zêde hatine vekirin nîşan bide + Malperên herî zêde hatine vekirin nîşan bide Nav @@ -1756,6 +1905,14 @@ Betal bike + + + Hilpekînên neçalak + + 30 roj + + 1 hefte + Bila lînkên ji malperan, e-posteyan û peyaman xweber di Firefoxê de bên vekirin. diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index 7ac1263db5..1deda930ed 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -1,5 +1,5 @@ - + ສ່ວນຕົວ %s @@ -50,7 +50,7 @@ - ຫາກໍບັນທຶກໄວ້ + ຫາກໍບັນທຶກໄວ້ ບຸກມາກທີ່ຫາກໍໄດ້ບັນທຶກໄວ້ @@ -111,6 +111,17 @@ ສະແດງທັງໝົດ + + + ການສໍາຫຼວດທີ່ຜ່ານມາ + + ຫາກໍ່ເຂົ້າໄປເບິງມື້ກີ້ນີ້ + + ລຶບ + ເປີດແທັບ @@ -187,6 +198,8 @@ ແກ້ໄຂ + + ປັບແຕ່ງຫນ້າທຳອິດ ໜ້າທຳອິດ @@ -380,6 +393,18 @@ ປັບແກ້ການເກັບສະສົມ Add-on ແລ້ວ. ກຳລັງອອກຈາກແອັບພຣິເຄຊັນເພື່ອນຳໃຊ້ການປ່ຽນແປງ… + + + ກັບເຂົ້າໄປ + + ຫາກໍບັນທຶກໄວ້ + + ຫາກໍບຸກມາກມື້ກີ້ນີ້ + + ຫາກໍ່ເຂົ້າໄປເບິງມື້ກີ້ນີ້ + + Pocket + Add-on ນີ້ແມ່ນບໍ່ໄດ້ຮັບການຊັບພອດ @@ -533,6 +558,11 @@ ເລື່ອນເພື່ອເຊື່ອງແທັບເຄືອງມື + + ປັດແຖບເຄື່ອງມືໄປທາງຂ້າງເພື່ອປ່ຽນແທັບ + + ປັດແຖບເຄື່ອງມືຂຶ້ນເພື່ອເປີດແທັບ + ເຊດຊັນ @@ -567,6 +597,13 @@ ປິດ + + %d ເວັບໄຊທ + + %d ເວັບໄຊທ + ແທັບທີ່ຫາກໍ່ປິດໄປມື້ກີ້ນີ້ @@ -587,6 +624,8 @@ ເບິງແທັບ ລາຍການ + + ເສັ້ນຕາຕະລາງ ປິດແທັບ @@ -621,6 +660,8 @@ ລຶບ ​ເປີດໃຊ້ງານ + + Firefox ອາດຈະຕິດຕັ້ງ ແລະ ເອີ້ນນຳໃຊ້ການສຶກສາເປັນບາງຄັ້ງ. ຮຽນຮູ້ເພີ່ມເຕີມ @@ -630,6 +671,9 @@ ຍົກເລີກ + + ກຳລັງອອກຈາກແອັບພລິເຄຊັນເພື່ອນຳໃຊ້ການປ່ຽນແປງ… + ເປີດແທັບ @@ -731,6 +775,9 @@ ບັນທຶກ + + ອື່ນໆ + ລຶບປະຫວັດການໃຊ້ງານ @@ -743,15 +790,15 @@ ລົບລ້າງ - ສຳເນົາ + ສຳເນົາ - ແບ່ງປັນ + ແບ່ງປັນ - ເປີດໃນແທັບໃຫມ່ + ເປີດໃນແທັບໃຫມ່ - ເປີດໃນແທັບສ່ວນຕົວ + ເປີດໃນແທັບສ່ວນຕົວ - ລຶບ + ລຶບ @@ -1119,7 +1166,7 @@ ລຶບ - ຍົກເລີກ + ຍົກເລີກ ເຂົ້າສູ່ໂມດເຕັມຫນ້າຈໍ @@ -1224,12 +1271,12 @@ ມີບັນຊີຢູ່ແລ້ວບໍ່? - ເບິ່ງວ່າມີຫຍັງໃຫມ່ + ເບິ່ງວ່າມີຫຍັງໃຫມ່ - ມີຄຳຖາມກ່ຽວກັບການອອກແບບ %s ຄືນໃຫມ່ບໍ່? ຕ້ອງການຢາກຮູ້ຈັກວ່າມີຫຍັງປ່ຽນແປງແດ່ບໍ່? + ມີຄຳຖາມກ່ຽວກັບການອອກແບບ %s ຄືນໃຫມ່ບໍ່? ຕ້ອງການຢາກຮູ້ຈັກວ່າມີຫຍັງປ່ຽນແປງແດ່ບໍ່? - ພົບຄຳຕອບໄດ້ທີ່ນີ້ + ພົບຄຳຕອບໄດ້ທີ່ນີ້ Sync Firefox ລະຫວ່າງອຸປະກອນ @@ -1260,21 +1307,21 @@ ເຂັ້ມງວດ - ທ່ອງເວັບແບບສ່ວນຕົວ + ທ່ອງເວັບແບບສ່ວນຕົວ - ເປີດແທັບສ່ວນຕົວຄັ້ງດຽວ: ແຕະທີ່ໄອຄອນ %s. + ເປີດແທັບສ່ວນຕົວຄັ້ງດຽວ: ແຕະທີ່ໄອຄອນ %s. - ເປີດແທັບສ່ວນຕົວທຸກຄັ້ງ: ອັບເດດການຕັ້ງຄ່າການທ່ອງເວັບແບບສ່ວນຕົວຂອງທ່ານ. + ເປີດແທັບສ່ວນຕົວທຸກຄັ້ງ: ອັບເດດການຕັ້ງຄ່າການທ່ອງເວັບແບບສ່ວນຕົວຂອງທ່ານ. - ເປີດການຕັ້ງຄ່າ + ເປີດການຕັ້ງຄ່າ ຄວາມເປັນສ່ວນຕົວຂອງທ່ານ ອ່ານນະໂຍບາຍຄວາມເປັນສ່ວນຕົວຂອງພວກເຮົາ - ປິດ + ປິດ ເລີ່ມການທ່ອງເວັບ @@ -1453,7 +1500,7 @@ ບໍ່ຕ້ອງບັນທຶກ - ຕື່ມຂໍ້ມູນອັດຕະໂນມັດ + ຕື່ມຂໍ້ມູນອັດຕະໂນມັດ Sync ລັອກອິນ @@ -1678,9 +1725,9 @@ %1$s ເປັນເປີດ]]> - ການເຊື່ອມຕໍ່ທີປອດໄພ + ການເຊື່ອມຕໍ່ທີປອດໄພ - ການເຊື່ອມຕໍ່ທີບໍ່ປອດໄພ + ການເຊື່ອມຕໍ່ທີບໍ່ປອດໄພ ບົດຄວາມຍອດນິຍົມ @@ -1701,25 +1748,51 @@ ຍົກເລີກການປ່ຽນແປງ ແກ້ໄຂ - + + ເພີ່ມການລັອກອິນໃຫມ່ + ຕ້ອງມີລະຫັດຜ່ານ + + ຕ້ອງການຊື່ຜູ້ໃຊ້ + + ຕ້ອງການຊື່ໂຮສທ ຄົ້ນຫາດ້ວຍສຽງ ເວົ້າຕອນນີ້ + + ການລັອກອິນດ້ວຍຊື່ຜູ້ໃຊ້ນັ້ນມີຢູ່ແລ້ວ + + https://www.example.com + + ທີ່ຢູ່ເວັບຈະຕ້ອງປະກອບມີ "https://" ຫລື "http://" + + ທີ່ຢູ່ເວັບຈະຕ້ອງປະກອບມີ "https://" ຫລື "http://" + + ຕ້ອງການຊື່ໂຮສທທີ່ຖືກຕ້ອງ + ເຊື່ອມຕໍ່ອຸປະກອນອື່ນ. ກະລຸນາຮັບຮອງຄວາມຖືກຕ້ອງອີກຄັ້ງຫນຶ່ງ. + + ກະລຸນາເປີດນຳໃຊ້ການ sync ແທັບ + + ທ່ານບໍ່ໄດ້ເປີດຈັກແທັບຢູ່ Firefox ໃນອຸປະກອນອື່ນໆຂອງທ່ານ. + + ເບິງລາຍຊື່ແທັບຈາກອຸປະກອນອື່ນໆຂອງທ່ານ ເຂົ້າສູ່ລະບົບເພື່ອ Sync ບໍ່ມີແທັບທີ່ເປີດຢູ່ + + + ຮອດຂີດຈຳກັດເວັບໄຊທຍອດນິຍົມແລ້ວ - ສະແດງເວັບໄຊທທີ່ໄດ້ເຂົ້າໄປຫຼາຍທີ່ສຸດ + ສະແດງເວັບໄຊທທີ່ໄດ້ເຂົ້າໄປຫຼາຍທີ່ສຸດ ຊື່ diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index fabf9c443f..b05fb71588 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -409,6 +409,9 @@ Add-oncollectie aangepast. Toepassing wordt afgesloten om wijzigingen toe te passen… + + + Terugspringen Onlangs opgeslagen diff --git a/app/src/main/res/values-nn-rNO/strings.xml b/app/src/main/res/values-nn-rNO/strings.xml index c17bff043e..3c64e05ee4 100644 --- a/app/src/main/res/values-nn-rNO/strings.xml +++ b/app/src/main/res/values-nn-rNO/strings.xml @@ -55,7 +55,9 @@ - Nyleg lagra + Nyleg lagra + + Nyleg bokmerkte Nylig lagra bokmerke @@ -125,7 +127,14 @@ - Tidlegare utforskingar + Tidlegare utforskingar + + + Nyleg besøkte + + Fjern @@ -777,15 +786,15 @@ Tøm - Kopier + Kopier - Del + Del - Opne i ny fane + Opne i ny fane - Opne i privat fane + Opne i privat fane - Slett + Slett %1$d valde @@ -1263,13 +1272,13 @@ Har du allereie ein konto? - Sjå kva som er nytt + Sjå kva som er nytt - Har du spørsmål om den nye utforminga av %s? Vil du vite kva som er endra? + Har du spørsmål om den nye utforminga av %s? Vil du vite kva som er endra? - Få svar her + Få svar her Synkroniser Firefox mellom einingar @@ -1311,14 +1320,14 @@ Plassere verktøylinja innanfor rekkjevidde. Ha henne i botn eller topp. - Surf privat + Surf privat - Opne ei privat fane ein gong: Trykk på %s-ikonet. + Opne ei privat fane ein gong: Trykk på %s-ikonet. - Opne private faner kvar gong: Oppdater innstillingane dine for privat nettlesing. + Opne private faner kvar gong: Oppdater innstillingane dine for privat nettlesing. - Opne innstillingar + Opne innstillingar Ditt personvern - Recentemente guardados + Recentemente guardados + + Marcadores recentes Marcadores guardados recentemente @@ -123,7 +125,14 @@ - Explorações anteriores + Explorações anteriores + + + Visualizados recentemente + + Remover @@ -199,6 +208,8 @@ Editar + + Personalizar página principal Ecrã inicial @@ -391,6 +402,18 @@ Coleção de extras modificada. A sair da aplicação para aplicar alterações… + + + Regressar + + Recentemente guardados + + Marcadores recentes + + Visualizados recentemente + + Pocket + O extra não é suportado @@ -579,6 +602,13 @@ Fechar + + %d site + + %d sites + Separadores fechados recentemente @@ -749,6 +779,9 @@ Guardar + + Outro + Eliminar histórico @@ -761,15 +794,15 @@ Limpar - Copiar + Copiar - Partilhar + Partilhar - Abrir num novo separador + Abrir num novo separador - Abrir num novo separador privado + Abrir num novo separador privado - Eliminar + Eliminar %1$d selecionados(s) @@ -1241,12 +1274,12 @@ Já tem uma conta? - Veja as novidades + Veja as novidades - Tem questões sobre o redesenhado %s? Quer saber o que mudou? + Tem questões sobre o redesenhado %s? Quer saber o que mudou? - Obtenha respostas aqui + Obtenha respostas aqui Sincronizar o Firefox entre dispositivos @@ -1288,14 +1321,14 @@ Facilite o acesso à barra de ferramentas. Mantenha a mesma na parte inferior ou mova-a para cima. - Navegue em privado + Navegue em privado - Abra um separador privado: toque no ícone %s. + Abra um separador privado: toque no ícone %s. - Abra sempre separadores privados: atualize as suas definições de navegação privada. + Abra sempre separadores privados: atualize as suas definições de navegação privada. - Abrir definições + Abrir definições A sua privacidade Limpa as cookies definidas por redirecionamentos para sites de rastreamento conhecidos. + + Alguns rastreadores marcados abaixo foram parcialmente desbloqueados nesta página porque interagiu com os mesmos *. + + Saber mais + Apoio @@ -1531,6 +1570,9 @@ Preencher nomes de utilizador e palavras-passe noutras aplicações no seu dispositivo. + + Adicionar credenciais + Sincronização de credenciais @@ -1761,9 +1803,9 @@ %1$s]]> - Ligação segura + Ligação segura - Ligação insegura + Ligação insegura Tem certeza que deseja limpar todas as permissões em todos os sites? @@ -1803,7 +1845,7 @@ Descartar alterações Editar - + É necessária uma palavra-passe Pesquisa por voz @@ -1839,7 +1881,7 @@ OK, percebi - Mostrar os sites mais visitados + Mostrar os sites mais visitados Nome diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml index 6e659aaf8f..52d1e829e6 100644 --- a/app/src/main/res/values-rm/strings.xml +++ b/app/src/main/res/values-rm/strings.xml @@ -50,7 +50,9 @@ - Memorisà dacurt + Memorisà dacurt + + Tschernì dacurt sco segnapagina Segnapaginas memorisads dacurt @@ -119,7 +121,14 @@ - Retschertgas passadas + Retschertgas passadas + + + Visità dacurt + + Allontanar @@ -195,6 +204,8 @@ Modifitgar + + Persunalisar la pagina da partenza Visur da partenza @@ -386,6 +397,18 @@ Modifitgà la collecziun da supplements. L\'applicaziun vegn serrada per applitgar las midadas… + + + Cuntinuar qua + + Memorisà dacurt + + Tschernì dacurt sco segnapagina + + Visità dacurt + + Pocket + Il supplement na vegn betg sustegnì @@ -570,6 +593,13 @@ Serrar + + %d website + + %d websites + Tabs serrads dacurt @@ -739,6 +769,9 @@ Memorisar + + Auters + Stizzar la cronologia @@ -751,15 +784,15 @@ Stizzar - Copiar + Copiar - Cundivider + Cundivider - Avrir en in nov tab + Avrir en in nov tab - Avrir en in tab privat + Avrir en in tab privat - Stizzar + Stizzar %1$d tschernids @@ -1234,12 +1267,12 @@ Ti has gia in conto? - Qua las novaziuns + Qua las novaziuns - Has ti dumondas areguard il nov %s? Vuls savair tge ch\'è sa midà? + Has ti dumondas areguard il nov %s? Vuls savair tge ch\'è sa midà? - Qua datti respostas + Qua datti respostas Sincronisescha Firefox sin differents apparats @@ -1281,14 +1314,14 @@ Plazzescha la trav d\'utensils uschia ch\'ella è cuntanschibla a moda optimala. La mantegna giusut u la plazzescha sisum. - Navighescha en il modus privat + Navighescha en il modus privat - Per avrir ina giada in tab privat: Tutgar il simbol %s. + Per avrir ina giada in tab privat: Tutgar il simbol %s. - Per avrir mintga giada tabs privats: Adattar ils parameters da la navigaziun privata. + Per avrir mintga giada tabs privats: Adattar ils parameters da la navigaziun privata. - Avrir ils parameters + Avrir ils parameters Tia sfera privata Stizza cookies definids cun agid da renviaments a websites enconuschentas per fastizar. + + Tscherts fastizaders inditgads sutvart èn vegnids debloccads parzialmain sin questa pagina perquai che ti has interagì cun els *. + + Ulteriuras infurmaziuns + Agid @@ -1527,6 +1566,9 @@ Emplenir automaticamain nums d\'utilisader e pleds-clav en autras applicaziuns sin tes apparat. + + Agiuntar infurmaziuns d\'annunzia + Sincronisar las infurmaziuns d\'annunzia @@ -1591,6 +1633,8 @@ Copiar il num d\'utilisader Stizzar il num d\'utilisader + + Stizzar il num dal server Copiar la pagina @@ -1760,9 +1804,13 @@ %1$s sin ACTIVÀ]]> - Connexiun segirada + Connexiun segirada - Connexiun betg segirada + Connexiun betg segirada + + Connexiun segirada + + Connexiun betg segirada Vuls ti propi stizzar tut las autorisaziuns per tut las paginas? @@ -1803,8 +1851,14 @@ Sbittar las midadas Modifitgar - + + Agiuntar novas datas d\'annunzia + Pled-clav obligatoric + + Num d\'utilisader obligatoric + + Num dal server obligatoric Tschertga vocala @@ -1812,6 +1866,15 @@ Datas d\'annunzia cun quest num d\'utilisader existan gia + + https://www.example.com + + L\'adressa d\'internet sto cuntegnair «https://» u «http://» + + L\'adressa d\'internet sto cuntegnair «https://» u «http://» + + In num da server valid è necessari + Colliar in auter apparat. @@ -1838,8 +1901,10 @@ OK, chapì + + Mussar las websites principalas visitadas il pli savens - Mussar las websites visitadas il pli savens + Mussar las websites visitadas il pli savens Num diff --git a/app/src/main/res/values-tt/strings.xml b/app/src/main/res/values-tt/strings.xml index 8e12e1fac7..55e5a09896 100644 --- a/app/src/main/res/values-tt/strings.xml +++ b/app/src/main/res/values-tt/strings.xml @@ -1,5 +1,5 @@ - + Хосусый %s @@ -49,16 +49,24 @@ - Күптән түгел сакланган + Күптән түгел сакланган + + Соңгы сакланган кыстыргычлар Барысын да күрсәтү + + Барлык сакланган кыстыргычларны да күрсәтү төймәсе + %1$s Mozilla тарафыннан җитештерелә. Сез хосусый сессиядә + + Хосусый гизү турында киң таралган мифлар + Сессияне бетерү @@ -103,6 +111,13 @@ Барысын да күрсәтү + + Соңгы каралган + + Бетерү + Ачык битләр @@ -365,6 +380,16 @@ Җыентык иясе (Кулланучы ID) + + + Кире кайту + + Соңгы сакланганнар + + Соңгы каралганнар + + Pocket + Кушымчаны кулланып булмый @@ -544,6 +569,13 @@ Ябу + + %d сайт + + %d сайт + Күптән түгел ябылган таблар @@ -705,15 +737,15 @@ Чистарту - Күчереп алу + Күчереп алу - Уртаклашу + Уртаклашу - Яңа биттә ачу + Яңа биттә ачу - Хосусый биттә ачу + Хосусый биттә ачу - Бетерү + Бетерү %1$d сайланды @@ -1061,7 +1093,7 @@ Бетерү - Баш тарту + Баш тарту Тулы экран режимына күчү @@ -1149,9 +1181,9 @@ Хисап язмагыз бармы инде? - Яңалыкларны карагыз + Яңалыкларны карагыз - Җаваплар монда + Җаваплар монда Firefox-ны җиһазлар арасында синхронлау @@ -1179,20 +1211,20 @@ Кораллар панеленең урнашуын сайлау - Хосусый режимда гизү + Хосусый режимда гизү - Бер тапкыр гына хосусый таб ачу өчен %s тамгачыгына басыгыз. + Бер тапкыр гына хосусый таб ачу өчен %s тамгачыгына басыгыз. - Хосусый табларны һәрвакыт ачу өчен хосусый гизү көйләүләрен яңартыгыз. + Хосусый табларны һәрвакыт ачу өчен хосусый гизү көйләүләрен яңартыгыз. - Көйләүләрне ачу + Көйләүләрне ачу Сезнең хосусыйлык Безнең xоусыйлык аңлатмасын укыгыз - Ябу + Ябу Гизүне башлау @@ -1278,6 +1310,7 @@ Криптомайнерләр Бармак эзләрен җыючылар (идентификаторлар) + Тыелган Рөхсәт ителгән @@ -1368,7 +1401,8 @@ Беркайчан да cакламау - Автотутыру + Автотутыру + Логиннарны синхронлау @@ -1535,7 +1569,7 @@ Эзләнәчәк сүз я сүзтезмә - Сорауны “%s” юлы белән алыштырыгыз. Мисал өчен:\nhttps://www.google.com/search?q=%s + Сорауны “%s” юлы белән алыштырыгыз. Мисал өчен:\nhttps://www.google.com/search?q=%s Күбрәк белү @@ -1583,9 +1617,9 @@ %1$s көйләнешен кабызыгыз]]> - Хәвефсез бәйләнеш + Хәвефсез бәйләнеш - Хәвефсез булмаган бәйләнеш + Хәвефсез булмаган бәйләнеш Барлык сайтлардагы барлык рөхсәтләрне дә чистартуны раслыйсызмы? @@ -1625,7 +1659,7 @@ Үзгәрешләрдән ваз кичү Үзгәртү - + Серсүз кирәк @@ -1658,7 +1692,7 @@ Яхшы, аңладым - Иң күп каралган сайтларны күрсәтү + Иң күп каралган сайтларны күрсәтү Исем diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index d8c89f5727..11ff5208b8 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -68,7 +68,7 @@ Ви у приватному сеансі - %1$s стирає вашу історію пошуку та перегляду для приватних вкладок, коли ви закриваєте їх чи виходите з програми. Це не робить вас анонімними для вебсайтів чи вашого провайдера, але дозволяє приховати вашу діяльність в Інтернеті від будь-кого іншого, хто використовує цей пристрій. + %1$s стирає вашу історію пошуку та перегляду для приватних вкладок, коли ви закриваєте їх чи виходите з програми. Це не робить вас анонімними для вебсайтів чи вашого провайдера, але дозволяє приховати вашу діяльність в інтернеті від будь-кого іншого, хто використовує цей пристрій. Поширені міфи про приватний перегляд From 13663043ce7cb8f2c398a30166b2bd455aa268e3 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Thu, 30 Sep 2021 00:59:19 +0000 Subject: [PATCH 311/517] Update Android Components version to 94.0.20210929224637. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 55b405fc26..dafb4a0998 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20210928190108" + const val VERSION = "94.0.20210929224637" } From 0e88c25df1b67b213a73906c167808ef67ce5a1e Mon Sep 17 00:00:00 2001 From: "codrut.topliceanu" Date: Mon, 23 Aug 2021 15:42:07 +0300 Subject: [PATCH 312/517] For #20762 - Adds grey border to inactive tabs --- .../tabstray/browser/InactiveTabViewHolder.kt | 7 ++ .../main/res/layout/inactive_footer_item.xml | 64 +++++++++------ .../main/res/layout/inactive_header_item.xml | 82 +++++++++++-------- .../layout/inactive_recently_closed_item.xml | 77 +++++++++-------- .../res/layout/inactive_tab_list_item.xml | 24 ++++-- 5 files changed, 150 insertions(+), 104 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt index 68fee1b8e5..93e22b3685 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt @@ -6,6 +6,7 @@ package org.mozilla.fenix.tabstray.browser import android.view.View import androidx.annotation.StringRes +import androidx.core.view.updatePadding import androidx.recyclerview.widget.RecyclerView import mozilla.components.browser.toolbar.MAX_URI_LENGTH import mozilla.components.concept.tabstray.Tab @@ -17,6 +18,7 @@ import org.mozilla.fenix.databinding.InactiveTabListItemBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.ext.toShortUrl +import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.dpToPx import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.Manual import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.OneDay import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.OneMonth @@ -42,6 +44,11 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite it.isActivated = newState binding.chevron.rotation = ROTATION_DEGREE + + // When the header is collapsed we use its bottom border instead of the footer's + binding.inactiveHeaderBorder.updatePadding( + bottom = binding.root.context.dpToPx(if (it.isActivated) 0f else 1f) + ) } } } diff --git a/app/src/main/res/layout/inactive_footer_item.xml b/app/src/main/res/layout/inactive_footer_item.xml index 68c1d774e4..955ff5b42a 100644 --- a/app/src/main/res/layout/inactive_footer_item.xml +++ b/app/src/main/res/layout/inactive_footer_item.xml @@ -2,40 +2,50 @@ - 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/. --> - + android:backgroundTint="@color/photonLightGrey30" + android:importantForAccessibility="no" + android:paddingStart="1dp" + android:paddingEnd="1dp" + android:paddingBottom="1dp"> - - - + android:background="@drawable/rounded_bottom_corners" + android:paddingBottom="8dp"> + + + + - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/inactive_header_item.xml b/app/src/main/res/layout/inactive_header_item.xml index 2af0c5d37a..a0a84b1a6e 100644 --- a/app/src/main/res/layout/inactive_header_item.xml +++ b/app/src/main/res/layout/inactive_header_item.xml @@ -1,46 +1,58 @@ - + android:backgroundTint="@color/photonLightGrey30" + android:importantForAccessibility="no" + android:paddingStart="1dp" + android:paddingTop="1dp" + android:paddingEnd="1dp"> + + - + - + - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/inactive_recently_closed_item.xml b/app/src/main/res/layout/inactive_recently_closed_item.xml index 0c008f1093..7f019216e3 100644 --- a/app/src/main/res/layout/inactive_recently_closed_item.xml +++ b/app/src/main/res/layout/inactive_recently_closed_item.xml @@ -1,45 +1,54 @@ - + android:background="@color/photonLightGrey30" + android:importantForAccessibility="no" + android:paddingStart="1dp" + android:paddingEnd="1dp"> - - + android:background="?above" + android:foreground="?android:attr/selectableItemBackground" + android:minHeight="@dimen/mozac_widget_site_item_height"> - - + + + + + + + diff --git a/app/src/main/res/layout/inactive_tab_list_item.xml b/app/src/main/res/layout/inactive_tab_list_item.xml index 8a576c788a..3a83e2d677 100644 --- a/app/src/main/res/layout/inactive_tab_list_item.xml +++ b/app/src/main/res/layout/inactive_tab_list_item.xml @@ -1,14 +1,22 @@ - - - + android:background="@color/photonLightGrey30" + android:importantForAccessibility="no" + android:paddingStart="1dp" + android:paddingEnd="1dp"> + + + \ No newline at end of file From 4c33b1b75c90db04e1cfd784b473a9826ae55833 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Fri, 17 Sep 2021 16:32:29 -0700 Subject: [PATCH 313/517] For #21294: add partial test for existing factToEvent code. When we refactor, this will help ensure we've done it correctly. --- .../metrics/MetricControllerTest.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/app/src/test/java/org/mozilla/fenix/components/metrics/MetricControllerTest.kt b/app/src/test/java/org/mozilla/fenix/components/metrics/MetricControllerTest.kt index d07c8c7fc1..8c2e5cf15d 100644 --- a/app/src/test/java/org/mozilla/fenix/components/metrics/MetricControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/metrics/MetricControllerTest.kt @@ -10,8 +10,14 @@ import io.mockk.impl.annotations.MockK import io.mockk.mockk import io.mockk.verify import io.mockk.verifyAll +import mozilla.components.feature.awesomebar.facts.AwesomeBarFacts +import mozilla.components.feature.customtabs.CustomTabsFacts +import mozilla.components.feature.prompts.dialog.LoginDialogFacts import mozilla.components.feature.prompts.facts.CreditCardAutofillDialogFacts +import mozilla.components.feature.pwa.ProgressiveWebAppFacts +import mozilla.components.feature.syncedtabs.facts.SyncedTabsFacts import mozilla.components.feature.top.sites.facts.TopSitesFacts +import mozilla.components.lib.dataprotect.SecurePrefsReliabilityExperiment import mozilla.components.support.base.Component import mozilla.components.support.base.facts.Action import mozilla.components.support.base.facts.Fact @@ -472,4 +478,36 @@ class MetricControllerTest { verify { dataService1.track(Event.CreditCardManagementAddTapped) } verify { dataService1.track(Event.CreditCardManagementCardTapped) } } + + @Test + fun `WHEN changing Fact(component, item) without additional vals to events THEN it returns the right event`() { + // This naive test was added for a refactoring. It only covers the comparisons that were easy to add. + val controller = ReleaseMetricController(emptyList(), { true }, { true }, mockk()) + + val simpleMappings = listOf( + Triple(Component.FEATURE_PROMPTS, LoginDialogFacts.Items.DISPLAY, Event.LoginDialogPromptDisplayed), + Triple(Component.FEATURE_PROMPTS, LoginDialogFacts.Items.CANCEL, Event.LoginDialogPromptCancelled), + Triple(Component.FEATURE_PROMPTS, LoginDialogFacts.Items.NEVER_SAVE, Event.LoginDialogPromptNeverSave), + Triple(Component.FEATURE_PROMPTS, LoginDialogFacts.Items.SAVE, Event.LoginDialogPromptSave), + // CreditCardAutofillDialogFacts.Items is already tested. + Triple(Component.FEATURE_CUSTOMTABS, CustomTabsFacts.Items.CLOSE, Event.CustomTabsClosed), + Triple(Component.FEATURE_CUSTOMTABS, CustomTabsFacts.Items.ACTION_BUTTON, Event.CustomTabsActionTapped), + Triple(Component.FEATURE_PWA, ProgressiveWebAppFacts.Items.HOMESCREEN_ICON_TAP, Event.ProgressiveWebAppOpenFromHomescreenTap), + Triple(Component.FEATURE_PWA, ProgressiveWebAppFacts.Items.INSTALL_SHORTCUT, Event.ProgressiveWebAppInstallAsShortcut), + Triple(Component.FEATURE_SYNCEDTABS, SyncedTabsFacts.Items.SYNCED_TABS_SUGGESTION_CLICKED, Event.SyncedTabSuggestionClicked), + Triple(Component.FEATURE_AWESOMEBAR, AwesomeBarFacts.Items.BOOKMARK_SUGGESTION_CLICKED, Event.BookmarkSuggestionClicked), + Triple(Component.FEATURE_AWESOMEBAR, AwesomeBarFacts.Items.CLIPBOARD_SUGGESTION_CLICKED, Event.ClipboardSuggestionClicked), + Triple(Component.FEATURE_AWESOMEBAR, AwesomeBarFacts.Items.HISTORY_SUGGESTION_CLICKED, Event.HistorySuggestionClicked), + Triple(Component.FEATURE_AWESOMEBAR, AwesomeBarFacts.Items.SEARCH_ACTION_CLICKED, Event.SearchActionClicked), + Triple(Component.FEATURE_AWESOMEBAR, AwesomeBarFacts.Items.SEARCH_SUGGESTION_CLICKED, Event.SearchSuggestionClicked), + Triple(Component.FEATURE_AWESOMEBAR, AwesomeBarFacts.Items.OPENED_TAB_SUGGESTION_CLICKED, Event.OpenedTabSuggestionClicked), + Triple(Component.LIB_DATAPROTECT, SecurePrefsReliabilityExperiment.Companion.Actions.RESET, Event.SecurePrefsReset), + ) + + simpleMappings.forEach { (component, item, expectedEvent) -> + val fact = Fact(component, Action.CANCEL, item) + val message = "$expectedEvent $component $item" + assertEquals(message, expectedEvent, controller.factToEvent(fact)) + } + } } From d33e38e020ca0c8823713175455c5218e8642137 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Wed, 22 Sep 2021 08:37:41 -0700 Subject: [PATCH 314/517] For #21294: change `when (condition) {` `when {` in Fact.toEvent. This will not compile. However, it enables the subsequent PR to remove allocations from Fact.toEvent. --- .../org/mozilla/fenix/components/metrics/MetricController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt index 5b4cb2e541..d7e7566665 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt @@ -158,7 +158,7 @@ internal class ReleaseMetricController( } @Suppress("LongMethod") - private fun Fact.toEvent(): Event? = when (Pair(component, item)) { + private fun Fact.toEvent(): Event? = when { Component.FEATURE_PROMPTS to LoginDialogFacts.Items.DISPLAY -> Event.LoginDialogPromptDisplayed Component.FEATURE_PROMPTS to LoginDialogFacts.Items.CANCEL -> Event.LoginDialogPromptCancelled Component.FEATURE_PROMPTS to LoginDialogFacts.Items.NEVER_SAVE -> Event.LoginDialogPromptNeverSave From c10f41164d1b4755bc1428c3bf49a5de54b43936 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Wed, 22 Sep 2021 08:49:42 -0700 Subject: [PATCH 315/517] For #21294: remove allocations in Fact.toEvent. This commit was generated primarily by a macro that: - appends `== component &&` - appends `== item` - (if applicable) Skips to the ending brace - Go down one line and move cursor to the front of the line to prep for repeat My only intervention was to skip extra lines to line it up to run again and specify how many times in a row it should run. --- The `to` in this code is an infix function that calls instantiates a Pair under the hood. Subjectively observed, when this method is called it generally hits the else case so 35 Pairs are instantiated each call - that's 560 bytes. This method is called frequently - for example, an estimated 4 times each time a letter is typed on the homescreen and a measured 116 times in a simple navigation (see the issue). The latter generates an estimated 63.4 KiB. It was straightforward to remove these allocations so that's what this change does. The primary risk from this change is that it's difficult to test each case to ensure it's working. --- .../components/metrics/MetricController.kt | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt index d7e7566665..b1975d2508 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt @@ -159,25 +159,25 @@ internal class ReleaseMetricController( @Suppress("LongMethod") private fun Fact.toEvent(): Event? = when { - Component.FEATURE_PROMPTS to LoginDialogFacts.Items.DISPLAY -> Event.LoginDialogPromptDisplayed - Component.FEATURE_PROMPTS to LoginDialogFacts.Items.CANCEL -> Event.LoginDialogPromptCancelled - Component.FEATURE_PROMPTS to LoginDialogFacts.Items.NEVER_SAVE -> Event.LoginDialogPromptNeverSave - Component.FEATURE_PROMPTS to LoginDialogFacts.Items.SAVE -> Event.LoginDialogPromptSave - Component.FEATURE_PROMPTS to CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_FORM_DETECTED -> + Component.FEATURE_PROMPTS == component && LoginDialogFacts.Items.DISPLAY == item -> Event.LoginDialogPromptDisplayed + Component.FEATURE_PROMPTS == component && LoginDialogFacts.Items.CANCEL == item -> Event.LoginDialogPromptCancelled + Component.FEATURE_PROMPTS == component && LoginDialogFacts.Items.NEVER_SAVE == item -> Event.LoginDialogPromptNeverSave + Component.FEATURE_PROMPTS == component && LoginDialogFacts.Items.SAVE == item -> Event.LoginDialogPromptSave + Component.FEATURE_PROMPTS == component && CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_FORM_DETECTED == item -> Event.CreditCardFormDetected - Component.FEATURE_PROMPTS to CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_SUCCESS -> + Component.FEATURE_PROMPTS == component && CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_SUCCESS == item -> Event.CreditCardAutofilled - Component.FEATURE_PROMPTS to CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_PROMPT_SHOWN -> + Component.FEATURE_PROMPTS == component && CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_PROMPT_SHOWN == item -> Event.CreditCardAutofillPromptShown - Component.FEATURE_PROMPTS to CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_PROMPT_EXPANDED -> + Component.FEATURE_PROMPTS == component && CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_PROMPT_EXPANDED == item -> Event.CreditCardAutofillPromptExpanded - Component.FEATURE_PROMPTS to CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_PROMPT_DISMISSED -> + Component.FEATURE_PROMPTS == component && CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_PROMPT_DISMISSED == item -> Event.CreditCardAutofillPromptDismissed - Component.FEATURE_CONTEXTMENU to ContextMenuFacts.Items.ITEM -> { + Component.FEATURE_CONTEXTMENU == component && ContextMenuFacts.Items.ITEM == item -> { metadata?.get("item")?.let { Event.ContextMenuItemTapped.create(it.toString()) } } - Component.FEATURE_CONTEXTMENU to ContextMenuFacts.Items.TEXT_SELECTION_OPTION -> { + Component.FEATURE_CONTEXTMENU == component && ContextMenuFacts.Items.TEXT_SELECTION_OPTION == item -> { when (metadata?.get("textSelectionOption")?.toString()) { CONTEXT_MENU_COPY -> Event.ContextMenuCopyTapped CONTEXT_MENU_SEARCH, CONTEXT_MENU_SEARCH_PRIVATELY -> Event.ContextMenuSearchTapped @@ -187,23 +187,23 @@ internal class ReleaseMetricController( } } - Component.BROWSER_TOOLBAR to ToolbarFacts.Items.MENU -> { + Component.BROWSER_TOOLBAR == component && ToolbarFacts.Items.MENU == item -> { metadata?.get("customTab")?.let { Event.CustomTabsMenuOpened } ?: Event.ToolbarMenuShown } - Component.BROWSER_MENU to BrowserMenuFacts.Items.WEB_EXTENSION_MENU_ITEM -> { + Component.BROWSER_MENU == component && BrowserMenuFacts.Items.WEB_EXTENSION_MENU_ITEM == item -> { metadata?.get("id")?.let { Event.AddonsOpenInToolbarMenu(it.toString()) } } - Component.FEATURE_CUSTOMTABS to CustomTabsFacts.Items.CLOSE -> Event.CustomTabsClosed - Component.FEATURE_CUSTOMTABS to CustomTabsFacts.Items.ACTION_BUTTON -> Event.CustomTabsActionTapped + Component.FEATURE_CUSTOMTABS == component && CustomTabsFacts.Items.CLOSE == item -> Event.CustomTabsClosed + Component.FEATURE_CUSTOMTABS == component && CustomTabsFacts.Items.ACTION_BUTTON == item -> Event.CustomTabsActionTapped - Component.FEATURE_MEDIA to MediaFacts.Items.NOTIFICATION -> { + Component.FEATURE_MEDIA == component && MediaFacts.Items.NOTIFICATION == item -> { when (action) { Action.PLAY -> Event.NotificationMediaPlay Action.PAUSE -> Event.NotificationMediaPause else -> null } } - Component.FEATURE_MEDIA to MediaFacts.Items.STATE -> { + Component.FEATURE_MEDIA == component && MediaFacts.Items.STATE == item -> { when (action) { Action.PLAY -> Event.MediaPlayState Action.PAUSE -> Event.MediaPauseState @@ -211,7 +211,7 @@ internal class ReleaseMetricController( else -> null } } - Component.SUPPORT_WEBEXTENSIONS to WebExtensionFacts.Items.WEB_EXTENSIONS_INITIALIZED -> { + Component.SUPPORT_WEBEXTENSIONS == component && WebExtensionFacts.Items.WEB_EXTENSIONS_INITIALIZED == item -> { metadata?.get("installed")?.let { installedAddons -> if (installedAddons is List<*>) { settings.installedAddonsCount = installedAddons.size @@ -228,7 +228,7 @@ internal class ReleaseMetricController( null } - Component.COMPOSE_AWESOMEBAR to ComposeAwesomeBarFacts.Items.PROVIDER_DURATION -> { + Component.BROWSER_AWESOMEBAR == component && ComposeAwesomeBarFacts.Items.PROVIDER_DURATION == item -> { metadata?.get(ComposeAwesomeBarFacts.MetadataKeys.DURATION_PAIR)?.let { providerTiming -> require(providerTiming is Pair<*, *>) { "Expected providerTiming to be a Pair" } when (val provider = providerTiming.first as AwesomeBar.SuggestionProvider) { @@ -247,13 +247,13 @@ internal class ReleaseMetricController( } null } - Component.FEATURE_PWA to ProgressiveWebAppFacts.Items.HOMESCREEN_ICON_TAP -> { + Component.FEATURE_PWA == component && ProgressiveWebAppFacts.Items.HOMESCREEN_ICON_TAP == item -> { Event.ProgressiveWebAppOpenFromHomescreenTap } - Component.FEATURE_PWA to ProgressiveWebAppFacts.Items.INSTALL_SHORTCUT -> { + Component.FEATURE_PWA == component && ProgressiveWebAppFacts.Items.INSTALL_SHORTCUT == item -> { Event.ProgressiveWebAppInstallAsShortcut } - Component.FEATURE_TOP_SITES to TopSitesFacts.Items.COUNT -> { + Component.FEATURE_TOP_SITES == component && TopSitesFacts.Items.COUNT == item -> { value?.let { var count = 0 try { @@ -266,59 +266,59 @@ internal class ReleaseMetricController( } null } - Component.FEATURE_SYNCEDTABS to SyncedTabsFacts.Items.SYNCED_TABS_SUGGESTION_CLICKED -> { + Component.FEATURE_SYNCEDTABS == component && SyncedTabsFacts.Items.SYNCED_TABS_SUGGESTION_CLICKED == item -> { Event.SyncedTabSuggestionClicked } - Component.FEATURE_AWESOMEBAR to AwesomeBarFacts.Items.BOOKMARK_SUGGESTION_CLICKED -> { + Component.FEATURE_AWESOMEBAR == component && AwesomeBarFacts.Items.BOOKMARK_SUGGESTION_CLICKED == item -> { Event.BookmarkSuggestionClicked } - Component.FEATURE_AWESOMEBAR to AwesomeBarFacts.Items.CLIPBOARD_SUGGESTION_CLICKED -> { + Component.FEATURE_AWESOMEBAR == component && AwesomeBarFacts.Items.CLIPBOARD_SUGGESTION_CLICKED == item -> { Event.ClipboardSuggestionClicked } - Component.FEATURE_AWESOMEBAR to AwesomeBarFacts.Items.HISTORY_SUGGESTION_CLICKED -> { + Component.FEATURE_AWESOMEBAR == component && AwesomeBarFacts.Items.HISTORY_SUGGESTION_CLICKED == item -> { Event.HistorySuggestionClicked } - Component.FEATURE_AWESOMEBAR to AwesomeBarFacts.Items.SEARCH_ACTION_CLICKED -> { + Component.FEATURE_AWESOMEBAR == component && AwesomeBarFacts.Items.SEARCH_ACTION_CLICKED == item -> { Event.SearchActionClicked } - Component.FEATURE_AWESOMEBAR to AwesomeBarFacts.Items.SEARCH_SUGGESTION_CLICKED -> { + Component.FEATURE_AWESOMEBAR == component && AwesomeBarFacts.Items.SEARCH_SUGGESTION_CLICKED == item -> { Event.SearchSuggestionClicked } - Component.FEATURE_AWESOMEBAR to AwesomeBarFacts.Items.OPENED_TAB_SUGGESTION_CLICKED -> { + Component.FEATURE_AWESOMEBAR == component && AwesomeBarFacts.Items.OPENED_TAB_SUGGESTION_CLICKED == item -> { Event.OpenedTabSuggestionClicked } - Component.LIB_DATAPROTECT to SecurePrefsReliabilityExperiment.Companion.Actions.EXPERIMENT -> { + Component.LIB_DATAPROTECT == component && SecurePrefsReliabilityExperiment.Companion.Actions.EXPERIMENT == item -> { Event.SecurePrefsExperimentFailure(metadata?.get("javaClass") as String? ?: "null") } - Component.LIB_DATAPROTECT to SecurePrefsReliabilityExperiment.Companion.Actions.GET -> { + Component.LIB_DATAPROTECT == component && SecurePrefsReliabilityExperiment.Companion.Actions.GET == item -> { if (SecurePrefsReliabilityExperiment.Companion.Values.FAIL.v == value?.toInt()) { Event.SecurePrefsGetFailure(metadata?.get("javaClass") as String? ?: "null") } else { Event.SecurePrefsGetSuccess(value ?: "") } } - Component.LIB_DATAPROTECT to SecurePrefsReliabilityExperiment.Companion.Actions.WRITE -> { + Component.LIB_DATAPROTECT == component && SecurePrefsReliabilityExperiment.Companion.Actions.WRITE == item -> { if (SecurePrefsReliabilityExperiment.Companion.Values.FAIL.v == value?.toInt()) { Event.SecurePrefsWriteFailure(metadata?.get("javaClass") as String? ?: "null") } else { Event.SecurePrefsWriteSuccess } } - Component.LIB_DATAPROTECT to SecurePrefsReliabilityExperiment.Companion.Actions.RESET -> { + Component.LIB_DATAPROTECT == component && SecurePrefsReliabilityExperiment.Companion.Actions.RESET == item -> { Event.SecurePrefsReset } - Component.FEATURE_SEARCH to AdsTelemetry.SERP_ADD_CLICKED -> { + Component.FEATURE_SEARCH == component && AdsTelemetry.SERP_ADD_CLICKED == item -> { Event.SearchAdClicked(value!!) } - Component.FEATURE_SEARCH to AdsTelemetry.SERP_SHOWN_WITH_ADDS -> { + Component.FEATURE_SEARCH == component && AdsTelemetry.SERP_SHOWN_WITH_ADDS == item -> { Event.SearchWithAds(value!!) } - Component.FEATURE_SEARCH to InContentTelemetry.IN_CONTENT_SEARCH -> { + Component.FEATURE_SEARCH == component && InContentTelemetry.IN_CONTENT_SEARCH == item -> { Event.SearchInContent(value!!) } - Component.FEATURE_AUTOFILL to AutofillFacts.Items.AUTOFILL_REQUEST -> { + Component.FEATURE_AUTOFILL == component && AutofillFacts.Items.AUTOFILL_REQUEST == item -> { val hasMatchingLogins = metadata?.get(AutofillFacts.Metadata.HAS_MATCHING_LOGINS) as Boolean? if (hasMatchingLogins == true) { Event.AndroidAutofillRequestWithLogins @@ -326,21 +326,21 @@ internal class ReleaseMetricController( Event.AndroidAutofillRequestWithoutLogins } } - Component.FEATURE_AUTOFILL to AutofillFacts.Items.AUTOFILL_SEARCH -> { + Component.FEATURE_AUTOFILL == component && AutofillFacts.Items.AUTOFILL_SEARCH == item -> { if (action == Action.SELECT) { Event.AndroidAutofillSearchItemSelected } else { Event.AndroidAutofillSearchDisplayed } } - Component.FEATURE_AUTOFILL to AutofillFacts.Items.AUTOFILL_LOCK -> { + Component.FEATURE_AUTOFILL == component && AutofillFacts.Items.AUTOFILL_LOCK == item -> { if (action == Action.CONFIRM) { Event.AndroidAutofillUnlockSuccessful } else { Event.AndroidAutofillUnlockCanceled } } - Component.FEATURE_AUTOFILL to AutofillFacts.Items.AUTOFILL_CONFIRMATION -> { + Component.FEATURE_AUTOFILL == component && AutofillFacts.Items.AUTOFILL_CONFIRMATION == item -> { if (action == Action.CONFIRM) { Event.AndroidAutofillConfirmationSuccessful } else { From 365983d4b063ad8702082283edef1ad7df2b4505 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Wed, 22 Sep 2021 09:01:12 -0700 Subject: [PATCH 316/517] For #21294: suppress MaxLineLength in Fact.toEvent. These double comparisons are easier to read and see the pattern of on one line so I'd rather keep them on one line. Additionally, it's difficult to test each change individually so I'd rather not make additional changes. To do this, I suppressed the max line length warning. --- .../org/mozilla/fenix/components/metrics/MetricController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt index b1975d2508..ca29647344 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt @@ -157,7 +157,7 @@ internal class ReleaseMetricController( MetricServiceType.Marketing -> isMarketingDataTelemetryEnabled() } - @Suppress("LongMethod") + @Suppress("LongMethod", "MaxLineLength") private fun Fact.toEvent(): Event? = when { Component.FEATURE_PROMPTS == component && LoginDialogFacts.Items.DISPLAY == item -> Event.LoginDialogPromptDisplayed Component.FEATURE_PROMPTS == component && LoginDialogFacts.Items.CANCEL == item -> Event.LoginDialogPromptCancelled From f72ab0a4f3a9e43529ad14154c211e4014a639c2 Mon Sep 17 00:00:00 2001 From: AndiAJ Date: Thu, 30 Sep 2021 11:50:57 +0300 Subject: [PATCH 317/517] For #21540 fix flaky editCustomSearchEngineTest UI test --- .../org/mozilla/fenix/ui/robots/SearchRobot.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt index 3b1e2b9d21..e57276533c 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt @@ -75,7 +75,7 @@ class SearchRobot { } fun verifyDefaultSearchEngine(expectedText: String) = assertDefaultSearchEngine(expectedText) - fun verifyEnginesListShortcutContains(rule: ComposeTestRule, searchEngineName: String) = rule.assertEngineListShortcutContains(searchEngineName) + fun verifyEnginesListShortcutContains(rule: ComposeTestRule, searchEngineName: String) = assertEngineListShortcutContains(rule, searchEngineName) fun changeDefaultSearchEngine(rule: ComposeTestRule, searchEngineName: String) = rule.selectDefaultSearchEngine(searchEngineName) @@ -378,17 +378,19 @@ private fun ComposeTestRule.assertSearchEngineList() { } @OptIn(ExperimentalTestApi::class) -private fun ComposeTestRule.assertEngineListShortcutContains(searchEngineName: String) { - mDevice.findObject(UiSelector().resourceId("$packageName:id/awesome_bar")) - .waitForExists(waitingTime) +private fun assertEngineListShortcutContains(rule: ComposeTestRule, searchEngineName: String) { + rule.waitForIdle() - mDevice.findObject(UiSelector().text("Google")) - .waitForExists(waitingTime) + mDevice.waitForObjects( + mDevice.findObject( + UiSelector().textContains("Google") + ) + ) - onNodeWithTag("mozac.awesomebar.suggestions") + rule.onNodeWithTag("mozac.awesomebar.suggestions") .performScrollToIndex(5) - onNodeWithText(searchEngineName) + rule.onNodeWithText(searchEngineName) .assertExists() .assertIsDisplayed() .assertHasClickAction() From 2bde49f911d74b6bb35ef2081b36af6a8e38a2af Mon Sep 17 00:00:00 2001 From: Aaron Train Date: Wed, 29 Sep 2021 16:27:34 -0400 Subject: [PATCH 318/517] No issue: Fix AssertionError in openThreeDotMenu fix: ktlint --- .../org/mozilla/fenix/ui/robots/HomeScreenRobot.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt index 3ac6502073..c1006a420f 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt @@ -170,8 +170,17 @@ class HomeScreenRobot { } fun openThreeDotMenu(interact: ThreeDotMenuMainRobot.() -> Unit): ThreeDotMenuMainRobot.Transition { - mDevice.waitNotNull(Until.findObject(By.res("$packageName:id/menuButton")), waitingTime) - threeDotButton().perform(click()) + // Issue: https://github.com/mozilla-mobile/fenix/issues/21578 + try { + mDevice.waitNotNull( + Until.findObject(By.res("$packageName:id/menuButton")), + waitingTime + ) + } catch (e: AssertionError) { + mDevice.pressBack() + } finally { + threeDotButton().perform(click()) + } ThreeDotMenuMainRobot().interact() return ThreeDotMenuMainRobot.Transition() From ea6d5e3d38c650ad26bd12c0c722e12dc7d71f42 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Thu, 30 Sep 2021 10:23:50 +0300 Subject: [PATCH 319/517] For #21562 - Snap to next item when scrolling This works by replacing the fling animation with snapping to the next item in the scroll direction. --- .../compose/LazyListEagerFlingBehavior.kt | 47 +++++++++++++++++++ .../pocket/PocketStoriesComposables.kt | 6 ++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/mozilla/fenix/compose/LazyListEagerFlingBehavior.kt diff --git a/app/src/main/java/org/mozilla/fenix/compose/LazyListEagerFlingBehavior.kt b/app/src/main/java/org/mozilla/fenix/compose/LazyListEagerFlingBehavior.kt new file mode 100644 index 0000000000..c777d8a341 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/compose/LazyListEagerFlingBehavior.kt @@ -0,0 +1,47 @@ +/* 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.compose + +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.gestures.ScrollScope +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** + * [FlingBehavior] for a [LazyRow] that will automatically scroll the list in the fling direction + * to fully show the next item. + */ +@Composable +fun EagerFlingBehavior( + lazyRowState: LazyListState +): FlingBehavior { + val scope = rememberCoroutineScope() + + return LazyListEagerFlingBehavior(lazyRowState, scope) +} + +private class LazyListEagerFlingBehavior( + private val lazyRowState: LazyListState, + private val scope: CoroutineScope +) : FlingBehavior { + override suspend fun ScrollScope.performFling(initialVelocity: Float): Float { + val firstItemIndex = lazyRowState.firstVisibleItemIndex + + val itemIndexToScrollTo = when (initialVelocity <= 0) { + true -> firstItemIndex + false -> firstItemIndex + 1 + } + + scope.launch { + lazyRowState.animateScrollToItem(itemIndexToScrollTo) + } + + return 0f // we've consumed the entire fling + } +} diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index 2de2d24886..ca58ae6194 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -37,6 +38,7 @@ import mozilla.components.service.pocket.PocketRecommendedStory import mozilla.components.ui.colors.PhotonColors import org.mozilla.fenix.R import org.mozilla.fenix.compose.ClickableSubstringLink +import org.mozilla.fenix.compose.EagerFlingBehavior import org.mozilla.fenix.compose.FakeClient import org.mozilla.fenix.compose.ListItemTabLarge import org.mozilla.fenix.compose.ListItemTabLargePlaceholder @@ -96,8 +98,10 @@ fun PocketStories( ) { // Show stories in a 3 by 3 grid val gridLength = 3 + val listState = rememberLazyListState() + val flingBehavior = EagerFlingBehavior(lazyRowState = listState) - LazyRow { + LazyRow(state = listState, flingBehavior = flingBehavior) { itemsIndexed(stories.chunked(gridLength)) { rowIndex, columnItems -> Column(Modifier.padding(end = if (rowIndex < gridLength - 1) 8.dp else 0.dp)) { for (index in 0 until gridLength) { From 53d4336939670a6130a26a4c26c599a320f9b02f Mon Sep 17 00:00:00 2001 From: Mugurell Date: Thu, 30 Sep 2021 13:57:28 +0300 Subject: [PATCH 320/517] For #21592 - Don't topup with general stories --- .../mozilla/fenix/ext/HomeFragmentState.kt | 43 ++------ .../mozilla/fenix/home/HomeFragmentStore.kt | 4 +- .../pocket/PocketStoriesComposables.kt | 32 ++++-- .../fenix/ext/HomeFragmentStateTest.kt | 100 +++--------------- .../fenix/home/HomeFragmentStoreTest.kt | 7 +- 5 files changed, 50 insertions(+), 136 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt b/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt index ff82c2d241..5f3734f832 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt @@ -11,15 +11,12 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.POCKET_STORIES_D import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory /** - * Get the list of stories to be displayed. - * Either the stories from the [POCKET_STORIES_DEFAULT_CATEGORY_NAME] either - * filtered stories based on the user selected categories. + * Get the list of stories to be displayed based on the user selected categories. * * @param neededStoriesCount how many stories are intended to be displayed. * This only impacts filtered results guaranteeing an even spread of stories from each category. * - * @return a list of [PocketRecommendedStory]es from the currently selected categories - * topped if necessary with stories from the [POCKET_STORIES_DEFAULT_CATEGORY_NAME] up to [neededStoriesCount]. + * @return a list of [PocketRecommendedStory]es from the currently selected categories. */ fun HomeFragmentState.getFilteredStories( neededStoriesCount: Int @@ -39,43 +36,27 @@ fun HomeFragmentState.getFilteredStories( .sortedByDescending { it.lastInteractedWithTimestamp } val filteredStoriesCount = getFilteredStoriesCount( - pocketStoriesCategories, oldestSortedCategories, neededStoriesCount + oldestSortedCategories, neededStoriesCount ) - // Add general stories at the end of the stories list to show until neededStoriesCount - val generalStoriesTopup = filteredStoriesCount[POCKET_STORIES_DEFAULT_CATEGORY_NAME]?.let { neededTopups -> - pocketStoriesCategories - .find { it.name == POCKET_STORIES_DEFAULT_CATEGORY_NAME } - ?.stories - ?.sortedBy { it.timesShown } - ?.take(neededTopups) - } ?: emptyList() - return oldestSortedCategories .flatMap { category -> category.stories.sortedBy { it.timesShown }.take(filteredStoriesCount[category.name]!!) - }.plus(generalStoriesTopup) - .take(neededStoriesCount) + }.take(neededStoriesCount) } /** * Get how many stories needs to be shown from each currently selected category. * - * If the selected categories together don't have [neededStoriesCount] stories then - * the difference is added from the [POCKET_STORIES_DEFAULT_CATEGORY_NAME] category. - * - * @param allCategories the list of all Pocket stories categories. * @param selectedCategories ordered list of categories from which to return results. * @param neededStoriesCount how many stories are intended to be displayed. * This impacts the results by guaranteeing an even spread of stories from each category in that stories count. * * @return a mapping of how many stories are to be shown from each category from [selectedCategories]. - * The result is topped with stories counts from the [POCKET_STORIES_DEFAULT_CATEGORY_NAME] up to [neededStoriesCount]. */ @VisibleForTesting @Suppress("ReturnCount", "NestedBlockDepth") internal fun getFilteredStoriesCount( - allCategories: List, selectedCategories: List, neededStoriesCount: Int ): Map { @@ -83,17 +64,8 @@ internal fun getFilteredStoriesCount( availableStories + category.stories.size } - when { - totalStoriesInFilteredCategories == neededStoriesCount -> { - return selectedCategories.map { it.name to it.stories.size }.toMap() - } - totalStoriesInFilteredCategories < neededStoriesCount -> { - return selectedCategories.map { it.name to it.stories.size }.toMap() + - allCategories.filter { it.name == POCKET_STORIES_DEFAULT_CATEGORY_NAME }.map { - it.name to (neededStoriesCount - totalStoriesInFilteredCategories).coerceAtMost(it.stories.size) - }.toMap() - } - else -> { + when (totalStoriesInFilteredCategories > neededStoriesCount) { + true -> { val storiesCountFromEachCategory = mutableMapOf() var currentFilteredStoriesCount = 0 @@ -110,6 +82,9 @@ internal fun getFilteredStoriesCount( } } } + false -> { + return selectedCategories.map { it.name to it.stories.size }.toMap() + } } return emptyMap() diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt index 8f825d96c8..6bf220da1e 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt @@ -149,7 +149,7 @@ private fun homeFragmentStateReducer( val updatedCategoriesState = state.copy( pocketStoriesCategories = state.pocketStoriesCategories.map { when (it.name == action.categoryName) { - true -> it.copy(isSelected = true) + true -> it.copy(isSelected = true, lastInteractedWithTimestamp = System.currentTimeMillis()) false -> it } } @@ -163,7 +163,7 @@ private fun homeFragmentStateReducer( // Deselecting a category means the stories to be displayed needs to also be changed. pocketStoriesCategories = state.pocketStoriesCategories.map { when (it.name == action.categoryName) { - true -> it.copy(isSelected = false) + true -> it.copy(isSelected = false, lastInteractedWithTimestamp = System.currentTimeMillis()) false -> it } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index ca58ae6194..8f4d269586 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -50,6 +50,13 @@ import org.mozilla.fenix.theme.FirefoxTheme import kotlin.math.roundToInt import kotlin.random.Random +/** + * Placeholder [PocketRecommendedStory] allowing to combine other items in the same list that shows stories. + * It uses empty values for it's properties ensuring that no conflict is possible since real stories have + * mandatory values. + */ +private val placeholderStory = PocketRecommendedStory("", "", "", "", "", 0, 0) + /** * Displays a single [PocketRecommendedStory]. * @@ -96,25 +103,28 @@ fun PocketStories( client: Client, onExternalLinkClicked: (String) -> Unit ) { - // Show stories in a 3 by 3 grid - val gridLength = 3 + // Show stories in at most 3 rows but on any number of columns depending on the data received. + val maxRowsNo = 3 + val storiesToShow = (stories + placeholderStory).chunked(maxRowsNo) + val listState = rememberLazyListState() val flingBehavior = EagerFlingBehavior(lazyRowState = listState) LazyRow(state = listState, flingBehavior = flingBehavior) { - itemsIndexed(stories.chunked(gridLength)) { rowIndex, columnItems -> - Column(Modifier.padding(end = if (rowIndex < gridLength - 1) 8.dp else 0.dp)) { - for (index in 0 until gridLength) { - columnItems.getOrNull(index)?.let { story -> + itemsIndexed(storiesToShow) { columnIndex, columnItems -> + Column(Modifier.padding(end = if (columnIndex < storiesToShow.size - 1) 8.dp else 0.dp)) { + columnItems.forEachIndexed { rowIndex, story -> + if (story == placeholderStory) { + ListItemTabLargePlaceholder(stringResource(R.string.pocket_stories_placeholder_text)) { + onExternalLinkClicked("http://getpocket.com/explore") + } + } else { PocketStory(story, client) { onExternalLinkClicked(story.url) } - } ?: ListItemTabLargePlaceholder(stringResource(R.string.pocket_stories_placeholder_text)) { - onExternalLinkClicked("http://getpocket.com/explore") } - // Add padding between all rows. Not also after the last. - if (index < gridLength - 1) { + if (rowIndex < maxRowsNo - 1) { Spacer(modifier = Modifier.height(8.dp)) } } @@ -137,7 +147,7 @@ fun PocketStoriesCategories( StaggeredHorizontalGrid( horizontalItemsSpacing = 16.dp ) { - categories.forEach { category -> + categories.filter { it.name != POCKET_STORIES_DEFAULT_CATEGORY_NAME }.forEach { category -> SelectableChip(category.name, category.isSelected) { onCategoryClick(category) } diff --git a/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt b/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt index 42a9199af3..97f07c4059 100644 --- a/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt +++ b/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt @@ -74,24 +74,6 @@ class HomeFragmentStateTest { assertNull(result.firstOrNull { it.category != otherStoriesCategory.name }) } - @Test - fun `GIVEN a category is selected WHEN getFilteredStories is called for more than in the category THEN results topped with ones from the default category are returned`() { - val homeState = HomeFragmentState( - pocketStoriesCategories = listOf( - otherStoriesCategory.copy(isSelected = true), anotherStoriesCategory, defaultStoriesCategory - ) - ) - - val result = homeState.getFilteredStories(5) - - assertEquals(5, result.size) - assertEquals(3, result.filter { it.category == otherStoriesCategory.name }.size) - assertEquals( - 2, - result.filter { it.category == POCKET_STORIES_DEFAULT_CATEGORY_NAME }.size - ) - } - @Test fun `GIVEN two categories are selected WHEN getFilteredStories is called for fewer than in both THEN only stories from those categories are returned`() { val homeState = HomeFragmentState( @@ -119,27 +101,6 @@ class HomeFragmentStateTest { ) } - @Test - fun `GIVEN two categories are selected WHEN getFilteredStories is called for more than in the categories THEN results topped with ones from the default category are returned`() { - val homeState = HomeFragmentState( - pocketStoriesCategories = listOf( - otherStoriesCategory.copy(isSelected = true), - anotherStoriesCategory.copy(isSelected = true), - defaultStoriesCategory - ) - ) - - val result = homeState.getFilteredStories(8) - - assertEquals(8, result.size) - assertEquals(3, result.filter { it.category == otherStoriesCategory.name }.size) - assertEquals(3, result.filter { it.category == anotherStoriesCategory.name }.size) - assertEquals( - 2, - result.filter { it.category == POCKET_STORIES_DEFAULT_CATEGORY_NAME }.size - ) - } - @Test fun `GIVEN two categories are selected WHEN getFilteredStories is called for an odd number of stories THEN there are more by one stories from the newest category`() { val firstSelectedCategory = otherStoriesCategory.copy(lastInteractedWithTimestamp = 0, isSelected = true) @@ -158,59 +119,36 @@ class HomeFragmentStateTest { } @Test - fun `GIVEN no category is selected WHEN getFilteredStoriesCount is called THEN Pocket stories count only from the default category are returned`() { - val availableCategories = listOf(otherStoriesCategory, defaultStoriesCategory, anotherStoriesCategory) - - var result = getFilteredStoriesCount(availableCategories, emptyList(), 2) - assertEquals(1, result.keys.size) - assertEquals(defaultStoriesCategory.name, result.entries.first().key) - assertEquals(2, result[defaultStoriesCategory.name]) + fun `GIVEN no category is selected WHEN getFilteredStoriesCount is called THEN return an empty result`() { + val result = getFilteredStoriesCount(emptyList(), 1) - result = getFilteredStoriesCount(availableCategories, emptyList(), 5) - assertEquals(1, result.keys.size) - assertEquals(defaultStoriesCategory.name, result.entries.first().key) - assertEquals(3, result[defaultStoriesCategory.name]) + assertTrue(result.isEmpty()) } @Test fun `GIVEN a category is selected WHEN getFilteredStoriesCount is called for at most the stories from this category THEN only stories count only from that category are returned`() { - val availableCategories = listOf(otherStoriesCategory, defaultStoriesCategory, anotherStoriesCategory) - - var result = getFilteredStoriesCount(availableCategories, listOf(otherStoriesCategory), 2) + var result = getFilteredStoriesCount(listOf(otherStoriesCategory), 2) assertEquals(1, result.keys.size) assertEquals(otherStoriesCategory.name, result.entries.first().key) assertEquals(2, result[otherStoriesCategory.name]) - result = getFilteredStoriesCount(availableCategories, listOf(otherStoriesCategory), 3) + result = getFilteredStoriesCount(listOf(otherStoriesCategory), 3) assertEquals(1, result.keys.size) assertEquals(otherStoriesCategory.name, result.entries.first().key) assertEquals(3, result[otherStoriesCategory.name]) } @Test - fun `GIVEN a category is selected WHEN getFilteredStoriesCount is called for more stories than this category has THEN results topped with ones from the default category are returned`() { - val availableCategories = listOf(otherStoriesCategory, defaultStoriesCategory, anotherStoriesCategory) - - val result = getFilteredStoriesCount(availableCategories, listOf(otherStoriesCategory), 5) - - assertEquals(2, result.keys.size) - assertTrue( - result.keys.containsAll( - listOf( - defaultStoriesCategory.name, - otherStoriesCategory.name - ) - ) - ) + fun `GIVEN a category is selected WHEN getFilteredStoriesCount is called for more stories than in this category THEN return only that`() { + var result = getFilteredStoriesCount(listOf(otherStoriesCategory), 4) + assertEquals(1, result.keys.size) + assertEquals(otherStoriesCategory.name, result.entries.first().key) assertEquals(3, result[otherStoriesCategory.name]) - assertEquals(2, result[defaultStoriesCategory.name]) } @Test fun `GIVEN two categories are selected WHEN getFilteredStoriesCount is called for at most the stories count in both THEN only stories counts from those categories are returned`() { - val availableCategories = listOf(otherStoriesCategory, defaultStoriesCategory, anotherStoriesCategory) - - var result = getFilteredStoriesCount(availableCategories, listOf(otherStoriesCategory, anotherStoriesCategory), 2) + var result = getFilteredStoriesCount(listOf(otherStoriesCategory, anotherStoriesCategory), 2) assertEquals(2, result.keys.size) assertTrue( result.keys.containsAll( @@ -223,7 +161,7 @@ class HomeFragmentStateTest { assertEquals(1, result[otherStoriesCategory.name]) assertEquals(1, result[anotherStoriesCategory.name]) - result = getFilteredStoriesCount(availableCategories, listOf(otherStoriesCategory, anotherStoriesCategory), 6) + result = getFilteredStoriesCount(listOf(otherStoriesCategory, anotherStoriesCategory), 6) assertEquals(2, result.keys.size) assertTrue( result.keys.containsAll( @@ -238,16 +176,12 @@ class HomeFragmentStateTest { } @Test - fun `GIVEN two categories are selected WHEN getFilteredStoriesCount is called for more results than in those categories THEN results topped with ones from the default category are returned`() { - val availableCategories = listOf(otherStoriesCategory, defaultStoriesCategory, anotherStoriesCategory) - - val result = getFilteredStoriesCount(availableCategories, listOf(otherStoriesCategory, anotherStoriesCategory), 8) - - assertEquals(3, result.size) + fun `GIVEN two categories are selected WHEN getFilteredStoriesCount is called for more results than stories in both THEN only stories counts from those categories are returned`() { + val result = getFilteredStoriesCount(listOf(otherStoriesCategory, anotherStoriesCategory), 8) + assertEquals(2, result.keys.size) assertTrue( result.keys.containsAll( listOf( - defaultStoriesCategory.name, otherStoriesCategory.name, anotherStoriesCategory.name ) @@ -255,15 +189,11 @@ class HomeFragmentStateTest { ) assertEquals(3, result[otherStoriesCategory.name]) assertEquals(3, result[anotherStoriesCategory.name]) - assertEquals(2, result[defaultStoriesCategory.name]) } @Test fun `GIVEN two categories are selected WHEN getFilteredStoriesCount is called for an odd number of results THEN there are more by one results from first selected category`() { - val availableCategories = listOf(otherStoriesCategory, defaultStoriesCategory, anotherStoriesCategory) - - // The lastInteractedWithTimestamp is not checked in this method but the selected categories order - val result = getFilteredStoriesCount(availableCategories, listOf(otherStoriesCategory, anotherStoriesCategory), 5) + val result = getFilteredStoriesCount(listOf(otherStoriesCategory, anotherStoriesCategory), 5) assertTrue( result.keys.containsAll( diff --git a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt index 83a89a754e..fb9a5075ff 100644 --- a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt @@ -208,10 +208,9 @@ class HomeFragmentStoreTest { verify { any().getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) } } - assertTrue( - listOf(otherStoriesCategory.copy(isSelected = true)) - .containsAll(homeFragmentStore.state.pocketStoriesCategories.filter { it.isSelected }) - ) + val selectedCategories = homeFragmentStore.state.pocketStoriesCategories.filter { it.isSelected } + assertEquals(1, selectedCategories.size) + assertTrue(otherStoriesCategory.name === selectedCategories[0].name) assertSame(filteredStories, homeFragmentStore.state.pocketStories) } From 5358552a62c9a99e09c0d89aa4eeac16bb86cb12 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Thu, 30 Sep 2021 15:33:53 +0000 Subject: [PATCH 321/517] Update Android Components version to 94.0.20210930143139. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index dafb4a0998..303e259bef 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20210929224637" + const val VERSION = "94.0.20210930143139" } From 25d069610169f78d10715f7bec52812bb91ac12f Mon Sep 17 00:00:00 2001 From: Noah Bond <87384386+MozillaNoah@users.noreply.github.com> Date: Thu, 30 Sep 2021 12:01:31 -0700 Subject: [PATCH 322/517] Inactive tabs changes (#21524) * Issue mozilla-mobile#21319 - Moved inactive tabs to the top of the normal tabs tray. * Issue mozilla-mobile#21319 - Added a delete icon to delete ALL inactive tabs. * Issue mozilla-mobile#21319 - Changed default inactive time period to 14 days * Issue mozilla-mobile#21319 - Hooked inactive tabs setting to UI code Inactive tabs setting is also disabled when the user has selected the one day or week auto-close tab setting. * Issue mozilla-mobile#21319 - File and Lint cleanup * PR: Fixed bug causing grouped tabs to also show in "Other" when marked as inactive but inactive is OFF in Settings * PR: Fixed lint warnings * PR: Removed redundant feature check * PR - Ignore test until search term tab groups switch is done --- .../fenix/settings/TabsSettingsFragment.kt | 33 +++++++++++++ .../fenix/tabstray/TabsTrayController.kt | 15 +++++- .../fenix/tabstray/TabsTrayInteractor.kt | 9 ++++ .../fenix/tabstray/TrayPagerAdapter.kt | 5 +- .../tabstray/browser/InactiveTabViewHolder.kt | 10 +++- .../tabstray/browser/InactiveTabsAdapter.kt | 4 +- .../tabstray/browser/NormalBrowserTrayList.kt | 46 +++++++++++-------- .../tabstray/browser/TitleHeaderAdapter.kt | 6 ++- .../tabstray/browser/TitleHeaderBinding.kt | 6 ++- .../fenix/tabstray/ext/TabSelectors.kt | 23 +++++----- .../fenix/tabstray/ext/TabSessionState.kt | 7 +++ .../NormalBrowserPageViewHolder.kt | 13 +++--- .../java/org/mozilla/fenix/utils/Settings.kt | 9 ++++ .../main/res/layout/inactive_header_item.xml | 25 +++++++--- app/src/main/res/values/preference_keys.xml | 2 + app/src/main/res/values/strings.xml | 10 ++++ app/src/main/res/xml/tabs_preferences.xml | 14 ++++++ .../tabstray/DefaultTabsTrayControllerTest.kt | 37 ++++++++++++++- .../tabstray/DefaultTabsTrayInteractorTest.kt | 7 +++ .../browser/InactiveTabViewHolderTest.kt | 4 +- .../browser/TitleHeaderBindingTest.kt | 18 ++++++-- .../org/mozilla/fenix/utils/SettingsTest.kt | 7 +++ 22 files changed, 253 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt index 889b0fe2b9..9a9d3dfe70 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt @@ -8,12 +8,14 @@ import android.os.Bundle import android.view.View import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreference import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event.TabViewSettingChanged import org.mozilla.fenix.components.metrics.Event.TabViewSettingChanged.Type import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.utils.view.addToRadioGroup @@ -30,6 +32,8 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { private lateinit var startOnHomeRadioFourHours: RadioButtonPreference private lateinit var startOnHomeRadioAlways: RadioButtonPreference private lateinit var startOnHomeRadioNever: RadioButtonPreference + private lateinit var inactiveTabsCategory: PreferenceCategory + private lateinit var inactiveTabs: SwitchPreference override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.tabs_preferences, rootKey) @@ -67,9 +71,24 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { requirePreference(R.string.pref_key_start_on_home_category).isVisible = FeatureFlags.showStartOnHomeSettings + inactiveTabs = requirePreference(R.string.pref_key_inactive_tabs).also { + it.isChecked = it.context.settings().inactiveTabsAreEnabled + it.onPreferenceChangeListener = SharedPreferenceUpdater() + } + + inactiveTabsCategory = requirePreference(R.string.pref_key_inactive_tabs_category).also { + it.isVisible = FeatureFlags.inactiveTabs + it.isEnabled = !(it.context.settings().closeTabsAfterOneDay || it.context.settings().closeTabsAfterOneWeek) + } + listRadioButton.onClickListener(::sendTabViewTelemetry) gridRadioButton.onClickListener(::sendTabViewTelemetry) + radioManual.onClickListener(::enableInactiveTabsSetting) + radioOneDay.onClickListener(::disableInactiveTabsSetting) + radioOneWeek.onClickListener(::disableInactiveTabsSetting) + radioOneMonth.onClickListener(::enableInactiveTabsSetting) + setupRadioGroups() } @@ -102,4 +121,18 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { metrics.track(TabViewSettingChanged(Type.GRID)) } } + + private fun enableInactiveTabsSetting() { + inactiveTabsCategory.apply { + isEnabled = true + } + } + + private fun disableInactiveTabsSetting() { + inactiveTabsCategory.apply { + isEnabled = false + inactiveTabs.isChecked = false + context.settings().inactiveTabsAreEnabled = false + } + } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt index f273008c07..8bdee189cd 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt @@ -24,6 +24,7 @@ import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.home.HomeFragment import org.mozilla.fenix.tabstray.browser.DEFAULT_ACTIVE_DAYS +import org.mozilla.fenix.tabstray.ext.inactiveTabs import java.util.concurrent.TimeUnit interface TabsTrayController { @@ -76,6 +77,11 @@ interface TabsTrayController { tabs: Collection, numOfDays: Long = DEFAULT_ACTIVE_DAYS + 1 ) + + /** + * Deletes all inactive tabs. + */ + fun handleDeleteAllInactiveTabs() } class DefaultTabsTrayController( @@ -179,7 +185,7 @@ class DefaultTabsTrayController( } /** - * Marks all the [tabs] with the [TabSessionState.lastAccess] to 5 days; enough time to + * Marks all the [tabs] with the [TabSessionState.lastAccess] to 15 days; enough time to * have a tab considered as inactive. * * ⚠️ DO NOT USE THIS OUTSIDE OF DEBUGGING/TESTING. @@ -211,4 +217,11 @@ class DefaultTabsTrayController( dismissTray() navigateToHomeAndDeleteSession(sessionId) } + + override fun handleDeleteAllInactiveTabs() { + browserStore.state.inactiveTabs.map { it.id }.let { + tabsUseCases.removeTabs(it) + } + showUndoSnackbarForTab(false) + } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInteractor.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInteractor.kt index 9923b0eb07..bae7a729a8 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInteractor.kt @@ -34,6 +34,11 @@ interface TabsTrayInteractor { * Called when clicking the debug menu option for inactive tabs. */ fun onInactiveDebugClicked(tabs: Collection) + + /** + * Deletes all inactive tabs. + */ + fun onDeleteInactiveTabs() } /** @@ -63,4 +68,8 @@ class DefaultTabsTrayInteractor( override fun onInactiveDebugClicked(tabs: Collection) { controller.forceTabsAsInactive(tabs) } + + override fun onDeleteInactiveTabs() { + controller.handleDeleteAllInactiveTabs() + } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TrayPagerAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TrayPagerAdapter.kt index 74fac7abd9..32844de463 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TrayPagerAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TrayPagerAdapter.kt @@ -11,6 +11,7 @@ import androidx.annotation.VisibleForTesting import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.RecyclerView import mozilla.components.browser.state.store.BrowserStore +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.sync.SyncedTabsAdapter import org.mozilla.fenix.tabstray.browser.BrowserTabsAdapter import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor @@ -39,9 +40,9 @@ class TrayPagerAdapter( */ private val normalAdapter by lazy { ConcatAdapter( - InactiveTabsAdapter(context, browserInteractor, INACTIVE_TABS_FEATURE_NAME), + InactiveTabsAdapter(context, browserInteractor, interactor, INACTIVE_TABS_FEATURE_NAME), TabGroupAdapter(context, browserInteractor, tabsTrayStore, TAB_GROUP_FEATURE_NAME), - TitleHeaderAdapter(browserStore), + TitleHeaderAdapter(browserStore, context.settings()), BrowserTabsAdapter(context, browserInteractor, tabsTrayStore, TABS_TRAY_FEATURE_NAME) ) } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt index 93e22b3685..62101bed61 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt @@ -19,6 +19,7 @@ import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.dpToPx +import org.mozilla.fenix.tabstray.TabsTrayInteractor import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.Manual import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.OneDay import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.OneMonth @@ -28,7 +29,8 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite class HeaderHolder( itemView: View, - interactor: InactiveTabsInteractor + inactiveTabsInteractor: InactiveTabsInteractor, + tabsTrayInteractor: TabsTrayInteractor, ) : InactiveTabViewHolder(itemView) { private val binding = InactiveHeaderItemBinding.bind(itemView) @@ -40,7 +42,7 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite setOnClickListener { val newState = !it.isActivated - interactor.onHeaderClicked(newState) + inactiveTabsInteractor.onHeaderClicked(newState) it.isActivated = newState binding.chevron.rotation = ROTATION_DEGREE @@ -50,6 +52,10 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite bottom = binding.root.context.dpToPx(if (it.isActivated) 0f else 1f) ) } + + binding.delete.setOnClickListener { + tabsTrayInteractor.onDeleteInactiveTabs() + } } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt index cc7e1377f5..8c3f325c17 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt @@ -14,6 +14,7 @@ import mozilla.components.concept.tabstray.Tabs import mozilla.components.concept.tabstray.TabsTray import mozilla.components.support.base.observer.ObserverRegistry import org.mozilla.fenix.components.Components +import org.mozilla.fenix.tabstray.TabsTrayInteractor import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.FooterHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.HeaderHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.RecentlyClosedHolder @@ -42,6 +43,7 @@ private typealias Observable = ComponentObservable class InactiveTabsAdapter( private val context: Context, private val browserTrayInteractor: BrowserTrayInteractor, + private val tabsTrayInteractor: TabsTrayInteractor, private val featureName: String, delegate: Observable = ObserverRegistry() ) : Adapter(DiffCallback), TabsTray, Observable by delegate { @@ -53,7 +55,7 @@ class InactiveTabsAdapter( .inflate(viewType, parent, false) return when (viewType) { - HeaderHolder.LAYOUT_ID -> HeaderHolder(view, inactiveTabsInteractor) + HeaderHolder.LAYOUT_ID -> HeaderHolder(view, inactiveTabsInteractor, tabsTrayInteractor) TabViewHolder.LAYOUT_ID -> TabViewHolder(view, browserTrayInteractor, featureName) FooterHolder.LAYOUT_ID -> FooterHolder(view) RecentlyClosedHolder.LAYOUT_ID -> RecentlyClosedHolder(view, browserTrayInteractor) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt index c29d6602c8..e21713f718 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt @@ -12,19 +12,22 @@ import mozilla.components.browser.tabstray.TabViewHolder import mozilla.components.feature.tabs.tabstray.TabsFeature import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.tabstray.ext.browserAdapter import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter import org.mozilla.fenix.tabstray.ext.isNormalTabActive +import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithSearchTerm import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithoutSearchTerm +import org.mozilla.fenix.tabstray.ext.isNormalTabWithoutSearchTerm +import org.mozilla.fenix.tabstray.ext.isNormalTabWithSearchTerm import org.mozilla.fenix.tabstray.ext.isNormalTabInactive -import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithSearchTerm import org.mozilla.fenix.tabstray.ext.tabGroupAdapter import java.util.concurrent.TimeUnit /** * The time until which a tab is considered in-active (in days). */ -const val DEFAULT_ACTIVE_DAYS = 4L +const val DEFAULT_ACTIVE_DAYS = 14L /** * The maximum time from when a tab was created or accessed until it is considered "inactive". @@ -41,34 +44,41 @@ class NormalBrowserTrayList @JvmOverloads constructor( override val tabsFeature by lazy { val tabsAdapter = concatAdapter.browserAdapter + val inactiveTabsEnabled = context.settings().inactiveTabsAreEnabled + val tabFilter: (TabSessionState) -> Boolean = { + when { + FeatureFlags.tabGroupFeature && inactiveTabsEnabled -> + it.isNormalTabActiveWithoutSearchTerm(maxActiveTime) + + inactiveTabsEnabled -> it.isNormalTabActive(maxActiveTime) + + FeatureFlags.tabGroupFeature -> it.isNormalTabWithoutSearchTerm() + + else -> !it.content.private + } + } TabsFeature( tabsAdapter, context.components.core.store, selectTabUseCase, removeTabUseCase, - { state -> - if (!FeatureFlags.inactiveTabs) { - return@TabsFeature !state.content.private - } - - if (!FeatureFlags.tabGroupFeature) { - state.isNormalTabActive(maxActiveTime) - } else { - state.isNormalTabActiveWithoutSearchTerm(maxActiveTime) - } - }, + tabFilter, {} ) } private val searchTermFeature by lazy { val store = context.components.core.store - val tabFilter: (TabSessionState) -> Boolean = filter@{ - if (!FeatureFlags.tabGroupFeature) { - return@filter false + val inactiveTabsEnabled = context.settings().inactiveTabsAreEnabled + val tabFilter: (TabSessionState) -> Boolean = { + when { + FeatureFlags.tabGroupFeature && inactiveTabsEnabled -> it.isNormalTabActiveWithSearchTerm(maxActiveTime) + + FeatureFlags.tabGroupFeature -> it.isNormalTabWithSearchTerm() + + else -> false } - it.isNormalTabActiveWithSearchTerm(maxActiveTime) } val tabsAdapter = concatAdapter.tabGroupAdapter @@ -89,7 +99,7 @@ class NormalBrowserTrayList @JvmOverloads constructor( private val inactiveFeature by lazy { val store = context.components.core.store val tabFilter: (TabSessionState) -> Boolean = filter@{ - if (!FeatureFlags.inactiveTabs) { + if (!context.settings().inactiveTabsAreEnabled) { return@filter false } it.isNormalTabInactive(maxActiveTime) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderAdapter.kt index 445e7b7932..bc95c15165 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderAdapter.kt @@ -13,17 +13,19 @@ import androidx.recyclerview.widget.RecyclerView import mozilla.components.browser.state.store.BrowserStore import org.mozilla.fenix.R import org.mozilla.fenix.databinding.TabTrayTitleHeaderItemBinding +import org.mozilla.fenix.utils.Settings /** * A [RecyclerView.Adapter] for tab header. */ class TitleHeaderAdapter( - browserStore: BrowserStore + browserStore: BrowserStore, + settings: Settings ) : ListAdapter(DiffCallback) { class Header - private val normalTabsHeaderBinding = TitleHeaderBinding(browserStore, ::handleListChanges) + private val normalTabsHeaderBinding = TitleHeaderBinding(browserStore, settings, ::handleListChanges) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt index ece921dde1..18c51831ac 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt @@ -12,7 +12,8 @@ import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.lib.state.helpers.AbstractBinding import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged -import org.mozilla.fenix.tabstray.ext.normalTrayTabs +import org.mozilla.fenix.tabstray.ext.getNormalTrayTabs +import org.mozilla.fenix.utils.Settings /** * A binding class to notify an observer to show a title if there is at least one tab available. @@ -20,10 +21,11 @@ import org.mozilla.fenix.tabstray.ext.normalTrayTabs @OptIn(ExperimentalCoroutinesApi::class) class TitleHeaderBinding( store: BrowserStore, + private val settings: Settings, private val showHeader: (Boolean) -> Unit ) : AbstractBinding(store) { override suspend fun onState(flow: Flow) { - flow.map { it.normalTrayTabs } + flow.map { it.getNormalTrayTabs(settings.inactiveTabsAreEnabled) } .ifChanged { it.size } .collect { if (it.isEmpty()) { diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt index b24fcfdcaf..7b6c49388b 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt @@ -41,17 +41,16 @@ val BrowserState.inactiveTabs: List /** * The list of normal tabs in the tabs tray filtered appropriately based on feature flags. */ -val BrowserState.normalTrayTabs: List - get() { - return normalTabs.run { - if (FeatureFlags.tabGroupFeature && FeatureFlags.inactiveTabs) { - filter { it.isNormalTabActiveWithoutSearchTerm(maxActiveTime) } - } else if (FeatureFlags.inactiveTabs) { - filter { it.isNormalTabActive(maxActiveTime) } - } else if (FeatureFlags.tabGroupFeature) { - filter { it.isNormalTabWithSearchTerm() } - } else { - this - } +fun BrowserState.getNormalTrayTabs(inactiveTabsEnabled: Boolean): List { + return normalTabs.run { + if (FeatureFlags.tabGroupFeature && inactiveTabsEnabled) { + filter { it.isNormalTabActiveWithoutSearchTerm(maxActiveTime) } + } else if (inactiveTabsEnabled) { + filter { it.isNormalTabActive(maxActiveTime) } + } else if (FeatureFlags.tabGroupFeature) { + filter { it.isNormalTabWithSearchTerm() } + } else { + this } } +} diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSessionState.kt b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSessionState.kt index ab0e3069d3..5d968c2bea 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSessionState.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSessionState.kt @@ -48,6 +48,13 @@ internal fun TabSessionState.isNormalTabWithSearchTerm(): Boolean { return hasSearchTerm() && !content.private } +/** + * Returns true if the [TabSessionState] has a search term but may or may not be active. + */ +internal fun TabSessionState.isNormalTabWithoutSearchTerm(): Boolean { + return !hasSearchTerm() && !content.private +} + /** * Returns true if the [TabSessionState] is considered active based on the [maxActiveTime]. */ diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt index d3dc99f6ec..961ec656c9 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt @@ -14,20 +14,21 @@ import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.tabstray.Tab import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.selection.SelectionHolder import org.mozilla.fenix.tabstray.TabsTrayInteractor import org.mozilla.fenix.tabstray.TabsTrayStore -import org.mozilla.fenix.tabstray.browser.InactiveTabsState import org.mozilla.fenix.tabstray.browser.containsTabId +import org.mozilla.fenix.tabstray.browser.InactiveTabsState import org.mozilla.fenix.tabstray.browser.maxActiveTime import org.mozilla.fenix.tabstray.ext.browserAdapter import org.mozilla.fenix.tabstray.ext.defaultBrowserLayoutColumns +import org.mozilla.fenix.tabstray.ext.getNormalTrayTabs import org.mozilla.fenix.tabstray.ext.inactiveTabs import org.mozilla.fenix.tabstray.ext.titleHeaderAdapter import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter -import org.mozilla.fenix.tabstray.ext.isNormalTabInactive import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithSearchTerm -import org.mozilla.fenix.tabstray.ext.normalTrayTabs +import org.mozilla.fenix.tabstray.ext.isNormalTabInactive import org.mozilla.fenix.tabstray.ext.observeFirstInsert import org.mozilla.fenix.tabstray.ext.tabGroupAdapter @@ -60,7 +61,6 @@ class NormalBrowserPageViewHolder( val browserAdapter = concatAdapter.browserAdapter val tabGroupAdapter = concatAdapter.tabGroupAdapter val manager = setupLayoutManager(containerView.context, concatAdapter) - browserAdapter.selectionHolder = this tabGroupAdapter.selectionHolder = this @@ -79,11 +79,12 @@ class NormalBrowserPageViewHolder( val browserAdapter = concatAdapter.browserAdapter val inactiveTabAdapter = concatAdapter.inactiveTabsAdapter val tabGroupAdapter = concatAdapter.tabGroupAdapter + val inactiveTabsAreEnabled = containerView.context.settings().inactiveTabsAreEnabled val selectedTab = browserStore.state.selectedNormalTab ?: return // Update tabs into the inactive adapter. - if (FeatureFlags.inactiveTabs && selectedTab.isNormalTabInactive(maxActiveTime)) { + if (inactiveTabsAreEnabled && selectedTab.isNormalTabInactive(maxActiveTime)) { val inactiveTabsList = browserStore.state.inactiveTabs // We want to expand the inactive section first before we want to fire our scroll observer. InactiveTabsState.isExpanded = true @@ -126,7 +127,7 @@ class NormalBrowserPageViewHolder( // Updates tabs into the normal browser tabs adapter. browserAdapter.observeFirstInsert { - val activeTabsList = browserStore.state.normalTrayTabs + val activeTabsList = browserStore.state.getNormalTrayTabs(inactiveTabsAreEnabled) activeTabsList.forEachIndexed { tabIndex, trayTab -> if (trayTab.id == selectedTab.id) { diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index 5c9c94d0fd..d731c92054 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -412,6 +412,15 @@ class Settings(private val appContext: Context) : PreferencesHolder { } } + /** + * Indicates if the user has enabled the inactive tabs feature. + */ + var inactiveTabsAreEnabled by featureFlagPreference( + appContext.getPreferenceKey(R.string.pref_key_inactive_tabs), + default = FeatureFlags.inactiveTabs, + featureFlag = FeatureFlags.inactiveTabs + ) + @VisibleForTesting internal fun timeNowInMillis(): Long = System.currentTimeMillis() diff --git a/app/src/main/res/layout/inactive_header_item.xml b/app/src/main/res/layout/inactive_header_item.xml index a0a84b1a6e..f61c871acd 100644 --- a/app/src/main/res/layout/inactive_header_item.xml +++ b/app/src/main/res/layout/inactive_header_item.xml @@ -25,8 +25,7 @@ android:clipToPadding="false" android:focusable="true" android:foreground="?android:attr/selectableItemBackground" - android:paddingStart="16dp" - android:paddingEnd="16dp"> + android:paddingStart="16dp"> + + + app:srcCompat="@drawable/ic_delete" /> \ No newline at end of file diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index cd044465af..fe991766aa 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -268,6 +268,8 @@ pref_key_start_on_home_never pref_key_start_on_home_category pref_key_camera_permissions_needed + pref_key_inactive_tabs_category + pref_key_inactive_tabs pref_key_return_to_browser diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 23225f3d72..1d3dd21465 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -668,6 +668,14 @@ Close after one month + + + Inactive tabs on tab tray + + Make tabs inactive after 14 days + + To improve speed, tabs will be moved into the inactive tabs section on your tab tray + Remove @@ -1889,6 +1897,8 @@ Inactive tabs + + Close all inactive tabs Tabs are available here for %s. After that time, tabs will be automatically closed. diff --git a/app/src/main/res/xml/tabs_preferences.xml b/app/src/main/res/xml/tabs_preferences.xml index 641ad7c2ad..6717a49df2 100644 --- a/app/src/main/res/xml/tabs_preferences.xml +++ b/app/src/main/res/xml/tabs_preferences.xml @@ -69,4 +69,18 @@ android:key="@string/pref_key_start_on_home_never" android:title="@string/start_on_home_never" /> + + + + diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt index f67f302135..c5dda16aef 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt @@ -34,6 +34,8 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.home.HomeFragment +import org.mozilla.fenix.tabstray.browser.maxActiveTime +import org.mozilla.fenix.tabstray.ext.inactiveTabs class DefaultTabsTrayControllerTest { @MockK(relaxed = true) @@ -379,7 +381,7 @@ class DefaultTabsTrayControllerTest { } @Test - fun `WHEN dismissTabsTrayAndNavigateHome is called with a spefic tab id THEN tray is dismissed and navigates home is opened to delete that tab`() { + fun `WHEN dismissTabsTrayAndNavigateHome is called with a specific tab id THEN tray is dismissed and navigates home is opened to delete that tab`() { var dismissTrayInvoked = false var navigateToHomeAndDeleteSessionInvoked = false createController( @@ -396,6 +398,39 @@ class DefaultTabsTrayControllerTest { assertTrue(navigateToHomeAndDeleteSessionInvoked) } + @ExperimentalCoroutinesApi + @Test + fun `WHEN deleteAllInactiveTabs is called THEN that it uses tabsUseCases#removeTabs and shows an undo snackbar`() { + var showUndoSnackbarForTabInvoked = false + val controller = spyk( + createController( + showUndoSnackbarForTab = { + showUndoSnackbarForTabInvoked = true + } + ) + ) + val inactiveTab: TabSessionState = mockk { + every { lastAccess } returns maxActiveTime + every { createdAt } returns 0 + every { id } returns "24" + every { content } returns mockk { + every { private } returns false + } + } + every { browserStore.state } returns mockk() + try { + mockkStatic("mozilla.components.browser.state.selector.SelectorsKt") + every { browserStore.state.inactiveTabs } returns listOf(inactiveTab) + + controller.handleDeleteAllInactiveTabs() + + verify { tabsUseCases.removeTabs(listOf("24")) } + assertTrue(showUndoSnackbarForTabInvoked) + } finally { + unmockkStatic("mozilla.components.browser.state.selector.SelectorsKt") + } + } + private fun createController( navigateToHomeAndDeleteSession: (String) -> Unit = { }, selectTabPosition: (Int, Boolean) -> Unit = { _, _ -> }, diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayInteractorTest.kt index 55b84c759f..c0080644c6 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayInteractorTest.kt @@ -42,4 +42,11 @@ class DefaultTabsTrayInteractorTest { verifySequence { controller.handleMultipleTabsDeletion(tabsToDelete) } } + + @Test + fun `GIVEN user selecting delete all inactive tabs WHEN onDeleteTabs is called THEN the Interactor delegates the controller`() { + trayInteractor.onDeleteInactiveTabs() + + verifySequence { controller.handleDeleteAllInactiveTabs() } + } } diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolderTest.kt index d33dc9626d..0a1beb09ec 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolderTest.kt @@ -12,6 +12,7 @@ import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.tabstray.TabsTrayInteractor import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.HeaderHolder @RunWith(FenixRobolectricTestRunner::class) @@ -20,7 +21,8 @@ class InactiveTabViewHolderTest { fun `HeaderHolder - WHEN clicked THEN notify the interactor`() { val view = LayoutInflater.from(testContext).inflate(HeaderHolder.LAYOUT_ID, null) val interactor: InactiveTabsInteractor = mockk(relaxed = true) - val viewHolder = HeaderHolder(view, interactor) + val tabsTrayInteractor: TabsTrayInteractor = mockk(relaxed = true) + val viewHolder = HeaderHolder(view, interactor, tabsTrayInteractor) val initialActivatedState = view.isActivated diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBindingTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBindingTest.kt index e81b657d7f..c79c63e712 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBindingTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBindingTest.kt @@ -4,6 +4,8 @@ package org.mozilla.fenix.tabstray.browser +import io.mockk.every +import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runBlockingTest import mozilla.components.browser.state.action.TabListAction @@ -16,8 +18,10 @@ import mozilla.components.support.test.libstate.ext.waitUntilIdle import mozilla.components.support.test.rule.MainCoroutineRule import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue +import org.junit.Ignore import org.junit.Rule import org.junit.Test +import org.mozilla.fenix.utils.Settings @ExperimentalCoroutinesApi class TitleHeaderBindingTest { @@ -29,7 +33,10 @@ class TitleHeaderBindingTest { fun `WHEN normal tabs are added to the list THEN return true`() = runBlockingTest { var result = false val store = BrowserStore() - val binding = TitleHeaderBinding(store) { result = it } + val settings: Settings = mockk(relaxed = true) + val binding = TitleHeaderBinding(store, settings) { result = it } + + every { settings.inactiveTabsAreEnabled } returns true binding.start() @@ -40,11 +47,15 @@ class TitleHeaderBindingTest { assertTrue(result) } + @Ignore // To be fixed with https://github.com/mozilla-mobile/fenix/issues/21360 @Test fun `WHEN grouped tabs are added to the list THEN return false`() = runBlockingTest { var result = false val store = BrowserStore() - val binding = TitleHeaderBinding(store) { result = it } + val settings: Settings = mockk(relaxed = true) + val binding = TitleHeaderBinding(store, settings) { result = it } + + every { settings.inactiveTabsAreEnabled } returns true binding.start() @@ -73,7 +84,8 @@ class TitleHeaderBindingTest { tabs = listOf(createTab("https://getpocket.com", id = "123")) ) ) - val binding = TitleHeaderBinding(store) { result = it } + val settings: Settings = mockk(relaxed = true) + val binding = TitleHeaderBinding(store, settings) { result = it } binding.start() diff --git a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt index 0bc99e78cc..ba946d0ced 100644 --- a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt +++ b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt @@ -746,4 +746,11 @@ class SettingsTest { localSetting.defaultBrowserNotificationDisplayed = true assertFalse(localSetting.shouldShowDefaultBrowserNotification()) } + + @Test + fun inactiveTabsAreEnabled() { + // When just created + // Then + assertTrue(settings.inactiveTabsAreEnabled) + } } From 2198c0f0141d14543af494a3d34d6617a6c3b828 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Wed, 29 Sep 2021 12:13:20 +0200 Subject: [PATCH 323/517] Don't manually exclude glean-native The latest geckoview-omni package correctly declares its capabilities, including the `glean-native` one. Additionally it is able to pick geckoview-omni over glean-native in all configurations. --- app/build.gradle | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b64a69437f..f4cdeccc9e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -479,9 +479,7 @@ dependencies { implementation Deps.mozilla_service_sync_autofill implementation Deps.mozilla_service_sync_logins implementation Deps.mozilla_service_firefox_accounts - implementation(Deps.mozilla_service_glean) { - exclude group: 'org.mozilla.telemetry', module: 'glean-native' - } + implementation(Deps.mozilla_service_glean) implementation Deps.mozilla_service_location implementation Deps.mozilla_service_nimbus From 38b6736211e1577d046b6225f0099fac4d49a78f Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Thu, 30 Sep 2021 18:25:45 +0000 Subject: [PATCH 324/517] Update Android Components version to 94.0.20210930173134. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 303e259bef..87672d57e8 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20210930143139" + const val VERSION = "94.0.20210930173134" } From 0d9e2b3a3604924dea1c3d3b47b480ad0591318f Mon Sep 17 00:00:00 2001 From: Grisha Kruglov Date: Thu, 30 Sep 2021 10:45:10 -0700 Subject: [PATCH 325/517] Do not show home screen behind search if we have search terms Home screen isn't actually visible in case we're displaying awesomebar search results. The navigation is thus unnecessary and actually causes visual jankiness as we display home for a moment before covering it up with search results. --- .../toolbar/BrowserToolbarController.kt | 6 ++++- .../fenix/search/SearchDialogFragment.kt | 5 +++- .../DefaultBrowserToolbarControllerTest.kt | 25 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt index 2686f04114..9e1b84b769 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt @@ -92,7 +92,11 @@ class DefaultBrowserToolbarController( override fun handleToolbarClick() { metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)) - if (FeatureFlags.showHomeBehindSearch) { + // If we're displaying awesomebar search results, Home screen will not be visible (it's + // covered up with the search results). So, skip the navigation event in that case. + // If we don't, there's a visual flickr as we navigate to Home and then display search + // results on top it. + if (FeatureFlags.showHomeBehindSearch && currentSession?.content?.searchTerms.isNullOrBlank()) { navController.navigate( BrowserFragmentDirections.actionGlobalHome() ) diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchDialogFragment.kt index ffd1fce441..bd0a8e1ded 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchDialogFragment.kt @@ -434,7 +434,10 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler { true } else -> { - if (FeatureFlags.showHomeBehindSearch) { + // In case we're displaying search results, we wouldn't have navigated to home, and + // so we don't need to navigate "back to" browser fragment. + // See mirror of this logic in BrowserToolbarController#handleToolbarClick. + if (FeatureFlags.showHomeBehindSearch && store.state.searchTerms.isBlank()) { val args by navArgs() args.sessionId?.let { findNavController().navigate( diff --git a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt index 01eab8599a..15a6c643ae 100644 --- a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt @@ -238,6 +238,31 @@ class DefaultBrowserToolbarControllerTest { } } + @Test + fun handleToolbackClickWithSearchTerms() { + val searchResultsTab = createTab("https://google.com?q=mozilla+website", searchTerms = "mozilla website") + store.dispatch(TabListAction.AddTabAction(searchResultsTab, select = true)).joinBlocking() + + val controller = createController() + controller.handleToolbarClick() + + val homeDirections = BrowserFragmentDirections.actionGlobalHome() + val searchDialogDirections = BrowserFragmentDirections.actionGlobalSearchDialog( + sessionId = searchResultsTab.id + ) + + verify { + metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)) + } + // Does not show the home screen "behind" the search dialog if the current session has search terms. + verify(exactly = 0) { + navController.navigate(homeDirections) + } + verify { + navController.navigate(searchDialogDirections, any()) + } + } + @Test fun handleToolbarCloseTabPressWithLastPrivateSession() { val item = TabCounterMenu.Item.CloseTab From ef2a491614ee9b21b4ed816cc3e7c1d670a07a46 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Thu, 30 Sep 2021 22:26:54 +0000 Subject: [PATCH 326/517] Update Android Components version to 94.0.20210930211433. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 87672d57e8..36f7a3ecf8 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20210930173134" + const val VERSION = "94.0.20210930211433" } From ca30f518d8da46f9fdde57af447a09c0cfac73b3 Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Thu, 30 Sep 2021 16:12:17 -0400 Subject: [PATCH 327/517] For #21608: Update strings for home screen dialog features --- .../java/org/mozilla/fenix/FeatureFlags.kt | 5 ----- .../sessioncontrol/SessionControlController.kt | 11 ++++------- .../layout/fragment_onboarding_home_dialog.xml | 16 ++++++++-------- app/src/main/res/values/strings.xml | 18 +++++++++--------- 4 files changed, 21 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index b420fd9795..d621137f94 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -85,9 +85,4 @@ object FeatureFlags { return Config.channel.isNightlyOrDebug && "en-US" == LocaleManager.getCurrentLocale(context)?.toLanguageTag() ?: getSystemDefault().toLanguageTag() } - - /** - * Enables showing the homescreen onboarding card. - */ - val showHomeOnboarding = Config.channel.isDebug } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt index 9b956a3ddc..078110cc4e 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt @@ -27,7 +27,6 @@ import mozilla.components.feature.top.sites.TopSite import mozilla.components.support.ktx.android.view.showKeyboard import mozilla.components.support.ktx.kotlin.isUrl import org.mozilla.fenix.BrowserDirection -import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.browser.BrowserFragmentDirections @@ -455,12 +454,10 @@ class DefaultSessionControlController( } override fun handleShowOnboardingDialog() { - if (FeatureFlags.showHomeOnboarding) { - navController.nav( - R.id.homeFragment, - HomeFragmentDirections.actionGlobalHomeOnboardingDialog() - ) - } + navController.nav( + R.id.homeFragment, + HomeFragmentDirections.actionGlobalHomeOnboardingDialog() + ) } override fun handleReadPrivacyNoticeClicked() { diff --git a/app/src/main/res/layout/fragment_onboarding_home_dialog.xml b/app/src/main/res/layout/fragment_onboarding_home_dialog.xml index b5356211b7..f648279450 100644 --- a/app/src/main/res/layout/fragment_onboarding_home_dialog.xml +++ b/app/src/main/res/layout/fragment_onboarding_home_dialog.xml @@ -37,7 +37,7 @@ android:layout_marginTop="16dp" android:layout_marginHorizontal="@dimen/oboarding_home_dialog_margin_horizontal" android:lineHeight="24sp" - android:text="@string/onboarding_home_screen_title" + android:text="@string/onboarding_home_screen_title_2" android:textAppearance="@style/Header20TextStyle" android:textAlignment="center" app:layout_constraintEnd_toEndOf="parent" @@ -52,7 +52,7 @@ android:layout_marginHorizontal="@dimen/oboarding_home_dialog_margin_horizontal" android:lineHeight="24sp" android:textAppearance="@style/Body14TextStyle" - android:text="@string/onboarding_home_screen_description" + android:text="@string/onboarding_home_screen_description_2" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/welcome_title" /> @@ -78,7 +78,7 @@ android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal" android:layout_marginTop="26dp" android:lineHeight="20sp" - android:text="@string/onboarding_home_screen_section_home_title" + android:text="@string/onboarding_home_screen_section_home_title_2" android:textAppearance="@style/Header14TextStyle" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/home_icon" @@ -101,7 +101,7 @@ android:layout_marginTop="6dp" android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal" android:lineHeight="20sp" - android:text="@string/onboarding_home_screen_section_home_description" + android:text="@string/onboarding_home_screen_section_home_description_2" android:textAppearance="@style/Body14TextStyle" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/home_placeholder" @@ -127,7 +127,7 @@ android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal" android:layout_marginTop="26dp" android:lineHeight="20sp" - android:text="@string/onboarding_home_screen_section_cleaner_tab_tray_title" + android:text="@string/onboarding_home_screen_section_cleaner_tab_tray_title_2" android:textAppearance="@style/Header14TextStyle" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/cleaner_tab_tray_icon" @@ -150,7 +150,7 @@ android:layout_marginTop="6dp" android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal" android:lineHeight="20sp" - android:text="@string/onboarding_home_screen_section_cleaner_tab_tray_description" + android:text="@string/onboarding_home_screen_section_cleaner_tab_tray_description_2" android:textAppearance="@style/Body14TextStyle" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/cleaner_tab_tray_placeholder" @@ -176,7 +176,7 @@ android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal" android:layout_marginTop="26dp" android:lineHeight="20sp" - android:text="@string/onboarding_home_screen_section_useful_history_title" + android:text="@string/onboarding_home_screen_section_useful_history_title_2" android:textAppearance="@style/Header14TextStyle" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/useful_history_icon" @@ -199,7 +199,7 @@ android:layout_marginTop="6dp" android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal" android:lineHeight="20sp" - android:text="@string/onboarding_home_screen_section_useful_history_description" + android:text="@string/onboarding_home_screen_section_useful_history_description_2" android:textAppearance="@style/Body14TextStyle" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/useful_history_placeholder" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1d3dd21465..dbcfd1757d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -258,21 +258,21 @@ - What’s new in Firefox! + What’s new in Firefox - It’s now easier to pick up where you left off so you can stay in the flow. + It’s now easier to pick back up where you left off. - Personalized Firefox home + Personalized Firefox homepage - Access your open tabs, bookmarks, and history directly from your home screen. + Jump to your open tabs, bookmarks, and browsing history. - Cleaner tab tray - - Firefox helps you to manage your tabs and quickly find what you’re looking for. + Clean, organized tabs + + Clear away tab clutter with improved layout and auto-closing tabs. - Useful history + Recent searches - Get a better overview of your browsing history. + Revisit your latest searches from your homepage and tabs. From c60de0bc6cb40c099d454dab0bd828e8b97d97aa Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Tue, 14 Sep 2021 16:35:44 -0700 Subject: [PATCH 328/517] For #21287: long press menu on recently visited homescreen groups --- .../controller/HistoryMetadataController.kt | 22 ++- .../interactor/HistoryMetadataInteractor.kt | 7 + .../view/HistoryMetadataGroupViewHolder.kt | 4 +- .../historymetadata/view/RecentlyVisited.kt | 32 ++++- .../org/mozilla/fenix/home/HomeFragment.kt | 4 +- .../SessionControlInteractor.kt | 7 +- .../HistoryMetadataControllerTest.kt | 50 ++++++- .../HistoryMetadataInteractorTest.kt | 127 ++++++++++++++++++ .../view/HistoryMetadataViewHolderTest.kt | 37 +++++ .../home/SessionControlInteractorTest.kt | 33 +---- 10 files changed, 281 insertions(+), 42 deletions(-) create mode 100644 app/src/test/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractorTest.kt create mode 100644 app/src/test/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataViewHolderTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt index c5866dc921..af8cd6f031 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt @@ -7,7 +7,11 @@ package org.mozilla.fenix.historymetadata.controller import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting.PRIVATE import androidx.navigation.NavController +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import mozilla.components.concept.storage.HistoryMetadataStorage import org.mozilla.fenix.R +import org.mozilla.fenix.browser.BrowserFragmentDirections import org.mozilla.fenix.historymetadata.HistoryMetadataGroup import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor import org.mozilla.fenix.home.HomeFragmentDirections @@ -27,13 +31,20 @@ interface HistoryMetadataController { * @see [HistoryMetadataInteractor.onHistoryMetadataGroupClicked] */ fun handleHistoryMetadataGroupClicked(historyMetadataGroup: HistoryMetadataGroup) + + /** + * @see [HistoryMetadataInteractor.onRemoveGroup] + */ + fun handleRemoveGroup(searchTerm: String) } /** * The default implementation of [HistoryMetadataController]. */ class DefaultHistoryMetadataController( - private val navController: NavController + private val navController: NavController, + private val storage: HistoryMetadataStorage, + private val scope: CoroutineScope ) : HistoryMetadataController { override fun handleHistoryShowAllClicked() { @@ -53,6 +64,15 @@ class DefaultHistoryMetadataController( ) } + override fun handleRemoveGroup(searchTerm: String) { + scope.launch { + storage.deleteHistoryMetadata(searchTerm) + } + navController.navigate( + BrowserFragmentDirections.actionGlobalHome() + ) + } + @VisibleForTesting(otherwise = PRIVATE) fun dismissSearchDialogIfDisplayed() { if (navController.currentDestination?.id == R.id.searchDialogFragment) { diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractor.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractor.kt index 6b60a0377a..adb8b006ae 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractor.kt @@ -24,4 +24,11 @@ interface HistoryMetadataInteractor { * @param historyMetadataGroup The [HistoryMetadataGroup] to toggle its expanded state. */ fun onHistoryMetadataGroupClicked(historyMetadataGroup: HistoryMetadataGroup) + + /** + * Removes a history metadata group with the given search term from the homescreen. + * + * @param searchTerm The search term to be removed. + */ + fun onRemoveGroup(searchTerm: String) } diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt index 196fc12e4f..057fb8835e 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt @@ -42,8 +42,8 @@ class HistoryMetadataGroupViewHolder( menuItems = listOfNotNull( RecentVisitMenuItem( title = stringResource(R.string.recently_visited_menu_item_remove), - onClick = { - // no-op + onClick = { group -> + interactor.onRemoveGroup(group.title) } ) ), diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt index 2c93a08b2e..462b2f536e 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt @@ -6,15 +6,20 @@ package org.mozilla.fenix.historymetadata.view import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -33,6 +38,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow @@ -63,7 +69,7 @@ fun RecentlyVisited( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(8.dp), backgroundColor = FirefoxTheme.colors.surface, - elevation = 6.dp, + elevation = 6.dp ) { LazyRow( contentPadding = PaddingValues(16.dp), @@ -73,7 +79,7 @@ fun RecentlyVisited( items(itemsList) { items -> Column( - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(8.dp)) ) { items.forEachIndexed { index, recentVisit -> RecentVisitItem( @@ -112,7 +118,8 @@ private fun RecentVisitItem( onClick = { onRecentVisitClick(recentVisit) }, onLongClick = { menuExpanded = true } ) - .size(268.dp, 56.dp), + .size(268.dp, 56.dp) + .background(color = FirefoxTheme.colors.surface), verticalAlignment = Alignment.CenterVertically ) { Image( @@ -150,17 +157,32 @@ private fun RecentVisitItem( expanded = menuExpanded, onDismissRequest = { menuExpanded = false }, modifier = Modifier.background(color = FirefoxTheme.colors.surface) + .height(52.dp) + .scrollable( + state = ScrollState(0), + orientation = Orientation.Vertical, + enabled = false + ) ) { for (item in menuItems) { DropdownMenuItem( onClick = { menuExpanded = false item.onClick(recentVisit) - } + }, + modifier = Modifier.fillMaxHeight() ) { Text( text = item.title, - color = FirefoxTheme.colors.textPrimary + color = FirefoxTheme.colors.textPrimary, + maxLines = 1, + modifier = Modifier.align(Alignment.Top) + .padding(top = 6.dp) + .scrollable( + state = ScrollState(0), + orientation = Orientation.Vertical, + enabled = false + ).fillMaxHeight() ) } } diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 46b0cd6b70..e97cbc7d39 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -345,7 +345,9 @@ class HomeFragment : Fragment() { navController = findNavController() ), historyMetadataController = DefaultHistoryMetadataController( - navController = findNavController() + navController = findNavController(), + storage = components.core.historyStorage, + scope = viewLifecycleOwner.lifecycleScope ), pocketStoriesController = DefaultPocketStoriesController( homeActivity = activity, diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt index 62704793d3..0a38a95a38 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt @@ -226,7 +226,8 @@ interface ExperimentCardInteractor { /** * Interactor for the Home screen. Provides implementations for the CollectionInteractor, * OnboardingInteractor, TopSiteInteractor, TipInteractor, TabSessionInteractor, - * ToolbarInteractor, ExperimentCardInteractor, RecentTabInteractor, RecentBookmarksInteractor and others. + * ToolbarInteractor, ExperimentCardInteractor, RecentTabInteractor, RecentBookmarksInteractor + * and others. */ @SuppressWarnings("TooManyFunctions") class SessionControlInteractor( @@ -382,6 +383,10 @@ class SessionControlInteractor( ) } + override fun onRemoveGroup(searchTerm: String) { + historyMetadataController.handleRemoveGroup(searchTerm) + } + override fun openCustomizeHomePage() { controller.handleCustomizeHomeTapped() } diff --git a/app/src/test/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataControllerTest.kt b/app/src/test/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataControllerTest.kt index 71b19bc36a..f9d5a64f06 100644 --- a/app/src/test/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataControllerTest.kt @@ -6,21 +6,25 @@ package org.mozilla.fenix.historymetadata.controller import androidx.navigation.NavController import androidx.navigation.NavDirections +import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import io.mockk.spyk import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.TestCoroutineScope import mozilla.components.concept.storage.DocumentType import mozilla.components.concept.storage.HistoryMetadata import mozilla.components.concept.storage.HistoryMetadataKey +import mozilla.components.concept.storage.HistoryMetadataStorage import mozilla.components.support.test.rule.MainCoroutineRule import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.mozilla.fenix.R +import org.mozilla.fenix.browser.BrowserFragmentDirections import org.mozilla.fenix.historymetadata.HistoryMetadataGroup import org.mozilla.fenix.home.HomeFragmentDirections @@ -33,6 +37,8 @@ class HistoryMetadataControllerTest { val coroutinesTestRule = MainCoroutineRule(testDispatcher) private val navController = mockk(relaxed = true) + private lateinit var storage: HistoryMetadataStorage + private val scope = TestCoroutineScope() private lateinit var controller: DefaultHistoryMetadataController @@ -41,17 +47,20 @@ class HistoryMetadataControllerTest { every { navController.currentDestination } returns mockk { every { id } returns R.id.homeFragment } + storage = mockk(relaxed = true) controller = spyk( DefaultHistoryMetadataController( - navController = navController + navController = navController, + scope = scope, + storage = storage ) ) } @After fun cleanUp() { - testDispatcher.cleanupTestCoroutines() + scope.cleanupTestCoroutines() } @Test @@ -90,4 +99,41 @@ class HistoryMetadataControllerTest { ) } } + + @Test + fun handleItemRemoved() { + val historyMetadataKey = HistoryMetadataKey( + "http://www.mozilla.com", + "mozilla", + null + ) + + val historyGroup = HistoryMetadataGroup( + title = "mozilla", + historyMetadata = listOf( + HistoryMetadata( + key = historyMetadataKey, + title = "mozilla", + createdAt = System.currentTimeMillis(), + updatedAt = System.currentTimeMillis(), + totalViewTime = 10, + documentType = DocumentType.Regular, + previewImageUrl = null + ) + ) + ) + + controller.handleRemoveGroup(historyGroup.title) + + testDispatcher.advanceUntilIdle() + + coVerify { + storage.deleteHistoryMetadata(historyGroup.title) + } + verify { + navController.navigate( + BrowserFragmentDirections.actionGlobalHome() + ) + } + } } diff --git a/app/src/test/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractorTest.kt new file mode 100644 index 0000000000..34421ae02c --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractorTest.kt @@ -0,0 +1,127 @@ +/* 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.historymetadata.interactor + +import androidx.navigation.NavController +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import mozilla.components.concept.storage.DocumentType +import mozilla.components.concept.storage.HistoryMetadata +import mozilla.components.concept.storage.HistoryMetadataKey +import mozilla.components.support.test.rule.MainCoroutineRule +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mozilla.fenix.R +import org.mozilla.fenix.historymetadata.HistoryMetadataGroup +import org.mozilla.fenix.historymetadata.controller.HistoryMetadataController +import org.mozilla.fenix.home.recentbookmarks.controller.RecentBookmarksController +import org.mozilla.fenix.home.recenttabs.controller.RecentTabController +import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController +import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketStoriesController + +@OptIn(ExperimentalCoroutinesApi::class) +class HistoryMetadataInteractorTest { + private val testDispatcher = TestCoroutineDispatcher() + + @get:Rule + val coroutinesTestRule = MainCoroutineRule(testDispatcher) + + private val navController = mockk(relaxed = true) + private val defaultSessionControlController: DefaultSessionControlController = + mockk(relaxed = true) + private val recentTabController: RecentTabController = mockk(relaxed = true) + private val recentBookmarksController: RecentBookmarksController = mockk(relaxed = true) + private val pocketStoriesController: PocketStoriesController = mockk(relaxed = true) + private val historyMetadataController: HistoryMetadataController = mockk(relaxed = true) + + private lateinit var interactor: SessionControlInteractor + + @Before + fun setup() { + every { navController.currentDestination } returns mockk { + every { id } returns R.id.homeFragment + } + + interactor = SessionControlInteractor( + defaultSessionControlController, + recentTabController, + recentBookmarksController, + historyMetadataController, + pocketStoriesController + ) + } + + @After + fun cleanUp() { + testDispatcher.cleanupTestCoroutines() + } + + @Test + fun onHistoryMetadataGroupClicked() { + val historyGroup = + HistoryMetadataGroup( + title = "mozilla", + historyMetadata = listOf( + HistoryMetadata( + key = HistoryMetadataKey("http://www.mozilla.com", null, null), + title = "mozilla", + createdAt = System.currentTimeMillis(), + updatedAt = System.currentTimeMillis(), + totalViewTime = 10, + documentType = DocumentType.Regular, + previewImageUrl = null + ) + ) + ) + + interactor.onHistoryMetadataGroupClicked(historyGroup) + verify { + historyMetadataController.handleHistoryMetadataGroupClicked(historyGroup) + } + } + + @Test + fun onHistoryMetadataShowAllClicked() { + interactor.onHistoryMetadataShowAllClicked() + verify { historyMetadataController.handleHistoryShowAllClicked() } + } + + @Test + fun onRemoveItem() { + val historyMetadataKey = HistoryMetadataKey( + "http://www.mozilla.com", + "mozilla", + null + ) + + val historyGroup = + HistoryMetadataGroup( + title = "mozilla", + historyMetadata = listOf( + HistoryMetadata( + key = historyMetadataKey, + title = "mozilla", + createdAt = System.currentTimeMillis(), + updatedAt = System.currentTimeMillis(), + totalViewTime = 10, + documentType = DocumentType.Regular, + previewImageUrl = null + ) + ) + ) + + interactor.onRemoveGroup(historyGroup.title) + + verify { + historyMetadataController.handleRemoveGroup(historyGroup.title) + } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataViewHolderTest.kt new file mode 100644 index 0000000000..f5531ee1aa --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataViewHolderTest.kt @@ -0,0 +1,37 @@ +/* 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.historymetadata.view + +// TODO: Needs testImplementation 'androidx.compose.ui:ui-test-junit4:1.0.0-beta04' +@Suppress("ForbiddenComment") +class HistoryMetadataViewHolderTest { + /* + @get:Rule + val composeTestRule = ComposeTestRule() + + @Test + fun `WHEN a group is removed via long press menu THEN interactor is called`() { + + val historyGroup = HistoryMetadataGroup( + title = "mozilla", + historyMetadata = listOf( + HistoryMetadata( + key = historyMetadataKey, + title = "mozilla", + createdAt = System.currentTimeMillis(), + updatedAt = System.currentTimeMillis(), + totalViewTime = 10, + documentType = DocumentType.Regular, + previewImageUrl = null + ) + ) + ) + + composeView.menuItems.performClick() + verify { interactor.onRemoveGroups(setOf(group)) } + } + + */ +} diff --git a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt index 4d005c22d0..62ed354bde 100644 --- a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt @@ -8,15 +8,11 @@ import io.mockk.mockk import io.mockk.verify import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNodeType -import mozilla.components.concept.storage.DocumentType -import mozilla.components.concept.storage.HistoryMetadata -import mozilla.components.concept.storage.HistoryMetadataKey import mozilla.components.feature.tab.collections.Tab import mozilla.components.feature.tab.collections.TabCollection import org.junit.Before import org.junit.Test import org.mozilla.fenix.browser.browsingmode.BrowsingMode -import org.mozilla.fenix.historymetadata.HistoryMetadataGroup import org.mozilla.fenix.historymetadata.controller.HistoryMetadataController import org.mozilla.fenix.home.recentbookmarks.controller.RecentBookmarksController import org.mozilla.fenix.home.recenttabs.controller.RecentTabController @@ -30,9 +26,11 @@ class SessionControlInteractorTest { private val controller: DefaultSessionControlController = mockk(relaxed = true) private val recentTabController: RecentTabController = mockk(relaxed = true) private val recentBookmarksController: RecentBookmarksController = mockk(relaxed = true) - private val historyMetadataController: HistoryMetadataController = mockk(relaxed = true) private val pocketStoriesController: PocketStoriesController = mockk(relaxed = true) + // Note: the historyMetadata tests are handled in [HistoryMetadataInteractorTest] and [HistoryMetadataControllerTest] + private val historyMetadataController: HistoryMetadataController = mockk(relaxed = true) + private lateinit var interactor: SessionControlInteractor @Before @@ -171,31 +169,6 @@ class SessionControlInteractorTest { verify { recentTabController.handleRecentTabShowAllClicked() } } - @Test - fun onHistoryMetadataShowAllClicked() { - interactor.onHistoryMetadataShowAllClicked() - verify { historyMetadataController.handleHistoryShowAllClicked() } - } - - @Test - fun onToggleHistoryMetadataGroupExpanded() { - val historyEntry = HistoryMetadata( - key = HistoryMetadataKey("http://www.mozilla.com", "mozilla", null), - title = "mozilla", - createdAt = System.currentTimeMillis(), - updatedAt = System.currentTimeMillis(), - totalViewTime = 10, - documentType = DocumentType.Regular, - previewImageUrl = null - ) - val historyGroup = HistoryMetadataGroup( - title = "mozilla", - historyMetadata = listOf(historyEntry) - ) - interactor.onHistoryMetadataGroupClicked(historyGroup) - verify { historyMetadataController.handleHistoryMetadataGroupClicked(historyGroup) } - } - @Test fun `WHEN a recently saved bookmark is clicked THEN the selected bookmark is handled`() { val bookmark = BookmarkNode( From 15d1a0aa1773ae868980011297a3cd53b436ec1d Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Wed, 29 Sep 2021 11:31:28 -0400 Subject: [PATCH 329/517] For #21551 - Add delete history metadata in the History view Co-authored-by: Christian Sadilek --- .../history/PagedHistoryProvider.kt | 41 ++++++++-- .../library/history/HistoryController.kt | 14 +--- .../fenix/library/history/HistoryFragment.kt | 77 +++++++++++++++---- .../library/history/HistoryFragmentStore.kt | 8 +- .../viewholders/HistoryListItemViewHolder.kt | 2 - .../HistoryMetadataGroupFragment.kt | 24 ++++-- .../HistoryMetadataGroupFragmentStore.kt | 9 +++ .../HistoryMetadataGroupController.kt | 31 ++++++++ .../HistoryMetadataGroupInteractor.kt | 14 ++-- .../HistoryMetadataGroupItemViewHolder.kt | 10 ++- .../HistoryMetadataGroupFragmentStoreTest.kt | 21 +++++ .../HistoryMetadataGroupControllerTest.kt | 48 +++++++++++- 12 files changed, 243 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/history/PagedHistoryProvider.kt b/app/src/main/java/org/mozilla/fenix/components/history/PagedHistoryProvider.kt index 90f17434fb..2a88c2be6e 100644 --- a/app/src/main/java/org/mozilla/fenix/components/history/PagedHistoryProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/components/history/PagedHistoryProvider.kt @@ -13,6 +13,8 @@ import org.mozilla.fenix.library.history.History import org.mozilla.fenix.library.history.toHistoryMetadata import org.mozilla.fenix.perf.runBlockingIncrement +private const val BUFFER_TIME = 15000 /* 15 seconds in ms */ + /** * An Interface for providing a paginated list of [History]. */ @@ -35,7 +37,7 @@ class DefaultPagedHistoryProvider( private val showHistorySearchGroups: Boolean = FeatureFlags.showHistorySearchGroups, ) : PagedHistoryProvider { - private var historyGroups: List? = null + @Volatile private var historyGroups: List? = null @Suppress("LongMethod") override fun getHistory( @@ -52,7 +54,6 @@ class DefaultPagedHistoryProvider( // We need to refetch all the history metadata if the offset resets back at 0 // in the case of a pull to refresh. if (historyGroups == null || offset == 0L) { - historyGroups = historyStorage.getHistoryMetadataSince(Long.MIN_VALUE) .sortedByDescending { it.createdAt } .filter { it.key.searchTerm != null } @@ -90,6 +91,36 @@ class DefaultPagedHistoryProvider( } } + /** + * Returns the [History.Regular] corresponding to the given [History.Metadata] item. + * + * @param historyMetadata The [History.Metadata] to match. + * @return the [History.Regular] corresponding to the given [History.Metadata] item or null. + */ + suspend fun getMatchingHistory(historyMetadata: History.Metadata): VisitInfo? { + val history = historyStorage.getDetailedVisits( + start = historyMetadata.visitedAt - BUFFER_TIME, + end = historyMetadata.visitedAt, + excludeTypes = listOf( + VisitType.NOT_A_VISIT, + VisitType.DOWNLOAD, + VisitType.REDIRECT_TEMPORARY, + VisitType.RELOAD, + VisitType.EMBED, + VisitType.FRAMED_LINK, + VisitType.REDIRECT_PERMANENT + ) + ) + return history.lastOrNull { it.url == historyMetadata.url } + } + + /** + * Clears the history groups to refetch the most history metadata after any changes. + */ + fun clearHistoryGroups() { + historyGroups = null + } + @Suppress("MagicNumber") private suspend fun getHistoryAndSearchGroups( offset: Long, @@ -115,7 +146,7 @@ class DefaultPagedHistoryProvider( // History metadata items are recorded after their associated visited info, we add an // additional buffer time to the most recent visit to account for a history group // appearing as the most recent item. - val visitedAtBuffer = if (offset == 0L) 15000 else 0 /* 15 seconds in ms */ + val visitedAtBuffer = if (offset == 0L) BUFFER_TIME else 0 // Get the history groups that fit within the range of visited times in the current history // items. @@ -129,8 +160,8 @@ class DefaultPagedHistoryProvider( } val historyMetadata = historyGroupsInOffset.flatMap { it.items } - // Add all items that are not in a group filtering out any matches with a history metadata - // item. + // Add all history items that are not in a group filtering out any matches with a history + // metadata item. result.addAll(history.filter { item -> historyMetadata.find { it.url == item.url } == null }) // Filter history metadata items with no view time and dedupe by url. diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryController.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryController.kt index 7e033e69e7..5586a75042 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryController.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryController.kt @@ -33,7 +33,7 @@ class DefaultHistoryController( private val openToBrowser: (item: History.Regular) -> Unit, private val displayDeleteAll: () -> Unit, private val invalidateOptionsMenu: () -> Unit, - private val deleteHistoryItems: (Set) -> Unit, + private val deleteHistoryItems: (Set) -> Unit, private val syncHistory: suspend () -> Unit, private val metrics: MetricController ) : HistoryController { @@ -59,15 +59,11 @@ class DefaultHistoryController( return } - if (item is History.Regular) { - store.dispatch(HistoryFragmentAction.AddItemForRemoval(item)) - } + store.dispatch(HistoryFragmentAction.AddItemForRemoval(item)) } override fun handleDeselect(item: History) { - if (item is History.Regular) { - store.dispatch(HistoryFragmentAction.RemoveItemForRemoval(item)) - } + store.dispatch(HistoryFragmentAction.RemoveItemForRemoval(item)) } override fun handleBackPressed(): Boolean { @@ -88,9 +84,7 @@ class DefaultHistoryController( } override fun handleDeleteSome(items: Set) { - items.filterIsInstance().let { - deleteHistoryItems.invoke(it.toSet()) - } + deleteHistoryItems.invoke(items) } override fun handleRequestSync() { diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt index a79cbdbd0b..4bc1754354 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt @@ -53,6 +53,8 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandler { private lateinit var historyStore: HistoryFragmentStore private lateinit var historyInteractor: HistoryInteractor private lateinit var viewModel: HistoryViewModel + private lateinit var historyProvider: DefaultPagedHistoryProvider + private var undoScope: CoroutineScope? = null private var pendingHistoryDeletionJob: (suspend () -> Unit)? = null @@ -65,7 +67,7 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandler { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View { _binding = FragmentHistoryBinding.inflate(inflater, container, false) val view = binding.root @@ -110,9 +112,9 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandler { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - viewModel = HistoryViewModel( - historyProvider = DefaultPagedHistoryProvider(requireComponents.core.historyStorage) - ) + historyProvider = DefaultPagedHistoryProvider(requireComponents.core.historyStorage) + + viewModel = HistoryViewModel(historyProvider) viewModel.userHasHistory.observe( this, @@ -126,7 +128,7 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandler { setHasOptionsMenu(true) } - private fun deleteHistoryItems(items: Set) { + private fun deleteHistoryItems(items: Set) { updatePendingHistoryToDelete(items) undoScope = CoroutineScope(IO) undoScope?.allowUndo( @@ -178,8 +180,28 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandler { override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { R.id.share_history_multi_select -> { val selectedHistory = historyStore.state.mode.selectedItems - val shareTabs = selectedHistory.map { ShareData(url = it.url, title = it.title) } + val shareTabs = mutableListOf() + + for (history in selectedHistory) { + when (history) { + is History.Regular -> { + shareTabs.add(ShareData(url = history.url, title = history.title)) + } + is History.Group -> { + shareTabs.addAll( + history.items.map { metadata -> + ShareData(url = metadata.url, title = metadata.title) + } + ) + } + else -> { + // no-op, There is no [History.Metadata] in the HistoryFragment. + } + } + } + share(shareTabs) + true } R.id.delete_history_multi_select -> { @@ -227,15 +249,19 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandler { ) } - private fun getMultiSelectSnackBarMessage(historyItems: Set): String { + private fun getMultiSelectSnackBarMessage(historyItems: Set): String { return if (historyItems.size > 1) { getString(R.string.history_delete_multiple_items_snackbar) } else { + val historyItem = historyItems.first() + String.format( - requireContext().getString( - R.string.history_delete_single_item_snackbar - ), - historyItems.first().url.toShortUrl(requireComponents.publicSuffixList) + requireContext().getString(R.string.history_delete_single_item_snackbar), + if (historyItem is History.Regular) { + historyItem.url.toShortUrl(requireComponents.publicSuffixList) + } else { + historyItem.title + } ) } } @@ -318,14 +344,35 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandler { ) } - private fun getDeleteHistoryItemsOperation(items: Set): (suspend () -> Unit) { + private fun getDeleteHistoryItemsOperation(items: Set): (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) + + if (item is History.Regular) { + core.historyStorage.deleteVisit( + url = item.url, + timestamp = item.visitedAt + ) + } else if (item is History.Group) { + for (historyMetadata in item.items) { + historyProvider.getMatchingHistory(historyMetadata)?.let { + core.historyStorage.deleteVisit( + url = it.url, + timestamp = it.visitTime + ) + } + } + + core.historyStorage.deleteHistoryMetadata( + searchTerm = item.title + ) + + historyProvider.clearHistoryGroups() + } } } historyStore.dispatch(HistoryFragmentAction.ExitDeletionMode) @@ -334,13 +381,13 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandler { } } - private fun updatePendingHistoryToDelete(items: Set) { + private fun updatePendingHistoryToDelete(items: Set) { pendingHistoryDeletionJob = getDeleteHistoryItemsOperation(items) val ids = items.map { item -> item.visitedAt }.toSet() historyStore.dispatch(HistoryFragmentAction.AddPendingDeletionSet(ids)) } - private fun undoPendingDeletion(items: Set) { + private fun undoPendingDeletion(items: Set) { pendingHistoryDeletionJob = null val ids = items.map { item -> item.visitedAt }.toSet() historyStore.dispatch(HistoryFragmentAction.UndoPendingDeletionSet(ids)) diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragmentStore.kt index 5860661bf3..587e292f80 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragmentStore.kt @@ -105,8 +105,8 @@ class HistoryFragmentStore(initialState: HistoryFragmentState) : */ sealed class HistoryFragmentAction : Action { object ExitEditMode : HistoryFragmentAction() - data class AddItemForRemoval(val item: History.Regular) : HistoryFragmentAction() - data class RemoveItemForRemoval(val item: History.Regular) : HistoryFragmentAction() + data class AddItemForRemoval(val item: History) : HistoryFragmentAction() + data class RemoveItemForRemoval(val item: History) : HistoryFragmentAction() data class AddPendingDeletionSet(val itemIds: Set) : HistoryFragmentAction() data class UndoPendingDeletionSet(val itemIds: Set) : HistoryFragmentAction() object EnterDeletionMode : HistoryFragmentAction() @@ -127,11 +127,11 @@ data class HistoryFragmentState( val isDeletingItems: Boolean ) : State { sealed class Mode { - open val selectedItems = emptySet() + open val selectedItems = emptySet() object Normal : Mode() object Syncing : Mode() - data class Editing(override val selectedItems: Set) : Mode() + data class Editing(override val selectedItems: Set) : Mode() } } diff --git a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt index e81bb1d102..8e4a177282 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt @@ -53,8 +53,6 @@ class HistoryListItemViewHolder( binding.historyLayout.visibility = View.VISIBLE } - binding.historyLayout.overflowView.isVisible = item !is History.Group - binding.historyLayout.titleView.text = item.title binding.historyLayout.urlView.text = Do exhaustive when (item) { diff --git a/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragment.kt b/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragment.kt index d103364d25..fdf68108f4 100644 --- a/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragment.kt @@ -12,6 +12,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -35,7 +36,8 @@ import org.mozilla.fenix.library.historymetadata.view.HistoryMetadataGroupView /** * Displays a list of history metadata items for a history metadata search group. */ -class HistoryMetadataGroupFragment : LibraryPageFragment(), UserInteractionHandler { +class HistoryMetadataGroupFragment : + LibraryPageFragment(), UserInteractionHandler { private lateinit var historyMetadataGroupStore: HistoryMetadataGroupFragmentStore private lateinit var interactor: HistoryMetadataGroupInteractor @@ -43,6 +45,8 @@ class HistoryMetadataGroupFragment : LibraryPageFragment(), Us private var _historyMetadataGroupView: HistoryMetadataGroupView? = null private val historyMetadataGroupView: HistoryMetadataGroupView get() = _historyMetadataGroupView!! + private var _binding: FragmentHistoryMetadataGroupBinding? = null + private val binding get() = _binding!! private val args by navArgs() @@ -54,9 +58,9 @@ class HistoryMetadataGroupFragment : LibraryPageFragment(), Us override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val binding = FragmentHistoryMetadataGroupBinding.inflate(inflater, container, false) + savedInstanceState: Bundle?, + ): View { + _binding = FragmentHistoryMetadataGroupBinding.inflate(inflater, container, false) historyMetadataGroupStore = StoreProvider.get(this) { HistoryMetadataGroupFragmentStore( @@ -70,7 +74,9 @@ class HistoryMetadataGroupFragment : LibraryPageFragment(), Us controller = DefaultHistoryMetadataGroupController( activity = activity as HomeActivity, store = historyMetadataGroupStore, - navController = findNavController() + navController = findNavController(), + scope = lifecycleScope, + searchTerm = args.title ) ) @@ -117,7 +123,7 @@ class HistoryMetadataGroupFragment : LibraryPageFragment(), Us true } R.id.delete_history_multi_select -> { - interactor.onDeleteMenuItem(selectedItems) + interactor.onDelete(selectedItems) true } R.id.open_history_in_new_tabs_multi_select -> { @@ -152,10 +158,12 @@ class HistoryMetadataGroupFragment : LibraryPageFragment(), Us override fun onDestroyView() { super.onDestroyView() _historyMetadataGroupView = null + _binding = null } - override val selectedItems: Set get() = - historyMetadataGroupStore.state.items.filter { it.selected }.toSet() + override val selectedItems: Set + get() = + historyMetadataGroupStore.state.items.filter { it.selected }.toSet() override fun onBackPressed(): Boolean = interactor.onBackPressed(selectedItems) diff --git a/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragmentStore.kt index b3825e6c9c..b267f1f707 100644 --- a/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragmentStore.kt @@ -29,6 +29,8 @@ sealed class HistoryMetadataGroupFragmentAction : Action { data class Select(val item: History.Metadata) : HistoryMetadataGroupFragmentAction() data class Deselect(val item: History.Metadata) : HistoryMetadataGroupFragmentAction() object DeselectAll : HistoryMetadataGroupFragmentAction() + data class Delete(val item: History.Metadata) : HistoryMetadataGroupFragmentAction() + object DeleteAll : HistoryMetadataGroupFragmentAction() } /** @@ -82,5 +84,12 @@ private fun historyStateReducer( items = state.items.toMutableList() .map { it.copy(selected = false) } ) + is HistoryMetadataGroupFragmentAction.Delete -> { + val items = state.items.toMutableList() + items.remove(action.item) + state.copy(items = items) + } + is HistoryMetadataGroupFragmentAction.DeleteAll -> + state.copy(items = emptyList()) } } diff --git a/app/src/main/java/org/mozilla/fenix/library/historymetadata/controller/HistoryMetadataGroupController.kt b/app/src/main/java/org/mozilla/fenix/library/historymetadata/controller/HistoryMetadataGroupController.kt index dd64d60367..a42bd00ded 100644 --- a/app/src/main/java/org/mozilla/fenix/library/historymetadata/controller/HistoryMetadataGroupController.kt +++ b/app/src/main/java/org/mozilla/fenix/library/historymetadata/controller/HistoryMetadataGroupController.kt @@ -5,9 +5,12 @@ package org.mozilla.fenix.library.historymetadata.controller import androidx.navigation.NavController +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import mozilla.components.concept.engine.prompt.ShareData import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.ext.components import org.mozilla.fenix.library.history.History import org.mozilla.fenix.library.historymetadata.HistoryMetadataGroupFragmentAction import org.mozilla.fenix.library.historymetadata.HistoryMetadataGroupFragmentDirections @@ -53,6 +56,16 @@ interface HistoryMetadataGroupController { * @param items The set of [History]s to share. */ fun handleShare(items: Set) + + /** + * Deletes the given history metadata [items] from storage. + */ + fun handleDelete(items: Set) + + /** + * Deletes all the history metadata items in this group. + */ + fun handleDeleteAll() } /** @@ -62,6 +75,8 @@ class DefaultHistoryMetadataGroupController( private val activity: HomeActivity, private val store: HistoryMetadataGroupFragmentStore, private val navController: NavController, + private val scope: CoroutineScope, + private val searchTerm: String, ) : HistoryMetadataGroupController { override fun handleOpen(item: History.Metadata) { @@ -97,4 +112,20 @@ class DefaultHistoryMetadataGroupController( ) ) } + + override fun handleDelete(items: Set) { + scope.launch { + items.forEach { + store.dispatch(HistoryMetadataGroupFragmentAction.Delete(it)) + activity.components.core.historyStorage.deleteHistoryMetadata(it.historyMetadataKey) + } + } + } + + override fun handleDeleteAll() { + scope.launch { + store.dispatch(HistoryMetadataGroupFragmentAction.DeleteAll) + activity.components.core.historyStorage.deleteHistoryMetadata(searchTerm) + } + } } diff --git a/app/src/main/java/org/mozilla/fenix/library/historymetadata/interactor/HistoryMetadataGroupInteractor.kt b/app/src/main/java/org/mozilla/fenix/library/historymetadata/interactor/HistoryMetadataGroupInteractor.kt index 4af92e3abe..9e8953404f 100644 --- a/app/src/main/java/org/mozilla/fenix/library/historymetadata/interactor/HistoryMetadataGroupInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/library/historymetadata/interactor/HistoryMetadataGroupInteractor.kt @@ -21,15 +21,15 @@ interface HistoryMetadataGroupInteractor : SelectionInteractor fun onBackPressed(items: Set): Boolean /** - * Deletes the given set of history [items] that are selected. Called when a user clicks on the - * "Delete" menu item. + * Deletes the given set of history metadata [items]. Called when a user clicks on the + * "Delete" menu item or the "x" button associated with a history metadata item. * * @param items The set of [History]s to delete. */ - fun onDeleteMenuItem(items: Set) + fun onDelete(items: Set) /** - * Deletes the all the history items in the history metadata group. Called when a user clicks + * Deletes all the history items in the history metadata group. Called when a user clicks * on the "Delete history" menu item. */ fun onDeleteAllMenuItem() @@ -66,12 +66,12 @@ class DefaultHistoryMetadataGroupInteractor( return controller.handleBackPressed(items) } - override fun onDeleteMenuItem(items: Set) { - // no-op + override fun onDelete(items: Set) { + controller.handleDelete(items) } override fun onDeleteAllMenuItem() { - // no-op + controller.handleDeleteAll() } override fun onShareMenuItem(items: Set) { diff --git a/app/src/main/java/org/mozilla/fenix/library/historymetadata/view/HistoryMetadataGroupItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/historymetadata/view/HistoryMetadataGroupItemViewHolder.kt index cb536f32e3..1f5c9fc65d 100644 --- a/app/src/main/java/org/mozilla/fenix/library/historymetadata/view/HistoryMetadataGroupItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/historymetadata/view/HistoryMetadataGroupItemViewHolder.kt @@ -27,6 +27,14 @@ class HistoryMetadataGroupItemViewHolder( private var item: History.Metadata? = null + init { + binding.historyLayout.overflowView.setImageResource(R.drawable.ic_close) + binding.historyLayout.overflowView.setOnClickListener { + val item = this.item ?: return@setOnClickListener + interactor.onDelete(setOf(item)) + } + } + fun bind(item: History.Metadata) { binding.historyLayout.titleView.text = item.title binding.historyLayout.urlView.text = item.url @@ -38,8 +46,6 @@ class HistoryMetadataGroupItemViewHolder( binding.historyLayout.loadFavicon(item.url) } - binding.historyLayout.overflowView.setImageResource(R.drawable.ic_close) - if (selectionHolder.selectedItems.isEmpty()) { binding.historyLayout.overflowView.showAndEnable() } else { diff --git a/app/src/test/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragmentStoreTest.kt index 69a7751933..8552851bb9 100644 --- a/app/src/test/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragmentStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragmentStoreTest.kt @@ -82,4 +82,25 @@ class HistoryMetadataGroupFragmentStoreTest { assertFalse(store.state.items[0].selected) assertFalse(store.state.items[1].selected) } + + @Test + fun `Test deleting an item in HistoryMetadataGroupFragmentStore`() = runBlocking { + val items = listOf(mozillaHistoryMetadataItem, firefoxHistoryMetadataItem) + + store.dispatch(HistoryMetadataGroupFragmentAction.UpdateHistoryItems(items)).join() + store.dispatch(HistoryMetadataGroupFragmentAction.Delete(mozillaHistoryMetadataItem)).join() + + assertEquals(1, store.state.items.size) + assertEquals(firefoxHistoryMetadataItem, store.state.items.first()) + } + + @Test + fun `Test deleting all items in HistoryMetadataGroupFragmentStore`() = runBlocking { + val items = listOf(mozillaHistoryMetadataItem, firefoxHistoryMetadataItem) + + store.dispatch(HistoryMetadataGroupFragmentAction.UpdateHistoryItems(items)).join() + store.dispatch(HistoryMetadataGroupFragmentAction.DeleteAll).join() + + assertEquals(0, store.state.items.size) + } } diff --git a/app/src/test/java/org/mozilla/fenix/library/historymetadata/controller/HistoryMetadataGroupControllerTest.kt b/app/src/test/java/org/mozilla/fenix/library/historymetadata/controller/HistoryMetadataGroupControllerTest.kt index 064722edf9..a579d1ad77 100644 --- a/app/src/test/java/org/mozilla/fenix/library/historymetadata/controller/HistoryMetadataGroupControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/library/historymetadata/controller/HistoryMetadataGroupControllerTest.kt @@ -5,13 +5,19 @@ package org.mozilla.fenix.library.historymetadata.controller import androidx.navigation.NavController +import io.mockk.coVerify +import io.mockk.every import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.runBlockingTest +import mozilla.components.browser.storage.sync.PlacesHistoryStorage import mozilla.components.concept.engine.prompt.ShareData import mozilla.components.concept.storage.HistoryMetadataKey import mozilla.components.support.test.rule.MainCoroutineRule +import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before @@ -20,6 +26,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.directionsEq import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.library.history.History @@ -32,6 +39,7 @@ import org.mozilla.fenix.library.historymetadata.HistoryMetadataGroupFragmentSto class HistoryMetadataGroupControllerTest { private val testDispatcher = TestCoroutineDispatcher() + private val scope = TestCoroutineScope() @get:Rule val coroutinesTestRule = MainCoroutineRule(testDispatcher) @@ -39,14 +47,17 @@ class HistoryMetadataGroupControllerTest { private val activity: HomeActivity = mockk(relaxed = true) private val store: HistoryMetadataGroupFragmentStore = mockk(relaxed = true) private val navController: NavController = mockk(relaxed = true) + private val historyStorage: PlacesHistoryStorage = mockk(relaxed = true) + private val searchTerm = "mozilla" + private val historyMetadataKey = HistoryMetadataKey("http://www.mozilla.com", searchTerm, null) private val mozillaHistoryMetadataItem = History.Metadata( id = 0, title = "Mozilla", url = "mozilla.org", visitedAt = 0, totalViewTime = 1, - historyMetadataKey = HistoryMetadataKey("http://www.mozilla.com", "mozilla", null) + historyMetadataKey = historyMetadataKey ) private val firefoxHistoryMetadataItem = History.Metadata( id = 0, @@ -54,7 +65,7 @@ class HistoryMetadataGroupControllerTest { url = "firefox.com", visitedAt = 0, totalViewTime = 1, - historyMetadataKey = HistoryMetadataKey("http://www.firefox.com", "mozilla", null) + historyMetadataKey = historyMetadataKey ) private lateinit var controller: DefaultHistoryMetadataGroupController @@ -64,8 +75,17 @@ class HistoryMetadataGroupControllerTest { controller = DefaultHistoryMetadataGroupController( activity = activity, store = store, - navController = navController + navController = navController, + scope = scope, + searchTerm = "mozilla" ) + + every { activity.components.core.historyStorage } returns historyStorage + } + + @After + fun cleanUp() { + scope.cleanupTestCoroutines() } @Test @@ -132,4 +152,26 @@ class HistoryMetadataGroupControllerTest { ) } } + + @Test + fun handleDelete() = testDispatcher.runBlockingTest { + controller.handleDelete(setOf(mozillaHistoryMetadataItem, firefoxHistoryMetadataItem)) + + coVerify { + store.dispatch(HistoryMetadataGroupFragmentAction.Delete(mozillaHistoryMetadataItem)) + store.dispatch(HistoryMetadataGroupFragmentAction.Delete(firefoxHistoryMetadataItem)) + historyStorage.deleteHistoryMetadata(mozillaHistoryMetadataItem.historyMetadataKey) + historyStorage.deleteHistoryMetadata(firefoxHistoryMetadataItem.historyMetadataKey) + } + } + + @Test + fun handleDeleteAll() = testDispatcher.runBlockingTest { + controller.handleDeleteAll() + + coVerify { + store.dispatch(HistoryMetadataGroupFragmentAction.DeleteAll) + historyStorage.deleteHistoryMetadata(searchTerm) + } + } } From 10923832312e49fe5ec9ccd50ff5ef7f724f0bf7 Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Thu, 30 Sep 2021 19:13:22 -0400 Subject: [PATCH 330/517] No issue: Make sure jump back in group always have more than one tab --- .../org/mozilla/fenix/ext/BrowserState.kt | 2 +- app/src/main/res/values/strings.xml | 8 +- .../org/mozilla/fenix/ext/BrowserStateTest.kt | 53 ++++++++++++- .../fenix/home/RecentTabsListFeatureTest.kt | 78 +++++++++++++++++-- 4 files changed, 125 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt index 8b8851172f..0e01cec0c4 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt @@ -67,7 +67,7 @@ val BrowserState.inProgressMediaTab: TabSessionState? */ val BrowserState.lastSearchGroup: RecentTab.SearchGroup? get() { - val tabGroup = normalTabs.toSearchGroup().lastOrNull() ?: return null + val tabGroup = normalTabs.toSearchGroup().lastOrNull { it.tabs.count() > 1 } ?: return null val firstTab = tabGroup.tabs.firstOrNull() ?: return null return RecentTab.SearchGroup( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dbcfd1757d..ac4ef228b5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -120,10 +120,12 @@ Show all Show all recent tabs button - + Your search for \"%1$s\" - - %1$s sites + + Sites: %1$s Revisit your latest searches from your homepage and tabs. + + Your personalized Firefox homepage now makes it easier to pick up where you left off. Find your recent tabs, bookmarks, and search results. + Open a new Firefox tab From 782aea2d5bdc74eee718529ff53218949b4e2389 Mon Sep 17 00:00:00 2001 From: Jeff Klukas Date: Thu, 30 Sep 2021 16:03:02 -0400 Subject: [PATCH 332/517] Update language in adjust docs Cleaning up a bit in advance of this document being linked from the [Privacy Notice](https://www.mozilla.org/en-US/privacy/firefox/) as a replacement for the no-longer-existing https://firefox-source-docs.mozilla.org/mobile/android/adjust.html --- docs/adjust.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/adjust.md b/docs/adjust.md index 959250a759..87e57ef188 100644 --- a/docs/adjust.md +++ b/docs/adjust.md @@ -1,6 +1,6 @@ -Firefox Preview tracks certain types of installs using a third-party install tracking framework called Adjust. The intention is to determine the origin of Firefox Preview installs by answering the question, “Did this user on this device install Firefox Preview in response to a specific advertising campaign performed by Mozilla?” +Firefox for Android tracks certain types of installs using a third-party install tracking framework called Adjust. The intention is to determine the origin of Firefox for Android installs by answering the question, “Did this user on this device install Firefox for Android in response to a specific advertising campaign performed by Mozilla?” -The framework consists of a software development kit (SDK) linked into Firefox Preview and a data-collecting Internet service backend run by the German company [adjust GmbH](https://www.adjust.com). The Adjust SDK is open source and MIT licensed. It is hosted at [https://github.com/adjust/android_sdk](https://github.com/adjust/android_sdk). Firefox Preview pulls in an unmodified copy of the SDK using Gradle. The [Dependencies.kt](https://github.com/mozilla-mobile/fenix/blob/main/buildSrc/src/main/java/Dependencies.kt#L39) contains the version of the framework that is being used. The SDK is documented at [https://docs.adjust.com](https://docs.adjust.com). +The framework consists of a software development kit (SDK) linked into Firefox for Android and a data-collecting Internet service backend run by the German company [adjust GmbH](https://www.adjust.com). The Adjust SDK is open source and MIT licensed. It is hosted at [https://github.com/adjust/android_sdk](https://github.com/adjust/android_sdk). Firefox for Android pulls in an unmodified copy of the SDK using Gradle. The [Dependencies.kt](https://github.com/mozilla-mobile/fenix/blob/main/buildSrc/src/main/java/Dependencies.kt) contains the version of the framework that is being used. The SDK is documented at [https://docs.adjust.com](https://docs.adjust.com). ## Adjust Integration From b9b1e984b5eb338aa61c2ebfa331eafa8cfac09b Mon Sep 17 00:00:00 2001 From: Mozilla L10n Automation Bot Date: Fri, 1 Oct 2021 00:05:51 +0000 Subject: [PATCH 333/517] Import l10n. --- app/src/main/res/values-be/strings.xml | 47 ++++--- app/src/main/res/values-co/strings.xml | 149 +++++++++++++++------ app/src/main/res/values-cy/strings.xml | 52 +++---- app/src/main/res/values-en-rCA/strings.xml | 66 ++++++--- app/src/main/res/values-es-rAR/strings.xml | 4 +- app/src/main/res/values-fr/strings.xml | 3 + app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-pa-rIN/strings.xml | 109 ++++++++++++--- app/src/main/res/values-tr/strings.xml | 4 +- 9 files changed, 311 insertions(+), 125 deletions(-) diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 24a30dc5f8..88d29a983e 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -51,7 +51,7 @@ - Нядаўна захаваныя + Нядаўна захаваныя Нядаўна захаваныя закладкі @@ -384,6 +384,9 @@ Калекцыя дадаткаў зменена. Выхад з праграмы, каб прымяніць змены… + + Pocket + Дадатак не падтрымліваецца @@ -754,15 +757,15 @@ Ачысціць - Капіраваць + Капіраваць - Падзяліцца + Падзяліцца - Адкрыць у новай картцы + Адкрыць у новай картцы - Адкрыць у прыватнай картцы + Адкрыць у прыватнай картцы - Выдаліць + Выдаліць Выбрана: %1$d @@ -1246,12 +1249,12 @@ Ужо маеце ўліковы запіс? - Паглядзіце, што новага + Паглядзіце, што новага - Маеце пытанні па абноўленым выглядзе %s? Хочаце ведаць, што змянілася? + Маеце пытанні па абноўленым выглядзе %s? Хочаце ведаць, што змянілася? - Адказы тут + Адказы тут Сінхранізуйце Firefox паміж прыладамі @@ -1291,14 +1294,14 @@ Майце панэль інструментаў пад рукой. Пакіньце яе ўнізе або перамясціце ўверх. - Аглядайце прыватна + Аглядайце прыватна - Адкрыць прыватную картку адзін раз: Націсніце значок %s. + Адкрыць прыватную картку адзін раз: Націсніце значок %s. - Адкрываць прыватныя карткі кожны раз: Абнавіце налады прыватнага аглядання. + Адкрываць прыватныя карткі кожны раз: Абнавіце налады прыватнага аглядання. - Адкрыць налады + Адкрыць налады Ваша прыватнасць Аўтазапаўненне + + Дадаць лагін + Сінхранізацыя лагінаў @@ -1766,9 +1772,9 @@ %1$s на УКЛ]]> - Бяспечнае злучэнне + Бяспечнае злучэнне - Не бяспечнае злучэнне + Не бяспечнае злучэнне Ці ўпэўнены Вы, што хочаце выдаліць усе дазволы на ўсіх сайтах? @@ -1810,8 +1816,12 @@ Адмяніць змены Рэдагаванне - + + Дадаць новы лагін + Патрабуецца пароль + + Патрабуецца імя карыстальніка Галасавы пошук @@ -1820,6 +1830,9 @@ Лагін з такім імем карыстальніка ўжо існуе + + https://www.example.com + Падключыць іншую прыладу. @@ -1847,7 +1860,7 @@ OK, зразумела - Паказваць найбольш наведаныя сайты + Паказваць найбольш наведаныя сайты Назва diff --git a/app/src/main/res/values-co/strings.xml b/app/src/main/res/values-co/strings.xml index 0e9e958dc8..6430adf3b5 100644 --- a/app/src/main/res/values-co/strings.xml +++ b/app/src/main/res/values-co/strings.xml @@ -52,7 +52,9 @@ - Arregistrate pocu fà + Arregistrate pocu fà + + Indettati pocu fà Indette arregistrate pocu fà @@ -126,7 +128,14 @@ - Esplurazioni recente + Esplurazioni recente + + + Visitati pocu fà + + Caccià @@ -202,6 +211,8 @@ Mudificà + + Persunalizà l’accolta Screnu d’accolta @@ -286,7 +297,7 @@ Diritti di l’utilizatore - Parolle d’entrata + Parolle d’intesa Carte bancarie è indirizzi @@ -397,6 +408,18 @@ Cullezzione di moduli addiziunale mudificata. Chjusura di l’appiecazione per piglialla in contu… + + + Rivene in st’unghjetta + + Arregistrate pocu fà + + Indettati pocu fà + + Visitati pocu fà + + Pocket + Stu modulu addiziunale ùn hè micca accettatu @@ -479,7 +502,7 @@ Statistiche d’impiegu è dati tecnichi - Sparte cù Mozilla i dati d’andamentu, d’impiegu, di materiale è di persunalizazione di u vostru navigatore per aiutacci à amendà %1$s + Sparte cù Mozilla i dati di perfurmenza, d’impiegu, di materiale è di persunalizazione di u vostru navigatore per aiutacci à amendà %1$s Dati cummerciali @@ -583,6 +606,13 @@ Chjode + + %d situ + + %d siti + Unghjette chjose pocu fà @@ -756,6 +786,9 @@ Arregistrà + + Altri + Squassà a cronolugia @@ -768,15 +801,15 @@ Squassà - Cupià + Cupià - Sparte + Sparte - Apre in una nova unghjetta + Apre in una nova unghjetta - Apre in un’unghjetta privata + Apre in un’unghjetta privata - Squassà + Squassà %1$d selezziunatu(i) @@ -1254,16 +1287,16 @@ Avete dighjà un contu ? - Fighjate ciò chì hè novu + Fighjate ciò chì hè novu - Avete dumande apprupositu di u rinnovu di %s ? Vulete sapè ciò chì hà cambiatu ? + Avete dumande apprupositu di u rinnovu di %s ? Vulete sapè ciò chì hà cambiatu ? - Truvate risposte quì + Truvate risposte quì Sincrunizate Firefox trà i vostri apparechji - Impurtate e vostre indette, cronolugia è parolle d’entrata in Firefox nant’à st’apparechju. + Impurtate e vostre indette, cronolugia è parolle d’intesa in Firefox nant’à st’apparechju. @@ -1287,7 +1320,7 @@ Classica (predefinita) - Equilibratu trà a cunfidenzialità è a prestazione. E pagine si caricanu nurmalmente. + Equilibratu trà a cunfidenzialità è a perfurmenza. E pagine si caricanu nurmalmente. Severa (ricumandata) @@ -1299,14 +1332,14 @@ Piazzate a barra d’attrezzi à purtata di manu. Lasciatela quaghjò o dispiazzatela quassù. - Navigazione privata + Navigazione privata - Apre in una sola unghjetta privata : picchichjate nant’à l’icona %s. + Apre in una sola unghjetta privata : picchichjate nant’à l’icona %s. - Apre unghjette private ogni volta : definite e vostre preferenze di navigazione privata. + Apre unghjette private ogni volta : definite e vostre preferenze di navigazione privata. - Apre e preferenze + Apre e preferenze A vostra vita privata Classica (predefinita) - Equilibratu trà a cunfidenzialità è a prestazione. E pagine si caricanu nurmalmente. + Equilibratu trà a cunfidenzialità è a perfurmenza. E pagine si caricanu nurmalmente. Ciò chì hè bluccatu da a prutezzione classica contr’à u spiunagiu @@ -1471,6 +1504,12 @@ Squasseghja i canistrelli definiti da ridirezzione ver di siti web cunnisciuti per u spiunagiu. + + Certi perseguitatori marcati quì sottu sò stati in parte sbluccati nant’à sta pagina perchè vo avete interagite cù elli *. + + Sapene di più + Assistenza @@ -1520,9 +1559,9 @@ Hè faciule d’aghjunghje stu situ nant’à u screnu d’accolta di u vostru apparechju per accedeci direttamente è navigà più prestu cum’è s’ella fussi un’appiecazione. - Identificazioni è parolle d’entrata + Identificazioni è parolle d’intesa - Arregistrà l’identificazioni è e parolle d’entrata + Arregistrà l’identificazioni è e parolle d’intesa Dumandà per arregistrà @@ -1533,11 +1572,14 @@ Riempiimentu autumaticu in %1$s - Riimpiete è arregistrate nomi d’utilizatore è parolle d’entrata in i siti web quandu vi impiegate %1$s. + Riimpiete è arregistrate nomi d’utilizatore è parolle d’intesa in i siti web quandu vi impiegate %1$s. Riempiimentu autumaticu in l’altre appiecazioni - Riimpiete nomi d’utilizatore è parolle d’entrata in l’altre appiecazioni nant’à u vostru apparechju. + Riimpiete nomi d’utilizatore è parolle d’intesa in l’altre appiecazioni nant’à u vostru apparechju. + + + Aghjunghje un’identificazione di cunnessione Sincrunizà l’identificazioni @@ -1556,9 +1598,9 @@ Eccezzioni - L’identificazioni è parolle d’entrata chì ùn sò micca arregistrate seranu affissate quì. + L’identificazioni è parolle d’intesa chì ùn sò micca arregistrate seranu affissate quì. - L’identificazioni è parolle d’entrata ùn seranu micca arregistrate per sti siti. + L’identificazioni è parolle d’intesa ùn seranu micca arregistrate per sti siti. Squassà tutte l’eccezzioni @@ -1573,7 +1615,7 @@ Nome d’utilizatore - Parolla d’entrata + Parolla d’intesa Scrivite torna u vostru codice PIN @@ -1590,33 +1632,35 @@ Ùn arregistrà micca - Parolla d’entrata cupiata ver di u preme’papei + Parolla d’intesa cupiata ver di u preme’papei Nome d’utilizatore cupiatu ver di u preme’papei Situ cupiatu ver di u preme’papei - Cupià a parolla d’entrata + Cupià a parolla d’intesa - Squassà a parolla d’entrata + Squassà a parolla d’intesa Cupià u nome d’utilizatore Squassà u nome d’utilizatore + + Squassà u nome d’ospite Cupià u situ Apre u situ in u navigatore - Affissa a parolla d’entrata + Affissa a parolla d’intesa - Piattà a parolla d’entrata + Piattà a parolla d’intesa Spalancate per affissà l’identificazioni di cunnessione arregistrate - Prutigite e vostre identificazioni di cunnessione è parolle d’entrata + Prutigite e vostre identificazioni di cunnessione è parolle d’intesa - Definite un dissegnu di chjusura, un codice PIN o una parolla d’entrata per prutege e vostre identificazioni di cunnessione è parolle d’entrata arregistrate s’ellu ci era qualchissia chì accidissi à u vostru apparechju. + Definite un dissegnu di chjusura, un codice PIN o una parolla d’intesa per prutege e vostre identificazioni di cunnessione è parolle d’intesa arregistrate s’ellu ci era qualchissia chì accidissi à u vostru apparechju. Dopu @@ -1692,7 +1736,7 @@ Prutege e vostre carte bancarie - Definite un dissegnu di chjusura, un codice PIN o una parolla d’entrata per prutege e vostre carte arregistrate s’ellu ci era qualchissia chì accidissi à u vostru apparechju. + Definite un dissegnu di chjusura, un codice PIN o una parolla d’intesa per prutege e vostre carte arregistrate s’ellu ci era qualchissia chì accidissi à u vostru apparechju. Cunfigurà subitu @@ -1752,7 +1796,7 @@ Benvenuta in un tuttu novu %s - Un navigatore tuttu novu vi aspetta, cù un andamentu è funzioni più belli per aiutavvi à fà ancu più in linea.\n\nAspettate per piacè durante u rinnovu di %s cù + Un navigatore tuttu novu vi aspetta, cù perfurmenze è funzioni più belle per aiutavvi à fà ancu più in linea.\n\nAspettate per piacè durante u rinnovu di %s cù Rinnovu di %s… @@ -1760,7 +1804,7 @@ Migrazione compia - Parolle d’entrata + Parolle d’intesa Per permettelu : @@ -1773,9 +1817,13 @@ %1$s]]> - Cunnessione assicurizata + A cunnessione hè assicurata - Cunnessione micca assicurizata + A cunnessione ùn hè micca assicurata + + Cunnessione assicurizata + + Cunnessione micca assicurizata Site sicuru di vulè caccià tutti i permessi per tutti i siti ? @@ -1808,15 +1856,21 @@ U campu di testu mudifichevule per u nome d’utilizatore di l’identificazione. - U campu di testu mudifichevule per a parolla d’entrata di l’identificazione. + U campu di testu mudifichevule per a parolla d’intesa di l’identificazione. Arregistrà i cambiamenti di l’identificazione. Disfà i cambiamenti Mudificà - - Parolla d’entrata richiesta + + Aghjunghje una nova identificazione di cunnessione + + Parolla d’intesa richiesta + + U nome d’utilizatore hè richiestu + + U nome d’ospite hè richiestu Ricerca vucale @@ -1825,6 +1879,15 @@ L’identificazione di cunnessione cù stu nome d’utilizatore esiste dighjà + + https://www.esempiu.com + + L’indirizzu web deve cuntene « https:// » o « http:// » + + L’indirizzu web deve cuntene « https:// » o « http:// » + + Un nome d’ospite accettevule hè richiestu + Cunnittete un altru apparechju. @@ -1852,8 +1915,10 @@ Iè, aghju capitu + + Affissà i siti principale i più visitati - Affissà i siti visitati aspessu + Affissà i siti visitati aspessu Nome diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index 2a3255e064..dfbaf7552f 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -36,7 +36,7 @@ Gadael y modd aml-ddewis - Cadw’r tabiau hyn i gasgliad + Cadw’r tabiau hyn i Gasgliad Wedi dewis %1$s @@ -45,7 +45,7 @@ Wedi gadael y modd aml-ddewis - Modd aml-ddewis wedi’i ddewis, dewiswch dabiau i’w cadw i gasgliad + Modd aml-ddewis wedi’i ddewis, dewiswch dabiau i’w cadw i Gasgliad Dewiswyd @@ -177,7 +177,7 @@ Tab preifat - Cadw i gasgliad + Cadw i Gasgliad Rhannu @@ -365,7 +365,7 @@ Dangos mewn sesiynau preifat - Dangos awgrymiadau clipfwrdd + Dangos awgrymiadau o’r clipfwrdd Chwilio hanes pori @@ -632,11 +632,11 @@  llaw - Ar ôl un diwrnod + Ar ôl diwrnod - Ar ôl un wythnos + Ar ôl wythnos - Ar ôl un mis + Ar ôl mis @@ -650,11 +650,11 @@ Cau â llaw - Cau ar ôl un diwrnod + Cau ar ôl diwrnod - Cau ar ôl un wythnos + Cau ar ôl wythnos - Cau ar ôl un mis + Cau ar ôl mis @@ -696,7 +696,7 @@ Agor Tabiau - Cadw i gasgliad + Cadw i Gasgliad Dewis @@ -740,7 +740,7 @@ Rhannu tabiau - Cadw tabiau i gasgliad + Cadw tabiau i Gasgliad Dewislen tabiau @@ -754,7 +754,7 @@ Delwedd sesiwn gyfredol - Cadw i gasgliad + Cadw i Gasgliad Dileu casgliad @@ -972,9 +972,9 @@ Gofyn i ganiatáu - Rhwystrwyd + Rhwystro - Caniatawyd + Caniatáu Rhwystrwyd gan Android @@ -1138,7 +1138,7 @@ Golwg - Ychwanegwyd at fy hoff wefannau! + Cadw fel Hoff wefan! Tab preifat wedi’i gau @@ -1185,12 +1185,12 @@ Newid maint ffont awtomatig - Bydd maint y ffont yn cyd-fynd â’h gosodiadau Android. Analluogwch i reoli maint ffont yma. + Bydd maint y ffont yn cyd-fynd â’ch gosodiadau Android. Analluogwch nhw i reoli maint y ffont yma. Dileu data pori - Agor tabiau + Tabiau agored %d tab @@ -1442,7 +1442,7 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Manylion - Rhwystrwyd + Rhwystro Caniatawyd @@ -1558,11 +1558,11 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Awtolanw yn %1$s - Llenwch ac chadw enwau defnyddwyr a chyfrineiriau mewn gwefannau tra’n defnyddio %1$s. + Llenwi ac chadw enwau defnyddwyr a chyfrineiriau mewn gwefannau tra’n defnyddio %1$s. Awtolanw mewn apiau eraill - Llenwch enwau defnyddwyr a chyfrineiriau mewn apiau eraill ar eich dyfais. + Llenwi enwau defnyddwyr a chyfrineiriau mewn apiau eraill ar eich dyfais. Ychwanegu mewngofnod @@ -1668,7 +1668,7 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Cardiau Credyd - Cadw a awtolanw cardiau + Cadw ac awtolanw cardiau Mae data wedi’i amgryptio @@ -1791,7 +1791,7 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Cyfrineiriau - I ganiatáu iddo: + I’w ganiatáu: 1. Ewch i Gosodiadau Android @@ -1820,7 +1820,7 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Ydych chi’n siŵr eich bod am ddileu’r nod tudalen yma? - I’m hoff wefannau + Cadw fel Hoff wefan Dilyswyd gan: %1$s @@ -1891,14 +1891,14 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad - Wedi cyrraedd terfyn nifer eich hoff wefannau + Wedi cyrraedd terfyn nifer eich Hoff wefannau I ychwanegu hoff wefan newydd, tynnwch un. Pwyswch a dal y wefan a dewis tynnu. Iawn, Wedi deall! - Dangos yr hoff wefannau yr ymwelwyd â nhw amlaf + Dangos yr Hoff wefannau yr ymwelwyd â nhw amlaf Dangos y gwefannau yr ymwelwyd â nhw amlaf diff --git a/app/src/main/res/values-en-rCA/strings.xml b/app/src/main/res/values-en-rCA/strings.xml index f485a165fc..40ac30cffa 100644 --- a/app/src/main/res/values-en-rCA/strings.xml +++ b/app/src/main/res/values-en-rCA/strings.xml @@ -51,7 +51,9 @@ - Recently saved + Recently saved + + Recently bookmarked Recently saved bookmarks @@ -122,7 +124,14 @@ - Past explorations + Past explorations + + + Recently visited + + Remove @@ -198,6 +207,8 @@ Edit + + Customize home Home screen @@ -389,6 +400,18 @@ Add-on collection modified. Quitting the application to apply changes… + + + Jump back in + + Recently saved + + Recently bookmarked + + Recently visited + + Pocket + Add-on is not supported @@ -572,6 +595,13 @@ Close + + %d site + + %d sites + Recently closed tabs @@ -755,15 +785,15 @@ Clear - Copy + Copy - Share + Share - Open in new tab + Open in new tab - Open in private tab + Open in private tab - Delete + Delete %1$d selected @@ -1231,12 +1261,12 @@ Already have an account? - See what’s new + See what’s new - Have questions about the redesigned %s? Want to know what’s changed? + Have questions about the redesigned %s? Want to know what’s changed? - Get answers here + Get answers here Sync Firefox between devices @@ -1276,14 +1306,14 @@ Put the toolbar within easy reach. Keep it on the bottom, or move it to the top. - Browse privately + Browse privately - Open a private tab once: Tap the %s icon. + Open a private tab once: Tap the %s icon. - Open private tabs every time: Update your private browsing settings. + Open private tabs every time: Update your private browsing settings. - Open settings + Open settings Your privacy - Secure Connection + Secure Connection - Insecure Connection + Insecure Connection Are you sure that you want to clear all the permissions on all sites? @@ -1784,7 +1814,7 @@ Discard changes Edit - + Password required Voice search @@ -1820,7 +1850,7 @@ OK, Got It - Show most visited sites + Show most visited sites Name diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml index 89b4878f32..3a487309c3 100644 --- a/app/src/main/res/values-es-rAR/strings.xml +++ b/app/src/main/res/values-es-rAR/strings.xml @@ -1326,7 +1326,7 @@ Estándar (predeterminado) - Equilibrado para protección y rendimiento. Las páginas se van a cargar normalmente. + Equilibrado para privacidad y rendimiento. Las páginas se cargan normalmente. Estricta (recomendada) @@ -1888,7 +1888,7 @@ https://www.example.com - La dirección web debe contener \“https://\“ o \“http://\“ + La dirección web debe contener "https://" o "http://" La dirección web debe contener "https://" o "http://" diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 41a7aff412..ade59e495f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1526,6 +1526,9 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n Efface les cookies définis par redirection vers des sites web connus pour le pistage. + + Certains traqueurs repérés ci-dessous ont été partiellement débloqués sur cette page car vous avez interagi avec eux *. En savoir plus diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 37ef1d22b7..34ec594c8d 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1600,7 +1600,7 @@ Compila e salva nomi utente e password nei siti web quando utilizzi %1$s. - Compilazione automatica in altre app + Compila automaticamente in altre app Compila nomi utente e password nelle altre app del tuo dispositivo. diff --git a/app/src/main/res/values-pa-rIN/strings.xml b/app/src/main/res/values-pa-rIN/strings.xml index 42134ab58f..0e20b0ae59 100644 --- a/app/src/main/res/values-pa-rIN/strings.xml +++ b/app/src/main/res/values-pa-rIN/strings.xml @@ -52,7 +52,9 @@ - ਤਾਜ਼ਾ ਸੰਭਾਲੇ + ਤਾਜ਼ਾ ਸੰਭਾਲੇ + + ਤਾਜ਼ਾ ਬੁੱਕਮਾਰਕ ਕੀਤੇ ਤਾਜ਼ਾ ਸੰਭਾਲੇ ਬੁੱਕਮਾਰਕ @@ -124,6 +126,17 @@ ਸਭ ਵੇਖੋ + + + ਪਿਛਲੀਆਂ ਖੋਜਾਂ + + ਹਾਲ ਦੇ ਖੋਲ੍ਹੀਆਂ ਗਈਆਂ + + ਹਟਾਓ + ਟੈਬਾਂ ਖੋਲ੍ਹੋ @@ -200,6 +213,8 @@ ਸੋਧੋ + + ਘਰ ਨੂੰ ਕਸਟਮਾਈਜ਼ ਕਰੋ ਮੁੱਖ ਸਕਰੀਨ @@ -395,6 +410,18 @@ ਐਡ-ਆਨ ਭੰਡਾਰ ਸੋਧਿਆ ਗਿਆ। ਤਬਦੀਲੀਆਂ ਲਾਗੂ ਕਰਨ ਲਈ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਬੰਦ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ… + + + ਵਾਪਸ ਜਾਓ + + ਤਾਜ਼ਾ ਸੰਭਾਲੇ + + ਤਾਜ਼ਾ ਬੁੱਕਮਾਰਕ ਕੀਤੇ + + ਤਾਜ਼ਾ ਖੋਲ੍ਹੇ ਗਏ + + Pocket + ਐਡ-ਆਨ ਸਹਾਇਕ ਨਹੀਂ ਹੈ @@ -585,6 +612,13 @@ ਬੰਦ ਕਰੋ + + %d ਸਾਈਟ + + %d ਸਾਈਟਾਂ + ਤਾਜ਼ਾ ਬੰਦ ਕੀਤੀਆਂ ਟੈਬਾਂ @@ -642,6 +676,8 @@ ਹਟਾਓ ਸਰਗਰਮ + + Firefox ਸਮੇਂ ਸਮੇਂ ਉੱਤੇ ਅਧਿਐਨ ਇੰਸਟਾਲ ਅਤੇ ਚਲਾ ਸਕਦਾ ਹੈ। ਹੋਰ ਸਿੱਖੋ @@ -755,6 +791,9 @@ ਸੰਭਾਲੋ + + ਹੋਰ + ਅਤੀਤ ਹਟਾਓ @@ -767,15 +806,15 @@ ਸਾਫ਼ ਕਰੋ - ਕਾਪੀ ਕਰੋ + ਕਾਪੀ ਕਰੋ - ਸਾਂਝਾ ਕਰੋ + ਸਾਂਝਾ ਕਰੋ - ਨਵੀਂ ਟੈਬ ‘ਚ ਖੋਲ੍ਹੋ + ਨਵੀਂ ਟੈਬ ‘ਚ ਖੋਲ੍ਹੋ - ਪ੍ਰਾਈਵੇਟ ਟੈਬ ‘ਚ ਖੋਲ੍ਹੋ + ਪ੍ਰਾਈਵੇਟ ਟੈਬ ‘ਚ ਖੋਲ੍ਹੋ - ਹਟਾਓ + ਹਟਾਓ %1$d ਚੁਣੇ @@ -1253,12 +1292,12 @@ ਪਹਿਲਾਂ ਹੀ ਖਾਤਾ ਹੈ? - ਵੇਖੋ ਕਿ ਨਵਾਂ ਕੀ ਹੈ + ਵੇਖੋ ਕਿ ਨਵਾਂ ਕੀ ਹੈ - %s ਮੁੜ-ਡਿਜ਼ਾਈਨ ਬਾਰੇ ਕੋਈ ਸਵਾਲ ਹੈ? ਲੱਭ ਰਹੇ ਕੋ ਕਿ ਕੀ ਬਦਲਿਆ ਹੈ? + %s ਮੁੜ-ਡਿਜ਼ਾਈਨ ਬਾਰੇ ਕੋਈ ਸਵਾਲ ਹੈ? ਲੱਭ ਰਹੇ ਕੋ ਕਿ ਕੀ ਬਦਲਿਆ ਹੈ? - ਜਵਾਬ ਇੱਥੇ ਲਵੋ + ਜਵਾਬ ਇੱਥੇ ਲਵੋ ਡਿਵਾਈਸਾਂ ਵਿਚਾਲੇ Firefox ਸਿੰਕ ਕਰੋ @@ -1299,14 +1338,14 @@ ਸੌਖੀ ਪਹੁੰਚ ਲਈ ਟੂਲਬਾਰ ਨੂੰ ਰੱਖੋ। ਹੇਠਾਂ ਰੱਖੋ ਜਾਂ ਇਸ ਨੂੰ ਉੱਤੇ ਰੱਖੋ। - ਪ੍ਰਾਈਵੇਟ ਢੰਗ ਨਾਲ ਬਰਾਊਜ਼ ਕਰੋ + ਪ੍ਰਾਈਵੇਟ ਢੰਗ ਨਾਲ ਬਰਾਊਜ਼ ਕਰੋ - ਇੱਕ ਵਾਰ ਪ੍ਰਾਈਵੇਟ ਟੈਬ ਵਿੱਚ ਖੋਲ੍ਹੋ: %s ਆਈਕਾਨ ਨੂੰ ਛੂਹੋ। + ਇੱਕ ਵਾਰ ਪ੍ਰਾਈਵੇਟ ਟੈਬ ਵਿੱਚ ਖੋਲ੍ਹੋ: %s ਆਈਕਾਨ ਨੂੰ ਛੂਹੋ। - ਹਰ ਵਾਰ ਪ੍ਰਾਈਵੇਟ ਟੈਬਾਂ ਖੋਲ੍ਹੋ: ਤੁਹਾਡੀਆਂ ਪ੍ਰਾਈਵੇਟ ਬਰਾਊਜ਼ਿੰਗ ਸੈਟਿੰਗਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰੋ। + ਹਰ ਵਾਰ ਪ੍ਰਾਈਵੇਟ ਟੈਬਾਂ ਖੋਲ੍ਹੋ: ਤੁਹਾਡੀਆਂ ਪ੍ਰਾਈਵੇਟ ਬਰਾਊਜ਼ਿੰਗ ਸੈਟਿੰਗਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰੋ। - ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ + ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ ਤੁਹਾਡੀ ਪਰਦੇਦਾਰੀ ਜਾਣੀਆਂ-ਪਛਾਣੀਆਂ ਟਰੈਕਰ ਵੈੱਬਸਾਈਟਾਂ ਲਈ ਰਿ-ਡਿਰੈਕ ਰਾਹੀਂ ਸੈੱਟ ਕਰਨ ਵਾਲੇ ਕੂਕੀਜ਼ ਸਾਫ਼ ਕਰੋ। + + ਇਸ ਸਫ਼ੇ ਉੱਤੇ ਹੇਠ ਨਿਸ਼ਾਨ ਲਾਏ ਕੁਝ ਟਰੈਕਰਾਂ ਤੋਂ ਅਧੂਰੇ ਰੂਪ ਵਿੱਚ ਪਾਬੰਦੀ ਹਟਾਈ ਗਈ ਹੈ, ਕਿਉਂਕਿ ਤੁਸੀਂ ਉਹਨਾਂ ਨਾਲ ਸੰਪਰਕ ਕਰ ਰਹੇ ਹੋ *। + + ਹੋਰ ਜਾਣੋ + ਸਹਿਯੋਗ @@ -1537,6 +1582,11 @@ ਹੋਰ ਐਪਾਂ ਵਿੱਚ ਆਪੇ ਭਰੋ + + ਤੁਹਾਡੇ ਡਿਵਾਈਸ ਉੱਤੇ ਹੋਰ ਐਪਾਂ ਵਿੱਚ ਵਰਤੋਂਕਾਰ-ਨਾਂ ਅਤੇ ਪਾਸਵਰਡ ਭਰੋ। + + ਲਾਗਇਨ ਜੋੜੋ + ਲਾਗਇਨ ਸਿੰਕ ਕਰੋ @@ -1599,6 +1649,8 @@ ਵਰਤੋਂਕਾਰ-ਨਾਂ ਨੂੰ ਕਾਪੀ ਕਰੋ ਵਰਤੋਂਕਾਰ-ਨਾਂ ਨੂੰ ਮਟਾਓ + + ਹੋਸਟ-ਨਾਂ ਮਿਟਾਓ ਸਾਈਟ ਨੂੰ ਕਾਪੀ ਕਰੋ @@ -1766,9 +1818,13 @@ %1$s ਨੂੰ ਚਾਲੂ ਲਈ ਬਦਲੋ]]> - ਸੁਰੱਖਿਅਤ ਕਨੈਕਸ਼ਨ + ਕਨੈਕਸ਼ਨ ਸੁਰੱਖਿਅਤ ਹੈ - ਅਸੁਰੱਖਿਅਤ ਕਨੈਕਸ਼ਨ + ਕਨੈਕਸ਼ਨ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਹੈ + + ਸੁਰੱਖਿਅਤ ਕਨੈਕਸ਼ਨ + + ਅਸੁਰੱਖਿਅਤ ਕਨੈਕਸ਼ਨ ਕੀ ਤੁਸੀਂ ਸਾਰੀਆਂ ਸਾਈਟਾਂ ਲਈ ਸਭ ਸਹਿਮਤੀਆਂ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ? @@ -1809,8 +1865,14 @@ ਤਬਦੀਲੀਆਂ ਅਣਡਿੱਠੀਆਂ ਕਰੋ ਸੋਧੋ - + + ਨਵਾਂ ਲਾਗਇਨ ਜੋੜੋ + ਪਾਸਵਰਡ ਚਾਹੀਦਾ ਹੈ + + ਵਰਤੋਂਕਾਰ-ਨਾਂ ਚਾਹੀਦਾ ਹੈ + + ਹੋਸਟ-ਨਾਂ ਚਾਹੀਦਾ ਹੈ ਆਵਾਜ਼ ਰਾਹੀਂ ਖੋਜੋ @@ -1818,6 +1880,15 @@ ਉਸ ਵਰਤੋਂਕਾਰ ਨਾਲ ਲਾਗਇਨ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ + + https://www.example.com + + ਵੈੱਬ ਸਿਰਨਾਵੇਂ ਵਿੱਚ "https://" ਜਾਂ "http://" ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ + + ਵੈੱਬ ਸਿਰਨਾਵੇਂ ਵਿੱਚ "https://" ਜਾਂ "http://" ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ + + ਵਾਜਬ ਹੋਸਟ-ਨਾਂ ਚਾਹੀਦਾ ਹੈ + ਹੋਰ ਡਿਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰੋ। @@ -1843,8 +1914,10 @@ ਠੀਕ ਹੈ, ਸਮਝ ਗਏ + + ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹੀਆਂ ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ ਵੇਖੋ - ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹੀਆਂ ਸਾਈਟਾਂ ਵੇਖੋ + ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹੀਆਂ ਸਾਈਟਾਂ ਵੇਖੋ ਨਾਂ @@ -1858,6 +1931,8 @@ ਨਾ-ਸਰਗਰਮ ਟੈਬਾਂ + + ਟੈਬਾਂ %s ਦਿਨਾਂ ਲਈ ਇੱਥੇ ਮੌਜੂਦ ਹੁੰਦੀਆਂ ਹਨ। ਉਸ ਸਮੇਂ ਬਾਅਦ, ਟੈਬਾਂ ਨੂੰ ਆਪਣੇ-ਆਪ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ। 30 ਦਿਨ diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index d7b245dfcc..427d62d296 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -53,7 +53,7 @@ Son kaydedilenler - Yer imlerine son eklenenler + Son yer imleri Son kaydedilen yer imleri @@ -409,7 +409,7 @@ Son kaydedilenler - Yer imlerine son eklenenler + Son yer imleri Son bakılanlar From 920376ee5b825a9afe9f8a847180d68cb212f97e Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Thu, 30 Sep 2021 18:27:56 -0400 Subject: [PATCH 334/517] Close #21610: Update Strings for Tab Settings --- .../fenix/ui/robots/SettingsSubMenuTabsRobot.kt | 4 ---- .../org/mozilla/fenix/settings/TabsSettingsFragment.kt | 4 ++-- app/src/main/res/values/strings.xml | 10 ++++------ app/src/main/res/xml/tabs_preferences.xml | 3 +-- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt index 405af4bcf8..1ff07f888d 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt @@ -56,8 +56,6 @@ private fun assertTabViewOptions() { private fun assertCloseTabsOptions() { closeTabsHeading() .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) - manuallyToggle() - .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) afterOneDayToggle() .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) afterOneWeekToggle() @@ -73,8 +71,6 @@ private fun assertStartOnHomeOptions() { .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) alwaysStartOnHomeToggle() .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) - neverStartOnHomeToggle() - .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) } private fun tabViewHeading() = onView(withText("Tab view")) diff --git a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt index 9a9d3dfe70..fd131a7785 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt @@ -60,9 +60,9 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { gridRadioButton = requirePreference(R.string.pref_key_tab_view_grid) radioManual = requirePreference(R.string.pref_key_close_tabs_manually) - radioOneDay = requirePreference(R.string.pref_key_close_tabs_after_one_day) - radioOneWeek = requirePreference(R.string.pref_key_close_tabs_after_one_week) radioOneMonth = requirePreference(R.string.pref_key_close_tabs_after_one_month) + radioOneWeek = requirePreference(R.string.pref_key_close_tabs_after_one_week) + radioOneDay = requirePreference(R.string.pref_key_close_tabs_after_one_day) startOnHomeRadioFourHours = requirePreference(R.string.pref_key_start_on_home_after_four_hours) startOnHomeRadioAlways = requirePreference(R.string.pref_key_start_on_home_always) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 651aff8353..7bca8c85e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -647,7 +647,7 @@ Close tabs - Manually + Never After one day @@ -675,11 +675,9 @@ - Inactive tabs on tab tray - - Make tabs inactive after 14 days - - To improve speed, tabs will be moved into the inactive tabs section on your tab tray + Move old tabs to inactive + + Tabs you haven’t viewed for two weeks get moved to the inactive section. diff --git a/app/src/main/res/xml/tabs_preferences.xml b/app/src/main/res/xml/tabs_preferences.xml index 6717a49df2..3a4f01762d 100644 --- a/app/src/main/res/xml/tabs_preferences.xml +++ b/app/src/main/res/xml/tabs_preferences.xml @@ -80,7 +80,6 @@ + android:title="@string/preferences_inactive_tabs_title"/> From 526501160c453bc85750737677046ab3547ff3d2 Mon Sep 17 00:00:00 2001 From: Oana Horvath Date: Thu, 30 Sep 2021 15:32:11 +0300 Subject: [PATCH 335/517] For #21441, #21477, #21500, #21499 #21476, #21474: retry dismissing the search bar --- .../org/mozilla/fenix/ui/SettingsBasicsTest.kt | 3 --- .../org/mozilla/fenix/ui/SettingsPrivacyTest.kt | 3 --- .../java/org/mozilla/fenix/ui/SmokeTest.kt | 4 +--- .../java/org/mozilla/fenix/ui/TopSitesTest.kt | 16 ++++------------ .../org/mozilla/fenix/ui/robots/SearchRobot.kt | 10 +++++++++- 5 files changed, 14 insertions(+), 22 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt index a27615717c..7dc9396480 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt @@ -175,9 +175,6 @@ class SettingsBasicsTest { }.openNavigationToolbar { }.enterURLAndEnterToBrowser(webpage) { checkTextSizeOnWebsite(textSizePercentage, fenixApp.components) - }.openTabDrawer { - }.openNewTab { - }.dismissSearchBar { }.openThreeDotMenu { }.openSettings { }.openAccessibilitySubMenu { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt index 867b66c5ca..8178901ad0 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt @@ -230,9 +230,6 @@ class SettingsPrivacyTest { verifySaveLoginPromptIsShown() // Don't save the login, add to exceptions saveLoginFromPrompt("Never save") - }.openTabDrawer { - }.openNewTab { - }.dismissSearchBar { }.openThreeDotMenu { }.openSettings { }.openLoginsAndPasswordSubMenu { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt index 83c9756d96..8a5db2f7a5 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt @@ -322,9 +322,7 @@ class SmokeTest { expandMenu() }.addToFirefoxHome { verifySnackBarText("Added to top sites!") - }.openTabDrawer { - }.openNewTab { - }.dismissSearchBar { + }.goToHomescreen { verifyExistingTopSitesTabs(defaultWebPage.title) } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt index a59d38ef59..d989813ee5 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt @@ -61,9 +61,7 @@ class TopSitesTest { verifyAddToTopSitesButton() }.addToFirefoxHome { verifySnackBarText("Added to top sites!") - }.openTabDrawer { - }.openNewTab { - }.dismissSearchBar { + }.goToHomescreen { verifyExistingTopSitesList() verifyExistingTopSitesTabs(defaultWebPageTitle) } @@ -113,9 +111,7 @@ class TopSitesTest { verifyAddToTopSitesButton() }.addToFirefoxHome { verifySnackBarText("Added to top sites!") - }.openTabDrawer { - }.openNewTab { - }.dismissSearchBar { + }.goToHomescreen { verifyExistingTopSitesList() verifyExistingTopSitesTabs(defaultWebPageTitle) }.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) { @@ -140,9 +136,7 @@ class TopSitesTest { verifyAddToTopSitesButton() }.addToFirefoxHome { verifySnackBarText("Added to top sites!") - }.openTabDrawer { - }.openNewTab { - }.dismissSearchBar { + }.goToHomescreen { verifyExistingTopSitesList() verifyExistingTopSitesTabs(defaultWebPageTitle) }.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) { @@ -167,9 +161,7 @@ class TopSitesTest { verifyAddToTopSitesButton() }.addToFirefoxHome { verifySnackBarText("Added to top sites!") - }.openTabDrawer { - }.openNewTab { - }.dismissSearchBar { + }.goToHomescreen { verifyExistingTopSitesList() verifyExistingTopSitesTabs(defaultWebPageTitle) }.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt index e57276533c..35f317781b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt @@ -43,6 +43,7 @@ import org.mozilla.fenix.helpers.Constants.LONG_CLICK_DURATION import org.mozilla.fenix.helpers.SessionLoadedIdlingResource import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeShort import org.mozilla.fenix.helpers.TestHelper.packageName import org.mozilla.fenix.helpers.TestHelper.waitForObjects import org.mozilla.fenix.helpers.click @@ -185,6 +186,13 @@ class SearchRobot { mDevice.waitForIdle() closeSoftKeyboard() mDevice.pressBack() + try { + assertTrue(searchWrapper().waitUntilGone(waitingTimeShort)) + } catch (e: AssertionError) { + mDevice.pressBack() + assertTrue(searchWrapper().waitUntilGone(waitingTimeShort)) + } + HomeScreenRobot().interact() return HomeScreenRobot.Transition() } @@ -244,7 +252,7 @@ private fun scanButton(): ViewInteraction { private fun clearButton() = mDevice.findObject(UiSelector().resourceId("$packageName:id/mozac_browser_toolbar_clear_view")) -private fun searchWrapper() = onView(withId(R.id.search_wrapper)) +private fun searchWrapper() = mDevice.findObject(UiSelector().resourceId("$packageName:id/search_wrapper")) private fun assertSearchEngineURL(searchEngineName: String) { mDevice.waitNotNull( From a4a04173ccda3954a26b60bb0aac7f5031e48fc5 Mon Sep 17 00:00:00 2001 From: Oana Horvath Date: Fri, 1 Oct 2021 17:26:40 +0300 Subject: [PATCH 336/517] For #21540, #21620: disables failing UI tests --- app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt index 8a5db2f7a5..701f34aa74 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt @@ -544,6 +544,7 @@ class SmokeTest { } } + @Ignore("Started failing: https://github.com/mozilla-mobile/fenix/issues/21540") @Test // Verifies setting as default a customized search engine name and URL fun editCustomSearchEngineTest() { @@ -575,6 +576,7 @@ class SmokeTest { } } + @Ignore("Strated failing on Nighlty task: https://github.com/mozilla-mobile/fenix/issues/21620") @Test // Test running on beta/release builds in CI: // caution when making changes to it, so they don't block the builds From fafa72c0ff00b0a5f0b249c584ba15cf78dca52d Mon Sep 17 00:00:00 2001 From: Christian Sadilek Date: Fri, 1 Oct 2021 11:08:09 -0400 Subject: [PATCH 337/517] Allow opening pocket stories when search dialog is active --- .../org/mozilla/fenix/home/HomeFragment.kt | 3 +- .../pocket/PocketStoriesController.kt | 17 ++++++++-- .../DefaultPocketStoriesControllerTest.kt | 32 +++++++++++++++---- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index e97cbc7d39..e4444ec681 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -351,7 +351,8 @@ class HomeFragment : Fragment() { ), pocketStoriesController = DefaultPocketStoriesController( homeActivity = activity, - homeStore = homeFragmentStore + homeStore = homeFragmentStore, + navController = findNavController() ) ) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt index ea6f25610e..56ee8c989f 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt @@ -4,12 +4,15 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.pocket +import androidx.annotation.VisibleForTesting +import androidx.navigation.NavController import org.mozilla.fenix.home.HomeFragmentAction import org.mozilla.fenix.home.HomeFragmentStore import mozilla.components.lib.state.Store import mozilla.components.service.pocket.PocketRecommendedStory import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.R /** * Contract for how all user interactions with the Pocket recommended stories feature are to be handled. @@ -42,10 +45,12 @@ interface PocketStoriesController { * * @param homeActivity [HomeActivity] used to open URLs in a new tab. * @param homeStore [Store] from which to read the current Pocket recommendations and dispatch new actions on. + * @param navController [NavController] used for navigation. */ internal class DefaultPocketStoriesController( - val homeActivity: HomeActivity, - val homeStore: HomeFragmentStore + private val homeActivity: HomeActivity, + private val homeStore: HomeFragmentStore, + private val navController: NavController ) : PocketStoriesController { override fun handleCategoryClick(categoryClicked: PocketRecommendedStoryCategory) { val allCategories = homeStore.state.pocketStoriesCategories @@ -87,6 +92,14 @@ internal class DefaultPocketStoriesController( } override fun handleExternalLinkClick(link: String) { + dismissSearchDialogIfDisplayed() homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + fun dismissSearchDialogIfDisplayed() { + if (navController.currentDestination?.id == R.id.searchDialogFragment) { + navController.navigateUp() + } + } } diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt index b4105ac86c..c1548663a9 100644 --- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt @@ -4,6 +4,8 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.pocket +import androidx.navigation.NavController +import io.mockk.every import io.mockk.mockk import io.mockk.spyk import io.mockk.verify @@ -11,6 +13,7 @@ import mozilla.components.service.pocket.PocketRecommendedStory import org.junit.Test import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.R import org.mozilla.fenix.home.HomeFragmentAction import org.mozilla.fenix.home.HomeFragmentState import org.mozilla.fenix.home.HomeFragmentStore @@ -25,7 +28,7 @@ class DefaultPocketStoriesControllerTest { HomeFragmentState(pocketStoriesCategories = listOf(category1, category2)) ) ) - val controller = DefaultPocketStoriesController(mockk(), store) + val controller = DefaultPocketStoriesController(mockk(), store, mockk()) controller.handleCategoryClick(category1) verify(exactly = 0) { store.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(category1.name)) } @@ -58,7 +61,7 @@ class DefaultPocketStoriesControllerTest { ) ) ) - val controller = DefaultPocketStoriesController(mockk(), store) + val controller = DefaultPocketStoriesController(mockk(), store, mockk()) controller.handleCategoryClick(newSelectedCategory) @@ -89,7 +92,7 @@ class DefaultPocketStoriesControllerTest { ) ) ) - val controller = DefaultPocketStoriesController(mockk(), store) + val controller = DefaultPocketStoriesController(mockk(), store, mockk()) controller.handleCategoryClick(newSelectedCategory) @@ -100,7 +103,7 @@ class DefaultPocketStoriesControllerTest { @Test fun `WHEN new stories are shown THEN update the State`() { val store = spyk(HomeFragmentStore()) - val controller = DefaultPocketStoriesController(mockk(), store) + val controller = DefaultPocketStoriesController(mockk(), store, mockk()) val storiesShown: List = mockk() controller.handleStoriesShown(storiesShown) @@ -109,13 +112,30 @@ class DefaultPocketStoriesControllerTest { } @Test - fun `WHEN an external link is clicked then open that using HomeActivity`() { + fun `WHEN an external link is clicked THEN link is opened`() { val link = "https://www.mozilla.org/en-US/firefox/pocket/" val homeActivity: HomeActivity = mockk(relaxed = true) - val controller = DefaultPocketStoriesController(homeActivity, mockk()) + val controller = DefaultPocketStoriesController(homeActivity, mockk(), mockk(relaxed = true)) controller.handleExternalLinkClick(link) verify { homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) } } + + @Test + fun `WHEN an external link is clicked THEN link is opened and search dismissed`() { + val link = "https://www.mozilla.org/en-US/firefox/pocket/" + val homeActivity: HomeActivity = mockk(relaxed = true) + val navController: NavController = mockk(relaxed = true) + + every { navController.currentDestination } returns mockk { + every { id } returns R.id.searchDialogFragment + } + + val controller = DefaultPocketStoriesController(homeActivity, mockk(), navController) + controller.handleExternalLinkClick(link) + + verify { homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) } + verify { navController.navigateUp() } + } } From 903c25095a3d5ec710ea57c88ffa3623355cdf38 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Fri, 1 Oct 2021 14:36:16 +0000 Subject: [PATCH 338/517] Update Android Components version to 94.0.20211001125001. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 36f7a3ecf8..c19e7e96a2 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20210930211433" + const val VERSION = "94.0.20211001125001" } From bf876e2593054f670576940fdb3fd190d751de14 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Thu, 30 Sep 2021 10:44:59 +0300 Subject: [PATCH 339/517] For #21392 - Update new strings descriptions. --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7bca8c85e5..0f2053fc5f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1933,7 +1933,7 @@ Discover more Powered by Pocket - + Part of the Firefox family. %s Learn more From c1effd946da84aa489836701cf809477d5e25d4f Mon Sep 17 00:00:00 2001 From: Biren-Nayak Date: Fri, 1 Oct 2021 14:35:15 -0400 Subject: [PATCH 340/517] For #16437: Remove unnecessary space for tracking protection exceptions --- app/src/main/res/layout/fragment_site_permissions_exceptions.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/layout/fragment_site_permissions_exceptions.xml b/app/src/main/res/layout/fragment_site_permissions_exceptions.xml index 65bd02787d..92d90f969a 100644 --- a/app/src/main/res/layout/fragment_site_permissions_exceptions.xml +++ b/app/src/main/res/layout/fragment_site_permissions_exceptions.xml @@ -14,7 +14,6 @@ android:id="@+id/exceptions" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="16dp" android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" From 69485da0f88465d454ebd967ef6be8f5285ff2a2 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Fri, 1 Oct 2021 11:18:36 -0400 Subject: [PATCH 341/517] For #21632 - Preland strings for the inactive tab survey --- app/src/main/res/values/strings.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0f2053fc5f..172409d7b3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1909,6 +1909,22 @@ 1 week + + + Please help us to improve + + Why did you disable inactive tabs? + + Not interested in the feature + + Time to inactive is too long + + Time to inactive is too short + + Send + + Closes + Set links from websites, emails, and messages to open automatically in Firefox. From 07b53f1e735121ec579060f8bfeb4e0dd3e8e315 Mon Sep 17 00:00:00 2001 From: Noah Bond <87384386+MozillaNoah@users.noreply.github.com> Date: Fri, 1 Oct 2021 13:37:51 -0700 Subject: [PATCH 342/517] For #21635 - Preland strings for tab auto close message (#21636) * For #21635 - Prelanded strings for tab auto close message * PR: Updated String descriptions and added temporary tools ignore --- app/src/main/res/values/strings.xml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 172409d7b3..8e0da5f05c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1897,8 +1897,8 @@ Cancel - - + + Inactive tabs Close all inactive tabs @@ -1909,6 +1909,14 @@ 1 week + + + Auto-close after one month? + + Firefox can close tabs you haven’t viewed over the past month. + + TURN ON AUTO CLOSE + Please help us to improve From 3a2b9e5d2b30bed7149df24b2fe176c7ce324643 Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Fri, 1 Oct 2021 14:23:24 -0700 Subject: [PATCH 343/517] For #21643: Pre-land strings for inactive tabs CFR. Update section title for normal, non-search group tabs. (#21645) --- .../mozilla/fenix/tabstray/browser/TitleHeaderAdapter.kt | 2 +- app/src/main/res/layout/tab_tray_title_header_item.xml | 2 +- app/src/main/res/values/strings.xml | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderAdapter.kt index bc95c15165..0b421a694e 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderAdapter.kt @@ -60,7 +60,7 @@ class TitleHeaderAdapter( fun bind() { binding.tabTrayHeaderTitle.text = - itemView.context.getString(R.string.tab_tray_header_title) + itemView.context.getString(R.string.tab_tray_header_title_1) } companion object { diff --git a/app/src/main/res/layout/tab_tray_title_header_item.xml b/app/src/main/res/layout/tab_tray_title_header_item.xml index 04a548630a..d1dd329d20 100644 --- a/app/src/main/res/layout/tab_tray_title_header_item.xml +++ b/app/src/main/res/layout/tab_tray_title_header_item.xml @@ -17,7 +17,7 @@ android:focusable="false" android:gravity="start" android:maxLines="1" - android:text="@string/tab_tray_header_title" + android:text="@string/tab_tray_header_title_1" android:textAppearance="@style/Header16TextStyle" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8e0da5f05c..9a056805c4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,6 +104,10 @@ View options Dismiss + + Tabs you haven’t viewed for two weeks get moved here. + + Turn off in settings @@ -795,7 +799,9 @@ Save - Other + Other + + Other tabs From 4863b5627fb9287e1fc63746906c009217a64808 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Fri, 1 Oct 2021 19:42:12 +0000 Subject: [PATCH 344/517] Update Android Components version to 94.0.20211001190127. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index c19e7e96a2..231baddd61 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20211001125001" + const val VERSION = "94.0.20211001190127" } From c7067a50cb38824ca51095f4f15fadd129abf519 Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Fri, 1 Oct 2021 14:28:57 -0700 Subject: [PATCH 345/517] For #21646: Update section titles and customize button name on home --- .../pocket/PocketStoriesViewHolder.kt | 2 +- .../res/layout/customize_home_list_item.xml | 2 +- .../res/layout/history_metadata_header.xml | 2 +- .../res/layout/recent_bookmarks_header.xml | 2 +- app/src/main/res/values/strings.xml | 27 ++++++++++++++----- .../res/xml/customization_preferences.xml | 4 +-- 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt index e108f08de2..fd86adabc6 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt @@ -91,7 +91,7 @@ fun PocketStories( Column(modifier = Modifier.padding(vertical = 48.dp)) { SectionHeader( - text = stringResource(R.string.pocket_stories_header), + text = stringResource(R.string.pocket_stories_header_1), modifier = Modifier .fillMaxWidth() ) diff --git a/app/src/main/res/layout/customize_home_list_item.xml b/app/src/main/res/layout/customize_home_list_item.xml index fbd7ee524b..6e7e562946 100644 --- a/app/src/main/res/layout/customize_home_list_item.xml +++ b/app/src/main/res/layout/customize_home_list_item.xml @@ -19,7 +19,7 @@ android:background="@drawable/rounded_button_background" android:clickable="false" android:focusable="false" - android:text="@string/browser_menu_customize_home" + android:text="@string/browser_menu_customize_home_1" android:gravity="center" android:maxLines="1" app:layout_constraintBottom_toBottomOf="parent" diff --git a/app/src/main/res/layout/history_metadata_header.xml b/app/src/main/res/layout/history_metadata_header.xml index a277da264e..048f646970 100644 --- a/app/src/main/res/layout/history_metadata_header.xml +++ b/app/src/main/res/layout/history_metadata_header.xml @@ -14,7 +14,7 @@ style="@style/Header16TextStyle" android:layout_width="wrap_content" android:layout_height="0dp" - android:text="@string/history_metadata_header_2" + android:text="@string/history_metadata_header_3" android:maxLines="2" android:gravity="center_vertical" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/recent_bookmarks_header.xml b/app/src/main/res/layout/recent_bookmarks_header.xml index 82eda14ae9..29747174fd 100644 --- a/app/src/main/res/layout/recent_bookmarks_header.xml +++ b/app/src/main/res/layout/recent_bookmarks_header.xml @@ -18,7 +18,7 @@ android:layout_height="0dp" android:contentDescription="@string/recently_saved_bookmarks_content_description" android:maxLines="2" - android:text="@string/recently_bookmarked" + android:text="@string/recent_bookmarks_title" android:gravity="center_vertical" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9a056805c4..64a437e007 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -51,7 +51,9 @@ Recently saved - Recently bookmarked + Recently bookmarked + + Recent bookmarks Recently saved bookmarks @@ -137,7 +139,10 @@ Past explorations - Recently visited + Recently visited + + Recent searches Remove @@ -218,6 +223,8 @@ Edit Customize home + + Customize homepage Home screen @@ -432,9 +439,15 @@ Recently saved - Recently bookmarked - - Recently visited + Recently bookmarked + + Recent bookmarks + + Recently visited + + Recent searches Pocket @@ -1956,7 +1969,9 @@ - Thought provoking stories + Thought provoking stories + + Thought-provoking stories Stories by topic diff --git a/app/src/main/res/xml/customization_preferences.xml b/app/src/main/res/xml/customization_preferences.xml index 8d9206408d..a5ff3da4c9 100644 --- a/app/src/main/res/xml/customization_preferences.xml +++ b/app/src/main/res/xml/customization_preferences.xml @@ -57,12 +57,12 @@ Date: Fri, 1 Oct 2021 17:25:12 -0700 Subject: [PATCH 346/517] For #21360 - Added toggle for search term tab groups (#21615) * For #21360 - Added toggle for search term tab groups * For #21360 - Lint cleanup * PR: Added missing licenses and possibly fixed UI test * PR: Added a "scrollTo" to potentially fix a UI test * PR: Added potential fix for alwaysStartOnHomeTest * PR: Added temporary ignore to alwaysStartOnHomeTest * PR: added missing ignore comment * For #21360 - Added missing feature flag driven visibility logic Co-authored-by: Sebastian Kaspari Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../java/org/mozilla/fenix/ui/SmokeTest.kt | 1 + .../fenix/ui/robots/SettingsSubMenuTabsRobot.kt | 12 +++++++++++- .../fenix/home/recenttabs/view/RecentTabs.kt | 3 +-- .../mozilla/fenix/settings/TabsSettingsFragment.kt | 6 ++++++ .../fenix/tabstray/browser/NormalBrowserTrayList.kt | 12 +++++++----- .../fenix/tabstray/browser/TitleHeaderBinding.kt | 8 ++++++-- .../org/mozilla/fenix/tabstray/ext/TabSelectors.kt | 10 ++++++---- .../viewholders/NormalBrowserPageViewHolder.kt | 9 ++++++--- .../main/java/org/mozilla/fenix/utils/Settings.kt | 9 +++++++++ app/src/main/res/drawable-night/ic_all_tabs.xml | 13 +++++++++++++ app/src/main/res/drawable/ic_all_tabs.xml | 3 +++ app/src/main/res/values/preference_keys.xml | 1 + app/src/main/res/values/strings.xml | 4 ++++ app/src/main/res/xml/tabs_preferences.xml | 7 +++++++ .../tabstray/browser/TitleHeaderBindingTest.kt | 3 +-- 15 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 app/src/main/res/drawable-night/ic_all_tabs.xml diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt index 701f34aa74..6e7a06b096 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt @@ -1438,6 +1438,7 @@ class SmokeTest { } } + @Ignore // to be fixed here https://github.com/mozilla-mobile/fenix/issues/21644 @Test fun alwaysStartOnHomeTest() { val settings = activityTestRule.activity.applicationContext.settings() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt index 1ff07f888d..2361171b5a 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt @@ -16,6 +16,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import org.hamcrest.CoreMatchers.allOf +import org.mozilla.fenix.helpers.TestHelper.scrollToElementByText import org.mozilla.fenix.helpers.click /** @@ -29,7 +30,10 @@ class SettingsSubMenuTabsRobot { fun verifyStartOnHomeOptions() = assertStartOnHomeOptions() - fun clickAlwaysStartOnHomeToggle() = alwaysStartOnHomeToggle().click() + fun clickAlwaysStartOnHomeToggle() { + scrollToElementByText("Always") + alwaysStartOnHomeToggle().click() + } class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) @@ -51,6 +55,8 @@ private fun assertTabViewOptions() { .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) gridToggle() .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + searchTermTabGroupsToggle() + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) } private fun assertCloseTabsOptions() { @@ -65,6 +71,8 @@ private fun assertCloseTabsOptions() { } private fun assertStartOnHomeOptions() { + // Scroll to ensure all the items are visible. + scrollToElementByText("Never") startOnHomeHeading() .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) afterFourHoursToggle() @@ -79,6 +87,8 @@ private fun listToggle() = onView(withText("List")) private fun gridToggle() = onView(withText("Grid")) +private fun searchTermTabGroupsToggle() = onView(withText("Search groups")) + private fun closeTabsHeading() = onView(withText("Close tabs")) private fun manuallyToggle() = onView(withText("Manually")) diff --git a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt index cbdfc3d619..c090e1035f 100644 --- a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt +++ b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt @@ -43,7 +43,6 @@ import mozilla.components.browser.icons.compose.Placeholder import mozilla.components.browser.icons.compose.WithIcon import mozilla.components.support.ktx.kotlin.getRepresentativeSnippet import mozilla.components.ui.colors.PhotonColors -import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.components.components import org.mozilla.fenix.home.recenttabs.RecentTab @@ -78,7 +77,7 @@ fun RecentTabs( ) } is RecentTab.SearchGroup -> { - if (FeatureFlags.tabGroupFeature) { + if (components.settings.searchTermTabGroupsAreEnabled) { RecentSearchGroupItem( searchTerm = tab.searchTerm, tabId = tab.tabId, diff --git a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt index fd131a7785..964cfc4096 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt @@ -34,6 +34,7 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { private lateinit var startOnHomeRadioNever: RadioButtonPreference private lateinit var inactiveTabsCategory: PreferenceCategory private lateinit var inactiveTabs: SwitchPreference + private lateinit var searchTermTabGroups: SwitchPreference override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.tabs_preferences, rootKey) @@ -58,6 +59,11 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { // pref_key_tab_view_grid and look into using the native RadioGroup in the future. listRadioButton = requirePreference(R.string.pref_key_tab_view_list_do_not_use) gridRadioButton = requirePreference(R.string.pref_key_tab_view_grid) + searchTermTabGroups = requirePreference(R.string.pref_key_search_term_tab_groups).also { + it.isVisible = FeatureFlags.tabGroupFeature + it.isChecked = it.context.settings().searchTermTabGroupsAreEnabled + it.onPreferenceChangeListener = SharedPreferenceUpdater() + } radioManual = requirePreference(R.string.pref_key_close_tabs_manually) radioOneMonth = requirePreference(R.string.pref_key_close_tabs_after_one_month) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt index e21713f718..f7355c8b6a 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt @@ -10,7 +10,6 @@ import androidx.recyclerview.widget.ConcatAdapter import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.tabstray.TabViewHolder import mozilla.components.feature.tabs.tabstray.TabsFeature -import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.tabstray.ext.browserAdapter @@ -45,14 +44,15 @@ class NormalBrowserTrayList @JvmOverloads constructor( override val tabsFeature by lazy { val tabsAdapter = concatAdapter.browserAdapter val inactiveTabsEnabled = context.settings().inactiveTabsAreEnabled + val searchTermTabGroupsAreEnabled = context.settings().searchTermTabGroupsAreEnabled val tabFilter: (TabSessionState) -> Boolean = { when { - FeatureFlags.tabGroupFeature && inactiveTabsEnabled -> + searchTermTabGroupsAreEnabled && inactiveTabsEnabled -> it.isNormalTabActiveWithoutSearchTerm(maxActiveTime) inactiveTabsEnabled -> it.isNormalTabActive(maxActiveTime) - FeatureFlags.tabGroupFeature -> it.isNormalTabWithoutSearchTerm() + searchTermTabGroupsAreEnabled -> it.isNormalTabWithoutSearchTerm() else -> !it.content.private } @@ -71,11 +71,13 @@ class NormalBrowserTrayList @JvmOverloads constructor( private val searchTermFeature by lazy { val store = context.components.core.store val inactiveTabsEnabled = context.settings().inactiveTabsAreEnabled + val searchTermTabGroupsAreEnabled = context.settings().searchTermTabGroupsAreEnabled val tabFilter: (TabSessionState) -> Boolean = { when { - FeatureFlags.tabGroupFeature && inactiveTabsEnabled -> it.isNormalTabActiveWithSearchTerm(maxActiveTime) + searchTermTabGroupsAreEnabled && inactiveTabsEnabled -> + it.isNormalTabActiveWithSearchTerm(maxActiveTime) - FeatureFlags.tabGroupFeature -> it.isNormalTabWithSearchTerm() + searchTermTabGroupsAreEnabled -> it.isNormalTabWithSearchTerm() else -> false } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt index 18c51831ac..163ca067e5 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt @@ -25,8 +25,12 @@ class TitleHeaderBinding( private val showHeader: (Boolean) -> Unit ) : AbstractBinding(store) { override suspend fun onState(flow: Flow) { - flow.map { it.getNormalTrayTabs(settings.inactiveTabsAreEnabled) } - .ifChanged { it.size } + flow.map { + it.getNormalTrayTabs( + settings.searchTermTabGroupsAreEnabled, + settings.inactiveTabsAreEnabled + ) + }.ifChanged { it.size } .collect { if (it.isEmpty()) { showHeader(false) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt index 7b6c49388b..0cac457365 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt @@ -8,7 +8,6 @@ import mozilla.components.browser.state.selector.normalTabs import mozilla.components.browser.state.selector.privateTabs import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.TabSessionState -import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.tabstray.browser.maxActiveTime /** @@ -41,13 +40,16 @@ val BrowserState.inactiveTabs: List /** * The list of normal tabs in the tabs tray filtered appropriately based on feature flags. */ -fun BrowserState.getNormalTrayTabs(inactiveTabsEnabled: Boolean): List { +fun BrowserState.getNormalTrayTabs( + searchTermTabGroupsAreEnabled: Boolean, + inactiveTabsEnabled: Boolean +): List { return normalTabs.run { - if (FeatureFlags.tabGroupFeature && inactiveTabsEnabled) { + if (searchTermTabGroupsAreEnabled && inactiveTabsEnabled) { filter { it.isNormalTabActiveWithoutSearchTerm(maxActiveTime) } } else if (inactiveTabsEnabled) { filter { it.isNormalTabActive(maxActiveTime) } - } else if (FeatureFlags.tabGroupFeature) { + } else if (searchTermTabGroupsAreEnabled) { filter { it.isNormalTabWithSearchTerm() } } else { this diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt index 961ec656c9..f947013b19 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/viewholders/NormalBrowserPageViewHolder.kt @@ -12,7 +12,6 @@ import androidx.recyclerview.widget.RecyclerView import mozilla.components.browser.state.selector.selectedNormalTab import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.tabstray.Tab -import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.ext.settings import org.mozilla.fenix.selection.SelectionHolder @@ -80,6 +79,7 @@ class NormalBrowserPageViewHolder( val inactiveTabAdapter = concatAdapter.inactiveTabsAdapter val tabGroupAdapter = concatAdapter.tabGroupAdapter val inactiveTabsAreEnabled = containerView.context.settings().inactiveTabsAreEnabled + val searchTermTabGroupsAreEnabled = containerView.context.settings().searchTermTabGroupsAreEnabled val selectedTab = browserStore.state.selectedNormalTab ?: return @@ -102,7 +102,7 @@ class NormalBrowserPageViewHolder( } // Updates tabs into the search term group adapter. - if (FeatureFlags.tabGroupFeature && selectedTab.isNormalTabActiveWithSearchTerm(maxActiveTime)) { + if (searchTermTabGroupsAreEnabled && selectedTab.isNormalTabActiveWithSearchTerm(maxActiveTime)) { tabGroupAdapter.observeFirstInsert { // With a grouping, we need to use the list of the adapter that is already grouped // together for the UI, so we know the final index of the grouping to scroll to. @@ -127,7 +127,10 @@ class NormalBrowserPageViewHolder( // Updates tabs into the normal browser tabs adapter. browserAdapter.observeFirstInsert { - val activeTabsList = browserStore.state.getNormalTrayTabs(inactiveTabsAreEnabled) + val activeTabsList = browserStore.state.getNormalTrayTabs( + searchTermTabGroupsAreEnabled, + inactiveTabsAreEnabled + ) activeTabsList.forEachIndexed { tabIndex, trayTab -> if (trayTab.id == selectedTab.id) { diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index d731c92054..de9bb1ea61 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -421,6 +421,15 @@ class Settings(private val appContext: Context) : PreferencesHolder { featureFlag = FeatureFlags.inactiveTabs ) + /** + * Indicates if the user has enabled the search term tab groups feature. + */ + var searchTermTabGroupsAreEnabled by featureFlagPreference( + appContext.getPreferenceKey(R.string.pref_key_search_term_tab_groups), + default = FeatureFlags.tabGroupFeature, + featureFlag = FeatureFlags.tabGroupFeature + ) + @VisibleForTesting internal fun timeNowInMillis(): Long = System.currentTimeMillis() diff --git a/app/src/main/res/drawable-night/ic_all_tabs.xml b/app/src/main/res/drawable-night/ic_all_tabs.xml new file mode 100644 index 0000000000..b01e330937 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_all_tabs.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_all_tabs.xml b/app/src/main/res/drawable/ic_all_tabs.xml index 06064ec416..c48775b194 100644 --- a/app/src/main/res/drawable/ic_all_tabs.xml +++ b/app/src/main/res/drawable/ic_all_tabs.xml @@ -1,3 +1,6 @@ + pref_key_camera_permissions_needed pref_key_inactive_tabs_category pref_key_inactive_tabs + pref_key_search_term_tab_groups pref_key_return_to_browser diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 64a437e007..284077f086 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -661,6 +661,10 @@ List Grid + + Search groups + + Group related sites together Close tabs diff --git a/app/src/main/res/xml/tabs_preferences.xml b/app/src/main/res/xml/tabs_preferences.xml index 3a4f01762d..dd377f743e 100644 --- a/app/src/main/res/xml/tabs_preferences.xml +++ b/app/src/main/res/xml/tabs_preferences.xml @@ -18,6 +18,13 @@ android:defaultValue="true" android:key="@string/pref_key_tab_view_grid" android:title="@string/tab_view_grid" /> + + Date: Fri, 1 Oct 2021 16:05:03 -0700 Subject: [PATCH 347/517] For #21653: pre-land section title for tab preferences --- app/src/main/res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 284077f086..c88228bbe9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -675,6 +675,8 @@ After one week After one month + + Auto-close open tabs From c877a25400fd796180f237a61b75b12bbdfb7c78 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Fri, 1 Oct 2021 17:55:45 -0400 Subject: [PATCH 348/517] Issue #21641: Do not add a group of only one tab --- .../tabstray/browser/NormalBrowserTrayList.kt | 114 +++++--------- .../fenix/tabstray/browser/TabGroupAdapter.kt | 23 +-- .../fenix/tabstray/browser/TabSorter.kt | 143 ++++++++++++++++++ 3 files changed, 182 insertions(+), 98 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt index f7355c8b6a..f24c5ce37f 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt @@ -9,18 +9,15 @@ import android.util.AttributeSet import androidx.recyclerview.widget.ConcatAdapter import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.tabstray.TabViewHolder +import mozilla.components.concept.tabstray.Tab +import mozilla.components.concept.tabstray.TabsTray import mozilla.components.feature.tabs.tabstray.TabsFeature import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.tabstray.TrayPagerAdapter.Companion.TABS_TRAY_FEATURE_NAME import org.mozilla.fenix.tabstray.ext.browserAdapter import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter -import org.mozilla.fenix.tabstray.ext.isNormalTabActive -import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithSearchTerm -import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithoutSearchTerm -import org.mozilla.fenix.tabstray.ext.isNormalTabWithoutSearchTerm -import org.mozilla.fenix.tabstray.ext.isNormalTabWithSearchTerm import org.mozilla.fenix.tabstray.ext.isNormalTabInactive -import org.mozilla.fenix.tabstray.ext.tabGroupAdapter import java.util.concurrent.TimeUnit /** @@ -39,85 +36,33 @@ class NormalBrowserTrayList @JvmOverloads constructor( defStyleAttr: Int = 0 ) : AbstractBrowserTrayList(context, attrs, defStyleAttr) { + private val swipeDelegate = SwipeToDeleteDelegate() private val concatAdapter by lazy { adapter as ConcatAdapter } - - override val tabsFeature by lazy { - val tabsAdapter = concatAdapter.browserAdapter - val inactiveTabsEnabled = context.settings().inactiveTabsAreEnabled - val searchTermTabGroupsAreEnabled = context.settings().searchTermTabGroupsAreEnabled - val tabFilter: (TabSessionState) -> Boolean = { - when { - searchTermTabGroupsAreEnabled && inactiveTabsEnabled -> - it.isNormalTabActiveWithoutSearchTerm(maxActiveTime) - - inactiveTabsEnabled -> it.isNormalTabActive(maxActiveTime) - - searchTermTabGroupsAreEnabled -> it.isNormalTabWithoutSearchTerm() - - else -> !it.content.private - } - } - - TabsFeature( - tabsAdapter, - context.components.core.store, - selectTabUseCase, - removeTabUseCase, - tabFilter, - {} - ) - } - - private val searchTermFeature by lazy { - val store = context.components.core.store - val inactiveTabsEnabled = context.settings().inactiveTabsAreEnabled - val searchTermTabGroupsAreEnabled = context.settings().searchTermTabGroupsAreEnabled - val tabFilter: (TabSessionState) -> Boolean = { - when { - searchTermTabGroupsAreEnabled && inactiveTabsEnabled -> - it.isNormalTabActiveWithSearchTerm(maxActiveTime) - - searchTermTabGroupsAreEnabled -> it.isNormalTabWithSearchTerm() - - else -> false - } - } - val tabsAdapter = concatAdapter.tabGroupAdapter - - TabsFeature( - tabsAdapter, - store, - selectTabUseCase, - removeTabUseCase, - tabFilter, - {} - ) - } - - /** - * NB: The setup for this feature is a bit complicated without a better dependency injection - * solution to scope it down to just this view. - */ - private val inactiveFeature by lazy { - val store = context.components.core.store + private val tabSorter by lazy { TabSorter(context, concatAdapter, context.components.core.store) } + private val inactiveTabsInteractor by lazy { val tabFilter: (TabSessionState) -> Boolean = filter@{ if (!context.settings().inactiveTabsAreEnabled) { return@filter false } it.isNormalTabInactive(maxActiveTime) } - val tabsAdapter = concatAdapter.inactiveTabsAdapter.apply { - inactiveTabsInteractor = DefaultInactiveTabsInteractor( - InactiveTabsController(store, tabFilter, this, context.components.analytics.metrics) + DefaultInactiveTabsInteractor( + InactiveTabsController( + context.components.core.store, + tabFilter, + concatAdapter.inactiveTabsAdapter, + context.components.analytics.metrics ) - } + ) + } + override val tabsFeature by lazy { TabsFeature( - tabsAdapter, - store, + tabSorter, + context.components.core.store, selectTabUseCase, removeTabUseCase, - tabFilter, + { !it.content.private }, {} ) } @@ -135,10 +80,12 @@ class NormalBrowserTrayList @JvmOverloads constructor( override fun onAttachedToWindow() { super.onAttachedToWindow() - inactiveFeature.start() - searchTermFeature.start() + concatAdapter.inactiveTabsAdapter.inactiveTabsInteractor = inactiveTabsInteractor + tabsFeature.start() + concatAdapter.browserAdapter.register(swipeDelegate) + touchHelper.attachToRecyclerView(this) } @@ -146,9 +93,22 @@ class NormalBrowserTrayList @JvmOverloads constructor( super.onDetachedFromWindow() tabsFeature.stop() - searchTermFeature.stop() - inactiveFeature.stop() + + concatAdapter.browserAdapter.unregister(swipeDelegate) touchHelper.attachToRecyclerView(null) } + + /** + * A delegate for handling open/selected events from swipe-to-delete gestures. + */ + inner class SwipeToDeleteDelegate : TabsTray.Observer { + override fun onTabClosed(tab: Tab) { + removeTabUseCase.invoke(tab.id, TABS_TRAY_FEATURE_NAME) + } + + override fun onTabSelected(tab: Tab) { + selectTabUseCase.invoke(tab.id) + } + } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt index 3eea9e5a10..1298f85e5d 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt @@ -20,7 +20,6 @@ import org.mozilla.fenix.components.Components import org.mozilla.fenix.ext.components import org.mozilla.fenix.selection.SelectionHolder import org.mozilla.fenix.tabstray.TabsTrayStore -import kotlin.math.max import mozilla.components.concept.tabstray.Tab as TabsTrayTab import mozilla.components.support.base.observer.Observable @@ -102,27 +101,9 @@ class TabGroupAdapter( } /** - * Creates a grouping of data classes for how groupings will be structured. + * Not implemented; implementation is handled [List.toSearchGroups] */ - override fun updateTabs(tabs: Tabs) { - val data = tabs.list.groupBy { it.searchTerm.lowercase() } - - val grouping = data.map { mapEntry -> - val searchTerm = mapEntry.key.replaceFirstChar(Char::uppercase) - val groupTabs = mapEntry.value - val groupMax = groupTabs.fold(0L) { acc, tab -> - max(tab.lastAccess, acc) - } - - Group( - title = searchTerm, - tabs = groupTabs, - lastAccess = groupMax - ) - }.sortedBy { it.lastAccess } - - submitList(grouping) - } + override fun updateTabs(tabs: Tabs) = throw UnsupportedOperationException("Use submitList instead.") /** * Not implemented; handled by nested [RecyclerView]. diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt new file mode 100644 index 0000000000..ddc8aba3e4 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt @@ -0,0 +1,143 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.tabstray.browser + +import android.content.Context +import androidx.recyclerview.widget.ConcatAdapter +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.tabstray.Tab +import mozilla.components.concept.tabstray.Tabs +import mozilla.components.concept.tabstray.TabsTray +import mozilla.components.feature.tabs.tabstray.TabsFeature +import mozilla.components.support.base.observer.Observable +import mozilla.components.support.base.observer.ObserverRegistry +import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.tabstray.ext.browserAdapter +import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter +import org.mozilla.fenix.tabstray.ext.tabGroupAdapter +import kotlin.math.max + +/** + * An intermediary layer to consume tabs from [TabsFeature] for sorting into the various adapters. + */ +class TabSorter( + private val context: Context, + private val concatAdapter: ConcatAdapter, + private val store: BrowserStore +) : TabsTray, Observable by ObserverRegistry() { + override fun updateTabs(tabs: Tabs) { + val inactiveTabs = tabs.list.getInactiveTabs(context) + val searchTermTabs = tabs.list.getSearchGroupTabs(context) + val normalTabs = tabs.list - inactiveTabs - searchTermTabs + val selectedTabId = store.state.selectedTabId + + // Inactive tabs + val selectedInactiveIndex = inactiveTabs.findSelectedIndex(selectedTabId) + concatAdapter.inactiveTabsAdapter.updateTabs((Tabs(inactiveTabs, selectedInactiveIndex))) + + // Tab groups + // We don't need to provide a selectedId, because the [TabGroupAdapter] has that built-in with support from + // NormalBrowserPageViewHolder.scrollToTab. + val (groups, remainderTabs) = searchTermTabs.toSearchGroups() + concatAdapter.tabGroupAdapter.submitList(groups) + + // Normal tabs. + val totalNormalTabs = (normalTabs + remainderTabs) + val selectedTabIndex = totalNormalTabs.findSelectedIndex(selectedTabId) + + // N.B: For regular tabs, we cannot use submitList alone, because the `TabsAdapter` needs to have a reference + // to the new tabs in it. We considered moving the call within `updateTabs` but this would have the side-effect + // of notifying the adapter twice for private tabs which shared the `TabsAdapter`. + concatAdapter.browserAdapter.updateTabs(Tabs(totalNormalTabs, selectedTabIndex)) + concatAdapter.browserAdapter.submitList(totalNormalTabs) + } + + override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false + override fun onTabsChanged(position: Int, count: Int) = Unit + override fun onTabsInserted(position: Int, count: Int) = Unit + override fun onTabsMoved(fromPosition: Int, toPosition: Int) = Unit + override fun onTabsRemoved(position: Int, count: Int) = Unit +} + +private fun List.findSelectedIndex(tabId: String?): Int { + val id = tabId ?: return -1 + return indexOfFirst { it.id == id } +} + +/** + * Returns a list of inactive tabs based on our preferences. + */ +private fun List.getInactiveTabs(context: Context): List { + val inactiveTabsEnabled = context.settings().inactiveTabsAreEnabled + return if (inactiveTabsEnabled) { + filter { !it.isActive(maxActiveTime) } + } else { + emptyList() + } +} + +/** + * Returns a list of search term tabs based on our preferences. + */ +private fun List.getSearchGroupTabs(context: Context): List { + val inactiveTabsEnabled = context.settings().inactiveTabsAreEnabled + val tabGroupsEnabled = context.settings().searchTermTabGroupsAreEnabled + return when { + tabGroupsEnabled && inactiveTabsEnabled -> + filter { it.searchTerm.isNotBlank() && it.isActive(maxActiveTime) } + + tabGroupsEnabled -> + filter { it.searchTerm.isNotBlank() } + + else -> emptyList() + } +} + +/** + * Returns true if a tab has not been selected since [maxActiveTime]. + * + * N.B: This is duplicated from [TabSessionState.isActive(Long)] to work for [Tab]. + * + * See also: https://github.com/mozilla-mobile/android-components/issues/11012 + */ +private fun Tab.isActive(maxActiveTime: Long): Boolean { + val lastActiveTime = maxOf(lastAccess, createdAt) + val now = System.currentTimeMillis() + return (now - lastActiveTime <= maxActiveTime) +} + +/** + * Creates a list of grouped search term tabs sorted by last access time and a list of tabs + * that have search terms but would only create groups with a single tab. + * + * N.B: This is duplicated from [List.toSearchGroup()] to work for [Tab]. + * + * See also: https://github.com/mozilla-mobile/android-components/issues/11012 + */ +private fun List.toSearchGroups(): Pair, List> { + val data = groupBy { it.searchTerm.lowercase() } + + val groupings = data.map { mapEntry -> + // Uppercase since we use it for the title. + val searchTerm = mapEntry.key.replaceFirstChar(Char::uppercase) + val groupTabs = mapEntry.value + + // Calculate when the group was last used. + val groupMax = groupTabs.fold(0L) { acc, tab -> + max(tab.lastAccess, acc) + } + + TabGroupAdapter.Group( + title = searchTerm, + tabs = groupTabs, + lastAccess = groupMax + ) + } + + val groups = groupings.filter { it.tabs.size > 1 }.sortedBy { it.lastAccess } + val remainderTabs = (groupings - groups).flatMap { it.tabs } + + return groups to remainderTabs +} From 76c0c4ad1425cfd79c25d707921e8185620ad080 Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Fri, 1 Oct 2021 15:44:24 -0700 Subject: [PATCH 349/517] For #21648: match Pocket sections on home to designs --- .../fenix/compose/ClickableSubstringLink.kt | 9 ++++-- .../mozilla/fenix/compose/SectionHeader.kt | 31 +++++++++++++++++++ .../fenix/home/recenttabs/view/RecentTabs.kt | 3 +- .../pocket/PocketStoriesComposables.kt | 22 ++++++++++--- .../pocket/PocketStoriesViewHolder.kt | 6 ++-- app/src/main/res/values/strings.xml | 2 +- 6 files changed, 60 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/compose/ClickableSubstringLink.kt b/app/src/main/java/org/mozilla/fenix/compose/ClickableSubstringLink.kt index f8bc4bc2bc..89f27519e2 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/ClickableSubstringLink.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/ClickableSubstringLink.kt @@ -14,8 +14,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.sp import mozilla.components.ui.colors.PhotonColors /** @@ -46,7 +46,6 @@ fun ClickableSubstringLink( addStyle( SpanStyle( - textDecoration = TextDecoration.Underline, color = when (isSystemInDarkTheme()) { true -> PhotonColors.Violet40 false -> PhotonColors.Violet70 @@ -62,6 +61,12 @@ fun ClickableSubstringLink( end = text.length ) + addStyle( + SpanStyle(fontSize = 12.sp), + start = 0, + end = clickableEndIndex + ) + addStringAnnotation( tag = "link", annotation = "", diff --git a/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt b/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt index 3c273f720a..b53c87c48d 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt @@ -41,8 +41,39 @@ fun SectionHeader( ) } +/** + * Default layout for the header of a screen section. + * + * @param text [String] to be styled as header and displayed. + * @param modifier [Modifier] to be applied to the [Text]. + */ +@Composable +fun HomeSectionHeader( + text: String, + modifier: Modifier = Modifier +) { + Text( + modifier = modifier, + text = text, + style = TextStyle( + fontFamily = FontFamily(Font(R.font.metropolis_semibold)), + fontSize = 16.sp, + lineHeight = 20.sp + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = FirefoxTheme.colors.textPrimary + ) +} + @Composable @Preview private fun HeadingTextPreview() { SectionHeader(text = "Section title") } + +@Composable +@Preview +private fun HomeHeadingTextPreview() { + HomeSectionHeader(text = "Home section title") +} diff --git a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt index c090e1035f..21ade1eddf 100644 --- a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt +++ b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt @@ -29,7 +29,6 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.layout.ContentScale @@ -251,7 +250,7 @@ private fun RecentTabImage( Box( modifier = Modifier.background( color = when (isSystemInDarkTheme()) { - true -> Color(0xFF42414D) // DarkGrey30 + true -> PhotonColors.DarkGrey30 false -> PhotonColors.LightGrey30 } ) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index 8f4d269586..625868ea46 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -8,13 +8,14 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.pocket import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.itemsIndexed @@ -33,6 +34,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import mozilla.components.concept.fetch.Client import mozilla.components.service.pocket.PocketRecommendedStory import mozilla.components.ui.colors.PhotonColors @@ -110,9 +112,14 @@ fun PocketStories( val listState = rememberLazyListState() val flingBehavior = EagerFlingBehavior(lazyRowState = listState) - LazyRow(state = listState, flingBehavior = flingBehavior) { - itemsIndexed(storiesToShow) { columnIndex, columnItems -> - Column(Modifier.padding(end = if (columnIndex < storiesToShow.size - 1) 8.dp else 0.dp)) { + LazyRow( + contentPadding = PaddingValues(start = 8.dp, end = 8.dp), + state = listState, + flingBehavior = flingBehavior, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + itemsIndexed(storiesToShow) { _, columnItems -> + Column { columnItems.forEachIndexed { rowIndex, story -> if (story == placeholderStory) { ListItemTabLargePlaceholder(stringResource(R.string.pocket_stories_placeholder_text)) { @@ -195,7 +202,12 @@ fun PoweredByPocketHeader( Spacer(modifier = Modifier.width(16.dp)) Column { - Text(text = stringResource(R.string.pocket_stories_feature_title), color = color) + Text( + text = stringResource(R.string.pocket_stories_feature_title), + color = color, + fontSize = 12.sp, + lineHeight = 16.sp + ) ClickableSubstringLink(text, color, linkStartIndex, linkEndIndex) { onExternalLinkClicked("https://www.mozilla.org/en-US/firefox/pocket/") diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt index fd86adabc6..f06b9a0dc9 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt @@ -22,7 +22,7 @@ import mozilla.components.concept.fetch.Client import mozilla.components.lib.state.ext.observeAsComposableState import mozilla.components.service.pocket.PocketRecommendedStory import org.mozilla.fenix.R -import org.mozilla.fenix.compose.SectionHeader +import org.mozilla.fenix.compose.HomeSectionHeader import org.mozilla.fenix.home.HomeFragmentStore import org.mozilla.fenix.theme.FirefoxTheme @@ -90,7 +90,7 @@ fun PocketStories( } Column(modifier = Modifier.padding(vertical = 48.dp)) { - SectionHeader( + HomeSectionHeader( text = stringResource(R.string.pocket_stories_header_1), modifier = Modifier .fillMaxWidth() @@ -102,7 +102,7 @@ fun PocketStories( Spacer(Modifier.height(24.dp)) - SectionHeader( + HomeSectionHeader( text = stringResource(R.string.pocket_stories_categories_header), modifier = Modifier.fillMaxWidth() ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c88228bbe9..1ad89fa25a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1983,7 +1983,7 @@ Discover more - Powered by Pocket + Powered by Pocket. Part of the Firefox family. %s From 8f187af9ae93b52518dbde85bb62efa7b8a4c2e5 Mon Sep 17 00:00:00 2001 From: Christian Sadilek Date: Fri, 1 Oct 2021 19:38:20 -0400 Subject: [PATCH 350/517] Make search term grouping tolerant to (parent tab) navigation Co-authored-by: Grisha Kruglov --- .../history/PagedHistoryProvider.kt | 13 +- .../HistoryMetadataMiddleware.kt | 71 ++++++-- .../HistoryMetadataMiddlewareTest.kt | 155 +++++++++++++++--- 3 files changed, 198 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/history/PagedHistoryProvider.kt b/app/src/main/java/org/mozilla/fenix/components/history/PagedHistoryProvider.kt index 2a88c2be6e..2f6760e2e7 100644 --- a/app/src/main/java/org/mozilla/fenix/components/history/PagedHistoryProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/components/history/PagedHistoryProvider.kt @@ -12,6 +12,7 @@ import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.library.history.History import org.mozilla.fenix.library.history.toHistoryMetadata import org.mozilla.fenix.perf.runBlockingIncrement +import kotlin.math.abs private const val BUFFER_TIME = 15000 /* 15 seconds in ms */ @@ -100,7 +101,7 @@ class DefaultPagedHistoryProvider( suspend fun getMatchingHistory(historyMetadata: History.Metadata): VisitInfo? { val history = historyStorage.getDetailedVisits( start = historyMetadata.visitedAt - BUFFER_TIME, - end = historyMetadata.visitedAt, + end = historyMetadata.visitedAt + BUFFER_TIME, excludeTypes = listOf( VisitType.NOT_A_VISIT, VisitType.DOWNLOAD, @@ -111,7 +112,9 @@ class DefaultPagedHistoryProvider( VisitType.REDIRECT_PERMANENT ) ) - return history.lastOrNull { it.url == historyMetadata.url } + return history + .filter { it.url == historyMetadata.url } + .minByOrNull { abs(historyMetadata.visitedAt - it.visitTime) } } /** @@ -152,8 +155,8 @@ class DefaultPagedHistoryProvider( // items. val historyGroupsInOffset = if (history.isNotEmpty()) { historyGroups?.filter { - history.last().visitedAt <= it.visitedAt && - it.visitedAt <= (history.first().visitedAt + visitedAtBuffer) + history.last().visitedAt <= it.visitedAt - visitedAtBuffer && + it.visitedAt - visitedAtBuffer <= (history.first().visitedAt + visitedAtBuffer) } ?: emptyList() } else { emptyList() @@ -171,7 +174,7 @@ class DefaultPagedHistoryProvider( // url, but we don't have a use case for this currently in the history view. result.addAll( historyGroupsInOffset.map { group -> - group.copy(items = group.items.filter { it.totalViewTime > 0 }.distinctBy { it.url }) + group.copy(items = group.items.distinctBy { it.url }) } ) diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt index f61b5eba34..326b92550e 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt @@ -102,19 +102,17 @@ class HistoryMetadataMiddleware( // changes introduced by the action. These handlers rely on up-to-date tab state, which // is why they're in the "post" section. when (action) { + is TabListAction.AddTabAction -> { + if (!action.tab.content.private) { + createHistoryMetadataIfNeeded(context, action.tab) + } + } // NB: sometimes this fires multiple times after the page finished loading. is ContentAction.UpdateHistoryStateAction -> { context.state.findNormalTab(action.sessionId)?.let { tab -> - // When history state is ready, we can record metadata for this page. - val knownHistoryMetadata = tab.historyMetadata - val metadataPresentForUrl = knownHistoryMetadata != null && - knownHistoryMetadata.url == tab.content.url - // Record metadata for tab if there is no metadata present, or if url of the - // tab changes since we last recorded metadata. - if (!metadataPresentForUrl) { - createHistoryMetadata(context, tab) - } + createHistoryMetadataIfNeeded(context, tab) } + // Once we get a history update let's reset the flag for future loads. directLoadTriggered = false } @@ -127,13 +125,41 @@ class HistoryMetadataMiddleware( } } + private fun createHistoryMetadataIfNeeded( + context: MiddlewareContext, + tab: TabSessionState + ) { + // When history state is ready, we can record metadata for this page. + val knownHistoryMetadata = tab.historyMetadata + val metadataPresentForUrl = knownHistoryMetadata != null && + knownHistoryMetadata.url == tab.content.url + // Record metadata for tab if there is no metadata present, or if url of the + // tab changes since we last recorded metadata. + if (!metadataPresentForUrl) { + createHistoryMetadata(context, tab) + } + } + private fun createHistoryMetadata(context: MiddlewareContext, tab: TabSessionState) { val tabParent = tab.getParent(context.store) val previousUrlIndex = tab.content.history.currentIndex - 1 + val tabMetadataHasSearchTerms = !tab.historyMetadata?.searchTerm.isNullOrBlank() - // Obtain search terms and referrer url either from tab parent, or from the history stack. + // Obtain search terms and referrer url either from tab parent, from the history stack, or + // from the tab itself. + // At a high level, there are two main cases here - 1) either the tab was opened as a 'new tab' + // via the search results page, or 2) a page was opened in the same tab as the search results page. + // Details about the New Tab case: + // - we obtain search terms via tab's parent (the search results page) + // - however, it's possible that parent changed (e.g. user navigated away from the search + // results page). + // - our approach below is to capture search terms from the parent within the tab.historyMetadata + // state on the first load of the tab, and then rely on this data for subsequent page loads on that tab. + // - this way, once a tab becomes part of the search group, it won't leave this search group + // unless a direct navigation event happens. val (searchTerm, referrerUrl) = when { - tabParent != null -> { + // Loading page opened in a New Tab for the first time. + tabParent != null && !tabMetadataHasSearchTerms -> { val searchTerms = tabParent.content.searchTerms.takeUnless { it.isEmpty() } ?: context.state.search.parseSearchTerms(tabParent.content.url) searchTerms to tabParent.content.url @@ -142,14 +168,31 @@ class HistoryMetadataMiddleware( // web content i.e., they followed a link, not if the user navigated directly via // toolbar. !directLoadTriggered && previousUrlIndex >= 0 -> { - val previousUrl = tab.content.history.items[previousUrlIndex].uri - val searchTerms = context.state.search.parseSearchTerms(previousUrl) + // Once a tab is within the search group, only direct navigation event can change that. + val (searchTerms, referrerUrl) = if (tabMetadataHasSearchTerms) { + tab.historyMetadata?.searchTerm to tab.historyMetadata?.referrerUrl + } else { + val previousUrl = tab.content.history.items[previousUrlIndex].uri + context.state.search.parseSearchTerms(previousUrl) to previousUrl + } + if (searchTerms != null) { - searchTerms to previousUrl + searchTerms to referrerUrl } else { null to null } } + // In certain redirect cases, we won't have a previous url in the history stack of the tab, + // but will have the search terms already set on the tab from having gone through this logic + // for the redirecting url. + // In that case, we leave this tab within the search group it's already in. + tabMetadataHasSearchTerms -> { + tab.historyMetadata?.searchTerm to tab.historyMetadata?.referrerUrl + } + // We had no search terms, no history stack, and no parent. + // For example, this would be a search results page itself. + // For now, the original search results page is not part of the search group. + // See https://github.com/mozilla-mobile/fenix/issues/21659. else -> null to null } diff --git a/app/src/test/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddlewareTest.kt b/app/src/test/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddlewareTest.kt index eed33f9613..c84f98c674 100644 --- a/app/src/test/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddlewareTest.kt +++ b/app/src/test/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddlewareTest.kt @@ -27,6 +27,7 @@ import mozilla.components.concept.storage.HistoryMetadataKey import mozilla.components.support.test.ext.joinBlocking import mozilla.components.support.test.mock import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -58,7 +59,7 @@ class HistoryMetadataMiddlewareTest { every { service.createMetadata(any(), any()) } returns expectedKey store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() - verify { service wasNot Called } + verify(exactly = 1) { service.createMetadata(tab) } store.dispatch(ContentAction.UpdateHistoryStateAction(tab.id, emptyList(), currentIndex = 0)).joinBlocking() val capturedTab = slot() @@ -88,7 +89,7 @@ class HistoryMetadataMiddlewareTest { } @Test - fun `GIVEN normal tab has parent WHEN history metadata is recorded THEN search terms and referrer url are provided`() { + fun `GIVEN normal tab has parent with session search terms WHEN history metadata is recorded THEN search terms and referrer url are provided`() { val parentTab = createTab("https://google.com?q=mozilla+website", searchTerms = "mozilla website") val tab = createTab("https://mozilla.org", parent = parentTab) store.dispatch(TabListAction.AddTabAction(parentTab)).joinBlocking() @@ -101,7 +102,7 @@ class HistoryMetadataMiddlewareTest { } @Test - fun `GIVEN normal tab has search results parent without search terms WHEN history metadata is recorded THEN search terms and referrer url are provided`() { + fun `GIVEN normal tab has search results parent without session search terms WHEN history metadata is recorded THEN search terms and referrer url are provided`() { setupGoogleSearchEngine() val parentTab = createTab("https://google.com?q=mozilla+website") @@ -115,6 +116,76 @@ class HistoryMetadataMiddlewareTest { } } + @Test + fun `GIVEN tab opened as new tab from a search page WHEN search page navigates away THEN redirecting or navigating in tab retains original search terms`() { + service = TestingMetadataService() + middleware = HistoryMetadataMiddleware(service) + store = BrowserStore( + middleware = listOf(middleware) + EngineMiddleware.create(engine = mockk()), + initialState = BrowserState() + ) + setupGoogleSearchEngine() + + val parentTab = createTab("https://google.com?q=mozilla+website", searchTerms = "mozilla website") + val tab = createTab("https://google.com?url=https://mozilla.org", parent = parentTab) + store.dispatch(TabListAction.AddTabAction(parentTab, select = true)).joinBlocking() + store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + + with((service as TestingMetadataService).createdMetadata) { + assertEquals(2, this.count()) + assertEquals("https://google.com?q=mozilla+website", this[0].url) + assertNull(this[0].searchTerm) + assertNull(this[0].referrerUrl) + + assertEquals("https://google.com?url=https://mozilla.org", this[1].url) + assertEquals("mozilla website", this[1].searchTerm) + assertEquals("https://google.com?q=mozilla+website", this[1].referrerUrl) + } + + // Both tabs load. + store.dispatch(ContentAction.UpdateHistoryStateAction(parentTab.id, listOf(HistoryItem("Google - mozilla website", "https://google.com?q=mozilla+website")), 0)).joinBlocking() + store.dispatch(ContentAction.UpdateHistoryStateAction(tab.id, listOf(HistoryItem("", "https://google.com?url=mozilla+website")), currentIndex = 0)).joinBlocking() + with((service as TestingMetadataService).createdMetadata) { + assertEquals(2, this.count()) + } + + // Parent navigates away. Search terms are reset. + store.dispatch(ContentAction.UpdateUrlAction(parentTab.id, "https://firefox.com")).joinBlocking() + store.dispatch(ContentAction.UpdateSearchTermsAction(parentTab.id, "")).joinBlocking() + store.dispatch(ContentAction.UpdateHistoryStateAction(parentTab.id, listOf(HistoryItem("Google - mozilla website", "https://google.com?q=mozilla+website"), HistoryItem("Firefox", "https://firefox.com")), 1)).joinBlocking() + with((service as TestingMetadataService).createdMetadata) { + assertEquals(3, this.count()) + assertEquals("https://firefox.com", this[2].url) + assertEquals("mozilla website", this[2].searchTerm) + assertEquals("https://google.com?q=mozilla+website", this[2].referrerUrl) + } + + // Redirect the child tab (url changed, history stack has single item). + store.dispatch(ContentAction.UpdateUrlAction(tab.id, "https://mozilla.org")).joinBlocking() + store.dispatch(ContentAction.UpdateHistoryStateAction(tab.id, listOf(HistoryItem("Mozilla", "https://mozilla.org")), currentIndex = 0)).joinBlocking() + val tab2 = store.state.findTab(tab.id)!! + assertEquals("https://mozilla.org", tab2.content.url) + with((service as TestingMetadataService).createdMetadata) { + assertEquals(4, this.count()) + assertEquals("https://mozilla.org", this[3].url) + assertEquals("mozilla website", this[3].searchTerm) + assertEquals("https://google.com?q=mozilla+website", this[3].referrerUrl) + } + + // Navigate the child tab. + store.dispatch(ContentAction.UpdateUrlAction(tab.id, "https://mozilla.org/manifesto")).joinBlocking() + store.dispatch(ContentAction.UpdateHistoryStateAction(tab.id, listOf(HistoryItem("Mozilla", "https://mozilla.org"), HistoryItem("Mozilla Manifesto", "https://mozilla.org/manifesto")), currentIndex = 1)).joinBlocking() + val tab3 = store.state.findTab(tab.id)!! + assertEquals("https://mozilla.org/manifesto", tab3.content.url) + + with((service as TestingMetadataService).createdMetadata) { + assertEquals(5, this.count()) + assertEquals("https://mozilla.org/manifesto", this[4].url) + assertEquals("mozilla website", this[4].searchTerm) + assertEquals("https://google.com?q=mozilla+website", this[4].referrerUrl) + } + } + @Test fun `GIVEN normal tab has parent WHEN url is the same THEN nothing happens`() { val parentTab = createTab("https://mozilla.org", searchTerms = "mozilla website") @@ -122,8 +193,11 @@ class HistoryMetadataMiddlewareTest { store.dispatch(TabListAction.AddTabAction(parentTab)).joinBlocking() store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + verify(exactly = 1) { service.createMetadata(parentTab, null, null) } + // Without our referrer url check, we would have recorded this metadata. + verify(exactly = 0) { service.createMetadata(tab, "mozilla website", "https://mozilla.org") } store.dispatch(ContentAction.UpdateHistoryStateAction(tab.id, emptyList(), currentIndex = 0)).joinBlocking() - verify { service wasNot Called } + verify(exactly = 1) { service.createMetadata(any(), any(), any()) } } @Test @@ -226,14 +300,15 @@ class HistoryMetadataMiddlewareTest { } @Test - fun `GIVEN tab without meta data WHEN user navigates and new page starts loading THEN nothing happens`() { + fun `GIVEN tab without metadata WHEN user navigates and new page starts loading THEN nothing happens`() { val tab = createTab(url = "https://mozilla.org") store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() - verify { service wasNot Called } + verify(exactly = 1) { service.createMetadata(tab) } store.dispatch(ContentAction.UpdateLoadingStateAction(tab.id, true)).joinBlocking() - verify { service wasNot Called } + verify(exactly = 1) { service.createMetadata(any()) } + verify(exactly = 0) { service.updateMetadata(any(), any()) } } @Test @@ -260,13 +335,22 @@ class HistoryMetadataMiddlewareTest { every { service.createMetadata(any(), any()) } returns expectedKey store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() - verify { service wasNot Called } + val capturedTabs = mutableListOf() + verify { + service.createMetadata(capture(capturedTabs), null, null) + } + assertEquals(tab.id, capturedTabs[0].id) + assertNull(capturedTabs[0].historyMetadata) + assertEquals(expectedKey, store.state.findTab(tab.id)?.historyMetadata) store.dispatch(MediaSessionAction.UpdateMediaMetadataAction(tab.id, mockk())).joinBlocking() - val capturedTab = slot() - verify { service.createMetadata(capture(capturedTab)) } + verify { + service.createMetadata(capture(capturedTabs), null, null) + } - assertEquals(tab.id, capturedTab.captured.id) + // Ugh, why are there three captured tabs when only two invocations of createMetadata happened? + assertEquals(tab.id, capturedTabs[2].id) + assertEquals(expectedKey, capturedTabs[2].historyMetadata) assertEquals(expectedKey, store.state.findTab(tab.id)?.historyMetadata) } @@ -293,27 +377,30 @@ class HistoryMetadataMiddlewareTest { store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() store.dispatch(TabListAction.AddTabAction(otherTab)).joinBlocking() - verify { service wasNot Called } + verify(exactly = 1) { service.createMetadata(any()) } + verify(exactly = 0) { service.updateMetadata(any(), any()) } store.dispatch(TabListAction.AddTabAction(yetAnotherTab, select = true)).joinBlocking() val capturedTab = slot() + verify(exactly = 2) { service.createMetadata(any()) } verify(exactly = 1) { service.updateMetadata(existingKey, capture(capturedTab)) } assertEquals(tab.id, capturedTab.captured.id) } @Test - fun `GIVEN private tab is selected WHEN new tab will be added and selected THEN nothing happens`() { + fun `GIVEN private tab is selected WHEN new tab will be added and selected THEN metadata not updated for private tab`() { val existingKey = HistoryMetadataKey(url = "https://mozilla.org") val tab = createTab(url = "https://mozilla.org", historyMetadata = existingKey, private = true) val otherTab = createTab(url = "https://blog.mozilla.org") val yetAnotherTab = createTab(url = "https://media.mozilla.org") store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() - store.dispatch(TabListAction.AddTabAction(otherTab)).joinBlocking() verify { service wasNot Called } + store.dispatch(TabListAction.AddTabAction(otherTab)).joinBlocking() + verify { service.createMetadata(otherTab) } store.dispatch(TabListAction.AddTabAction(yetAnotherTab, select = true)).joinBlocking() - verify { service wasNot Called } + verify { service.createMetadata(yetAnotherTab) } } @Test @@ -378,10 +465,11 @@ class HistoryMetadataMiddlewareTest { store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() store.dispatch(TabListAction.AddTabAction(otherTab)).joinBlocking() - verify { service wasNot Called } + // 1 because 'tab' already has a metadata key set. + verify(exactly = 1) { service.createMetadata(any()) } store.dispatch(TabListAction.RemoveTabAction(otherTab.id)).joinBlocking() - verify { service wasNot Called } + verify(exactly = 1) { service.createMetadata(any()) } } @Test @@ -394,7 +482,8 @@ class HistoryMetadataMiddlewareTest { store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() store.dispatch(TabListAction.AddTabAction(otherTab)).joinBlocking() store.dispatch(TabListAction.AddTabAction(yetAnotherTab)).joinBlocking() - verify { service wasNot Called } + // 'tab' already has an existing key, so metadata isn't created for it. + verify(exactly = 2) { service.createMetadata(any()) } store.dispatch(TabListAction.RemoveTabsAction(listOf(tab.id, otherTab.id))).joinBlocking() val capturedTab = slot() @@ -410,12 +499,14 @@ class HistoryMetadataMiddlewareTest { val yetAnotherTab = createTab(url = "https://media.mozilla.org") store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + verify { service wasNot Called } store.dispatch(TabListAction.AddTabAction(otherTab)).joinBlocking() store.dispatch(TabListAction.AddTabAction(yetAnotherTab)).joinBlocking() - verify { service wasNot Called } + verify(exactly = 2) { service.createMetadata(any()) } store.dispatch(TabListAction.RemoveTabsAction(listOf(tab.id, otherTab.id))).joinBlocking() - verify { service wasNot Called } + verify(exactly = 2) { service.createMetadata(any()) } + verify(exactly = 0) { service.updateMetadata(any(), any()) } } @Test @@ -428,10 +519,12 @@ class HistoryMetadataMiddlewareTest { store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() store.dispatch(TabListAction.AddTabAction(otherTab)).joinBlocking() store.dispatch(TabListAction.AddTabAction(yetAnotherTab)).joinBlocking() - verify { service wasNot Called } + verify(exactly = 2) { service.createMetadata(any()) } + verify(exactly = 0) { service.updateMetadata(any(), any()) } store.dispatch(TabListAction.RemoveTabsAction(listOf(otherTab.id, yetAnotherTab.id))).joinBlocking() - verify { service wasNot Called } + verify(exactly = 2) { service.createMetadata(any()) } + verify(exactly = 0) { service.updateMetadata(any(), any()) } } private fun setupGoogleSearchEngine() { @@ -457,4 +550,22 @@ class HistoryMetadataMiddlewareTest { ) ).joinBlocking() } + + // Provides a more convenient way of capturing arguments for the functions we care about. + // I.e. capturing arguments in mockk was driving me mad and this is easy to understand and works. + class TestingMetadataService : HistoryMetadataService { + val createdMetadata = mutableListOf() + + override fun createMetadata( + tab: TabSessionState, + searchTerms: String?, + referrerUrl: String? + ): HistoryMetadataKey { + createdMetadata.add(HistoryMetadataKey(tab.content.url, searchTerms, referrerUrl)) + return HistoryMetadataKey(tab.content.url, searchTerms, referrerUrl) + } + + override fun updateMetadata(key: HistoryMetadataKey, tab: TabSessionState) {} + override fun cleanup(olderThan: Long) {} + } } From a199a48a16aea121bbffb06c61093774c24a4037 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Sat, 2 Oct 2021 15:35:28 +0000 Subject: [PATCH 351/517] Update Android Components version to 94.0.20211002143316. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 231baddd61..b34a811be3 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20211001190127" + const val VERSION = "94.0.20211002143316" } From e5f58eaade4dfeaaded9e328c8e2274f73023f62 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Sun, 3 Oct 2021 10:29:39 -0400 Subject: [PATCH 352/517] Issue #21632: Correct inactive tabs survey string (#21669) --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ad89fa25a..65922bd4ad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1956,7 +1956,7 @@ Send - Closes + Close Set links from websites, emails, and messages to open automatically in Firefox. From b2bf7ca5835144f26dc5c8511319108abdbd56d7 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Sun, 3 Oct 2021 15:39:00 +0000 Subject: [PATCH 353/517] Update Android Components version to 94.0.20211003143215. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index b34a811be3..b543b3a019 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20211002143316" + const val VERSION = "94.0.20211003143215" } From ebd336501b6b7296b24fbf0419deeda16c88edfd Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Sat, 2 Oct 2021 18:26:40 -0400 Subject: [PATCH 354/517] For #21611: Add jump back in Contextual Hints --- .../HomeOnboardingDialogFragment.kt | 66 +++++++++++++++++++ .../res/drawable/onboarding_popup_shape.xml | 11 ++++ .../layout/onboarding_jump_back_in_cfr.xml | 61 +++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 app/src/main/res/drawable/onboarding_popup_shape.xml create mode 100644 app/src/main/res/layout/onboarding_jump_back_in_cfr.xml diff --git a/app/src/main/java/org/mozilla/fenix/onboarding/HomeOnboardingDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/onboarding/HomeOnboardingDialogFragment.kt index ad078aadba..f776bead06 100644 --- a/app/src/main/java/org/mozilla/fenix/onboarding/HomeOnboardingDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/onboarding/HomeOnboardingDialogFragment.kt @@ -4,14 +4,22 @@ package org.mozilla.fenix.onboarding +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.os.Bundle +import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.RecyclerView import org.mozilla.fenix.R import org.mozilla.fenix.databinding.FragmentOnboardingHomeDialogBinding +import org.mozilla.fenix.databinding.OnboardingJumpBackInCfrBinding import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.home.recenttabs.view.RecentTabsHeaderViewHolder /** * Dialog displayed once when one or multiples of these sections are shown in the home screen @@ -37,7 +45,65 @@ class HomeOnboardingDialogFragment : DialogFragment() { context?.settings()?.let { settings -> settings.hasShownHomeOnboardingDialog = true } + showJumpCFR() dismiss() } } + + private fun showJumpCFR() { + val jumpBackInView = findJumpBackInView() + jumpBackInView?.let { + val crfDialog = createJumpCRF(anchor = jumpBackInView) + crfDialog?.show() + } + } + + private fun findJumpBackInView(): View? { + val list = activity?.findViewById(R.id.sessionControlRecyclerView) + val count = list?.adapter?.itemCount ?: return null + + for (index in 0..count) { + val viewHolder = list.findViewHolderForAdapterPosition(index) + if (viewHolder is RecentTabsHeaderViewHolder) { + return viewHolder.containerView + } + } + return null + } + + private fun createJumpCRF(anchor: View): Dialog? { + val context: Context = requireContext() + val anchorPosition = IntArray(2) + val popupBinding = OnboardingJumpBackInCfrBinding.inflate(LayoutInflater.from(context)) + val popup = Dialog(context) + + popup.apply { + setContentView(popupBinding.root) + setCancelable(false) + // removing title or setting it as an empty string does not prevent a11y services from assigning one + setTitle(" ") + } + popupBinding.closeInfoBanner.setOnClickListener { + popup.dismiss() + } + + anchor.getLocationOnScreen(anchorPosition) + val (x, y) = anchorPosition + + if (x == 0 && y == 0) { + return null + } + + popupBinding.root.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) + + popup.window?.apply { + val attr = attributes + setGravity(Gravity.START or Gravity.TOP) + attr.x = x + attr.y = y - popupBinding.root.measuredHeight + attributes = attr + setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + } + return popup + } } diff --git a/app/src/main/res/drawable/onboarding_popup_shape.xml b/app/src/main/res/drawable/onboarding_popup_shape.xml new file mode 100644 index 0000000000..7a7b48bd03 --- /dev/null +++ b/app/src/main/res/drawable/onboarding_popup_shape.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/onboarding_jump_back_in_cfr.xml b/app/src/main/res/layout/onboarding_jump_back_in_cfr.xml new file mode 100644 index 0000000000..a1a7a2b18a --- /dev/null +++ b/app/src/main/res/layout/onboarding_jump_back_in_cfr.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + \ No newline at end of file From 2b363b98683a92d8f51af3757b899294908d9308 Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Tue, 21 Sep 2021 12:02:35 -0400 Subject: [PATCH 355/517] For #21618: Integrate Nimbus with MR2 Home Page to enable experimentation --- .../debug/res/raw/initial_experiments.json | 223 +++--------------- .../java/org/mozilla/fenix/FeatureFlags.kt | 16 +- .../org/mozilla/fenix/FenixApplication.kt | 21 +- .../java/org/mozilla/fenix/HomeActivity.kt | 2 +- .../org/mozilla/fenix/components/Analytics.kt | 5 + .../settings/FeatureFlagPreference.kt | 23 ++ .../mozilla/fenix/experiments/Experiments.kt | 3 +- .../fenix/experiments/NimbusFeatures.kt | 120 ++++++++++ .../org/mozilla/fenix/home/HomeFragment.kt | 8 +- .../fenix/settings/CustomizationFragment.kt | 2 +- .../java/org/mozilla/fenix/utils/Settings.kt | 25 +- .../org/mozilla/fenix/FenixApplicationTest.kt | 15 +- 12 files changed, 236 insertions(+), 227 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/experiments/NimbusFeatures.kt diff --git a/app/src/debug/res/raw/initial_experiments.json b/app/src/debug/res/raw/initial_experiments.json index a255084cee..6bced9269e 100644 --- a/app/src/debug/res/raw/initial_experiments.json +++ b/app/src/debug/res/raw/initial_experiments.json @@ -4,163 +4,56 @@ "appId": "org.mozilla.fenix", "appName": "fenix", "channel": "nightly", - "branches": [{ - "slug": "control", - "ratio": 100, - "feature": { - "value": {}, - "enabled": true, - "featureId": "nimbus-validation" - } - }, - { - "slug": "fancy-settings", - "ratio": 0, - "feature": { - "value": { - "settings-title": "Fancy Settings" - }, - "enabled": true, - "featureId": "nimbus-validation" - } - }, + "branches": [ { - "slug": "smiley", + "slug": "no-mr2", "ratio": 0, "feature": { "value": { - "settings-title-punctuation": "\uD83D\uDE03" + "sections-enabled": { + "topSites": true, + "recentExplorations": true, + "recentlySaved": false, + "jumpBackIn": false, + "pocket": false + } }, "enabled": true, - "featureId": "nimbus-validation" + "featureId": "homescreen" } }, { - "slug": "bundled-text", - "ratio": 0, - "feature": { - "value": { - "settings-title": "preferences_category_general" - }, - "enabled": true, - "featureId": "nimbus-validation" - } - } - ], - "outcomes": [], - "arguments": {}, - "probeSets": [], - "startDate": null, - "targeting": "true", - "featureIds": [ - "nimbus-validation" - ], - "application": "org.mozilla.firefox_beta", - "bucketConfig": { - "count": 0, - "start": 0, - "total": 10000, - "namespace": "nimbus-validation-2", - "randomizationUnit": "nimbus_id" - }, - "schemaVersion": "1.5.0", - "userFacingName": "Nimbus Text Variables Validation", - "referenceBranch": "control", - "proposedDuration": 14, - "isEnrollmentPaused": false, - "proposedEnrollment": 7, - "userFacingDescription": "Demonstration experiment to make trivial visible changes to text in Settings", - "last_modified": 1621443780172 - }, - { - "slug": "feature-icon-variables-validation-android", - "appId": "org.mozilla.fenix", - "appName": "fenix", - "channel": "nightly", - "branches": [{ - "slug": "control", + "slug": "full-mr2", "ratio": 100, - "feature": { - "value": {}, - "enabled": true, - "featureId": "nimbus-validation" - } - }, - { - "slug": "edit-menu-icon", - "ratio": 0, "feature": { "value": { - "settings-title": "preferences_category_general", - "settings-icon": "ic_edit" + "sections-enabled": { + "topSites": true, + "recentExplorations": true, + "recentlySaved": true, + "jumpBackIn": true, + "pocket": true + } }, "enabled": true, - "featureId": "nimbus-validation" - } - } - ], - "outcomes": [], - "arguments": {}, - "probeSets": [], - "startDate": null, - "targeting": "true", - "featureIds": [ - "nimbus-validation" - ], - "application": "org.mozilla.firefox_beta", - "bucketConfig": { - "count": 0, - "start": 0, - "total": 10000, - "namespace": "nimbus-validation-2", - "randomizationUnit": "nimbus_id" - }, - "schemaVersion": "1.5.0", - "userFacingName": "Nimbus Icon Variables Validation", - "referenceBranch": "control", - "proposedDuration": 14, - "isEnrollmentPaused": false, - "proposedEnrollment": 7, - "userFacingDescription": "Demonstration experiment to make trivial visible changes to icons in Settings", - "last_modified": 1621443780172 - }, - - { - "slug": "feature-text-variables-validation-ios", - "appId": "org.mozilla.ios.Fennec", - "appName": "firefox_ios", - "channel": "nightly", - "branches": [{ - "slug": "control", - "ratio": 100, - "feature": { - "value": {}, - "enabled": true, - "featureId": "nimbus-validation" + "featureId": "homescreen" } }, { - "slug": "a1", + "slug": "distraction-free", "ratio": 0, "feature": { "value": { - "settings-title": "Menu/Menu.OpenSettingsAction.Title", - "settings-title-punctuation": "…" + "sections-enabled": { + "topSites": true, + "recentExplorations": false, + "recentlySaved": false, + "jumpBackIn": false, + "pocket": false + } }, "enabled": true, - "featureId": "nimbus-validation" - } - }, - { - "slug": "a2", - "ratio": 0, - "feature": { - "value": { - "settings-title": "Settings.General.SectionName", - "settings-title-punctuation": "!" - }, - "enabled": true, - "featureId": "nimbus-validation" + "featureId": "homescreen" } } ], @@ -170,61 +63,9 @@ "startDate": null, "targeting": "true", "featureIds": [ - "nimbus-validation" + "homescreen" ], - "application": "org.mozilla.ios.Fennec", - "bucketConfig": { - "count": 0, - "start": 0, - "total": 10000, - "namespace": "nimbus-validation-2", - "randomizationUnit": "nimbus_id" - }, - "schemaVersion": "1.5.0", - "userFacingName": "Nimbus Text Variables Validation", - "referenceBranch": "control", - "proposedDuration": 14, - "isEnrollmentPaused": false, - "proposedEnrollment": 7, - "userFacingDescription": "Demonstration experiment to make trivial visible changes to text in Settings", - "last_modified": 1621443780172 - }, - { - "slug": "feature-icon-variables-validation-ios", - "appId": "org.mozilla.ios.Fennec", - "appName": "firefox_ios", - "channel": "nightly", - "branches": [{ - "slug": "control", - "ratio": 100, - "feature": { - "value": {}, - "enabled": true, - "featureId": "nimbus-validation" - } - }, - { - "slug": "treatment", - "ratio": 0, - "feature": { - "value": { - "settings-title": "Fancy Settings", - "settings-icon": "menu-ViewMobile" - }, - "enabled": true, - "featureId": "nimbus-validation" - } - } - ], - "outcomes": [], - "arguments": {}, - "probeSets": [], - "startDate": null, - "targeting": "true", - "featureIds": [ - "nimbus-validation" - ], - "application": "org.mozilla.ios.Fennec", + "application": "org.mozilla.firefox_beta", "bucketConfig": { "count": 0, "start": 0, @@ -233,12 +74,12 @@ "randomizationUnit": "nimbus_id" }, "schemaVersion": "1.5.0", - "userFacingName": "Nimbus Icon Variables Validation", + "userFacingName": "Home screen sections test", "referenceBranch": "control", "proposedDuration": 14, "isEnrollmentPaused": false, "proposedEnrollment": 7, - "userFacingDescription": "Demonstration experiment to make trivial visible changes to icons in Settings", + "userFacingDescription": "Experiment to test the home screen configurations", "last_modified": 1621443780172 } ] diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index d621137f94..174eaae4c2 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -31,7 +31,7 @@ object FeatureFlags { /** * Enables the Home button in the browser toolbar to navigate back to the home screen. */ - val showHomeButtonFeature = Config.channel.isNightlyOrDebug + val showHomeButtonFeature = Config.channel.isNightlyOrDebug || Config.channel.isBeta /** * Enables the Start On Home feature in the settings page. @@ -41,17 +41,17 @@ object FeatureFlags { /** * Enables the "recent" tabs feature in the home screen. */ - val showRecentTabsFeature = Config.channel.isNightlyOrDebug + val showRecentTabsFeature = Config.channel.isNightlyOrDebug || Config.channel.isBeta /** * Enables UI features based on history metadata. */ - val historyMetadataUIFeature = Config.channel.isNightlyOrDebug + val historyMetadataUIFeature = Config.channel.isNightlyOrDebug || Config.channel.isBeta /** * Enables the recently saved bookmarks feature in the home screen. */ - val recentBookmarksFeature = Config.channel.isNightlyOrDebug + val recentBookmarksFeature = Config.channel.isNightlyOrDebug || Config.channel.isBeta /** * Identifies and separates the tabs list with a secondary section containing least used tabs. @@ -66,7 +66,7 @@ object FeatureFlags { /** * Enables customizing the home screen */ - val customizeHome = Config.channel.isNightlyOrDebug + val customizeHome = Config.channel.isNightlyOrDebug || Config.channel.isBeta /** * Identifies and separates the tabs list with a group containing search term tabs. @@ -82,7 +82,9 @@ object FeatureFlags { * Show Pocket recommended stories on home. */ fun isPocketRecommendationsFeatureEnabled(context: Context): Boolean { - return Config.channel.isNightlyOrDebug && - "en-US" == LocaleManager.getCurrentLocale(context)?.toLanguageTag() ?: getSystemDefault().toLanguageTag() + return Config.channel.isBeta || ( + Config.channel.isNightlyOrDebug && "en-US" == LocaleManager.getCurrentLocale(context) + ?.toLanguageTag() ?: getSystemDefault().toLanguageTag() + ) } } diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 0bb44796f5..7b2aae00ac 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -68,6 +68,8 @@ import mozilla.components.feature.autofill.AutofillUseCases import mozilla.components.feature.search.ext.buildSearchUrl import mozilla.components.feature.search.ext.waitForSelectedOrDefaultSearchEngine import mozilla.components.service.fxa.manager.SyncEnginesStorage +import org.mozilla.experiments.nimbus.NimbusInterface +import org.mozilla.experiments.nimbus.internal.EnrolledExperiment import org.mozilla.fenix.GleanMetrics.Addons import org.mozilla.fenix.GleanMetrics.AndroidAutofill import org.mozilla.fenix.GleanMetrics.CustomizeHome @@ -690,11 +692,20 @@ open class FenixApplication : LocaleAwareApplication(), Provider { } ) } - CustomizeHome.jumpBackIn.set(settings.showRecentTabsFeature) - CustomizeHome.recentlySaved.set(settings.showRecentBookmarksFeature) - CustomizeHome.mostVisitedSites.set(settings.showTopFrecentSites) - CustomizeHome.recentlyVisited.set(settings.historyMetadataUIFeature) - CustomizeHome.pocket.set(settings.pocketRecommendations) + reportHomeScreenMetrics(settings) + } + + @VisibleForTesting + internal fun reportHomeScreenMetrics(settings: Settings) { + components.analytics.experiments.register(object : NimbusInterface.Observer { + override fun onUpdatesApplied(updated: List) { + CustomizeHome.jumpBackIn.set(settings.showRecentTabsFeature) + CustomizeHome.recentlySaved.set(settings.showRecentBookmarksFeature) + CustomizeHome.mostVisitedSites.set(settings.showTopFrecentSites) + CustomizeHome.recentlyVisited.set(settings.historyMetadataUIFeature) + CustomizeHome.pocket.set(settings.showPocketRecommendationsFeature) + } + }) } protected fun recordOnInit() { diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 5ecaa5adb6..ff1fe2b329 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -269,7 +269,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { components.core.requestInterceptor.setNavigationController(navHost.navController) - if (settings().pocketRecommendations) { + if (settings().showPocketRecommendationsFeature) { components.core.pocketStoriesService.startPeriodicStoriesRefresh() } diff --git a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt index af365fd00c..c617856698 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt @@ -22,6 +22,7 @@ import org.mozilla.fenix.ReleaseChannel import org.mozilla.fenix.components.metrics.AdjustMetricsService import org.mozilla.fenix.components.metrics.GleanMetricsService import org.mozilla.fenix.components.metrics.MetricController +import org.mozilla.fenix.experiments.NimbusFeatures import org.mozilla.fenix.experiments.createNimbus import org.mozilla.fenix.ext.settings import org.mozilla.fenix.perf.lazyMonitored @@ -103,6 +104,10 @@ class Analytics( val experiments: NimbusApi by lazyMonitored { createNimbus(context, BuildConfig.NIMBUS_ENDPOINT) } + + val features: NimbusFeatures by lazyMonitored { + NimbusFeatures(context) + } } fun isSentryEnabled() = !BuildConfig.SENTRY_TOKEN.isNullOrEmpty() diff --git a/app/src/main/java/org/mozilla/fenix/components/settings/FeatureFlagPreference.kt b/app/src/main/java/org/mozilla/fenix/components/settings/FeatureFlagPreference.kt index 0df122c5f4..e363b67b64 100644 --- a/app/src/main/java/org/mozilla/fenix/components/settings/FeatureFlagPreference.kt +++ b/app/src/main/java/org/mozilla/fenix/components/settings/FeatureFlagPreference.kt @@ -23,3 +23,26 @@ fun featureFlagPreference(key: String, default: Boolean, featureFlag: Boolean) = } else { DummyProperty() } + +private class LazyPreference(val key: String, val default: () -> Boolean) : + ReadWriteProperty { + private val property: ReadWriteProperty by lazy { + booleanPreference(key, default()) + } + + override fun getValue(thisRef: PreferencesHolder, property: KProperty<*>) = + this.property.getValue(thisRef, property) + + override fun setValue(thisRef: PreferencesHolder, property: KProperty<*>, value: Boolean) = + this.property.setValue(thisRef, property, value) +} + +/** + * Property delegate for getting and setting lazily a boolean shared preference gated by a feature flag. + */ +fun lazyFeatureFlagPreference(key: String, featureFlag: Boolean, default: () -> Boolean) = + if (featureFlag) { + LazyPreference(key, default) + } else { + DummyProperty() + } diff --git a/app/src/main/java/org/mozilla/fenix/experiments/Experiments.kt b/app/src/main/java/org/mozilla/fenix/experiments/Experiments.kt index 6f83567f3f..cd5d7f9ddb 100644 --- a/app/src/main/java/org/mozilla/fenix/experiments/Experiments.kt +++ b/app/src/main/java/org/mozilla/fenix/experiments/Experiments.kt @@ -14,7 +14,8 @@ package org.mozilla.fenix.experiments enum class FeatureId(val jsonName: String) { NIMBUS_VALIDATION("nimbus-validation"), ANDROID_KEYSTORE("fenix-android-keystore"), - DEFAULT_BROWSER("fenix-default-browser") + DEFAULT_BROWSER("fenix-default-browser"), + HOME_PAGE("homescreen") } /** diff --git a/app/src/main/java/org/mozilla/fenix/experiments/NimbusFeatures.kt b/app/src/main/java/org/mozilla/fenix/experiments/NimbusFeatures.kt new file mode 100644 index 0000000000..79042b18b9 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/experiments/NimbusFeatures.kt @@ -0,0 +1,120 @@ +/* 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.experiments + +import android.content.Context +import org.mozilla.experiments.nimbus.mapKeysAsEnums +import org.mozilla.fenix.Config +import org.mozilla.fenix.FeatureFlags +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.getVariables + +/** + * Component for exposing nimbus Feature Variables. + * For more information see https://experimenter.info/feature-variables-and-me + * + * @param context - A [Context] for accessing the feature variables from nimbus. + */ +class NimbusFeatures(private val context: Context) { + + val homeScreen: HomeScreenFeatures by lazy { + HomeScreenFeatures(context) + } + + /** + * Component that indicates which features should be active on the home screen. + */ + class HomeScreenFeatures(private val context: Context) { + /** + * `FeatureId.HOME_PAGE` feature; the complete JSON, is shown here: + * + * ```json + * { + * "sections-enabled": { + * "topSites": true, + * "recentlySaved": false, + * "jumpBackIn": false, + * "pocket": false, + * "recentExplorations": false + * } + * } + * ``` + */ + + /** + * This enum accompanies the `FeatureId.HOME_PAGE` feature. + * + * These names here should match the names of entries in the JSON. + */ + @Suppress("EnumNaming") + private enum class HomeScreenSection(val default: Boolean) { + topSites(true), + recentlySaved(false), + jumpBackIn(false), + pocket(false), + recentExplorations(false); + + companion object { + /** + * CreateS a map with the corresponding default values for each sections. + */ + fun toMap(context: Context): Map { + return values().associate { section -> + val channelDefault = if (section == pocket) { + FeatureFlags.isPocketRecommendationsFeatureEnabled(context) + } else { + Config.channel.isNightlyOrDebug + } + section to (channelDefault || section.default) + } + } + } + } + + private val homeScreenFeatures: Map by lazy { + val experiments = context.components.analytics.experiments + val variables = experiments.getVariables(FeatureId.HOME_PAGE, false) + val sections: Map = + variables.getBoolMap("sections-enabled")?.mapKeysAsEnums() + ?: HomeScreenSection.toMap(context) + sections + } + + /** + * Indicates if the recently tabs feature is active. + */ + fun isRecentlyTabsActive(): Boolean { + return homeScreenFeatures[HomeScreenSection.jumpBackIn] == true + } + + /** + * Indicates if the recently saved feature is active. + */ + fun isRecentlySavedActive(): Boolean { + return homeScreenFeatures[HomeScreenSection.recentlySaved] == true + } + + /** + * Indicates if the recently exploration feature is active. + */ + fun isRecentExplorationsActive(): Boolean { + return homeScreenFeatures[HomeScreenSection.recentExplorations] == true + } + + /** + * Indicates if the pocket recommendations feature is active. + */ + fun isPocketRecommendationsActive(): Boolean { + return homeScreenFeatures[HomeScreenSection.pocket] == true + } + + /** + * Indicates if the top sites feature is active. + */ + fun isTopSitesActive(): Boolean { + return homeScreenFeatures[HomeScreenSection.topSites] == true + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index e4444ec681..32247d0aad 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -94,10 +94,12 @@ import org.mozilla.fenix.components.toolbar.FenixTabCounterMenu import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.databinding.FragmentHomeBinding import org.mozilla.fenix.ext.asRecentTabs +import org.mozilla.fenix.experiments.FeatureId import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.hideToolbar import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav +import org.mozilla.fenix.ext.recordExposureEvent import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.runIfFragmentIsAttached import org.mozilla.fenix.ext.settings @@ -253,9 +255,7 @@ class HomeFragment : Fragment() { } lifecycleScope.launch(IO) { - if (FeatureFlags.isPocketRecommendationsFeatureEnabled(requireContext()) && - requireContext().settings().pocketRecommendations - ) { + if (requireContext().settings().showPocketRecommendationsFeature) { val categories = components.core.pocketStoriesService.getStories() .groupBy { story -> story.category } .map { (category, stories) -> PocketRecommendedStoryCategory(category, stories) } @@ -370,6 +370,8 @@ class HomeFragment : Fragment() { appBarLayout = binding.homeAppBar activity.themeManager.applyStatusBarTheme(activity) + + requireContext().components.analytics.experiments.recordExposureEvent(FeatureId.HOME_PAGE) return binding.root } diff --git a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt index 607c59cc45..c6a1267a6c 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt @@ -160,7 +160,7 @@ class CustomizationFragment : PreferenceFragmentCompat() { requirePreference(R.string.pref_key_pocket_homescreen_recommendations).apply { isVisible = FeatureFlags.isPocketRecommendationsFeatureEnabled(context) - isChecked = context.settings().pocketRecommendations + isChecked = context.settings().showPocketRecommendationsFeature onPreferenceChangeListener = CustomizeHomeMetricsUpdater() } diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index de9bb1ea61..71d6258530 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -32,6 +32,7 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.components.metrics.MozillaProductDetector import org.mozilla.fenix.components.settings.counterPreference import org.mozilla.fenix.components.settings.featureFlagPreference +import org.mozilla.fenix.components.settings.lazyFeatureFlagPreference import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.experiments.ExperimentBranch import org.mozilla.fenix.experiments.FeatureId @@ -107,9 +108,10 @@ class Settings(private val appContext: Context) : PreferencesHolder { override val preferences: SharedPreferences = appContext.getSharedPreferences(FENIX_PREFERENCES, MODE_PRIVATE) - var showTopFrecentSites by booleanPreference( + var showTopFrecentSites by lazyFeatureFlagPreference( appContext.getPreferenceKey(R.string.pref_key_enable_top_frecent_sites), - default = true + featureFlag = true, + default = { appContext.components.analytics.features.homeScreen.isTopSitesActive() } ) var numberOfAppLaunches by intPreference( @@ -1140,9 +1142,9 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = false ) - var historyMetadataUIFeature by featureFlagPreference( + var historyMetadataUIFeature by lazyFeatureFlagPreference( appContext.getPreferenceKey(R.string.pref_key_history_metadata_feature), - default = FeatureFlags.historyMetadataUIFeature, + default = { appContext.components.analytics.features.homeScreen.isRecentExplorationsActive() }, featureFlag = FeatureFlags.historyMetadataUIFeature || isHistoryMetadataEnabled ) @@ -1150,19 +1152,19 @@ class Settings(private val appContext: Context) : PreferencesHolder { * Indicates if the recent tabs functionality should be visible. * Returns true if the [FeatureFlags.showRecentTabsFeature] and [R.string.pref_key_recent_tabs] are true. */ - var showRecentTabsFeature by featureFlagPreference( + var showRecentTabsFeature by lazyFeatureFlagPreference( appContext.getPreferenceKey(R.string.pref_key_recent_tabs), - default = FeatureFlags.showRecentTabsFeature, - featureFlag = FeatureFlags.showRecentTabsFeature + featureFlag = FeatureFlags.showRecentTabsFeature, + default = { appContext.components.analytics.features.homeScreen.isRecentlyTabsActive() } ) /** * Indicates if the recent saved bookmarks functionality should be visible. * Returns true if the [FeatureFlags.showRecentTabsFeature] and [R.string.pref_key_recent_bookmarks] are true. */ - var showRecentBookmarksFeature by featureFlagPreference( + var showRecentBookmarksFeature by lazyFeatureFlagPreference( appContext.getPreferenceKey(R.string.pref_key_recent_bookmarks), - default = FeatureFlags.recentBookmarksFeature, + default = { appContext.components.analytics.features.homeScreen.isRecentlySavedActive() }, featureFlag = FeatureFlags.recentBookmarksFeature ) @@ -1191,8 +1193,9 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = true ) - var pocketRecommendations by booleanPreference( + var showPocketRecommendationsFeature by lazyFeatureFlagPreference( appContext.getPreferenceKey(R.string.pref_key_pocket_homescreen_recommendations), - default = true + featureFlag = FeatureFlags.isPocketRecommendationsFeatureEnabled(appContext), + default = { appContext.components.analytics.features.homeScreen.isPocketRecommendationsActive() }, ) } diff --git a/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt b/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt index 463d626c3d..92c21aee2a 100644 --- a/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt +++ b/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt @@ -5,8 +5,11 @@ package org.mozilla.fenix import androidx.test.core.app.ApplicationProvider +import io.mockk.Runs import io.mockk.every +import io.mockk.just import io.mockk.mockk +import io.mockk.spyk import io.mockk.verify import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.webextension.DisabledFlags @@ -21,7 +24,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.GleanMetrics.Addons -import org.mozilla.fenix.GleanMetrics.CustomizeHome import org.mozilla.fenix.GleanMetrics.Metrics import org.mozilla.fenix.GleanMetrics.Preferences import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine @@ -81,6 +83,8 @@ class FenixApplicationTest { fun `WHEN setStartupMetrics is called THEN sets some base metrics`() { val expectedAppName = "org.mozilla.fenix" val settings: Settings = mockk() + val application = spyk(application) + every { browsersCache.all(any()).isDefaultBrowser } returns true every { mozillaProductDetector.getMozillaBrowserDefault(any()) } returns expectedAppName every { mozillaProductDetector.getInstalledMozillaProducts(any()) } returns listOf(expectedAppName) @@ -122,7 +126,9 @@ class FenixApplicationTest { every { settings.showRecentBookmarksFeature } returns true every { settings.showTopFrecentSites } returns true every { settings.historyMetadataUIFeature } returns true - every { settings.pocketRecommendations } returns true + every { settings.showPocketRecommendationsFeature } returns true + every { settings.showPocketRecommendationsFeature } returns true + every { application.reportHomeScreenMetrics(settings) } just Runs application.setStartupMetrics(browserStore, settings, browsersCache, mozillaProductDetector) @@ -158,11 +164,6 @@ class FenixApplicationTest { assertEquals("fixed_top", Preferences.toolbarPositionSetting.testGetValue()) assertEquals("standard", Preferences.enhancedTrackingProtection.testGetValue()) assertEquals(listOf("switch", "touch exploration"), Preferences.accessibilityServices.testGetValue()) - assertEquals(true, CustomizeHome.jumpBackIn.testGetValue()) - assertEquals(true, CustomizeHome.recentlySaved.testGetValue()) - assertEquals(true, CustomizeHome.mostVisitedSites.testGetValue()) - assertEquals(true, CustomizeHome.recentlyVisited.testGetValue()) - assertEquals(true, CustomizeHome.pocket.testGetValue()) // Verify that search engine defaults are NOT set. This test does // not mock most of the objects telemetry is collected from. From 361673ae3ee2b4e05f7d92686c07de46572c24ec Mon Sep 17 00:00:00 2001 From: Mugurell Date: Fri, 1 Oct 2021 13:35:52 +0300 Subject: [PATCH 356/517] For #21626 - Avoid showing story invalid properties --- .../viewholders/pocket/PocketStoriesComposables.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index 625868ea46..f3ecd5756b 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -46,6 +46,7 @@ import org.mozilla.fenix.compose.ListItemTabLarge import org.mozilla.fenix.compose.ListItemTabLargePlaceholder import org.mozilla.fenix.compose.SelectableChip import org.mozilla.fenix.compose.StaggeredHorizontalGrid +import org.mozilla.fenix.compose.TabSubtitle import org.mozilla.fenix.compose.TabSubtitleWithInterdot import org.mozilla.fenix.compose.TabTitle import org.mozilla.fenix.theme.FirefoxTheme @@ -76,6 +77,8 @@ fun PocketStory( "{wh}", with(LocalDensity.current) { "${116.dp.toPx().roundToInt()}x${84.dp.toPx().roundToInt()}" } ) + val isValidPublisher = story.publisher.isNotBlank() + val isValidTimeToRead = story.timeToRead >= 0 ListItemTabLarge( client = client, imageUrl = imageUrl, @@ -84,7 +87,13 @@ fun PocketStory( TabTitle(text = story.title, maxLines = 3) }, subtitle = { - TabSubtitleWithInterdot(story.publisher, "${story.timeToRead} min") + if (isValidPublisher && isValidTimeToRead) { + TabSubtitleWithInterdot(story.publisher, "${story.timeToRead} min") + } else if (isValidPublisher) { + TabSubtitle(story.publisher) + } else if (isValidTimeToRead) { + TabSubtitle("${story.timeToRead} min") + } } ) } From 061de542911f0798f5dda85c52a316117f5d2107 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Sat, 2 Oct 2021 03:14:01 -0400 Subject: [PATCH 357/517] Issue #21576: Hide 'Other' title when there are no search groups --- .../org/mozilla/fenix/ext/BrowserState.kt | 2 +- .../tabstray/browser/TitleHeaderBinding.kt | 16 +++---- .../fenix/tabstray/ext/TabSelectors.kt | 13 +++++ .../browser/TitleHeaderBindingTest.kt | 48 ++++++++++++++++++- 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt index 0e01cec0c4..b3fc5016c3 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt @@ -82,7 +82,7 @@ val BrowserState.lastSearchGroup: RecentTab.SearchGroup? /** * Get search term groups sorted by last access time. */ -private fun List.toSearchGroup(): List { +fun List.toSearchGroup(): List { val data = filter { it.isNormalTabActiveWithSearchTerm(maxActiveTime) }.groupBy { diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt index 163ca067e5..7a4b0514b8 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBinding.kt @@ -13,6 +13,7 @@ import mozilla.components.browser.state.store.BrowserStore import mozilla.components.lib.state.helpers.AbstractBinding import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged import org.mozilla.fenix.tabstray.ext.getNormalTrayTabs +import org.mozilla.fenix.tabstray.ext.getSearchTabGroups import org.mozilla.fenix.utils.Settings /** @@ -25,14 +26,13 @@ class TitleHeaderBinding( private val showHeader: (Boolean) -> Unit ) : AbstractBinding(store) { override suspend fun onState(flow: Flow) { - flow.map { - it.getNormalTrayTabs( - settings.searchTermTabGroupsAreEnabled, - settings.inactiveTabsAreEnabled - ) - }.ifChanged { it.size } - .collect { - if (it.isEmpty()) { + val groupsEnabled = settings.searchTermTabGroupsAreEnabled + val inactiveEnabled = settings.inactiveTabsAreEnabled + + flow.map { it.getSearchTabGroups(groupsEnabled) to it.getNormalTrayTabs(groupsEnabled, inactiveEnabled) } + .ifChanged() + .collect { (groups, normalTrayTabs) -> + if (groups.isEmpty() || normalTrayTabs.isEmpty()) { showHeader(false) } else { showHeader(true) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt index 0cac457365..a1a5d3d7b0 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/ext/TabSelectors.kt @@ -8,6 +8,8 @@ import mozilla.components.browser.state.selector.normalTabs import mozilla.components.browser.state.selector.privateTabs import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.TabSessionState +import org.mozilla.fenix.ext.toSearchGroup +import org.mozilla.fenix.tabstray.browser.TabGroup import org.mozilla.fenix.tabstray.browser.maxActiveTime /** @@ -56,3 +58,14 @@ fun BrowserState.getNormalTrayTabs( } } } + +/** + * The list of search groups filtered appropriately based on feature flags. + */ +fun BrowserState.getSearchTabGroups( + searchTermTabGroupsAreEnabled: Boolean +): List = if (searchTermTabGroupsAreEnabled) { + normalTabs.toSearchGroup().filter { it.tabs.size > 1 } +} else { + emptyList() +} diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBindingTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBindingTest.kt index 1fcb1c47ea..68c283b689 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBindingTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/TitleHeaderBindingTest.kt @@ -29,7 +29,7 @@ class TitleHeaderBindingTest { val coroutinesTestRule = MainCoroutineRule() @Test - fun `WHEN normal tabs are added to the list THEN return true`() = runBlockingTest { + fun `WHEN normal tabs are added to the list THEN return false`() = runBlockingTest { var result = false val store = BrowserStore() val settings: Settings = mockk(relaxed = true) @@ -43,11 +43,55 @@ class TitleHeaderBindingTest { store.waitUntilIdle() + assertFalse(result) + } + + @Test + fun `WHEN grouped and normal tabs are added THEN return true`() = runBlockingTest { + var result = false + val store = BrowserStore( + initialState = BrowserState( + listOf( + createTab( + url = "https://mozilla.org", + historyMetadata = HistoryMetadataKey( + url = "https://getpocket.com", + searchTerm = "Mozilla" + ) + ), + createTab( + url = "https://firefox.com", + historyMetadata = HistoryMetadataKey( + url = "https://getpocket.com", + searchTerm = "Mozilla" + ) + ) + ) + ) + ) + val settings: Settings = mockk(relaxed = true) + val binding = TitleHeaderBinding(store, settings) { result = it } + + every { settings.inactiveTabsAreEnabled } returns true + every { settings.searchTermTabGroupsAreEnabled } returns true + + binding.start() + + store.dispatch( + TabListAction.AddTabAction( + createTab( + url = "https://getpocket.com", + ) + ) + ).joinBlocking() + + store.waitUntilIdle() + assertTrue(result) } @Test - fun `WHEN grouped tabs are added to the list THEN return false`() = runBlockingTest { + fun `WHEN grouped tab is added to the list THEN return false`() = runBlockingTest { var result = false val store = BrowserStore() val settings: Settings = mockk(relaxed = true) From 84c61e24a7b922ce9d6256c28aabd2ccea29cd15 Mon Sep 17 00:00:00 2001 From: AndiAJ Date: Mon, 4 Oct 2021 09:43:45 +0300 Subject: [PATCH 358/517] For #21644 fix disabled alwaysStartOnHomeTest UI test --- app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt | 1 - .../org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt index 6e7a06b096..701f34aa74 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt @@ -1438,7 +1438,6 @@ class SmokeTest { } } - @Ignore // to be fixed here https://github.com/mozilla-mobile/fenix/issues/21644 @Test fun alwaysStartOnHomeTest() { val settings = activityTestRule.activity.applicationContext.settings() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt index 2361171b5a..fc1c336568 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt @@ -31,7 +31,7 @@ class SettingsSubMenuTabsRobot { fun verifyStartOnHomeOptions() = assertStartOnHomeOptions() fun clickAlwaysStartOnHomeToggle() { - scrollToElementByText("Always") + scrollToElementByText("Move old tabs to inactive") alwaysStartOnHomeToggle().click() } From 565beb88c98b21bb87a3cd5cb263f54441f9f601 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Mon, 4 Oct 2021 09:22:25 +0300 Subject: [PATCH 359/517] For #21593 - Refactor out "isSelected" from PocketRecommendedStoriesCategory Having the list of categories and the list of selected categories separate in State allows updating them independently. --- .../mozilla/fenix/ext/HomeFragmentState.kt | 17 +++--- .../org/mozilla/fenix/home/HomeFragment.kt | 4 +- .../mozilla/fenix/home/HomeFragmentStore.kt | 31 +++++----- .../SessionControlInteractor.kt | 4 +- ...kt => PocketRecommendedStoriesCategory.kt} | 13 ++-- ...ocketRecommendedStoriesSelectedCategory.kt | 16 +++++ .../pocket/PocketStoriesComposables.kt | 14 +++-- .../pocket/PocketStoriesController.kt | 26 +++----- .../pocket/PocketStoriesInteractor.kt | 4 +- .../pocket/PocketStoriesViewHolder.kt | 10 ++- .../fenix/ext/HomeFragmentStateTest.kt | 50 ++++++++------- .../fenix/home/HomeFragmentStoreTest.kt | 36 +++++------ .../home/SessionControlInteractorTest.kt | 4 +- .../DefaultPocketStoriesControllerTest.kt | 61 +++++++++---------- 14 files changed, 152 insertions(+), 138 deletions(-) rename app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/{PocketRecommendedStoryCategory.kt => PocketRecommendedStoriesCategory.kt} (58%) create mode 100644 app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoriesSelectedCategory.kt diff --git a/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt b/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt index 5f3734f832..488b3e0095 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt @@ -8,7 +8,7 @@ import androidx.annotation.VisibleForTesting import mozilla.components.service.pocket.PocketRecommendedStory import org.mozilla.fenix.home.HomeFragmentState import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.POCKET_STORIES_DEFAULT_CATEGORY_NAME -import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesCategory /** * Get the list of stories to be displayed based on the user selected categories. @@ -21,9 +21,7 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommende fun HomeFragmentState.getFilteredStories( neededStoriesCount: Int ): List { - val currentlySelectedCategories = pocketStoriesCategories.filter { it.isSelected } - - if (currentlySelectedCategories.isEmpty()) { + if (pocketStoriesCategoriesSelections.isEmpty()) { return pocketStoriesCategories .find { it.name == POCKET_STORIES_DEFAULT_CATEGORY_NAME @@ -32,8 +30,13 @@ fun HomeFragmentState.getFilteredStories( ?.take(neededStoriesCount) ?: emptyList() } - val oldestSortedCategories = currentlySelectedCategories - .sortedByDescending { it.lastInteractedWithTimestamp } + val oldestSortedCategories = pocketStoriesCategoriesSelections + .sortedByDescending { it.selectionTimestamp } + .map { selectedCategory -> + pocketStoriesCategories.first { + it.name == selectedCategory.name + } + } val filteredStoriesCount = getFilteredStoriesCount( oldestSortedCategories, neededStoriesCount @@ -57,7 +60,7 @@ fun HomeFragmentState.getFilteredStories( @VisibleForTesting @Suppress("ReturnCount", "NestedBlockDepth") internal fun getFilteredStoriesCount( - selectedCategories: List, + selectedCategories: List, neededStoriesCount: Int ): Map { val totalStoriesInFilteredCategories = selectedCategories.fold(0) { availableStories, category -> diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 32247d0aad..6f89c3ea22 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -116,7 +116,7 @@ import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlView import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.DefaultPocketStoriesController -import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesCategory import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.DefaultTopSitesView import org.mozilla.fenix.onboarding.FenixOnboarding import org.mozilla.fenix.settings.SupportUtils @@ -258,7 +258,7 @@ class HomeFragment : Fragment() { if (requireContext().settings().showPocketRecommendationsFeature) { val categories = components.core.pocketStoriesService.getStories() .groupBy { story -> story.category } - .map { (category, stories) -> PocketRecommendedStoryCategory(category, stories) } + .map { (category, stories) -> PocketRecommendedStoriesCategory(category, stories) } homeFragmentStore.dispatch(HomeFragmentAction.PocketStoriesCategoriesChange(categories)) } else { diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt index 6bf220da1e..9415324565 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt @@ -18,7 +18,8 @@ import org.mozilla.fenix.ext.getFilteredStories import org.mozilla.fenix.historymetadata.HistoryMetadataGroup import org.mozilla.fenix.home.recenttabs.RecentTab import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.POCKET_STORIES_TO_SHOW_COUNT -import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesSelectedCategory /** * The [Store] for holding the [HomeFragmentState] and applying [HomeFragmentAction]s. @@ -69,7 +70,8 @@ data class HomeFragmentState( val recentBookmarks: List = emptyList(), val historyMetadata: List = emptyList(), val pocketStories: List = emptyList(), - val pocketStoriesCategories: List = emptyList() + val pocketStoriesCategories: List = emptyList(), + val pocketStoriesCategoriesSelections: List = emptyList() ) : State sealed class HomeFragmentAction : Action { @@ -99,7 +101,7 @@ sealed class HomeFragmentAction : Action { data class DeselectPocketStoriesCategory(val categoryName: String) : HomeFragmentAction() data class PocketStoriesShown(val storiesShown: List) : HomeFragmentAction() data class PocketStoriesChange(val pocketStories: List) : HomeFragmentAction() - data class PocketStoriesCategoriesChange(val storiesCategories: List) : + data class PocketStoriesCategoriesChange(val storiesCategories: List) : HomeFragmentAction() object RemoveCollectionsPlaceholder : HomeFragmentAction() object RemoveSetDefaultBrowserCard : HomeFragmentAction() @@ -145,29 +147,26 @@ private fun homeFragmentStateReducer( is HomeFragmentAction.RecentBookmarksChange -> state.copy(recentBookmarks = action.recentBookmarks) is HomeFragmentAction.HistoryMetadataChange -> state.copy(historyMetadata = action.historyMetadata) is HomeFragmentAction.SelectPocketStoriesCategory -> { - // Selecting a category means the stories to be displayed needs to also be changed. val updatedCategoriesState = state.copy( - pocketStoriesCategories = state.pocketStoriesCategories.map { - when (it.name == action.categoryName) { - true -> it.copy(isSelected = true, lastInteractedWithTimestamp = System.currentTimeMillis()) - false -> it - } - } + pocketStoriesCategoriesSelections = + state.pocketStoriesCategoriesSelections + PocketRecommendedStoriesSelectedCategory( + name = action.categoryName + ) ) + + // Selecting a category means the stories to be displayed needs to also be changed. return updatedCategoriesState.copy( pocketStories = updatedCategoriesState.getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) ) } is HomeFragmentAction.DeselectPocketStoriesCategory -> { val updatedCategoriesState = state.copy( - // Deselecting a category means the stories to be displayed needs to also be changed. - pocketStoriesCategories = state.pocketStoriesCategories.map { - when (it.name == action.categoryName) { - true -> it.copy(isSelected = false, lastInteractedWithTimestamp = System.currentTimeMillis()) - false -> it - } + pocketStoriesCategoriesSelections = state.pocketStoriesCategoriesSelections.filterNot { + it.name == action.categoryName } ) + + // Deselecting a category means the stories to be displayed needs to also be changed. return updatedCategoriesState.copy( pocketStories = updatedCategoriesState.getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) ) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt index 0a38a95a38..2963da5967 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt @@ -18,7 +18,7 @@ import org.mozilla.fenix.home.recentbookmarks.controller.RecentBookmarksControll import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor import org.mozilla.fenix.home.recenttabs.controller.RecentTabController import org.mozilla.fenix.home.recenttabs.interactor.RecentTabInteractor -import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesCategory import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketStoriesController import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketStoriesInteractor @@ -391,7 +391,7 @@ class SessionControlInteractor( controller.handleCustomizeHomeTapped() } - override fun onCategoryClick(categoryClicked: PocketRecommendedStoryCategory) { + override fun onCategoryClick(categoryClicked: PocketRecommendedStoriesCategory) { pocketStoriesController.handleCategoryClick(categoryClicked) } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoryCategory.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoriesCategory.kt similarity index 58% rename from app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoryCategory.kt rename to app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoriesCategory.kt index f141bf0394..c7f99651cf 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoryCategory.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoriesCategory.kt @@ -13,18 +13,15 @@ import mozilla.components.service.pocket.PocketRecommendedStory const val POCKET_STORIES_DEFAULT_CATEGORY_NAME = "general" /** - * Pocket assigned topic of interest for each story. + * In memory cache of Pocket assigned topic of interest for recommended stories. + * Avoids multiple stories mappings for each time we are interested in their categories. * * One to many relationship with [PocketRecommendedStory]es. * * @property name The exact name of each category. Case sensitive. - * @property stories All [PocketRecommendedStory]es with this category. - * @property isSelected Whether this category is currently selected by the user. - * @property lastInteractedWithTimestamp Last time the user selected or deselected this category. + * @property stories All [PocketRecommendedStory]s with this category. */ -data class PocketRecommendedStoryCategory( +data class PocketRecommendedStoriesCategory( val name: String, - val stories: List = emptyList(), - val isSelected: Boolean = false, - val lastInteractedWithTimestamp: Long = 0L + val stories: List = emptyList() ) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoriesSelectedCategory.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoriesSelectedCategory.kt new file mode 100644 index 0000000000..b016e43ad8 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketRecommendedStoriesSelectedCategory.kt @@ -0,0 +1,16 @@ +/* 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.home.sessioncontrol.viewholders.pocket + +/** + * Details about a selected Pocket recommended stories category. + * + * @property name The exact name of a selected category. Case sensitive. + * @property selectionTimestamp The exact time at which a category was selected. Defaults to [System.currentTimeMillis]. + */ +data class PocketRecommendedStoriesSelectedCategory( + val name: String, + val selectionTimestamp: Long = System.currentTimeMillis() +) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index f3ecd5756b..20b0569787 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -150,21 +150,22 @@ fun PocketStories( } /** - * Displays a list of [PocketRecommendedStoryCategory]. + * Displays a list of [PocketRecommendedStoriesCategory]s. * * @param categories The categories needed to be displayed. * @param onCategoryClick Callback for when the user taps a category. */ @Composable fun PocketStoriesCategories( - categories: List, - onCategoryClick: (PocketRecommendedStoryCategory) -> Unit + categories: List, + selections: List, + onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit ) { StaggeredHorizontalGrid( horizontalItemsSpacing = 16.dp ) { categories.filter { it.name != POCKET_STORIES_DEFAULT_CATEGORY_NAME }.forEach { category -> - SelectableChip(category.name, category.isSelected) { + SelectableChip(category.name, selections.map { it.name }.contains(category.name)) { onCategoryClick(category) } } @@ -241,8 +242,9 @@ private fun PocketStoriesComposablesPreview() { PocketStoriesCategories( "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor".split(" ").map { - PocketRecommendedStoryCategory(it) - } + PocketRecommendedStoriesCategory(it) + }, + emptyList() ) { } Spacer(Modifier.height(10.dp)) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt index 56ee8c989f..627415e8ba 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt @@ -19,11 +19,11 @@ import org.mozilla.fenix.R */ interface PocketStoriesController { /** - * Callback allowing to handle a specific [PocketRecommendedStoryCategory] being clicked by the user. + * Callback allowing to handle a specific [PocketRecommendedStoriesCategory] being clicked by the user. * - * @param categoryClicked the just clicked [PocketRecommendedStoryCategory]. + * @param categoryClicked the just clicked [PocketRecommendedStoriesCategory]. */ - fun handleCategoryClick(categoryClicked: PocketRecommendedStoryCategory): Unit + fun handleCategoryClick(categoryClicked: PocketRecommendedStoriesCategory): Unit /** * Callback to decide what should happen as an effect of a new list of stories being shown. @@ -52,30 +52,20 @@ internal class DefaultPocketStoriesController( private val homeStore: HomeFragmentStore, private val navController: NavController ) : PocketStoriesController { - override fun handleCategoryClick(categoryClicked: PocketRecommendedStoryCategory) { - val allCategories = homeStore.state.pocketStoriesCategories + override fun handleCategoryClick(categoryClicked: PocketRecommendedStoriesCategory) { + val initialCategoriesSelections = homeStore.state.pocketStoriesCategoriesSelections // First check whether the category is clicked to be deselected. - if (categoryClicked.isSelected) { + if (initialCategoriesSelections.map { it.name }.contains(categoryClicked.name)) { homeStore.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(categoryClicked.name)) return } // If a new category is clicked to be selected: // Ensure the number of categories selected at a time is capped. - val currentlySelectedCategoriesCount = allCategories.fold(0) { count, category -> - if (category.isSelected) count + 1 else count - } val oldestCategoryToDeselect = - if (currentlySelectedCategoriesCount == POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT) { - allCategories - .filter { it.isSelected } - .reduce { oldestSelected, category -> - when (oldestSelected.lastInteractedWithTimestamp <= category.lastInteractedWithTimestamp) { - true -> oldestSelected - false -> category - } - } + if (initialCategoriesSelections.size == POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT) { + initialCategoriesSelections.minByOrNull { it.selectionTimestamp } } else { null } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt index 7f4d18a591..67dab51372 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt @@ -13,9 +13,9 @@ interface PocketStoriesInteractor { /** * Callback for when the user clicked a specific category. * - * @param categoryClicked the just clicked [PocketRecommendedStoryCategory]. + * @param categoryClicked the just clicked [PocketRecommendedStoriesCategory]. */ - fun onCategoryClick(categoryClicked: PocketRecommendedStoryCategory) + fun onCategoryClick(categoryClicked: PocketRecommendedStoriesCategory) /** * Callback for then new stories are shown to the user. diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt index f06b9a0dc9..88a58abb7a 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt @@ -72,7 +72,7 @@ fun PocketStories( store: HomeFragmentStore, client: Client, onStoriesShown: (List) -> Unit, - onCategoryClick: (PocketRecommendedStoryCategory) -> Unit, + onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit, onExternalLinkClicked: (String) -> Unit ) { val stories = store @@ -81,6 +81,9 @@ fun PocketStories( val categories = store .observeAsComposableState { state -> state.pocketStoriesCategories }.value + val categoriesSelections = store + .observeAsComposableState { state -> state.pocketStoriesCategoriesSelections }.value + LaunchedEffect(stories) { // We should report back when a certain story is actually being displayed. // Cannot do it reliably so for now we'll just mass report everything as being displayed. @@ -109,7 +112,10 @@ fun PocketStories( Spacer(Modifier.height(17.dp)) - PocketStoriesCategories(categories ?: emptyList()) { + PocketStoriesCategories( + categories = categories ?: emptyList(), + selections = categoriesSelections ?: emptyList() + ) { onCategoryClick(it) } diff --git a/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt b/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt index 97f07c4059..ec20715a04 100644 --- a/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt +++ b/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt @@ -12,15 +12,16 @@ import org.junit.Assert.assertTrue import org.junit.Test import org.mozilla.fenix.home.HomeFragmentState import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.POCKET_STORIES_DEFAULT_CATEGORY_NAME -import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesSelectedCategory import kotlin.random.Random class HomeFragmentStateTest { private val otherStoriesCategory = - PocketRecommendedStoryCategory("other", getFakePocketStories(3, "other")) + PocketRecommendedStoriesCategory("other", getFakePocketStories(3, "other")) private val anotherStoriesCategory = - PocketRecommendedStoryCategory("another", getFakePocketStories(3, "another")) - private val defaultStoriesCategory = PocketRecommendedStoryCategory( + PocketRecommendedStoriesCategory("another", getFakePocketStories(3, "another")) + private val defaultStoriesCategory = PocketRecommendedStoriesCategory( POCKET_STORIES_DEFAULT_CATEGORY_NAME, getFakePocketStories(3) ) @@ -60,9 +61,8 @@ class HomeFragmentStateTest { @Test fun `GIVEN a category is selected WHEN getFilteredStories is called for fewer than in the category THEN only stories from that category are returned`() { val homeState = HomeFragmentState( - pocketStoriesCategories = listOf( - otherStoriesCategory.copy(isSelected = true), anotherStoriesCategory, defaultStoriesCategory - ) + pocketStoriesCategories = listOf(otherStoriesCategory, anotherStoriesCategory, defaultStoriesCategory), + pocketStoriesCategoriesSelections = listOf(PocketRecommendedStoriesSelectedCategory(otherStoriesCategory.name)) ) var result = homeState.getFilteredStories(2) @@ -77,10 +77,10 @@ class HomeFragmentStateTest { @Test fun `GIVEN two categories are selected WHEN getFilteredStories is called for fewer than in both THEN only stories from those categories are returned`() { val homeState = HomeFragmentState( - pocketStoriesCategories = listOf( - otherStoriesCategory.copy(isSelected = true), - anotherStoriesCategory.copy(isSelected = true), - defaultStoriesCategory + pocketStoriesCategories = listOf(otherStoriesCategory, anotherStoriesCategory, defaultStoriesCategory), + pocketStoriesCategoriesSelections = listOf( + PocketRecommendedStoriesSelectedCategory(otherStoriesCategory.name), + PocketRecommendedStoriesSelectedCategory(anotherStoriesCategory.name) ) ) @@ -103,19 +103,19 @@ class HomeFragmentStateTest { @Test fun `GIVEN two categories are selected WHEN getFilteredStories is called for an odd number of stories THEN there are more by one stories from the newest category`() { - val firstSelectedCategory = otherStoriesCategory.copy(lastInteractedWithTimestamp = 0, isSelected = true) - val lastSelectedCategory = anotherStoriesCategory.copy(lastInteractedWithTimestamp = 1, isSelected = true) val homeState = HomeFragmentState( - pocketStoriesCategories = listOf( - firstSelectedCategory, lastSelectedCategory, defaultStoriesCategory + pocketStoriesCategories = listOf(otherStoriesCategory, anotherStoriesCategory, defaultStoriesCategory), + pocketStoriesCategoriesSelections = listOf( + PocketRecommendedStoriesSelectedCategory(otherStoriesCategory.name, selectionTimestamp = 0), + PocketRecommendedStoriesSelectedCategory(anotherStoriesCategory.name, selectionTimestamp = 1) ) ) val result = homeState.getFilteredStories(5) assertEquals(5, result.size) - assertEquals(2, result.filter { it.category == firstSelectedCategory.name }.size) - assertEquals(3, result.filter { it.category == lastSelectedCategory.name }.size) + assertEquals(2, result.filter { it.category == otherStoriesCategory.name }.size) + assertEquals(3, result.filter { it.category == anotherStoriesCategory.name }.size) } @Test @@ -209,8 +209,8 @@ class HomeFragmentStateTest { @Test fun `GIVEN two categories selected with more than needed stories WHEN getFilteredStories is called THEN the results are sorted in the order of least shown`() { - val firstCategory = PocketRecommendedStoryCategory( - "first", getFakePocketStories(3, "first"), true, 0 + val firstCategory = PocketRecommendedStoriesCategory( + "first", getFakePocketStories(3, "first") ).run { // Avoid the first item also being the oldest to eliminate a potential bug in code // that would still get the expected result. @@ -224,8 +224,8 @@ class HomeFragmentStateTest { } ) } - val secondCategory = PocketRecommendedStoryCategory( - "second", getFakePocketStories(3, "second"), true, 222 + val secondCategory = PocketRecommendedStoriesCategory( + "second", getFakePocketStories(3, "second") ).run { // Avoid the first item also being the oldest to eliminate a potential bug in code // that would still get the expected result. @@ -240,7 +240,13 @@ class HomeFragmentStateTest { ) } - val homeState = HomeFragmentState(pocketStoriesCategories = listOf(firstCategory, secondCategory)) + val homeState = HomeFragmentState( + pocketStoriesCategories = listOf(firstCategory, secondCategory), + pocketStoriesCategoriesSelections = listOf( + PocketRecommendedStoriesSelectedCategory(firstCategory.name, selectionTimestamp = 0), + PocketRecommendedStoriesSelectedCategory(secondCategory.name, selectionTimestamp = 222) + ) + ) val result = homeState.getFilteredStories(6) diff --git a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt index fb9a5075ff..bfad8e3443 100644 --- a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt @@ -28,7 +28,8 @@ import org.mozilla.fenix.ext.getFilteredStories import org.mozilla.fenix.historymetadata.HistoryMetadataGroup import org.mozilla.fenix.home.recenttabs.RecentTab import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.POCKET_STORIES_TO_SHOW_COUNT -import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesSelectedCategory import org.mozilla.fenix.onboarding.FenixOnboarding class HomeFragmentStoreTest { @@ -189,8 +190,8 @@ class HomeFragmentStoreTest { @Test fun `Test selecting a Pocket recommendations category`() = runBlocking { - val otherStoriesCategory = PocketRecommendedStoryCategory("other") - val anotherStoriesCategory = PocketRecommendedStoryCategory("another") + val otherStoriesCategory = PocketRecommendedStoriesCategory("other") + val anotherStoriesCategory = PocketRecommendedStoriesCategory("another") val filteredStories = listOf(mockk()) homeFragmentStore = HomeFragmentStore( HomeFragmentState( @@ -208,7 +209,7 @@ class HomeFragmentStoreTest { verify { any().getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) } } - val selectedCategories = homeFragmentStore.state.pocketStoriesCategories.filter { it.isSelected } + val selectedCategories = homeFragmentStore.state.pocketStoriesCategoriesSelections assertEquals(1, selectedCategories.size) assertTrue(otherStoriesCategory.name === selectedCategories[0].name) assertSame(filteredStories, homeFragmentStore.state.pocketStories) @@ -216,13 +217,15 @@ class HomeFragmentStoreTest { @Test fun `Test deselecting a Pocket recommendations category`() = runBlocking { - val otherStoriesCategory = PocketRecommendedStoryCategory("other", isSelected = true) - val anotherStoriesCategory = PocketRecommendedStoryCategory("another", isSelected = true) + val otherStoriesCategory = PocketRecommendedStoriesCategory("other") + val anotherStoriesCategory = PocketRecommendedStoriesCategory("another") val filteredStories = listOf(mockk()) homeFragmentStore = HomeFragmentStore( HomeFragmentState( - pocketStoriesCategories = listOf( - otherStoriesCategory, anotherStoriesCategory + pocketStoriesCategories = listOf(otherStoriesCategory, anotherStoriesCategory), + pocketStoriesCategoriesSelections = listOf( + PocketRecommendedStoriesSelectedCategory(otherStoriesCategory.name), + PocketRecommendedStoriesSelectedCategory(anotherStoriesCategory.name) ) ) ) @@ -235,10 +238,9 @@ class HomeFragmentStoreTest { verify { any().getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) } } - assertTrue( - listOf(anotherStoriesCategory) - .containsAll(homeFragmentStore.state.pocketStoriesCategories.filter { it.isSelected }) - ) + val selectedCategories = homeFragmentStore.state.pocketStoriesCategoriesSelections + assertEquals(1, selectedCategories.size) + assertTrue(anotherStoriesCategory.name === selectedCategories[0].name) assertSame(filteredStories, homeFragmentStore.state.pocketStories) } @@ -259,8 +261,8 @@ class HomeFragmentStoreTest { @Test fun `Test updating the list of Pocket recommendations categories`() = runBlocking { - val otherStoriesCategory = PocketRecommendedStoryCategory("other") - val anotherStoriesCategory = PocketRecommendedStoryCategory("another", isSelected = true) + val otherStoriesCategory = PocketRecommendedStoriesCategory("other") + val anotherStoriesCategory = PocketRecommendedStoriesCategory("another") homeFragmentStore = HomeFragmentStore(HomeFragmentState()) mockkStatic("org.mozilla.fenix.ext.HomeFragmentStateKt") { @@ -268,9 +270,7 @@ class HomeFragmentStoreTest { every { any().getFilteredStories(any()) } returns firstFilteredStories homeFragmentStore.dispatch( - HomeFragmentAction.PocketStoriesCategoriesChange( - listOf(otherStoriesCategory, anotherStoriesCategory) - ) + HomeFragmentAction.PocketStoriesCategoriesChange(listOf(otherStoriesCategory, anotherStoriesCategory)) ).join() verify { any().getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) } assertTrue( @@ -280,7 +280,7 @@ class HomeFragmentStoreTest { ) assertSame(firstFilteredStories, homeFragmentStore.state.pocketStories) - val updatedCategories = listOf(PocketRecommendedStoryCategory("yetAnother")) + val updatedCategories = listOf(PocketRecommendedStoriesCategory("yetAnother")) val secondFilteredStories = listOf(mockk()) every { any().getFilteredStories(any()) } returns secondFilteredStories homeFragmentStore.dispatch( diff --git a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt index 62ed354bde..7be805ce99 100644 --- a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt @@ -18,7 +18,7 @@ import org.mozilla.fenix.home.recentbookmarks.controller.RecentBookmarksControll import org.mozilla.fenix.home.recenttabs.controller.RecentTabController import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor -import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesCategory import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketStoriesController class SessionControlInteractorTest { @@ -215,7 +215,7 @@ class SessionControlInteractorTest { @Test fun `GIVEN a PocketStoriesInteractor WHEN a category is clicked THEN handle it in a PocketStoriesController`() { - val clickedCategory: PocketRecommendedStoryCategory = mockk() + val clickedCategory: PocketRecommendedStoriesCategory = mockk() interactor.onCategoryClick(clickedCategory) diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt index c1548663a9..ad4effd8f2 100644 --- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt @@ -21,11 +21,15 @@ import org.mozilla.fenix.home.HomeFragmentStore class DefaultPocketStoriesControllerTest { @Test fun `GIVEN a category is selected WHEN that same category is clicked THEN deselect it`() { - val category1 = PocketRecommendedStoryCategory("cat1", emptyList(), isSelected = false) - val category2 = PocketRecommendedStoryCategory("cat2", emptyList(), isSelected = true) + val category1 = PocketRecommendedStoriesCategory("cat1", emptyList()) + val category2 = PocketRecommendedStoriesCategory("cat2", emptyList()) + val selections = listOf(PocketRecommendedStoriesSelectedCategory(category2.name)) val store = spyk( HomeFragmentStore( - HomeFragmentState(pocketStoriesCategories = listOf(category1, category2)) + HomeFragmentState( + pocketStoriesCategories = listOf(category1, category2), + pocketStoriesCategoriesSelections = selections + ) ) ) val controller = DefaultPocketStoriesController(mockk(), store, mockk()) @@ -39,23 +43,19 @@ class DefaultPocketStoriesControllerTest { @Test fun `GIVEN 8 categories are selected WHEN when a new one is clicked THEN the oldest selected is deselected before selecting the new one`() { - val category1 = PocketRecommendedStoryCategory( - "cat1", emptyList(), isSelected = true, lastInteractedWithTimestamp = 111 - ) - val category2 = category1.copy("cat2", lastInteractedWithTimestamp = 222) - val category3 = category1.copy("cat3", lastInteractedWithTimestamp = 333) - val oldestSelectedCategory = category1.copy("oldestSelectedCategory", lastInteractedWithTimestamp = 0) - val category4 = category1.copy("cat4", lastInteractedWithTimestamp = 444) - val category5 = category1.copy("cat5", lastInteractedWithTimestamp = 555) - val category6 = category1.copy("cat6", lastInteractedWithTimestamp = 678) - val category7 = category1.copy("cat6", lastInteractedWithTimestamp = 890) - val newSelectedCategory = category1.copy( - "newSelectedCategory", isSelected = false, lastInteractedWithTimestamp = 654321 - ) + val category1 = PocketRecommendedStoriesSelectedCategory(name = "cat1", selectionTimestamp = 111) + val category2 = PocketRecommendedStoriesSelectedCategory(name = "cat2", selectionTimestamp = 222) + val category3 = PocketRecommendedStoriesSelectedCategory(name = "cat3", selectionTimestamp = 333) + val oldestSelectedCategory = PocketRecommendedStoriesSelectedCategory(name = "oldestSelectedCategory", selectionTimestamp = 0) + val category4 = PocketRecommendedStoriesSelectedCategory(name = "cat4", selectionTimestamp = 444) + val category5 = PocketRecommendedStoriesSelectedCategory(name = "cat5", selectionTimestamp = 555) + val category6 = PocketRecommendedStoriesSelectedCategory(name = "cat6", selectionTimestamp = 678) + val category7 = PocketRecommendedStoriesSelectedCategory(name = "cat7", selectionTimestamp = 890) + val newSelectedCategory = PocketRecommendedStoriesSelectedCategory(name = "newSelectedCategory", selectionTimestamp = 654321) val store = spyk( HomeFragmentStore( HomeFragmentState( - pocketStoriesCategories = listOf( + pocketStoriesCategoriesSelections = listOf( category1, category2, category3, category4, category5, category6, category7, oldestSelectedCategory ) ) @@ -63,7 +63,7 @@ class DefaultPocketStoriesControllerTest { ) val controller = DefaultPocketStoriesController(mockk(), store, mockk()) - controller.handleCategoryClick(newSelectedCategory) + controller.handleCategoryClick(PocketRecommendedStoriesCategory(newSelectedCategory.name)) verify { store.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(oldestSelectedCategory.name)) } verify { store.dispatch(HomeFragmentAction.SelectPocketStoriesCategory(newSelectedCategory.name)) } @@ -71,22 +71,17 @@ class DefaultPocketStoriesControllerTest { @Test fun `GIVEN fewer than 8 categories are selected WHEN when a new one is clicked THEN don't deselect anything but select the newly clicked category`() { - val category1 = PocketRecommendedStoryCategory( - "cat1", emptyList(), isSelected = true, lastInteractedWithTimestamp = 111 - ) - val category2 = category1.copy("cat2", lastInteractedWithTimestamp = 222) - val category3 = category1.copy("cat3", lastInteractedWithTimestamp = 333) - val oldestSelectedCategory = category1.copy("oldestSelectedCategory", lastInteractedWithTimestamp = 0) - val category4 = category1.copy("cat4", lastInteractedWithTimestamp = 444) - val category5 = category1.copy("cat5", lastInteractedWithTimestamp = 555) - val category6 = category1.copy("cat6", lastInteractedWithTimestamp = 678) - val newSelectedCategory = category1.copy( - "newSelectedCategory", isSelected = false, lastInteractedWithTimestamp = 654321 - ) + val category1 = PocketRecommendedStoriesSelectedCategory(name = "cat1", selectionTimestamp = 111) + val category2 = PocketRecommendedStoriesSelectedCategory(name = "cat2", selectionTimestamp = 222) + val category3 = PocketRecommendedStoriesSelectedCategory(name = "cat3", selectionTimestamp = 333) + val oldestSelectedCategory = PocketRecommendedStoriesSelectedCategory(name = "oldestSelectedCategory", selectionTimestamp = 0) + val category4 = PocketRecommendedStoriesSelectedCategory(name = "cat4", selectionTimestamp = 444) + val category5 = PocketRecommendedStoriesSelectedCategory(name = "cat5", selectionTimestamp = 555) + val category6 = PocketRecommendedStoriesSelectedCategory(name = "cat6", selectionTimestamp = 678) val store = spyk( HomeFragmentStore( HomeFragmentState( - pocketStoriesCategories = listOf( + pocketStoriesCategoriesSelections = listOf( category1, category2, category3, category4, category5, category6, oldestSelectedCategory ) ) @@ -94,10 +89,10 @@ class DefaultPocketStoriesControllerTest { ) val controller = DefaultPocketStoriesController(mockk(), store, mockk()) - controller.handleCategoryClick(newSelectedCategory) + controller.handleCategoryClick(PocketRecommendedStoriesCategory("newSelectedCategory")) verify(exactly = 0) { store.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(oldestSelectedCategory.name)) } - verify { store.dispatch(HomeFragmentAction.SelectPocketStoriesCategory(newSelectedCategory.name)) } + verify { store.dispatch(HomeFragmentAction.SelectPocketStoriesCategory("newSelectedCategory")) } } @Test From e4489b8d7d6060e20fb774d2cc59d713eae026a4 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Mon, 4 Oct 2021 10:00:38 +0300 Subject: [PATCH 360/517] For #21593 - Persist stories categories selections in a Proto DataStore A fast and easy solution with all the ACID requirements. Also supports easy migrations if later the data we need persisted changes. --- app/build.gradle | 22 +++ .../org/mozilla/fenix/datastore/DataStores.kt | 17 +++ ...SelectedPocketStoriesCategorySerializer.kt | 25 ++++ .../org/mozilla/fenix/home/HomeFragment.kt | 5 +- .../mozilla/fenix/home/HomeFragmentStore.kt | 16 +- .../fenix/home/PocketUpdatesMiddleware.kt | 105 ++++++++++++- .../selected_pocket_stories_categories.proto | 26 ++++ .../fenix/home/HomeFragmentStoreTest.kt | 39 ++++- .../fenix/home/PocketUpdatesMiddlewareTest.kt | 138 +++++++++++++++++- buildSrc/src/main/java/Dependencies.kt | 7 + 10 files changed, 391 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/datastore/DataStores.kt create mode 100644 app/src/main/java/org/mozilla/fenix/datastore/SelectedPocketStoriesCategorySerializer.kt create mode 100644 app/src/main/proto/selected_pocket_stories_categories.proto diff --git a/app/build.gradle b/app/build.gradle index f4cdeccc9e..54b726711b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ plugins { id "com.jetbrains.python.envs" version "0.0.26" + id "com.google.protobuf" version "0.8.17" } apply plugin: 'com.android.application' @@ -525,6 +526,8 @@ dependencies { implementation Deps.androidx_core_ktx implementation Deps.androidx_transition implementation Deps.androidx_work_ktx + implementation Deps.androidx_datastore + implementation Deps.protobuf_javalite implementation Deps.google_material implementation Deps.adjust @@ -589,6 +592,25 @@ dependencies { lintChecks project(":mozilla-lint-rules") } +protobuf { + protoc { + artifact = Deps.protobuf_compiler + } + + // Generates the java Protobuf-lite code for the Protobufs in this project. See + // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation + // for more information. + generateProtoTasks { + all().each { task -> + task.builtins { + java { + option 'lite' + } + } + } + } +} + if (project.hasProperty("coverage")) { tasks.withType(Test).configureEach { jacoco.includeNoLocationClasses = true diff --git a/app/src/main/java/org/mozilla/fenix/datastore/DataStores.kt b/app/src/main/java/org/mozilla/fenix/datastore/DataStores.kt new file mode 100644 index 0000000000..df7d85d188 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/datastore/DataStores.kt @@ -0,0 +1,17 @@ +/* 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.datastore + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.dataStore + +/** + * Application / process unique [DataStore] for IO operations related to Pocket recommended stories selected categories. + */ +internal val Context.pocketStoriesSelectedCategoriesDataStore: DataStore by dataStore( + fileName = "pocket_recommendations_selected_categories.pb", + serializer = SelectedPocketStoriesCategorySerializer +) diff --git a/app/src/main/java/org/mozilla/fenix/datastore/SelectedPocketStoriesCategorySerializer.kt b/app/src/main/java/org/mozilla/fenix/datastore/SelectedPocketStoriesCategorySerializer.kt new file mode 100644 index 0000000000..53acafe369 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/datastore/SelectedPocketStoriesCategorySerializer.kt @@ -0,0 +1,25 @@ +/* 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.datastore + +import androidx.datastore.core.Serializer +import java.io.InputStream +import java.io.OutputStream + +/** + * Serializer for [SelectedPocketStoriesCategories] defined in selected_pocket_stories_categories.proto. + */ +@Suppress("BlockingMethodInNonBlockingContext") +object SelectedPocketStoriesCategorySerializer : Serializer { + override val defaultValue: SelectedPocketStoriesCategories = SelectedPocketStoriesCategories.getDefaultInstance() + + override suspend fun readFrom(input: InputStream): SelectedPocketStoriesCategories { + return SelectedPocketStoriesCategories.parseFrom(input) + } + + override suspend fun writeTo(t: SelectedPocketStoriesCategories, output: OutputStream) { + t.writeTo(output) + } +} diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 6f89c3ea22..c2998bc21e 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -93,6 +93,7 @@ import org.mozilla.fenix.components.tips.providers.MasterPasswordTipProvider import org.mozilla.fenix.components.toolbar.FenixTabCounterMenu import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.databinding.FragmentHomeBinding +import org.mozilla.fenix.datastore.pocketStoriesSelectedCategoriesDataStore import org.mozilla.fenix.ext.asRecentTabs import org.mozilla.fenix.experiments.FeatureId import org.mozilla.fenix.ext.components @@ -248,7 +249,9 @@ class HomeFragment : Fragment() { ), listOf( PocketUpdatesMiddleware( - lifecycleScope, requireComponents.core.pocketStoriesService + lifecycleScope, + requireComponents.core.pocketStoriesService, + requireContext().pocketStoriesSelectedCategoriesDataStore ) ) ) diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt index 9415324565..dd18e0d121 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt @@ -103,6 +103,10 @@ sealed class HomeFragmentAction : Action { data class PocketStoriesChange(val pocketStories: List) : HomeFragmentAction() data class PocketStoriesCategoriesChange(val storiesCategories: List) : HomeFragmentAction() + data class PocketStoriesCategoriesSelectionsChange( + val storiesCategories: List, + val categoriesSelected: List + ) : HomeFragmentAction() object RemoveCollectionsPlaceholder : HomeFragmentAction() object RemoveSetDefaultBrowserCard : HomeFragmentAction() } @@ -172,8 +176,18 @@ private fun homeFragmentStateReducer( ) } is HomeFragmentAction.PocketStoriesCategoriesChange -> { - // Whenever categories change stories to be displayed needs to also be changed. val updatedCategoriesState = state.copy(pocketStoriesCategories = action.storiesCategories) + // Whenever categories change stories to be displayed needs to also be changed. + return updatedCategoriesState.copy( + pocketStories = updatedCategoriesState.getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) + ) + } + is HomeFragmentAction.PocketStoriesCategoriesSelectionsChange -> { + val updatedCategoriesState = state.copy( + pocketStoriesCategories = action.storiesCategories, + pocketStoriesCategoriesSelections = action.categoriesSelected + ) + // Whenever categories change stories to be displayed needs to also be changed. return updatedCategoriesState.copy( pocketStories = updatedCategoriesState.getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) ) diff --git a/app/src/main/java/org/mozilla/fenix/home/PocketUpdatesMiddleware.kt b/app/src/main/java/org/mozilla/fenix/home/PocketUpdatesMiddleware.kt index 12b700ab2a..48534aa734 100644 --- a/app/src/main/java/org/mozilla/fenix/home/PocketUpdatesMiddleware.kt +++ b/app/src/main/java/org/mozilla/fenix/home/PocketUpdatesMiddleware.kt @@ -4,25 +4,57 @@ package org.mozilla.fenix.home +import androidx.annotation.VisibleForTesting +import androidx.datastore.core.DataStore import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import mozilla.components.lib.state.Action import mozilla.components.lib.state.Middleware import mozilla.components.lib.state.MiddlewareContext +import mozilla.components.lib.state.Store import mozilla.components.service.pocket.PocketStoriesService +import org.mozilla.fenix.datastore.SelectedPocketStoriesCategories +import org.mozilla.fenix.datastore.SelectedPocketStoriesCategories.SelectedPocketStoriesCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesSelectedCategory /** * [HomeFragmentStore] middleware reacting in response to Pocket related [Action]s. + * + * @param coroutineScope [CoroutineScope] used for long running operations like disk IO. + * @param pocketStoriesService [PocketStoriesService] used for updating details about the Pocket recommended stories. + * @param selectedPocketCategoriesDataStore [DataStore] used for reading or persisting details about the + * currently selected Pocket recommended stories categories. */ class PocketUpdatesMiddleware( private val coroutineScope: CoroutineScope, - private val pocketStoriesService: PocketStoriesService + private val pocketStoriesService: PocketStoriesService, + private val selectedPocketCategoriesDataStore: DataStore ) : Middleware { override fun invoke( context: MiddlewareContext, next: (HomeFragmentAction) -> Unit, action: HomeFragmentAction ) { + // Pre process actions + when (action) { + is HomeFragmentAction.PocketStoriesCategoriesChange -> { + // Intercept the original action which would only update categories and + // dispatch a new action which also updates which categories are selected by the user + // from previous locally persisted data. + restoreSelectedCategories( + coroutineScope = coroutineScope, + currentCategories = action.storiesCategories, + store = context.store, + selectedPocketCategoriesDataStore = selectedPocketCategoriesDataStore + ) + } + else -> { + // no-op + } + } + next(action) // Post process actions @@ -36,9 +68,80 @@ class PocketUpdatesMiddleware( ) } } + is HomeFragmentAction.SelectPocketStoriesCategory, + is HomeFragmentAction.DeselectPocketStoriesCategory -> { + persistSelectedCategories( + coroutineScope = coroutineScope, + currentCategoriesSelections = context.state.pocketStoriesCategoriesSelections, + selectedPocketCategoriesDataStore = selectedPocketCategoriesDataStore + ) + } else -> { // no-op } } } } + +/** + * Persist [currentCategoriesSelections] for making this details available in between app restarts. + * + * @param coroutineScope [CoroutineScope] used for reading the locally persisted data. + * @param currentCategoriesSelections Currently selected Pocket recommended stories categories. + * @param selectedPocketCategoriesDataStore - DataStore used for persisting [currentCategoriesSelections]. + */ +@VisibleForTesting +internal fun persistSelectedCategories( + coroutineScope: CoroutineScope, + currentCategoriesSelections: List, + selectedPocketCategoriesDataStore: DataStore +) { + val selectedCategories = currentCategoriesSelections + .map { + SelectedPocketStoriesCategory.newBuilder().apply { + name = it.name + selectionTimestamp = it.selectionTimestamp + }.build() + } + + // Irrespective of the current selections or their number overwrite everything we had. + coroutineScope.launch { + selectedPocketCategoriesDataStore.updateData { data -> + data.newBuilderForType().addAllValues(selectedCategories).build() + } + } +} + +/** + * Combines [currentCategories] with the locally persisted data about previously selected categories + * and emits a new [HomeFragmentAction.PocketStoriesCategoriesSelectionsChange] to update these in store. + * + * @param coroutineScope [CoroutineScope] used for reading the locally persisted data. + * @param currentCategories Stories categories currently available + * @param store [Store] that will be updated. + * @param selectedPocketCategoriesDataStore [DataStore] containing details about the previously selected + * stories categories. + */ +@VisibleForTesting +internal fun restoreSelectedCategories( + coroutineScope: CoroutineScope, + currentCategories: List, + store: Store, + selectedPocketCategoriesDataStore: DataStore +) { + coroutineScope.launch { + selectedPocketCategoriesDataStore.data.collect { persistedSelectedCategories -> + store.dispatch( + HomeFragmentAction.PocketStoriesCategoriesSelectionsChange( + currentCategories, + persistedSelectedCategories.valuesList.map { + PocketRecommendedStoriesSelectedCategory( + name = it.name, + selectionTimestamp = it.selectionTimestamp + ) + } + ) + ) + } + } +} diff --git a/app/src/main/proto/selected_pocket_stories_categories.proto b/app/src/main/proto/selected_pocket_stories_categories.proto new file mode 100644 index 0000000000..b65bb34625 --- /dev/null +++ b/app/src/main/proto/selected_pocket_stories_categories.proto @@ -0,0 +1,26 @@ +/* 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/. */ + +syntax = "proto3"; + +package proto; + +option java_package = "org.mozilla.fenix.datastore"; +option java_multiple_files = true; + +// List of currently selected Pocket recommended stories categories. +message SelectedPocketStoriesCategories { + + // Details about a selected Pocket recommended stories category. + // See [org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesSelectedCategory] + message SelectedPocketStoriesCategory { + // Name of this category. + string name = 1; + // Timestamp for when this category was selected. + int64 selectionTimestamp = 2; + } + + // Currently selected Pocket stories categories. + repeated SelectedPocketStoriesCategory values = 1; +} diff --git a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt index bfad8e3443..f5bb4db992 100644 --- a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt @@ -195,8 +195,9 @@ class HomeFragmentStoreTest { val filteredStories = listOf(mockk()) homeFragmentStore = HomeFragmentStore( HomeFragmentState( - pocketStoriesCategories = listOf( - otherStoriesCategory, anotherStoriesCategory + pocketStoriesCategories = listOf(otherStoriesCategory, anotherStoriesCategory), + pocketStoriesCategoriesSelections = listOf( + PocketRecommendedStoriesSelectedCategory(otherStoriesCategory.name), ) ) ) @@ -204,13 +205,13 @@ class HomeFragmentStoreTest { mockkStatic("org.mozilla.fenix.ext.HomeFragmentStateKt") { every { any().getFilteredStories(any()) } returns filteredStories - homeFragmentStore.dispatch(HomeFragmentAction.SelectPocketStoriesCategory("other")).join() + homeFragmentStore.dispatch(HomeFragmentAction.SelectPocketStoriesCategory("another")).join() verify { any().getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) } } val selectedCategories = homeFragmentStore.state.pocketStoriesCategoriesSelections - assertEquals(1, selectedCategories.size) + assertEquals(2, selectedCategories.size) assertTrue(otherStoriesCategory.name === selectedCategories[0].name) assertSame(filteredStories, homeFragmentStore.state.pocketStories) } @@ -293,4 +294,34 @@ class HomeFragmentStoreTest { assertSame(secondFilteredStories, homeFragmentStore.state.pocketStories) } } + + @Test + fun `Test updating the list of selected Pocket recommendations categories`() = runBlocking { + val otherStoriesCategory = PocketRecommendedStoriesCategory("other") + val anotherStoriesCategory = PocketRecommendedStoriesCategory("another") + val selectedCategory = PocketRecommendedStoriesSelectedCategory("selected") + homeFragmentStore = HomeFragmentStore(HomeFragmentState()) + + mockkStatic("org.mozilla.fenix.ext.HomeFragmentStateKt") { + val firstFilteredStories = listOf(mockk()) + every { any().getFilteredStories(any()) } returns firstFilteredStories + + homeFragmentStore.dispatch( + HomeFragmentAction.PocketStoriesCategoriesSelectionsChange( + storiesCategories = listOf(otherStoriesCategory, anotherStoriesCategory), + categoriesSelected = listOf(selectedCategory) + ) + ).join() + verify { any().getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) } + assertTrue( + homeFragmentStore.state.pocketStoriesCategories.containsAll( + listOf(otherStoriesCategory, anotherStoriesCategory) + ) + ) + assertTrue( + homeFragmentStore.state.pocketStoriesCategoriesSelections.containsAll(listOf(selectedCategory)) + ) + assertSame(firstFilteredStories, homeFragmentStore.state.pocketStories) + } + } } diff --git a/app/src/test/java/org/mozilla/fenix/home/PocketUpdatesMiddlewareTest.kt b/app/src/test/java/org/mozilla/fenix/home/PocketUpdatesMiddlewareTest.kt index 591757ff79..97dfa0dd15 100644 --- a/app/src/test/java/org/mozilla/fenix/home/PocketUpdatesMiddlewareTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/PocketUpdatesMiddlewareTest.kt @@ -4,17 +4,26 @@ package org.mozilla.fenix.home +import androidx.datastore.core.DataStore import io.mockk.coVerify +import io.mockk.every import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.TestCoroutineScope import mozilla.components.service.pocket.PocketRecommendedStory import mozilla.components.service.pocket.PocketStoriesService import mozilla.components.support.test.ext.joinBlocking import org.junit.Test +import org.mozilla.fenix.datastore.SelectedPocketStoriesCategories +import org.mozilla.fenix.datastore.SelectedPocketStoriesCategories.SelectedPocketStoriesCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesCategory +import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoriesSelectedCategory +@ExperimentalCoroutinesApi class PocketUpdatesMiddlewareTest { - @ExperimentalCoroutinesApi @Test fun `WHEN PocketStoriesShown is dispatched THEN update PocketStoriesService`() { val story1 = PocketRecommendedStory("title", "url1", "imageUrl", "publisher", "category", 0, timesShown = 0) @@ -22,7 +31,7 @@ class PocketUpdatesMiddlewareTest { val story3 = story1.copy("title3", "url3") val coroutineScope = TestCoroutineScope() val pocketService: PocketStoriesService = mockk(relaxed = true) - val pocketMiddleware = PocketUpdatesMiddleware(coroutineScope, pocketService) + val pocketMiddleware = PocketUpdatesMiddleware(coroutineScope, pocketService, mockk()) val homeStore = HomeFragmentStore( HomeFragmentState( pocketStories = listOf(story1, story2, story3) @@ -34,4 +43,129 @@ class PocketUpdatesMiddlewareTest { coVerify { pocketService.updateStoriesTimesShown(listOf(story2.copy(timesShown = 1))) } } + + @Test + fun `WHEN PocketStoriesCategoriesChange is dispatched THEN intercept and dispatch PocketStoriesCategoriesSelectionsChange`() { + val persistedSelectedCategory: SelectedPocketStoriesCategory = mockk { + every { name } returns "testCategory" + every { selectionTimestamp } returns 123 + } + val persistedSelectedCategories: SelectedPocketStoriesCategories = mockk { + every { valuesList } returns mutableListOf(persistedSelectedCategory) + } + val dataStore: DataStore = mockk { + every { data } returns flowOf(persistedSelectedCategories) + } + val currentCategories = listOf(mockk()) + val pocketMiddleware = PocketUpdatesMiddleware(TestCoroutineScope(), mockk(), dataStore) + val homeStore = spyk( + HomeFragmentStore( + HomeFragmentState( + pocketStoriesCategories = currentCategories + ), + listOf(pocketMiddleware) + ) + ) + + homeStore.dispatch(HomeFragmentAction.PocketStoriesCategoriesChange(currentCategories)).joinBlocking() + + verify { + homeStore.dispatch( + HomeFragmentAction.PocketStoriesCategoriesSelectionsChange( + storiesCategories = currentCategories, + categoriesSelected = listOf( + PocketRecommendedStoriesSelectedCategory("testCategory", 123) + ) + ) + ) + } + } + + @Test + fun `WHEN SelectPocketStoriesCategory is dispatched THEN persist details in DataStore`() { + val categ1 = PocketRecommendedStoriesCategory("categ1") + val categ2 = PocketRecommendedStoriesCategory("categ2") + val dataStore: DataStore = mockk(relaxed = true) + val pocketMiddleware = PocketUpdatesMiddleware(TestCoroutineScope(), mockk(), dataStore) + val homeStore = spyk( + HomeFragmentStore( + HomeFragmentState( + pocketStoriesCategories = listOf(categ1, categ2) + ), + listOf(pocketMiddleware) + ) + ) + + homeStore.dispatch(HomeFragmentAction.SelectPocketStoriesCategory(categ2.name)).joinBlocking() + + // Seems like the most we can test is that an update was made. + coVerify { dataStore.updateData(any()) } + } + + @Test + fun `WHEN DeselectPocketStoriesCategory is dispatched THEN persist details in DataStore`() { + val categ1 = PocketRecommendedStoriesCategory("categ1") + val categ2 = PocketRecommendedStoriesCategory("categ2") + val dataStore: DataStore = mockk(relaxed = true) + val pocketMiddleware = PocketUpdatesMiddleware(TestCoroutineScope(), mockk(), dataStore) + val homeStore = spyk( + HomeFragmentStore( + HomeFragmentState( + pocketStoriesCategories = listOf(categ1, categ2) + ), + listOf(pocketMiddleware) + ) + ) + + homeStore.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(categ2.name)).joinBlocking() + + // Seems like the most we can test is that an update was made. + coVerify { dataStore.updateData(any()) } + } + + @Test + fun `WHEN persistCategories is called THEN update dataStore`() { + val dataStore: DataStore = mockk(relaxed = true) + + persistSelectedCategories(TestCoroutineScope(), listOf(mockk(relaxed = true)), dataStore) + + // Seems like the most we can test is that an update was made. + coVerify { dataStore.updateData(any()) } + } + + @Test + fun `WHEN restoreSelectedCategories is called THEN dispatch PocketStoriesCategoriesSelectionsChange with data read from the persistence layer`() { + val persistedSelectedCategory: SelectedPocketStoriesCategory = mockk { + every { name } returns "testCategory" + every { selectionTimestamp } returns 123 + } + val persistedSelectedCategories: SelectedPocketStoriesCategories = mockk { + every { valuesList } returns mutableListOf(persistedSelectedCategory) + } + val dataStore: DataStore = mockk { + every { data } returns flowOf(persistedSelectedCategories) + } + val currentCategories = listOf(mockk()) + val homeStore = spyk( + HomeFragmentStore(HomeFragmentState()) + ) + + restoreSelectedCategories( + coroutineScope = TestCoroutineScope(), + currentCategories = currentCategories, + store = homeStore, + selectedPocketCategoriesDataStore = dataStore + ) + + coVerify { + homeStore.dispatch( + HomeFragmentAction.PocketStoriesCategoriesSelectionsChange( + storiesCategories = currentCategories, + categoriesSelected = listOf( + PocketRecommendedStoriesSelectedCategory("testCategory", 123) + ) + ) + ) + } + } } diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 8cf74fc072..42f259f37c 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -35,6 +35,7 @@ object Versions { const val androidx_paging = "2.1.2" const val androidx_transition = "1.4.0" const val androidx_work = "2.5.0" + const val androidx_datastore = "1.0.0" const val google_material = "1.2.1" const val mozilla_android_components = AndroidComponents.VERSION @@ -52,6 +53,8 @@ object Versions { const val google_ads_id_version = "16.0.0" const val google_play_store_version = "1.8.0" + + const val protobuf = "3.11.4" // keep in sync with the version used in AS. } @Suppress("unused") @@ -199,8 +202,12 @@ object Deps { const val androidx_transition = "androidx.transition:transition:${Versions.androidx_transition}" const val androidx_work_ktx = "androidx.work:work-runtime-ktx:${Versions.androidx_work}" const val androidx_work_testing = "androidx.work:work-testing:${Versions.androidx_work}" + const val androidx_datastore = "androidx.datastore:datastore:${Versions.androidx_datastore}" const val google_material = "com.google.android.material:material:${Versions.google_material}" + const val protobuf_javalite = "com.google.protobuf:protobuf-javalite:${Versions.protobuf}" + const val protobuf_compiler = "com.google.protobuf:protoc:${Versions.protobuf}" + const val adjust = "com.adjust.sdk:adjust-android:${Versions.adjust}" const val installreferrer = "com.android.installreferrer:installreferrer:${Versions.installreferrer}" From 16a3b92d342f9c5cbb3731ae6390fd44346923c7 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Mon, 4 Oct 2021 12:32:18 +0300 Subject: [PATCH 361/517] For #21593 - Refactor the coroutine from PocketStoriesShown to outside the middleware In so this code will no longer have access to the MiddlewareContext which only makes sense in the thread of the Middleware itself. --- .../fenix/home/PocketUpdatesMiddleware.kt | 35 +++++++++++++++---- .../fenix/home/PocketUpdatesMiddlewareTest.kt | 14 ++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/PocketUpdatesMiddleware.kt b/app/src/main/java/org/mozilla/fenix/home/PocketUpdatesMiddleware.kt index 48534aa734..07df63ae0e 100644 --- a/app/src/main/java/org/mozilla/fenix/home/PocketUpdatesMiddleware.kt +++ b/app/src/main/java/org/mozilla/fenix/home/PocketUpdatesMiddleware.kt @@ -13,6 +13,7 @@ import mozilla.components.lib.state.Action import mozilla.components.lib.state.Middleware import mozilla.components.lib.state.MiddlewareContext import mozilla.components.lib.state.Store +import mozilla.components.service.pocket.PocketRecommendedStory import mozilla.components.service.pocket.PocketStoriesService import org.mozilla.fenix.datastore.SelectedPocketStoriesCategories import org.mozilla.fenix.datastore.SelectedPocketStoriesCategories.SelectedPocketStoriesCategory @@ -60,13 +61,13 @@ class PocketUpdatesMiddleware( // Post process actions when (action) { is HomeFragmentAction.PocketStoriesShown -> { - coroutineScope.launch { - pocketStoriesService.updateStoriesTimesShown( - action.storiesShown.map { - it.copy(timesShown = it.timesShown.inc()) - } - ) - } + persistStories( + coroutineScope = coroutineScope, + pocketStoriesService = pocketStoriesService, + updatedStories = action.storiesShown.map { + it.copy(timesShown = it.timesShown.inc()) + } + ) } is HomeFragmentAction.SelectPocketStoriesCategory, is HomeFragmentAction.DeselectPocketStoriesCategory -> { @@ -83,6 +84,26 @@ class PocketUpdatesMiddleware( } } +/** + * Persist [updatedStories] for making their details available in between app restarts. + * + * @param coroutineScope [CoroutineScope] used for reading the locally persisted data. + * @param pocketStoriesService [PocketStoriesService] used for updating details about the Pocket recommended stories. + * @param updatedStories the list of stories to persist. + */ +@VisibleForTesting +internal fun persistStories( + coroutineScope: CoroutineScope, + pocketStoriesService: PocketStoriesService, + updatedStories: List +) { + coroutineScope.launch { + pocketStoriesService.updateStoriesTimesShown( + updatedStories + ) + } +} + /** * Persist [currentCategoriesSelections] for making this details available in between app restarts. * diff --git a/app/src/test/java/org/mozilla/fenix/home/PocketUpdatesMiddlewareTest.kt b/app/src/test/java/org/mozilla/fenix/home/PocketUpdatesMiddlewareTest.kt index 97dfa0dd15..1314e28ca7 100644 --- a/app/src/test/java/org/mozilla/fenix/home/PocketUpdatesMiddlewareTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/PocketUpdatesMiddlewareTest.kt @@ -44,6 +44,20 @@ class PocketUpdatesMiddlewareTest { coVerify { pocketService.updateStoriesTimesShown(listOf(story2.copy(timesShown = 1))) } } + @Test + fun `WHEN persistStories is called THEN update PocketStoriesService`() { + val stories: List = mockk() + val pocketService: PocketStoriesService = mockk(relaxed = true) + + persistStories( + coroutineScope = TestCoroutineScope(), + pocketStoriesService = pocketService, + updatedStories = stories + ) + + coVerify { pocketService.updateStoriesTimesShown(stories) } + } + @Test fun `WHEN PocketStoriesCategoriesChange is dispatched THEN intercept and dispatch PocketStoriesCategoriesSelectionsChange`() { val persistedSelectedCategory: SelectedPocketStoriesCategory = mockk { From b8946f807a574f23d829fde4995121365d7b3bf7 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Sat, 2 Oct 2021 23:23:56 -0400 Subject: [PATCH 362/517] Issue #21671: Remove Recently Closed from Inactive Tabs --- .../tabstray/browser/InactiveTabViewHolder.kt | 23 -------- .../tabstray/browser/InactiveTabsAdapter.kt | 13 +---- .../layout/inactive_recently_closed_item.xml | 54 ------------------- app/src/main/res/values/strings.xml | 2 +- 4 files changed, 2 insertions(+), 90 deletions(-) delete mode 100644 app/src/main/res/layout/inactive_recently_closed_item.xml diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt index 62101bed61..0e83219d4c 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt @@ -12,7 +12,6 @@ import mozilla.components.browser.toolbar.MAX_URI_LENGTH import mozilla.components.concept.tabstray.Tab import org.mozilla.fenix.R import org.mozilla.fenix.databinding.InactiveFooterItemBinding -import org.mozilla.fenix.databinding.InactiveRecentlyClosedItemBinding import org.mozilla.fenix.databinding.InactiveHeaderItemBinding import org.mozilla.fenix.databinding.InactiveTabListItemBinding import org.mozilla.fenix.ext.components @@ -106,28 +105,6 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite } } - class RecentlyClosedHolder( - itemView: View, - private val browserTrayInteractor: BrowserTrayInteractor, - ) : InactiveTabViewHolder(itemView) { - - val binding = InactiveRecentlyClosedItemBinding.bind(itemView) - - fun bind() { - val context = itemView.context - binding.inactiveRecentlyClosedText.text = - context.getString(R.string.tab_tray_inactive_recently_closed) - - binding.inactiveRecentlyClosed.setOnClickListener { - browserTrayInteractor.onRecentlyClosedClicked() - } - } - - companion object { - const val LAYOUT_ID = R.layout.inactive_recently_closed_item - } - } - class FooterHolder(itemView: View) : InactiveTabViewHolder(itemView) { val binding = InactiveFooterItemBinding.bind(itemView) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt index 8c3f325c17..49dec1eeea 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt @@ -17,7 +17,6 @@ import org.mozilla.fenix.components.Components import org.mozilla.fenix.tabstray.TabsTrayInteractor import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.FooterHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.HeaderHolder -import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.RecentlyClosedHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.TabViewHolder import org.mozilla.fenix.tabstray.ext.autoCloseInterval import mozilla.components.support.base.observer.Observable as ComponentObservable @@ -58,7 +57,6 @@ class InactiveTabsAdapter( HeaderHolder.LAYOUT_ID -> HeaderHolder(view, inactiveTabsInteractor, tabsTrayInteractor) TabViewHolder.LAYOUT_ID -> TabViewHolder(view, browserTrayInteractor, featureName) FooterHolder.LAYOUT_ID -> FooterHolder(view) - RecentlyClosedHolder.LAYOUT_ID -> RecentlyClosedHolder(view, browserTrayInteractor) else -> throw IllegalStateException("Unknown viewType: $viewType") } } @@ -76,16 +74,12 @@ class InactiveTabsAdapter( is HeaderHolder -> { // do nothing. } - is RecentlyClosedHolder -> { - holder.bind() - } } } override fun getItemViewType(position: Int): Int { return when (position) { 0 -> HeaderHolder.LAYOUT_ID - itemCount - 2 -> RecentlyClosedHolder.LAYOUT_ID itemCount - 1 -> FooterHolder.LAYOUT_ID else -> TabViewHolder.LAYOUT_ID } @@ -107,7 +101,7 @@ class InactiveTabsAdapter( val items = tabs.list.map { Item.Tab(it) } val footer = Item.Footer(context.autoCloseInterval) - submitList(listOf(Item.Header) + items + listOf(Item.RecentlyClosed, footer)) + submitList(listOf(Item.Header) + items + listOf(footer)) } override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false @@ -146,11 +140,6 @@ class InactiveTabsAdapter( */ data class Tab(val tab: TabsTrayTab) : Item() - /** - * A button that leads to the Recently Closed section in History. - */ - object RecentlyClosed : Item() - /** * A footer for the inactive tab section. This may be seen only * when at least one inactive tab is present. diff --git a/app/src/main/res/layout/inactive_recently_closed_item.xml b/app/src/main/res/layout/inactive_recently_closed_item.xml deleted file mode 100644 index 7f019216e3..0000000000 --- a/app/src/main/res/layout/inactive_recently_closed_item.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 65922bd4ad..e75078f2a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -748,7 +748,7 @@ Recently closed tabs - Recently closed + Recently closed Account settings From faab3eb7f1d18fa6d84eb679992e614ff2b7c04d Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Mon, 4 Oct 2021 15:37:08 +0000 Subject: [PATCH 363/517] Update Android Components version to 94.0.20211003190228. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index b543b3a019..df13ff9fde 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20211003143215" + const val VERSION = "94.0.20211003190228" } From 6abb2fffa1beaaf0b893c5ff936dcceed692be42 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Fri, 1 Oct 2021 22:15:32 -0400 Subject: [PATCH 364/517] For #21658 - Don't pass Client into composable functions --- .../java/org/mozilla/fenix/compose/Image.kt | 21 ++----------------- .../mozilla/fenix/compose/ListItemTabLarge.kt | 18 +++------------- .../sessioncontrol/SessionControlAdapter.kt | 5 ++--- .../pocket/PocketStoriesComposables.kt | 8 +------ .../pocket/PocketStoriesViewHolder.kt | 7 +------ 5 files changed, 9 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/compose/Image.kt b/app/src/main/java/org/mozilla/fenix/compose/Image.kt index 46d53ec789..d05b10a57e 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/Image.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/Image.kt @@ -11,20 +11,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient -import mozilla.components.concept.fetch.Client -import mozilla.components.concept.fetch.MutableHeaders -import mozilla.components.concept.fetch.Request -import mozilla.components.concept.fetch.Response import mozilla.components.support.images.compose.loader.ImageLoader import mozilla.components.support.images.compose.loader.WithImage +import org.mozilla.fenix.components.components /** * A composable that lays out and draws the image from a given URL while showing a default placeholder * while that image is downloaded or a default fallback image when downloading failed. * - * @param client [Client] instance to be used for downloading the image. - * When using [GeckoViewFetchClient] the image will automatically be cached if it has the right headers. * @param url URL from where the to download the image to be shown. * @param modifier [Modifier] to be applied to the layout. * @param private Whether or not this is a private request. Like in private browsing mode, @@ -37,7 +31,6 @@ import mozilla.components.support.images.compose.loader.WithImage @Composable @Suppress("LongParameterList") fun Image( - client: Client, url: String, modifier: Modifier = Modifier, private: Boolean = false, @@ -46,7 +39,7 @@ fun Image( ) { ImageLoader( url = url, - client = client, + client = components.core.client, private = private, targetSize = targetSize ) { @@ -68,17 +61,7 @@ fun Image( @Preview private fun ImagePreview() { Image( - FakeClient(), "https://mozilla.com", Modifier.height(100.dp).width(200.dp) ) } - -internal class FakeClient : Client() { - override fun fetch(request: Request) = Response( - url = request.url, - status = 200, - body = Response.Body.empty(), - headers = MutableHeaders() - ) -} diff --git a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt index c7836cde83..6d1eb8d776 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt @@ -20,8 +20,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient -import mozilla.components.concept.fetch.Client import org.mozilla.fenix.theme.FirefoxTheme /** @@ -37,8 +35,6 @@ import org.mozilla.fenix.theme.FirefoxTheme * --------------------------------------------- * ``` * - * @param client [Client] instance to be used for downloading the image. - * When using [GeckoViewFetchClient] the image will automatically be cached if it has the right headers. * @param imageUrl URL from where the to download a header image of the tab this composable renders. * @param title Title off the tab this composable renders. * @param caption Optional caption text. @@ -46,13 +42,12 @@ import org.mozilla.fenix.theme.FirefoxTheme */ @Composable fun ListItemTabLarge( - client: Client, imageUrl: String, title: String, caption: String? = null, onClick: (() -> Unit)? = null ) { - ListItemTabSurface(client, imageUrl, onClick) { + ListItemTabSurface(imageUrl, onClick) { TabTitle(text = title, maxLines = 3) if (caption != null) { @@ -77,8 +72,6 @@ fun ListItemTabLarge( * --------------------------------------------- * ``` * - * @param client [Client] instance to be used for downloading the image. - * When using [GeckoViewFetchClient] the image will automatically be cached if it has the right headers. * @param imageUrl URL from where the to download a header image of the tab this composable renders. * @param title Composable rendering the title of the tab this composable represents. * @param subtitle Optional tab caption composable. @@ -86,13 +79,12 @@ fun ListItemTabLarge( */ @Composable fun ListItemTabLarge( - client: Client, imageUrl: String, onClick: () -> Unit, title: @Composable () -> Unit, subtitle: @Composable (() -> Unit)? = null ) { - ListItemTabSurface(client, imageUrl, onClick) { + ListItemTabSurface(imageUrl, onClick) { title() subtitle?.invoke() @@ -102,15 +94,12 @@ fun ListItemTabLarge( /** * Shared default configuration of a ListItemTabLarge Composable. * - * @param client [Client] instance to be used for downloading the image. - * When using [GeckoViewFetchClient] the image will automatically be cached if it has the right headers. * @param imageUrl URL from where the to download a header image of the tab this composable renders. * @param onClick Optional callback to be invoked when this composable is clicked. * @param tabDetails [Composable] Displayed to the the end of the image. Allows for variation in the item text style. */ @Composable private fun ListItemTabSurface( - client: Client, imageUrl: String, onClick: (() -> Unit)? = null, tabDetails: @Composable () -> Unit @@ -132,7 +121,7 @@ private fun ListItemTabSurface( .size(imageWidth, imageHeight) .clip(RoundedCornerShape(8.dp)) - Image(client, imageUrl, imageModifier, false, imageWidth) + Image(imageUrl, imageModifier, false, imageWidth) Spacer(Modifier.width(16.dp)) @@ -151,7 +140,6 @@ private fun ListItemTabSurface( private fun ListItemTabLargePreview() { FirefoxTheme { ListItemTabLarge( - client = FakeClient(), imageUrl = "", title = "This is a very long title for a tab but needs to be so for this preview", caption = "And this is a caption" diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt index ddacd882d8..6f9d299ca2 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt @@ -232,9 +232,8 @@ class SessionControlAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { when (viewType) { PocketStoriesViewHolder.LAYOUT_ID -> return PocketStoriesViewHolder( - ComposeView(parent.context), - store, - components.core.client, + composeView = ComposeView(parent.context), + store = store, interactor = interactor ) RecentTabViewHolder.LAYOUT_ID -> return RecentTabViewHolder( diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index 20b0569787..87b63920d4 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -41,7 +41,6 @@ import mozilla.components.ui.colors.PhotonColors import org.mozilla.fenix.R import org.mozilla.fenix.compose.ClickableSubstringLink import org.mozilla.fenix.compose.EagerFlingBehavior -import org.mozilla.fenix.compose.FakeClient import org.mozilla.fenix.compose.ListItemTabLarge import org.mozilla.fenix.compose.ListItemTabLargePlaceholder import org.mozilla.fenix.compose.SelectableChip @@ -64,13 +63,11 @@ private val placeholderStory = PocketRecommendedStory("", "", "", "", "", 0, 0) * Displays a single [PocketRecommendedStory]. * * @param story The [PocketRecommendedStory] to be displayed. - * @param client [Client] instance to be used for downloading the story header image. * @param onStoryClick Callback for when the user taps on this story. */ @Composable fun PocketStory( @PreviewParameter(PocketStoryProvider::class) story: PocketRecommendedStory, - client: Client, onStoryClick: (PocketRecommendedStory) -> Unit, ) { val imageUrl = story.imageUrl.replace( @@ -80,7 +77,6 @@ fun PocketStory( val isValidPublisher = story.publisher.isNotBlank() val isValidTimeToRead = story.timeToRead >= 0 ListItemTabLarge( - client = client, imageUrl = imageUrl, onClick = { onStoryClick(story) }, title = { @@ -111,7 +107,6 @@ fun PocketStory( @Composable fun PocketStories( @PreviewParameter(PocketStoryProvider::class) stories: List, - client: Client, onExternalLinkClicked: (String) -> Unit ) { // Show stories in at most 3 rows but on any number of columns depending on the data received. @@ -135,7 +130,7 @@ fun PocketStories( onExternalLinkClicked("http://getpocket.com/explore") } } else { - PocketStory(story, client) { + PocketStory(story) { onExternalLinkClicked(story.url) } } @@ -235,7 +230,6 @@ private fun PocketStoriesComposablesPreview() { Column { PocketStories( stories = getFakePocketStories(8), - client = FakeClient(), onExternalLinkClicked = { } ) Spacer(Modifier.height(10.dp)) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt index 88a58abb7a..5f6c7805a4 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt @@ -18,7 +18,6 @@ import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.recyclerview.widget.RecyclerView -import mozilla.components.concept.fetch.Client import mozilla.components.lib.state.ext.observeAsComposableState import mozilla.components.service.pocket.PocketRecommendedStory import org.mozilla.fenix.R @@ -35,13 +34,11 @@ internal const val POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT = 8 * * @param composeView [ComposeView] which will be populated with Jetpack Compose UI content. * @param store [HomeFragmentStore] containing the list of Pocket stories to be displayed. - * @param client [Client] instance used for the stories header images. * @param interactor [PocketStoriesInteractor] callback for user interaction. */ class PocketStoriesViewHolder( val composeView: ComposeView, val store: HomeFragmentStore, - val client: Client, val interactor: PocketStoriesInteractor ) : RecyclerView.ViewHolder(composeView) { @@ -53,7 +50,6 @@ class PocketStoriesViewHolder( FirefoxTheme { PocketStories( store, - client, interactor::onStoriesShown, interactor::onCategoryClick, interactor::onExternalLinkClicked @@ -70,7 +66,6 @@ class PocketStoriesViewHolder( @Composable fun PocketStories( store: HomeFragmentStore, - client: Client, onStoriesShown: (List) -> Unit, onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit, onExternalLinkClicked: (String) -> Unit @@ -101,7 +96,7 @@ fun PocketStories( Spacer(Modifier.height(17.dp)) - PocketStories(stories ?: emptyList(), client, onExternalLinkClicked) + PocketStories(stories ?: emptyList(), onExternalLinkClicked) Spacer(Modifier.height(24.dp)) From e2c9d9abb179db768569a6e80f9bbdc90bd9fa02 Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Mon, 4 Oct 2021 13:10:49 -0700 Subject: [PATCH 365/517] CI for Docs: Add extra context to events.browser_menu_action (#21696) * Docs only: Add extra context to `events.browser_menu_action` This came up as a potential point of confusion in a discussion with DS. This is a docs only change. * Update metrics.yaml Co-authored-by: Will Lachance --- app/metrics.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/metrics.yaml b/app/metrics.yaml index b5985e84c7..3faa4cae88 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -107,7 +107,9 @@ events: browser_menu_action: type: event description: | - A browser menu item was tapped + A browser menu item was tapped. + The name of the item that the user tapped is stored in extras with the + key `item`. extra_keys: item: description: | From f359557ef43b9d1f8a710d17cfa817e81a00f0c8 Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Mon, 4 Oct 2021 13:59:02 -0700 Subject: [PATCH 366/517] CI for Fix description for home_screen_displayed metric (#21697) * Fix description for `home_screen_displayed` metric The current description appears to be incorrect by my reading of the source. * Update app/metrics.yaml Co-authored-by: Gabriel Luong Co-authored-by: Will Lachance Co-authored-by: Gabriel Luong --- app/metrics.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/metrics.yaml b/app/metrics.yaml index 3faa4cae88..4442b0ef99 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -4996,9 +4996,9 @@ home_menu: home_screen: home_screen_displayed: type: event - description: The user clicked the settings option in home menu. + description: The home screen was displayed. bugs: - - https://github.com/mozilla-mobile/fenix/issues/18856 + - https://github.com/mozilla-mobile/fenix/issues/18854 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/19025 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 From 66e54860bb6857ab65d3ace0d340489a57b82115 Mon Sep 17 00:00:00 2001 From: Christian Sadilek Date: Mon, 4 Oct 2021 15:08:02 -0400 Subject: [PATCH 367/517] Move tabs out of search group if direct load occurs This regressed in our previous fix that made sure child tabs don't mistakenly get moved out of the group if their parent is navigated away, or in case the child tabs are redirected. However, when a subsequent load occurs in any tab in the group the search terms need to be cleared and the tab removed from the group to prevent false positives. --- .../HistoryMetadataMiddleware.kt | 14 ++--- .../HistoryMetadataMiddlewareTest.kt | 58 ++++++++++++++++++- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt index 326b92550e..c1c2a8cbd8 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt @@ -168,7 +168,7 @@ class HistoryMetadataMiddleware( // web content i.e., they followed a link, not if the user navigated directly via // toolbar. !directLoadTriggered && previousUrlIndex >= 0 -> { - // Once a tab is within the search group, only direct navigation event can change that. + // Once a tab is within the search group, only a direct load event (via the toolbar) can change that. val (searchTerms, referrerUrl) = if (tabMetadataHasSearchTerms) { tab.historyMetadata?.searchTerm to tab.historyMetadata?.referrerUrl } else { @@ -184,15 +184,15 @@ class HistoryMetadataMiddleware( } // In certain redirect cases, we won't have a previous url in the history stack of the tab, // but will have the search terms already set on the tab from having gone through this logic - // for the redirecting url. - // In that case, we leave this tab within the search group it's already in. - tabMetadataHasSearchTerms -> { + // for the redirecting url. So we leave this tab within the search group it's already in + // unless a new direct load (via the toolbar) was triggered. + tabMetadataHasSearchTerms && !(directLoadTriggered && previousUrlIndex >= 0) -> { tab.historyMetadata?.searchTerm to tab.historyMetadata?.referrerUrl } // We had no search terms, no history stack, and no parent. - // For example, this would be a search results page itself. - // For now, the original search results page is not part of the search group. - // See https://github.com/mozilla-mobile/fenix/issues/21659. + // This would be the case for any page loaded directly via the toolbar including + // a search results page itself. For now, the original search results page is not + // part of the search group: https://github.com/mozilla-mobile/fenix/issues/21659. else -> null to null } diff --git a/app/src/test/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddlewareTest.kt b/app/src/test/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddlewareTest.kt index c84f98c674..2fe95d63eb 100644 --- a/app/src/test/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddlewareTest.kt +++ b/app/src/test/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddlewareTest.kt @@ -149,7 +149,7 @@ class HistoryMetadataMiddlewareTest { assertEquals(2, this.count()) } - // Parent navigates away. Search terms are reset. + // Parent navigates away. store.dispatch(ContentAction.UpdateUrlAction(parentTab.id, "https://firefox.com")).joinBlocking() store.dispatch(ContentAction.UpdateSearchTermsAction(parentTab.id, "")).joinBlocking() store.dispatch(ContentAction.UpdateHistoryStateAction(parentTab.id, listOf(HistoryItem("Google - mozilla website", "https://google.com?q=mozilla+website"), HistoryItem("Firefox", "https://firefox.com")), 1)).joinBlocking() @@ -186,6 +186,62 @@ class HistoryMetadataMiddlewareTest { } } + @Test + fun `GIVEN tab with search terms WHEN subsequent direct load occurs THEN search terms are not retained`() { + service = TestingMetadataService() + middleware = HistoryMetadataMiddleware(service) + store = BrowserStore( + middleware = listOf(middleware) + EngineMiddleware.create(engine = mockk()), + initialState = BrowserState() + ) + setupGoogleSearchEngine() + + val parentTab = createTab("https://google.com?q=mozilla+website", searchTerms = "mozilla website") + val tab = createTab("https://google.com?url=https://mozilla.org", parent = parentTab) + store.dispatch(TabListAction.AddTabAction(parentTab, select = true)).joinBlocking() + store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + + with((service as TestingMetadataService).createdMetadata) { + assertEquals(2, this.count()) + assertEquals("https://google.com?q=mozilla+website", this[0].url) + assertNull(this[0].searchTerm) + assertNull(this[0].referrerUrl) + + assertEquals("https://google.com?url=https://mozilla.org", this[1].url) + assertEquals("mozilla website", this[1].searchTerm) + assertEquals("https://google.com?q=mozilla+website", this[1].referrerUrl) + } + + // Both tabs load. + store.dispatch(ContentAction.UpdateHistoryStateAction(parentTab.id, listOf(HistoryItem("Google - mozilla website", "https://google.com?q=mozilla+website")), 0)).joinBlocking() + store.dispatch(ContentAction.UpdateHistoryStateAction(tab.id, listOf(HistoryItem("", "https://google.com?url=mozilla+website")), currentIndex = 0)).joinBlocking() + with((service as TestingMetadataService).createdMetadata) { + assertEquals(2, this.count()) + } + + // Direct load occurs on child tab. Search terms should be cleared. + store.dispatch(EngineAction.LoadUrlAction(tab.id, "https://firefox.com")).joinBlocking() + store.dispatch(ContentAction.UpdateUrlAction(tab.id, "https://firefox.com")).joinBlocking() + store.dispatch(ContentAction.UpdateHistoryStateAction(tab.id, listOf(HistoryItem("", "https://google.com?url=mozilla+website"), HistoryItem("Firefox", "https://firefox.com")), 1)).joinBlocking() + with((service as TestingMetadataService).createdMetadata) { + assertEquals(3, this.count()) + assertEquals("https://firefox.com", this[2].url) + assertNull(this[2].searchTerm) + assertNull(this[2].referrerUrl) + } + + // Direct load occurs on parent tab. Search terms should be cleared. + store.dispatch(EngineAction.LoadUrlAction(parentTab.id, "https://firefox.com")).joinBlocking() + store.dispatch(ContentAction.UpdateUrlAction(parentTab.id, "https://firefox.com")).joinBlocking() + store.dispatch(ContentAction.UpdateHistoryStateAction(parentTab.id, listOf(HistoryItem("Google - mozilla website", "https://google.com?q=mozilla+website"), HistoryItem("Firefox", "https://firefox.com")), 1)).joinBlocking() + with((service as TestingMetadataService).createdMetadata) { + assertEquals(4, this.count()) + assertEquals("https://firefox.com", this[3].url) + assertNull(this[3].searchTerm) + assertNull(this[3].referrerUrl) + } + } + @Test fun `GIVEN normal tab has parent WHEN url is the same THEN nothing happens`() { val parentTab = createTab("https://mozilla.org", searchTerms = "mozilla website") From 8c2cbb4e41a811f01514aeb8998c3b53cf84fa33 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Sat, 2 Oct 2021 23:16:08 -0400 Subject: [PATCH 368/517] Issue #21642: Remove in-progress media tab from homescreen --- .../org/mozilla/fenix/ext/BrowserState.kt | 7 ---- .../org/mozilla/fenix/ext/BrowserStateTest.kt | 37 +++++++++++-------- .../fenix/home/RecentTabsListFeatureTest.kt | 10 +++-- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt index b3fc5016c3..4b573c374a 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt @@ -24,16 +24,9 @@ import kotlin.math.max fun BrowserState.asRecentTabs(): List { return mutableListOf().apply { val lastOpenedNormalTab = lastOpenedNormalTab - val inProgressMediaTab = inProgressMediaTab lastOpenedNormalTab?.let { add(RecentTab.Tab(it)) } - if (inProgressMediaTab == lastOpenedNormalTab) { - secondToLastOpenedNormalTab?.let { add(RecentTab.Tab(it)) } - } else { - inProgressMediaTab?.let { add(RecentTab.Tab(it)) } - } - lastSearchGroup?.let { add(it) } } } diff --git a/app/src/test/java/org/mozilla/fenix/ext/BrowserStateTest.kt b/app/src/test/java/org/mozilla/fenix/ext/BrowserStateTest.kt index 711cd4ed3f..3b25265a0b 100644 --- a/app/src/test/java/org/mozilla/fenix/ext/BrowserStateTest.kt +++ b/app/src/test/java/org/mozilla/fenix/ext/BrowserStateTest.kt @@ -85,13 +85,12 @@ class BrowserStateTest { val result = browserState.asRecentTabs() - assertEquals(2, result.size) + assertEquals(1, result.size) assertEquals(selectedTab, (result[0] as RecentTab.Tab).state) - assertEquals(mediaTab, (result[1] as RecentTab.Tab).state) } @Test - fun `GIVEN the selected tab is a private tab and another media tab exists WHEN asRecentTabs is called THEN return a list of the last normal tab and the media tab`() { + fun `GIVEN the selected tab is a private tab and another tab exists WHEN asRecentTabs is called THEN return a list of the last normal tab`() { val lastAccessedNormalTab = createTab(url = "url2", id = "2", lastAccess = 2) val selectedPrivateTab = createTab(url = "url", id = "1", lastAccess = 1, private = true) val mediaTab = createTab( @@ -110,13 +109,12 @@ class BrowserStateTest { val result = browserState.asRecentTabs() - assertEquals(2, result.size) + assertEquals(1, result.size) assertEquals(lastAccessedNormalTab, (result[0] as RecentTab.Tab).state) - assertEquals(mediaTab, (result[1] as RecentTab.Tab).state) } @Test - fun `GIVEN the selected tab is a private tab and the media tab is the last accessed normal tab WHEN asRecentTabs is called THEN return a list of the media tab and the second-to-last normal tab`() { + fun `GIVEN the selected tab is a private tab and the media tab is the last accessed normal tab WHEN asRecentTabs is called THEN return a list of the second-to-last normal tab`() { val selectedPrivateTab = createTab(url = "url", id = "1", lastAccess = 1, private = true) val normalTab = createTab(url = "url2", id = "2", lastAccess = 2) val mediaTab = createTab( @@ -130,7 +128,7 @@ class BrowserStateTest { val result = browserState.asRecentTabs() - assertEquals(2, result.size) + assertEquals(1, result.size) assertEquals(mediaTab, (result[0] as RecentTab.Tab).state) } @@ -196,8 +194,17 @@ class BrowserStateTest { referrerUrl = "https://www.mozilla.org" ) ) + val searchGroupTab2 = createTab( + url = "https://www.mozilla.org", + id = "5", + historyMetadata = HistoryMetadataKey( + url = "https://www.firefox.com", + searchTerm = "Test", + referrerUrl = "https://www.mozilla.org" + ) + ) val browserState = BrowserState( - tabs = listOf(mockk(relaxed = true), selectedTab, searchGroupTab), + tabs = listOf(mockk(relaxed = true), selectedTab, searchGroupTab, searchGroupTab2), selectedTabId = selectedTab.id ) @@ -226,14 +233,14 @@ class BrowserStateTest { val result = browserState.asRecentTabs() - assertEquals(3, result.size) + assertEquals(2, result.size) assertEquals(selectedTab, (result[0] as RecentTab.Tab).state) - assert(result[2] is RecentTab.SearchGroup) - assertEquals(searchGroupTab.historyMetadata?.searchTerm, (result[2] as RecentTab.SearchGroup).searchTerm) - assertEquals(searchGroupTab.id, (result[2] as RecentTab.SearchGroup).tabId) - assertEquals(searchGroupTab.content.url, (result[2] as RecentTab.SearchGroup).url) - assertEquals(searchGroupTab.content.thumbnail, (result[2] as RecentTab.SearchGroup).thumbnail) - assertEquals(2, (result[2] as RecentTab.SearchGroup).count) + assert(result[1] is RecentTab.SearchGroup) + assertEquals(searchGroupTab.historyMetadata?.searchTerm, (result[1] as RecentTab.SearchGroup).searchTerm) + assertEquals(searchGroupTab.id, (result[1] as RecentTab.SearchGroup).tabId) + assertEquals(searchGroupTab.content.url, (result[1] as RecentTab.SearchGroup).url) + assertEquals(searchGroupTab.content.thumbnail, (result[1] as RecentTab.SearchGroup).thumbnail) + assertEquals(2, (result[1] as RecentTab.SearchGroup).count) } @Test diff --git a/app/src/test/java/org/mozilla/fenix/home/RecentTabsListFeatureTest.kt b/app/src/test/java/org/mozilla/fenix/home/RecentTabsListFeatureTest.kt index 306809b4c5..30c31ce530 100644 --- a/app/src/test/java/org/mozilla/fenix/home/RecentTabsListFeatureTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/RecentTabsListFeatureTest.kt @@ -29,6 +29,7 @@ import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.mozilla.fenix.home.HomeFragmentAction.RecentTabsChange @@ -118,6 +119,7 @@ class RecentTabsListFeatureTest { assertEquals(1, homeStore.state.recentTabs.size) } + @Ignore("Disabled until we want to enable this feature. See #21670.") @Test fun `GIVEN a valid inProgressMediaTabId and another selected tab exists WHEN the feature starts THEN dispatch both as as a recent tabs list`() { val mediaTab = createTab( @@ -146,6 +148,7 @@ class RecentTabsListFeatureTest { assertEquals(mediaTab, (homeStore.state.recentTabs[1] as RecentTab.Tab).state) } + @Ignore("Disabled until we want to enable this feature. See #21670.") @Test fun `GIVEN a valid inProgressMediaTabId exists and that is the selected tab WHEN the feature starts THEN dispatch just one tab as the recent tabs list`() { val selectedMediaTab = createTab( @@ -210,6 +213,7 @@ class RecentTabsListFeatureTest { assertEquals(tab2, (homeStore.state.recentTabs[0] as RecentTab.Tab).state) } + @Ignore("Disabled until we want to enable this feature. See #21670.") @Test fun `WHEN the browser state has an in progress media tab THEN dispatch the new recent tab list`() { val initialMediaTab = createTab( @@ -602,12 +606,10 @@ class RecentTabsListFeatureTest { feature.start() homeStore.waitUntilIdle() - assertEquals(3, homeStore.state.recentTabs.size) + assertEquals(2, homeStore.state.recentTabs.size) assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab) assertEquals(selectedTab, (homeStore.state.recentTabs[0] as RecentTab.Tab).state) - assertTrue(homeStore.state.recentTabs[1] is RecentTab.Tab) - assertEquals(mediaTab, (homeStore.state.recentTabs[1] as RecentTab.Tab).state) - val searchGroup = (homeStore.state.recentTabs[2] as RecentTab.SearchGroup) + val searchGroup = (homeStore.state.recentTabs[1] as RecentTab.SearchGroup) assertEquals(searchGroup.searchTerm, "Test search term") assertEquals(searchGroup.tabId, "44") assertEquals(searchGroup.url, "https://www.mozilla.org") From 5fc979090a4eb6e2f822eccc3249af585abb309e Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Mon, 4 Oct 2021 15:58:58 -0400 Subject: [PATCH 369/517] For #21694 - Only show the divider line in between items in the Recent Searches --- .../org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt index 462b2f536e..b37a498ef8 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt @@ -85,7 +85,7 @@ fun RecentlyVisited( RecentVisitItem( recentVisit = recentVisit, menuItems = menuItems, - showDividerLine = index < VISITS_PER_COLUMN - 1, + showDividerLine = index < items.size - 1, onRecentVisitClick = onRecentVisitClick ) } From a1e6872f6f7ba3a72357261e655c95b13ba8eaac Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Mon, 4 Oct 2021 16:16:35 -0400 Subject: [PATCH 370/517] For #21694 - Remove RoundedCornerShape around the inner Column in RecentlyVisited --- .../mozilla/fenix/historymetadata/view/RecentlyVisited.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt index b37a498ef8..b1728b74ce 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/view/RecentlyVisited.kt @@ -38,7 +38,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow @@ -79,7 +78,7 @@ fun RecentlyVisited( items(itemsList) { items -> Column( - modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(8.dp)) + modifier = Modifier.fillMaxWidth() ) { items.forEachIndexed { index, recentVisit -> RecentVisitItem( @@ -118,8 +117,7 @@ private fun RecentVisitItem( onClick = { onRecentVisitClick(recentVisit) }, onLongClick = { menuExpanded = true } ) - .size(268.dp, 56.dp) - .background(color = FirefoxTheme.colors.surface), + .size(268.dp, 56.dp), verticalAlignment = Alignment.CenterVertically ) { Image( From f9dd0d9f6fecaf968fb20302380285b132ec7d21 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Sat, 2 Oct 2021 01:45:26 -0400 Subject: [PATCH 371/517] Issue #21582: Use ThumbnailStorage in recent tabs on home --- .../fenix/home/recenttabs/view/RecentTabs.kt | 101 +++++++++++++----- 1 file changed, 74 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt index 21ade1eddf..b5fa459049 100644 --- a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt +++ b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt @@ -27,11 +27,16 @@ import androidx.compose.material.Card import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow @@ -40,6 +45,7 @@ import androidx.compose.ui.unit.sp import mozilla.components.browser.icons.compose.Loader import mozilla.components.browser.icons.compose.Placeholder import mozilla.components.browser.icons.compose.WithIcon +import mozilla.components.concept.base.images.ImageLoadRequest import mozilla.components.support.ktx.kotlin.getRepresentativeSnippet import mozilla.components.ui.colors.PhotonColors import org.mozilla.fenix.R @@ -125,6 +131,7 @@ private fun RecentTabItem( ) { RecentTabImage( url = url, + tabId = tabId, modifier = Modifier.size(116.dp, 84.dp), icon = thumbnail, contentScale = ContentScale.FillWidth, @@ -227,42 +234,53 @@ private fun RecentSearchGroupItem( * is null. */ @Composable +@Suppress("LongParameterList") private fun RecentTabImage( url: String, modifier: Modifier = Modifier, + tabId: String? = null, contentScale: ContentScale = ContentScale.Fit, alignment: Alignment = Alignment.Center, icon: Bitmap? = null ) { - if (icon != null) { - Image( - painter = BitmapPainter(icon.asImageBitmap()), - contentDescription = null, - modifier = modifier, - contentScale = contentScale, - alignment = alignment - ) - } else { - components.core.icons.Loader( - url = url - ) { - Placeholder { - Box( - modifier = Modifier.background( - color = when (isSystemInDarkTheme()) { - true -> PhotonColors.DarkGrey30 - false -> PhotonColors.LightGrey30 - } + when { + icon != null -> { + Image( + painter = BitmapPainter(icon.asImageBitmap()), + contentDescription = null, + modifier = modifier, + contentScale = contentScale, + alignment = alignment + ) + } + tabId != null -> { + ThumbnailImage( + tabId = tabId, + modifier = modifier, + contentScale = contentScale, + alignment = alignment + ) + } + else -> { + components.core.icons.Loader(url) { + Placeholder { + Box( + modifier = Modifier.background( + color = when (isSystemInDarkTheme()) { + true -> PhotonColors.DarkGrey30 + false -> PhotonColors.LightGrey30 + } + ) ) - ) - } + } - WithIcon { icon -> - Image( - painter = icon.painter, - contentDescription = null, - modifier = modifier - ) + WithIcon { icon -> + Image( + painter = icon.painter, + contentDescription = null, + modifier = modifier + ) + } } } } @@ -299,3 +317,32 @@ private fun RecentTabSubtitle(subtitle: String) { maxLines = 1 ) } + +@Composable +private fun ThumbnailImage( + tabId: String, + modifier: Modifier, + contentScale: ContentScale, + alignment: Alignment +) { + val rememberBitmap = remember(tabId) { mutableStateOf(null) } + val size = LocalDensity.current.run { 116.dp.toPx().toInt() } + val request = ImageLoadRequest(tabId, size) + val storage = components.core.thumbnailStorage + val bitmap = rememberBitmap.value + + LaunchedEffect(tabId) { + rememberBitmap.value = storage.loadThumbnail(request).await()?.asImageBitmap() + } + + if (bitmap != null) { + val painter = BitmapPainter(bitmap) + Image( + painter = painter, + contentDescription = null, + modifier = modifier, + contentScale = contentScale, + alignment = alignment + ) + } +} From d189b37a09b9d4b0801fa84b480cb3fb15a8add2 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Mon, 4 Oct 2021 12:59:10 +0300 Subject: [PATCH 372/517] For #21621 - Remove the parent horizontal padding --- app/src/main/res/layout/fragment_home.xml | 2 +- app/src/main/res/values/dimens.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 91a44265ff..0df599f714 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -69,7 +69,7 @@ android:clipChildren="false" android:clipToPadding="false" android:layout_marginBottom="88dp" - android:padding="16dp" + android:paddingVertical="16dp" android:scrollbars="none" android:transitionGroup="false" android:importantForAccessibility="yes" diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index b2e6531c20..0b72295a75 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -93,6 +93,7 @@ 60dp 5dp + 16dp 132dp From bb498cc223fca4cb85a13c4357854356cc88f791 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Mon, 4 Oct 2021 13:21:48 +0300 Subject: [PATCH 373/517] For #21621 - Add 16dp horizontal spacing to all onboarding items --- .../main/res/layout/default_browser_experiment_preference.xml | 3 ++- app/src/main/res/layout/experiment_default_browser.xml | 3 ++- app/src/main/res/layout/onboarding_automatic_signin.xml | 1 + app/src/main/res/layout/onboarding_finish.xml | 1 + app/src/main/res/layout/onboarding_header.xml | 3 ++- app/src/main/res/layout/onboarding_manual_signin.xml | 1 + app/src/main/res/layout/onboarding_privacy_notice.xml | 3 ++- app/src/main/res/layout/onboarding_section_header.xml | 3 ++- app/src/main/res/layout/onboarding_theme_picker.xml | 3 ++- app/src/main/res/layout/onboarding_toolbar_position_picker.xml | 1 + app/src/main/res/layout/onboarding_tracking_protection.xml | 1 + 11 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/default_browser_experiment_preference.xml b/app/src/main/res/layout/default_browser_experiment_preference.xml index 028e8d0170..b3777a25fe 100644 --- a/app/src/main/res/layout/default_browser_experiment_preference.xml +++ b/app/src/main/res/layout/default_browser_experiment_preference.xml @@ -8,7 +8,8 @@ style="@style/OnboardingCardLightWithPadding" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="8dp"> + android:layout_marginVertical="8dp" + android:layout_marginHorizontal="24dp"> + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"> diff --git a/app/src/main/res/layout/onboarding_header.xml b/app/src/main/res/layout/onboarding_header.xml index b4e6cd0405..b83785447e 100644 --- a/app/src/main/res/layout/onboarding_header.xml +++ b/app/src/main/res/layout/onboarding_header.xml @@ -8,7 +8,8 @@ android:id="@+id/onboarding_header" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="8dp"> + android:layout_marginBottom="8dp" + android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"> + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"> + android:layout_marginBottom="16dp" + android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"/> diff --git a/app/src/main/res/layout/onboarding_theme_picker.xml b/app/src/main/res/layout/onboarding_theme_picker.xml index 0236c7a151..2e911063ba 100644 --- a/app/src/main/res/layout/onboarding_theme_picker.xml +++ b/app/src/main/res/layout/onboarding_theme_picker.xml @@ -9,7 +9,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/OnboardingCardLight" - android:paddingTop="16dp"> + android:paddingTop="16dp" + android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"> diff --git a/app/src/main/res/layout/onboarding_tracking_protection.xml b/app/src/main/res/layout/onboarding_tracking_protection.xml index 97ce4d7b12..be5a33bea8 100644 --- a/app/src/main/res/layout/onboarding_tracking_protection.xml +++ b/app/src/main/res/layout/onboarding_tracking_protection.xml @@ -8,6 +8,7 @@ style="@style/OnboardingCardLightWithPadding" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/home_item_horizontal_margin" android:clipChildren="false" android:clipToPadding="false"> From a6b4c3e370de7f816da7f8d027f04aede2deab36 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Mon, 4 Oct 2021 13:24:45 +0300 Subject: [PATCH 374/517] For #21621 - Add 16dp horizontal spacing to the private browsing home description --- app/src/main/res/layout/private_browsing_description.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/private_browsing_description.xml b/app/src/main/res/layout/private_browsing_description.xml index d964945a04..fe6430f16c 100644 --- a/app/src/main/res/layout/private_browsing_description.xml +++ b/app/src/main/res/layout/private_browsing_description.xml @@ -9,7 +9,8 @@ android:layout_height="wrap_content" android:layout_margin="0.5dp" android:importantForAccessibility="no" - android:orientation="vertical"> + android:orientation="vertical" + android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"> Date: Mon, 4 Oct 2021 13:47:30 +0300 Subject: [PATCH 375/517] For #21621 - Add 16dp horizontal spacing to all normal browsing home items --- app/src/main/res/layout/button_tip_item.xml | 4 ++-- app/src/main/res/layout/collection_header.xml | 1 + app/src/main/res/layout/collection_home_list_row.xml | 1 + app/src/main/res/layout/component_recent_bookmarks.xml | 1 + app/src/main/res/layout/component_top_sites_pager.xml | 1 + app/src/main/res/layout/customize_home_list_item.xml | 1 + app/src/main/res/layout/history_metadata_header.xml | 1 + app/src/main/res/layout/no_collections_message.xml | 1 + app/src/main/res/layout/recent_tabs_header.xml | 4 ++-- app/src/main/res/layout/site_list_item.xml | 4 ++-- 10 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/button_tip_item.xml b/app/src/main/res/layout/button_tip_item.xml index c83a525324..be9c1ae0e9 100644 --- a/app/src/main/res/layout/button_tip_item.xml +++ b/app/src/main/res/layout/button_tip_item.xml @@ -9,8 +9,8 @@ style="@style/OnboardingCardLight" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/cfr_background_gradient" - android:padding="0dp"> + android:layout_marginHorizontal="@dimen/home_item_horizontal_margin" + android:background="@drawable/cfr_background_gradient"> diff --git a/app/src/main/res/layout/component_top_sites_pager.xml b/app/src/main/res/layout/component_top_sites_pager.xml index 9b4540cae9..bacf84f25b 100644 --- a/app/src/main/res/layout/component_top_sites_pager.xml +++ b/app/src/main/res/layout/component_top_sites_pager.xml @@ -6,6 +6,7 @@ diff --git a/app/src/main/res/layout/history_metadata_header.xml b/app/src/main/res/layout/history_metadata_header.xml index 048f646970..56f30d3844 100644 --- a/app/src/main/res/layout/history_metadata_header.xml +++ b/app/src/main/res/layout/history_metadata_header.xml @@ -7,6 +7,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/home_item_horizontal_margin" android:layout_marginTop="40dp"> - - From 571a2fc88e1a44b95652e8577fed6d8d6096775a Mon Sep 17 00:00:00 2001 From: Mugurell Date: Mon, 4 Oct 2021 17:14:44 +0300 Subject: [PATCH 376/517] For #21621 - Add 16dp horizontal spacing to home composables. Added from the ViewHolders, the same as for XML Views. --- .../view/HistoryMetadataGroupViewHolder.kt | 3 ++ .../recenttabs/view/RecentTabViewHolder.kt | 4 ++ .../pocket/PocketStoriesComposables.kt | 50 +++++++++++-------- .../pocket/PocketStoriesViewHolder.kt | 35 +++++++++---- 4 files changed, 61 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt index 057fb8835e..a698b29dae 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt @@ -30,6 +30,9 @@ class HistoryMetadataGroupViewHolder( ) : ViewHolder(composeView) { init { + val horizontalPadding = composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin) + composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0) + composeView.setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed ) diff --git a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabViewHolder.kt index 3cf8cb3a19..b88c858ecb 100644 --- a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabViewHolder.kt @@ -8,6 +8,7 @@ import android.view.View import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import mozilla.components.lib.state.ext.observeAsComposableState +import org.mozilla.fenix.R import org.mozilla.fenix.home.HomeFragmentStore import org.mozilla.fenix.home.recenttabs.interactor.RecentTabInteractor import org.mozilla.fenix.theme.FirefoxTheme @@ -27,6 +28,9 @@ class RecentTabViewHolder( ) : ViewHolder(composeView) { init { + val horizontalPadding = composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin) + composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0) + composeView.setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed ) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index 87b63920d4..cc7bd685e3 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -18,7 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.Icon import androidx.compose.material.Text @@ -33,9 +33,9 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import mozilla.components.concept.fetch.Client import mozilla.components.service.pocket.PocketRecommendedStory import mozilla.components.ui.colors.PhotonColors import org.mozilla.fenix.R @@ -100,13 +100,15 @@ fun PocketStory( * to go to Pocket for more recommendations are added. * * @param stories The list of [PocketRecommendedStory]ies to be displayed. Expect a list with 8 items. - * @param client [Client] instance to be used for downloading the story header image. + * @param contentPadding Dimension for padding the content after it has been clipped. + * This space will be used for shadows and also content rendering when the list is scrolled. * @param onExternalLinkClicked Callback for when the user taps an element which contains an * external link for where user can go for more recommendations. */ @Composable fun PocketStories( @PreviewParameter(PocketStoryProvider::class) stories: List, + contentPadding: Dp, onExternalLinkClicked: (String) -> Unit ) { // Show stories in at most 3 rows but on any number of columns depending on the data received. @@ -117,14 +119,14 @@ fun PocketStories( val flingBehavior = EagerFlingBehavior(lazyRowState = listState) LazyRow( - contentPadding = PaddingValues(start = 8.dp, end = 8.dp), + contentPadding = PaddingValues(horizontal = contentPadding), state = listState, flingBehavior = flingBehavior, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - itemsIndexed(storiesToShow) { _, columnItems -> - Column { - columnItems.forEachIndexed { rowIndex, story -> + items(storiesToShow) { columnItems -> + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + columnItems.forEach { story -> if (story == placeholderStory) { ListItemTabLargePlaceholder(stringResource(R.string.pocket_stories_placeholder_text)) { onExternalLinkClicked("http://getpocket.com/explore") @@ -134,10 +136,6 @@ fun PocketStories( onExternalLinkClicked(story.url) } } - - if (rowIndex < maxRowsNo - 1) { - Spacer(modifier = Modifier.height(8.dp)) - } } } } @@ -148,20 +146,25 @@ fun PocketStories( * Displays a list of [PocketRecommendedStoriesCategory]s. * * @param categories The categories needed to be displayed. + * @param selections List of categories currently selected. * @param onCategoryClick Callback for when the user taps a category. + * @param modifier [Modifier] to be applied to the layout. */ @Composable fun PocketStoriesCategories( categories: List, selections: List, - onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit + onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit, + modifier: Modifier = Modifier ) { - StaggeredHorizontalGrid( - horizontalItemsSpacing = 16.dp - ) { - categories.filter { it.name != POCKET_STORIES_DEFAULT_CATEGORY_NAME }.forEach { category -> - SelectableChip(category.name, selections.map { it.name }.contains(category.name)) { - onCategoryClick(category) + Box(modifier = modifier) { + StaggeredHorizontalGrid( + horizontalItemsSpacing = 16.dp + ) { + categories.filter { it.name != POCKET_STORIES_DEFAULT_CATEGORY_NAME }.forEach { category -> + SelectableChip(category.name, selections.map { it.name }.contains(category.name)) { + onCategoryClick(category) + } } } } @@ -173,10 +176,12 @@ fun PocketStoriesCategories( * * @param onExternalLinkClicked Callback invoked when the user clicks the "Learn more" link. * Contains the full URL for where the user should be navigated to. + * @param modifier [Modifier] to be applied to the layout. */ @Composable fun PoweredByPocketHeader( onExternalLinkClicked: (String) -> Unit, + modifier: Modifier = Modifier ) { val color = when (isSystemInDarkTheme()) { true -> PhotonColors.LightGrey30 @@ -189,6 +194,7 @@ fun PoweredByPocketHeader( val linkEndIndex = linkStartIndex + link.length Column( + modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally, ) { Row( @@ -230,6 +236,7 @@ private fun PocketStoriesComposablesPreview() { Column { PocketStories( stories = getFakePocketStories(8), + contentPadding = 0.dp, onExternalLinkClicked = { } ) Spacer(Modifier.height(10.dp)) @@ -238,11 +245,12 @@ private fun PocketStoriesComposablesPreview() { "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor".split(" ").map { PocketRecommendedStoriesCategory(it) }, - emptyList() - ) { } + emptyList(), + { } + ) Spacer(Modifier.height(10.dp)) - PoweredByPocketHeader { } + PoweredByPocketHeader({ }) } } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt index 5f6c7805a4..e3d357e2c3 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.pocket import android.view.View +import androidx.annotation.Dimension import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -52,7 +53,10 @@ class PocketStoriesViewHolder( store, interactor::onStoriesShown, interactor::onCategoryClick, - interactor::onExternalLinkClicked + interactor::onExternalLinkClicked, + with(composeView.resources) { + getDimensionPixelSize(R.dimen.home_item_horizontal_margin) / displayMetrics.density + } ) } } @@ -68,7 +72,8 @@ fun PocketStories( store: HomeFragmentStore, onStoriesShown: (List) -> Unit, onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit, - onExternalLinkClicked: (String) -> Unit + onExternalLinkClicked: (String) -> Unit, + @Dimension horizontalPadding: Float = 0f ) { val stories = store .observeAsComposableState { state -> state.pocketStories }.value @@ -87,35 +92,45 @@ fun PocketStories( } } - Column(modifier = Modifier.padding(vertical = 48.dp)) { + Column(modifier = Modifier.padding(vertical = 44.dp)) { HomeSectionHeader( text = stringResource(R.string.pocket_stories_header_1), modifier = Modifier .fillMaxWidth() + .padding(horizontal = horizontalPadding.dp) ) Spacer(Modifier.height(17.dp)) - PocketStories(stories ?: emptyList(), onExternalLinkClicked) + PocketStories(stories ?: emptyList(), horizontalPadding.dp, onExternalLinkClicked) Spacer(Modifier.height(24.dp)) HomeSectionHeader( text = stringResource(R.string.pocket_stories_categories_header), - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = horizontalPadding.dp) ) Spacer(Modifier.height(17.dp)) PocketStoriesCategories( categories = categories ?: emptyList(), - selections = categoriesSelections ?: emptyList() - ) { - onCategoryClick(it) - } + selections = categoriesSelections ?: emptyList(), + onCategoryClick = onCategoryClick, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = horizontalPadding.dp) + ) Spacer(Modifier.height(24.dp)) - PoweredByPocketHeader(onExternalLinkClicked) + PoweredByPocketHeader( + onExternalLinkClicked, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = horizontalPadding.dp) + ) } } From a53b52b764976baa56024ed3eda68ad5f8ff4351 Mon Sep 17 00:00:00 2001 From: "codrut.topliceanu" Date: Wed, 29 Sep 2021 12:08:04 +0300 Subject: [PATCH 377/517] For #21565 - Adds inactive tabs onboarding popup --- .../fenix/tabstray/TabsTrayFragment.kt | 15 ++- .../TabsTrayInactiveTabsOnboardingBinding.kt | 114 ++++++++++++++++++ .../java/org/mozilla/fenix/utils/Settings.kt | 5 + .../layout/onboarding_inactive_tabs_cfr.xml | 60 +++++++++ app/src/main/res/values/preference_keys.xml | 4 + app/src/main/res/values/strings.xml | 4 +- 6 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInactiveTabsOnboardingBinding.kt create mode 100644 app/src/main/res/layout/onboarding_inactive_tabs_cfr.xml diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt index 509149b6e1..2f216d2576 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt @@ -31,7 +31,6 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.R import org.mozilla.fenix.components.FenixSnackbar -import org.mozilla.fenix.share.ShareFragment import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.databinding.ComponentTabstray2Binding @@ -43,6 +42,7 @@ import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings import org.mozilla.fenix.home.HomeScreenViewModel +import org.mozilla.fenix.share.ShareFragment import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor import org.mozilla.fenix.tabstray.browser.DefaultBrowserTrayInteractor import org.mozilla.fenix.tabstray.browser.SelectionBannerBinding @@ -72,6 +72,7 @@ class TabsTrayFragment : AppCompatDialogFragment() { private val selectionHandleBinding = ViewBoundFeatureWrapper() private val tabsTrayCtaBinding = ViewBoundFeatureWrapper() private val secureTabsTrayBinding = ViewBoundFeatureWrapper() + private val tabsTrayInactiveTabsOnboardingBinding = ViewBoundFeatureWrapper() @VisibleForTesting @Suppress("VariableNaming") internal var _tabsTrayBinding: ComponentTabstray2Binding? = null @@ -319,6 +320,18 @@ class TabsTrayFragment : AppCompatDialogFragment() { view = view ) + tabsTrayInactiveTabsOnboardingBinding.set( + feature = TabsTrayInactiveTabsOnboardingBinding( + context = requireContext(), + store = requireComponents.core.store, + tabsTrayBinding = tabsTrayBinding, + settings = requireComponents.settings, + navigationInteractor = navigationInteractor + ), + owner = this, + view = view + ) + setFragmentResultListener(ShareFragment.RESULT_KEY) { _, _ -> dismissTabsTray() } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInactiveTabsOnboardingBinding.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInactiveTabsOnboardingBinding.kt new file mode 100644 index 0000000000..a9414a7341 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayInactiveTabsOnboardingBinding.kt @@ -0,0 +1,114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.tabstray + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.style.UnderlineSpan +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import androidx.annotation.VisibleForTesting +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.map +import mozilla.components.browser.state.selector.normalTabs +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.lib.state.helpers.AbstractBinding +import mozilla.components.support.ktx.android.util.dpToPx +import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged +import org.mozilla.fenix.R +import org.mozilla.fenix.browser.infobanner.InfoBanner +import org.mozilla.fenix.databinding.ComponentTabstray2Binding +import org.mozilla.fenix.databinding.OnboardingInactiveTabsCfrBinding +import org.mozilla.fenix.tabstray.ext.inactiveTabs +import org.mozilla.fenix.utils.Settings + +@OptIn(ExperimentalCoroutinesApi::class) +class TabsTrayInactiveTabsOnboardingBinding( + private val context: Context, + private val store: BrowserStore, + private val tabsTrayBinding: ComponentTabstray2Binding?, + private val settings: Settings, + private val navigationInteractor: NavigationInteractor +) : AbstractBinding(store) { + + @VisibleForTesting + internal var banner: InfoBanner? = null + + override suspend fun onState(flow: Flow) { + flow.map { state -> state.normalTabs.size } + .ifChanged() + .collect { + val inactiveTabsList = + if (settings.inactiveTabsAreEnabled) { store.state.inactiveTabs } else emptyList() + if (inactiveTabsList.isNotEmpty() && shouldShowOnboardingForInactiveTabs()) { + createInactiveCFR() + } + } + } + + private fun shouldShowOnboardingForInactiveTabs() = + settings.shouldShowInactiveTabsOnboardingPopup && + settings.canShowCfr + + private fun createInactiveCFR() { + val context: Context = context + val anchorPosition = IntArray(2) + val popupBinding = OnboardingInactiveTabsCfrBinding.inflate(LayoutInflater.from(context)) + val popup = Dialog(context) + + popup.apply { + setContentView(popupBinding.root) + setCancelable(false) + // removing title or setting it as an empty string does not prevent a11y services from assigning one + setTitle(" ") + } + popupBinding.closeInfoBanner.setOnClickListener { + popup.dismiss() + settings.shouldShowInactiveTabsOnboardingPopup = false + } + + popupBinding.bannerInfoMessage.setOnClickListener { + popup.dismiss() + settings.shouldShowInactiveTabsOnboardingPopup = false + navigationInteractor.onTabSettingsClicked() + } + + val messageText = context.getString(R.string.tab_tray_inactive_onboarding_message) + val actionText = context.getString(R.string.tab_tray_inactive_onboarding_button_text) + val spannableStringBuilder = SpannableStringBuilder(messageText) + + spannableStringBuilder.append(" ") + .append(actionText, UnderlineSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + popupBinding.bannerInfoMessage.text = spannableStringBuilder + + tabsTrayBinding?.tabsTray?.getLocationOnScreen(anchorPosition) + + val (x, y) = anchorPosition + + if (x == 0 && y == 0) { + return + } + + popupBinding.root.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) + + popup.window?.apply { + val attr = attributes + setGravity(Gravity.START or Gravity.TOP) + attr.x = x + 15.dpToPx(context.resources.displayMetrics) + attr.y = y + 20.dpToPx(context.resources.displayMetrics) + attributes = attr + setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + } + popup.show() + } +} diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index 71d6258530..a7b7459722 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -832,6 +832,11 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = true ) + var shouldShowInactiveTabsOnboardingPopup by booleanPreference( + appContext.getPreferenceKey(R.string.pref_key_should_show_inactive_tabs_popup), + default = true + ) + fun getSitePermissionsPhoneFeatureAction( feature: PhoneFeature, default: Action = Action.ASK_TO_ALLOW diff --git a/app/src/main/res/layout/onboarding_inactive_tabs_cfr.xml b/app/src/main/res/layout/onboarding_inactive_tabs_cfr.xml new file mode 100644 index 0000000000..785e581968 --- /dev/null +++ b/app/src/main/res/layout/onboarding_inactive_tabs_cfr.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index e6923d1317..d3d2873fac 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -225,6 +225,10 @@ pref_key_should_show_auto_close_tabs_banner + + pref_key_should_show_inactive_tabs_popup + + pref_key_migrating_from_fenix_nightly_tip pref_key_migrating_from_firefox_nightly_tip pref_key_migrating_from_fenix_tip diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e75078f2a1..6039ed8ca6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -107,9 +107,9 @@ Dismiss - Tabs you haven’t viewed for two weeks get moved here. + Tabs you haven’t viewed for two weeks get moved here. - Turn off in settings + Turn off in settings From 6faafe46881e73b48055f7db209dc0e9838bc95a Mon Sep 17 00:00:00 2001 From: Mugurell Date: Thu, 30 Sep 2021 19:06:03 +0300 Subject: [PATCH 378/517] For #21599 - Add UTM parameters for Pocket recommendations links --- .../pocket/PocketStoriesComposables.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index cc7bd685e3..d410cd8ad6 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -6,6 +6,7 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.pocket +import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement @@ -52,6 +53,10 @@ import org.mozilla.fenix.theme.FirefoxTheme import kotlin.math.roundToInt import kotlin.random.Random +private const val URI_PARAM_UTM_KEY = "utm_source" +private const val POCKET_STORIES_UTM_VALUE = "pocket-newtab-android" +private const val POCKET_FEATURE_UTM_KEY_VALUE = "utm_source=ff_android" + /** * Placeholder [PocketRecommendedStory] allowing to combine other items in the same list that shows stories. * It uses empty values for it's properties ensuring that no conflict is possible since real stories have @@ -129,11 +134,15 @@ fun PocketStories( columnItems.forEach { story -> if (story == placeholderStory) { ListItemTabLargePlaceholder(stringResource(R.string.pocket_stories_placeholder_text)) { - onExternalLinkClicked("http://getpocket.com/explore") + onExternalLinkClicked("https://getpocket.com/explore?$POCKET_FEATURE_UTM_KEY_VALUE") } } else { + val uri = Uri.parse(story.url) + .buildUpon() + .appendQueryParameter(URI_PARAM_UTM_KEY, POCKET_STORIES_UTM_VALUE) + .build().toString() PocketStory(story) { - onExternalLinkClicked(story.url) + onExternalLinkClicked(uri) } } } @@ -221,7 +230,7 @@ fun PoweredByPocketHeader( ) ClickableSubstringLink(text, color, linkStartIndex, linkEndIndex) { - onExternalLinkClicked("https://www.mozilla.org/en-US/firefox/pocket/") + onExternalLinkClicked("https://www.mozilla.org/en-US/firefox/pocket/?$POCKET_FEATURE_UTM_KEY_VALUE") } } } From 8a8bbd95173d88101e5a703d3497edee5712d1f4 Mon Sep 17 00:00:00 2001 From: AndiAJ Date: Mon, 4 Oct 2021 16:50:18 +0300 Subject: [PATCH 379/517] For #20814 fix flaky tabMediaControlButtonTest UI test --- .../mozilla/fenix/ui/robots/TabDrawerRobot.kt | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/TabDrawerRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/TabDrawerRobot.kt index 0e4e982cd1..8dd4431afa 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/TabDrawerRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/TabDrawerRobot.kt @@ -148,16 +148,18 @@ class TabDrawerRobot { } fun verifyTabMediaControlButtonState(action: String) { - mDevice.waitNotNull( - findObject( - By - .res("$packageName:id/play_pause_button") - .desc(action) - ), - waitingTime - ) + mDevice.waitForIdle() - tabMediaControlButton().check(matches(withContentDescription(action))) + mDevice.findObject( + UiSelector() + .resourceId("$packageName:id/play_pause_button") + ).waitForExists(waitingTime) + + assertTrue( + mDevice.findObject( + UiSelector().descriptionContains(action) + ).waitForExists(waitingTime) + ) } fun clickTabMediaControlButton() = tabMediaControlButton().click() From bd7476cce45cb17d462ac3054382ef726fe61ed2 Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Mon, 4 Oct 2021 12:41:28 -0400 Subject: [PATCH 380/517] Update feature flags for 94. --- .../java/org/mozilla/fenix/FeatureFlags.kt | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index 174eaae4c2..4fc1cb0046 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -31,42 +31,42 @@ object FeatureFlags { /** * Enables the Home button in the browser toolbar to navigate back to the home screen. */ - val showHomeButtonFeature = Config.channel.isNightlyOrDebug || Config.channel.isBeta + const val showHomeButtonFeature = true /** * Enables the Start On Home feature in the settings page. */ - val showStartOnHomeSettings = Config.channel.isNightlyOrDebug + const val showStartOnHomeSettings = true /** * Enables the "recent" tabs feature in the home screen. */ - val showRecentTabsFeature = Config.channel.isNightlyOrDebug || Config.channel.isBeta + const val showRecentTabsFeature = true /** * Enables UI features based on history metadata. */ - val historyMetadataUIFeature = Config.channel.isNightlyOrDebug || Config.channel.isBeta + const val historyMetadataUIFeature = true /** * Enables the recently saved bookmarks feature in the home screen. */ - val recentBookmarksFeature = Config.channel.isNightlyOrDebug || Config.channel.isBeta + const val recentBookmarksFeature = true /** * Identifies and separates the tabs list with a secondary section containing least used tabs. */ - val inactiveTabs = Config.channel.isNightlyOrDebug + const val inactiveTabs = true /** * Enables showing the home screen behind the search dialog */ - val showHomeBehindSearch = Config.channel.isNightlyOrDebug + const val showHomeBehindSearch = true /** * Enables customizing the home screen */ - val customizeHome = Config.channel.isNightlyOrDebug || Config.channel.isBeta + const val customizeHome = true /** * Identifies and separates the tabs list with a group containing search term tabs. @@ -76,15 +76,13 @@ object FeatureFlags { /** * Enables showing search groupings in the History. */ - val showHistorySearchGroups = Config.channel.isNightlyOrDebug + const val showHistorySearchGroups = true /** * Show Pocket recommended stories on home. */ fun isPocketRecommendationsFeatureEnabled(context: Context): Boolean { - return Config.channel.isBeta || ( - Config.channel.isNightlyOrDebug && "en-US" == LocaleManager.getCurrentLocale(context) - ?.toLanguageTag() ?: getSystemDefault().toLanguageTag() - ) + return "en-US" == LocaleManager.getCurrentLocale(context) + ?.toLanguageTag() ?: getSystemDefault().toLanguageTag() } } From 8c34ccd8c9a61b4a99a26e4abb6d690c4b6f7831 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Tue, 5 Oct 2021 15:38:33 +0000 Subject: [PATCH 381/517] Update Android Components version to 94.0.20211005143407. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index df13ff9fde..33544e580e 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20211003190228" + const val VERSION = "94.0.20211005143407" } From b1a5025610cdefc0f3732adc737ff0a05eff7102 Mon Sep 17 00:00:00 2001 From: "codrut.topliceanu" Date: Tue, 5 Oct 2021 15:08:25 +0300 Subject: [PATCH 382/517] For #21708 - Fixes missing header bottom border The bottom gray border of the header item from the Inactive Tabs section was correctly set when collapsing or expanding said section, but not on init. So if the section was initialized collapsed the gray border would not be present. --- .../tabstray/browser/InactiveTabViewHolder.kt | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt index 0e83219d4c..7ab9ea2c02 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt @@ -38,6 +38,8 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite itemView.apply { isActivated = InactiveTabsState.isExpanded + correctHeaderBorder(isActivated) + setOnClickListener { val newState = !it.isActivated @@ -46,10 +48,7 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite it.isActivated = newState binding.chevron.rotation = ROTATION_DEGREE - // When the header is collapsed we use its bottom border instead of the footer's - binding.inactiveHeaderBorder.updatePadding( - bottom = binding.root.context.dpToPx(if (it.isActivated) 0f else 1f) - ) + correctHeaderBorder(isActivated) } binding.delete.setOnClickListener { @@ -58,6 +57,15 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite } } + /** + * When the header is collapsed we use its bottom border instead of the footer's + */ + private fun correctHeaderBorder(isActivated: Boolean) { + binding.inactiveHeaderBorder.updatePadding( + bottom = binding.root.context.dpToPx(if (isActivated) 0f else 1f) + ) + } + companion object { const val LAYOUT_ID = R.layout.inactive_header_item private const val ROTATION_DEGREE = 180F From fb345a4131c061b4a3786ccc0024d5a1c9de9737 Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Tue, 5 Oct 2021 13:30:48 -0400 Subject: [PATCH 383/517] Close #21573: Update design for jump back in section and recently bookmarked section --- .../fenix/home/recenttabs/view/RecentTabs.kt | 131 +++++++++++++----- .../main/res/drawable-night/ic_all_tabs.xml | 13 -- app/src/main/res/drawable/ic_all_tabs.xml | 3 +- .../drawable/ic_search_group_thumbnail.xml | 49 +++++++ .../drawable/recent_bookmark_background.xml | 16 +++ .../main/res/layout/recent_bookmark_item.xml | 30 ++-- .../res/layout/recent_bookmarks_header.xml | 1 - app/src/main/res/values/dimens.xml | 6 +- 8 files changed, 188 insertions(+), 61 deletions(-) delete mode 100644 app/src/main/res/drawable-night/ic_all_tabs.xml create mode 100644 app/src/main/res/drawable/ic_search_group_thumbnail.xml create mode 100644 app/src/main/res/drawable/recent_bookmark_background.xml diff --git a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt index b5fa459049..0b1fe7850d 100644 --- a/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt +++ b/app/src/main/java/org/mozilla/fenix/home/recenttabs/view/RecentTabs.kt @@ -37,6 +37,8 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow @@ -86,8 +88,6 @@ fun RecentTabs( RecentSearchGroupItem( searchTerm = tab.searchTerm, tabId = tab.tabId, - url = tab.url, - thumbnail = tab.thumbnail, count = tab.count, onSearchGroupClicked = onRecentSearchGroupClicked ) @@ -120,7 +120,7 @@ private fun RecentTabItem( Card( modifier = Modifier .fillMaxWidth() - .height(116.dp) + .height(112.dp) .clickable { onRecentTabClick(tabId) }, shape = RoundedCornerShape(8.dp), backgroundColor = FirefoxTheme.colors.surface, @@ -132,10 +132,9 @@ private fun RecentTabItem( RecentTabImage( url = url, tabId = tabId, - modifier = Modifier.size(116.dp, 84.dp), - icon = thumbnail, - contentScale = ContentScale.FillWidth, - alignment = Alignment.TopCenter + modifier = Modifier.size(108.dp, 80.dp) + .clip(RoundedCornerShape(8.dp)), + thumbnail = thumbnail ) Spacer(modifier = Modifier.width(16.dp)) @@ -147,9 +146,10 @@ private fun RecentTabItem( RecentTabTitle(title = title) Row { - RecentTabImage( + RecentTabIcon( url = url, - modifier = Modifier.size(18.dp, 18.dp), + modifier = Modifier.size(18.dp, 18.dp) + .clip(RoundedCornerShape(2.dp)), icon = icon ) @@ -167,8 +167,6 @@ private fun RecentTabItem( * * @param searchTerm The search term for the group. * @param tabId The id of the last accessed tab in the group. - * @param url The loaded URL of the last accessed tab in the group. - * @param thumbnail The icon of the group. * @param count Count of how many tabs belongs to the group. * @param onSearchGroupClicked Invoked when the user clicks on a group. */ @@ -177,15 +175,13 @@ private fun RecentTabItem( private fun RecentSearchGroupItem( searchTerm: String, tabId: String, - url: String, - thumbnail: Bitmap?, count: Int, onSearchGroupClicked: (String) -> Unit = {} ) { Card( modifier = Modifier .fillMaxWidth() - .height(116.dp) + .height(112.dp) .clickable { onSearchGroupClicked(tabId) }, shape = RoundedCornerShape(8.dp), backgroundColor = FirefoxTheme.colors.surface, @@ -194,12 +190,12 @@ private fun RecentSearchGroupItem( Row( modifier = Modifier.padding(16.dp) ) { - RecentTabImage( - url = url, - modifier = Modifier.size(116.dp, 84.dp), - icon = thumbnail, + Image( + painter = painterResource(id = R.drawable.ic_search_group_thumbnail), + contentDescription = null, + modifier = Modifier.size(108.dp, 80.dp), contentScale = ContentScale.FillWidth, - alignment = Alignment.TopCenter + alignment = Alignment.Center ) Spacer(modifier = Modifier.width(16.dp)) @@ -213,7 +209,9 @@ private fun RecentSearchGroupItem( Row { Icon( painter = painterResource(id = R.drawable.ic_all_tabs), - contentDescription = null // decorative element + modifier = Modifier.size(18.dp), + contentDescription = null, + tint = FirefoxTheme.colors.textSecondary ) Spacer(modifier = Modifier.width(8.dp)) @@ -229,8 +227,11 @@ private fun RecentSearchGroupItem( * A recent tab image. * * @param url The loaded URL of the tab. - * @param modifier modifier Modifier used to draw the image content. - * @param icon The icon of the tab. Fallback to loading the icon from the [url] if the [icon] + * @param modifier [Modifier] used to draw the image content. + * @param tabId The id of the tab. + * @param contentScale [ContentScale] used to draw image content. + * @param alignment [Alignment] used to draw the image content. + * @param thumbnail The icon of the tab. Fallback to loading the icon from the [url] if the [thumbnail] * is null. */ @Composable @@ -239,23 +240,88 @@ private fun RecentTabImage( url: String, modifier: Modifier = Modifier, tabId: String? = null, - contentScale: ContentScale = ContentScale.Fit, - alignment: Alignment = Alignment.Center, - icon: Bitmap? = null + thumbnail: Bitmap? = null, + contentScale: ContentScale = ContentScale.FillWidth, + alignment: Alignment = Alignment.TopCenter ) { when { - icon != null -> { + thumbnail != null -> { Image( - painter = BitmapPainter(icon.asImageBitmap()), + painter = BitmapPainter(thumbnail.asImageBitmap()), contentDescription = null, modifier = modifier, contentScale = contentScale, alignment = alignment ) } - tabId != null -> { - ThumbnailImage( - tabId = tabId, + else -> { + Card( + modifier = modifier, + backgroundColor = colorResource(id = R.color.photonGrey20) + ) { + components.core.icons.Loader(url) { + Placeholder { + Box( + modifier = Modifier.background( + color = when (isSystemInDarkTheme()) { + true -> PhotonColors.DarkGrey30 + false -> PhotonColors.LightGrey30 + } + ) + ) + } + + WithIcon { icon -> + Box( + modifier = Modifier.size(36.dp), + contentAlignment = Alignment.Center + ) { + Image( + painter = icon.painter, + contentDescription = null, + modifier = Modifier.size(36.dp).clip(RoundedCornerShape(8.dp)), + contentScale = ContentScale.Fit + ) + } + } + } + + if (tabId != null) { + ThumbnailImage( + tabId = tabId, + modifier = modifier, + contentScale = contentScale, + alignment = alignment + ) + } + } + } + } +} + +/** + * A recent tab icon. + * + * @param url The loaded URL of the tab. + * @param modifier [Modifier] used to draw the image content. + * @param contentScale [ContentScale] used to draw image content. + * @param alignment [Alignment] used to draw the image content. + * @param icon The icon of the tab. Fallback to loading the icon from the [url] if the [icon] + * is null. + */ +@Composable +private fun RecentTabIcon( + url: String, + modifier: Modifier = Modifier, + contentScale: ContentScale = ContentScale.Fit, + alignment: Alignment = Alignment.Center, + icon: Bitmap? = null +) { + when { + icon != null -> { + Image( + painter = BitmapPainter(icon.asImageBitmap()), + contentDescription = null, modifier = modifier, contentScale = contentScale, alignment = alignment @@ -278,7 +344,8 @@ private fun RecentTabImage( Image( painter = icon.painter, contentDescription = null, - modifier = modifier + modifier = modifier, + contentScale = ContentScale.Fit ) } } @@ -326,7 +393,7 @@ private fun ThumbnailImage( alignment: Alignment ) { val rememberBitmap = remember(tabId) { mutableStateOf(null) } - val size = LocalDensity.current.run { 116.dp.toPx().toInt() } + val size = LocalDensity.current.run { 108.dp.toPx().toInt() } val request = ImageLoadRequest(tabId, size) val storage = components.core.thumbnailStorage val bitmap = rememberBitmap.value diff --git a/app/src/main/res/drawable-night/ic_all_tabs.xml b/app/src/main/res/drawable-night/ic_all_tabs.xml deleted file mode 100644 index b01e330937..0000000000 --- a/app/src/main/res/drawable-night/ic_all_tabs.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_all_tabs.xml b/app/src/main/res/drawable/ic_all_tabs.xml index c48775b194..0818555ec3 100644 --- a/app/src/main/res/drawable/ic_all_tabs.xml +++ b/app/src/main/res/drawable/ic_all_tabs.xml @@ -1,3 +1,4 @@ + @@ -8,6 +9,6 @@ android:viewportHeight="18"> diff --git a/app/src/main/res/drawable/ic_search_group_thumbnail.xml b/app/src/main/res/drawable/ic_search_group_thumbnail.xml new file mode 100644 index 0000000000..72166bd9b8 --- /dev/null +++ b/app/src/main/res/drawable/ic_search_group_thumbnail.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/recent_bookmark_background.xml b/app/src/main/res/drawable/recent_bookmark_background.xml new file mode 100644 index 0000000000..f62435f3c6 --- /dev/null +++ b/app/src/main/res/drawable/recent_bookmark_background.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/app/src/main/res/layout/recent_bookmark_item.xml b/app/src/main/res/layout/recent_bookmark_item.xml index 68b77e0398..f7533cbeaa 100644 --- a/app/src/main/res/layout/recent_bookmark_item.xml +++ b/app/src/main/res/layout/recent_bookmark_item.xml @@ -11,17 +11,25 @@ android:layout_height="@dimen/recent_bookmark_item_height" android:padding="4dp"> - + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintLeft_toLeftOf="parent"> + + + + diff --git a/app/src/main/res/layout/recent_bookmarks_header.xml b/app/src/main/res/layout/recent_bookmarks_header.xml index 29747174fd..955b0d31b1 100644 --- a/app/src/main/res/layout/recent_bookmarks_header.xml +++ b/app/src/main/res/layout/recent_bookmarks_header.xml @@ -31,7 +31,6 @@ android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:contentDescription="@string/recently_saved_show_all_content_description" - android:paddingVertical="16dp" android:paddingStart="16dp" android:paddingEnd="0dp" android:gravity="end" diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 0b72295a75..c43155a78a 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -96,9 +96,9 @@ 16dp - 132dp - 161dp - 72dp + 136dp + 156dp + 96dp 0dp 8dp From 2ddb57038d8dd3ec6c77ff1d6f376e31bc765425 Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Tue, 5 Oct 2021 19:14:55 -0400 Subject: [PATCH 384/517] Set version to 94.0.0. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 33544e580e..7c310b7b34 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.20211005143407" + const val VERSION = "94.0.0" } From 54d80751bfc9a4aa4341e78221060940a36e3d17 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Wed, 6 Oct 2021 00:45:02 +0000 Subject: [PATCH 385/517] Update to Android-Components 94.0.1. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 7c310b7b34..85792b443f 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.0" + const val VERSION = "94.0.1" } From b1453028dfa4676a9568a0576f53009f67874d0b Mon Sep 17 00:00:00 2001 From: Mozilla Releng Treescript Date: Wed, 6 Oct 2021 02:39:29 +0000 Subject: [PATCH 386/517] Automatic version bump CLOSED TREE NO BUG a=release --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index b94447a7fb..f383d67f6b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -94.0.0-beta.1 +94.0.0-beta.2 From 62118be809aa07ad341fb362479bc46010e4bf5b Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Tue, 5 Oct 2021 17:18:11 -0400 Subject: [PATCH 387/517] For #21574: disabled the homescreen onboarding dialog. (cherry picked from commit 7e3a2ba89d2ecfb3b4c9c35153d8b81554339ba1) --- app/src/debug/res/raw/initial_experiments.json | 2 +- app/src/main/java/org/mozilla/fenix/FeatureFlags.kt | 5 +++++ .../home/sessioncontrol/SessionControlController.kt | 11 +++++++---- .../fenix/home/DefaultSessionControlControllerTest.kt | 1 + 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/src/debug/res/raw/initial_experiments.json b/app/src/debug/res/raw/initial_experiments.json index 6bced9269e..3514af8ee0 100644 --- a/app/src/debug/res/raw/initial_experiments.json +++ b/app/src/debug/res/raw/initial_experiments.json @@ -12,7 +12,7 @@ "value": { "sections-enabled": { "topSites": true, - "recentExplorations": true, + "recentExplorations": false, "recentlySaved": false, "jumpBackIn": false, "pocket": false diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index 4fc1cb0046..165beb7822 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -85,4 +85,9 @@ object FeatureFlags { return "en-US" == LocaleManager.getCurrentLocale(context) ?.toLanguageTag() ?: getSystemDefault().toLanguageTag() } + + /** + * Enables showing the homescreen onboarding card. + */ + const val showHomeOnboarding = false } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt index 078110cc4e..9b956a3ddc 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt @@ -27,6 +27,7 @@ import mozilla.components.feature.top.sites.TopSite import mozilla.components.support.ktx.android.view.showKeyboard import mozilla.components.support.ktx.kotlin.isUrl import org.mozilla.fenix.BrowserDirection +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.browser.BrowserFragmentDirections @@ -454,10 +455,12 @@ class DefaultSessionControlController( } override fun handleShowOnboardingDialog() { - navController.nav( - R.id.homeFragment, - HomeFragmentDirections.actionGlobalHomeOnboardingDialog() - ) + if (FeatureFlags.showHomeOnboarding) { + navController.nav( + R.id.homeFragment, + HomeFragmentDirections.actionGlobalHomeOnboardingDialog() + ) + } } override fun handleReadPrivacyNoticeClicked() { diff --git a/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt b/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt index 0b429eecab..e82204ccc8 100644 --- a/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt @@ -178,6 +178,7 @@ class DefaultSessionControlControllerTest { } @Test + @Ignore("Until the feature is enabled again") fun handleShowOnboardingDialog() { createController().handleShowOnboardingDialog() From 358037ceb9fe1fa394109c80d9a3664251b5c58f Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Tue, 5 Oct 2021 16:47:59 -0400 Subject: [PATCH 388/517] For #21611: Show the jump back in Contextual Hints independently of the home onboarding dialog. (cherry picked from commit 0f07703c3eb51c08a48b17124038dbcf50a31f1f) --- .../org/mozilla/fenix/ui/BookmarksTest.kt | 4 +- .../org/mozilla/fenix/ui/CollectionTest.kt | 2 +- .../java/org/mozilla/fenix/ui/HistoryTest.kt | 2 +- .../org/mozilla/fenix/ui/HomeScreenTest.kt | 2 +- .../mozilla/fenix/ui/NavigationToolbarTest.kt | 3 + .../fenix/ui/NoNetworkAccessStartupTests.kt | 2 +- .../org/mozilla/fenix/ui/SettingsAboutTest.kt | 2 +- .../mozilla/fenix/ui/SettingsBasicsTest.kt | 4 +- .../mozilla/fenix/ui/SettingsPrivacyTest.kt | 6 +- .../java/org/mozilla/fenix/ui/SmokeTest.kt | 18 ++-- .../StrictEnhancedTrackingProtectionTest.kt | 4 +- .../mozilla/fenix/ui/TabbedBrowsingTest.kt | 5 +- .../mozilla/fenix/ui/ThreeDotMenuMainTest.kt | 2 +- .../java/org/mozilla/fenix/ui/TopSitesTest.kt | 10 +-- .../debug/res/raw/initial_experiments.json | 2 +- .../home/sessioncontrol/SessionControlView.kt | 9 +- .../HomeOnboardingDialogFragment.kt | 66 -------------- .../fenix/onboarding/JumpBackInCFRDialog.kt | 89 +++++++++++++++++++ .../java/org/mozilla/fenix/utils/Settings.kt | 8 ++ app/src/main/res/values/preference_keys.xml | 2 + 20 files changed, 144 insertions(+), 98 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/onboarding/JumpBackInCFRDialog.kt diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt index 6e6333359a..98be189808 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt @@ -52,6 +52,8 @@ class BookmarksTest { dispatcher = AndroidAssetDispatcher() start() } + val settings = activityTestRule.activity.settings() + settings.shouldShowJumpBackInCFR = false } @After @@ -346,7 +348,7 @@ class BookmarksTest { @Test fun openSelectionInNewTabTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) browserScreen { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt index 926e937106..3062db3909 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt @@ -40,7 +40,7 @@ class CollectionTest { @Before fun setUp() { - activityTestRule.activity.applicationContext.settings().hasShownHomeOnboardingDialog = true + activityTestRule.activity.applicationContext.settings().shouldShowJumpBackInCFR = false mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt index 448be81cee..094080b47b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt @@ -43,7 +43,7 @@ class HistoryTest { @Before fun setUp() { InstrumentationRegistry.getInstrumentation().targetContext.settings() - .hasShownHomeOnboardingDialog = true + .shouldShowJumpBackInCFR = false mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt index c69a90c77e..dcd4fa299b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt @@ -119,7 +119,7 @@ class HomeScreenTest { @Test fun dismissOnboardingUsingHelpTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false homeScreen { verifyWelcomeHeader() }.openThreeDotMenu { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt index c0efe42f71..0fdaa1810b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt @@ -12,6 +12,7 @@ import org.junit.Before import org.junit.Ignore import org.junit.Rule import org.junit.Test +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.TestAssetHelper @@ -41,6 +42,8 @@ class NavigationToolbarTest { dispatcher = AndroidAssetDispatcher() start() } + val settings = activityTestRule.activity.settings() + settings.shouldShowJumpBackInCFR = false } @After diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/NoNetworkAccessStartupTests.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/NoNetworkAccessStartupTests.kt index 67edeaa260..0720497078 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/NoNetworkAccessStartupTests.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/NoNetworkAccessStartupTests.kt @@ -56,7 +56,7 @@ class NoNetworkAccessStartupTests { fun networkInterruptedFromBrowserToHomeTest() { val url = "example.com" val settings = InstrumentationRegistry.getInstrumentation().targetContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false activityTestRule.launchActivity(null) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt index 05492c4530..5e2a09bf03 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt @@ -77,7 +77,7 @@ class SettingsAboutTest { @Test fun verifyAboutFirefoxPreview() { val settings = activityIntentTestRule.activity.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false homeScreen { }.openThreeDotMenu { }.openSettings { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt index 7dc9396480..1b70724990 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt @@ -43,6 +43,8 @@ class SettingsBasicsTest { dispatcher = AndroidAssetDispatcher() start() } + val settings = activityIntentTestRule.activity.settings() + settings.shouldShowJumpBackInCFR = false } @After @@ -157,7 +159,7 @@ class SettingsBasicsTest { val fenixApp = activityIntentTestRule.activity.applicationContext as FenixApplication val webpage = getLoremIpsumAsset(mockWebServer).url val settings = fenixApp.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false // This value will represent the text size percentage the webpage will scale to. The default value is 100%. val textSizePercentage = 180 diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt index 8178901ad0..cc58dbfd69 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt @@ -47,7 +47,7 @@ class SettingsPrivacyTest { } val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false } @After @@ -223,7 +223,7 @@ class SettingsPrivacyTest { fun neverSaveLoginFromPromptTest() { val saveLoginTest = TestAssetHelper.getSaveLoginAsset(mockWebServer) val settings = activityTestRule.activity.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false navigationToolbar { }.enterURLAndEnterToBrowser(saveLoginTest.url) { @@ -330,7 +330,7 @@ class SettingsPrivacyTest { @Test fun launchLinksInPrivateToggleOffStateDoesntChangeTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) setOpenLinksInPrivateOn() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt index 701f34aa74..ef975a5280 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt @@ -108,7 +108,7 @@ class SmokeTest { // So we are initializing this here instead of in all related tests. browserStore = activityTestRule.activity.components.core.store - activityTestRule.activity.applicationContext.settings().hasShownHomeOnboardingDialog = true + activityTestRule.activity.applicationContext.settings().shouldShowJumpBackInCFR = false mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() start() @@ -312,7 +312,7 @@ class SmokeTest { // Verifies the Add to top sites option in a tab's 3 dot menu fun openMainMenuAddTopSiteTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -824,7 +824,7 @@ class SmokeTest { @Ignore("https://github.com/mozilla-mobile/fenix/issues/21397") fun createFirstCollectionTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2) @@ -858,7 +858,7 @@ class SmokeTest { @Ignore("https://github.com/mozilla-mobile/fenix/issues/21397") fun verifyExpandedCollectionItemsTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val webPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) navigationToolbar { @@ -912,7 +912,7 @@ class SmokeTest { @Test fun shareCollectionTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val webPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -938,7 +938,7 @@ class SmokeTest { // caution when making changes to it, so they don't block the builds fun deleteCollectionTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val webPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) navigationToolbar { @@ -1357,7 +1357,7 @@ class SmokeTest { @Test fun goToHomeScreenBottomToolbarTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) navigationToolbar { @@ -1371,7 +1371,7 @@ class SmokeTest { @Test fun goToHomeScreenTopToolbarTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -1441,7 +1441,7 @@ class SmokeTest { @Test fun alwaysStartOnHomeTest() { val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) navigationToolbar { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt index d22b12206e..1a5320a1bb 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt @@ -44,7 +44,9 @@ class StrictEnhancedTrackingProtectionTest { start() } - activityTestRule.activity.settings().setStrictETP() + val settings = activityTestRule.activity.settings() + settings.setStrictETP() + settings.shouldShowJumpBackInCFR = false } @After diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt index 685e3bc903..abe2f4ce96 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt @@ -48,14 +48,11 @@ class TabbedBrowsingTest { @Before fun setUp() { - activityTestRule.activity.applicationContext.settings().hasShownHomeOnboardingDialog = true + activityTestRule.activity.applicationContext.settings().shouldShowJumpBackInCFR = false mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() start() } - - val settings = activityTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true } @After diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt index c1689eac7d..ac0d9e7daf 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt @@ -29,7 +29,7 @@ class ThreeDotMenuMainTest { @Before fun setUp() { - activityTestRule.activity.applicationContext.settings().hasShownHomeOnboardingDialog = true + activityTestRule.activity.applicationContext.settings().shouldShowJumpBackInCFR = false mockWebServer = MockWebServer().apply { dispatcher = AndroidAssetDispatcher() start() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt index d989813ee5..1e3616734f 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt @@ -50,7 +50,7 @@ class TopSitesTest { @Test fun verifyAddToFirefoxHome() { val settings = activityIntentTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) val defaultWebPageTitle = "Test_Page_1" @@ -70,7 +70,7 @@ class TopSitesTest { @Test fun verifyOpenTopSiteNormalTab() { val settings = activityIntentTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) val defaultWebPageTitle = "Test_Page_1" @@ -100,7 +100,7 @@ class TopSitesTest { @Test fun verifyOpenTopSitePrivateTab() { val settings = activityIntentTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) val defaultWebPageTitle = "Test_Page_1" @@ -124,7 +124,7 @@ class TopSitesTest { @Test fun verifyRenameTopSite() { val settings = activityIntentTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) val defaultWebPageTitle = "Test_Page_1" val defaultWebPageTitleNew = "Test_Page_2" @@ -150,7 +150,7 @@ class TopSitesTest { @Test fun verifyRemoveTopSite() { val settings = activityIntentTestRule.activity.applicationContext.settings() - settings.hasShownHomeOnboardingDialog = true + settings.shouldShowJumpBackInCFR = false val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) val defaultWebPageTitle = "Test_Page_1" diff --git a/app/src/debug/res/raw/initial_experiments.json b/app/src/debug/res/raw/initial_experiments.json index 3514af8ee0..6bced9269e 100644 --- a/app/src/debug/res/raw/initial_experiments.json +++ b/app/src/debug/res/raw/initial_experiments.json @@ -12,7 +12,7 @@ "value": { "sections-enabled": { "topSites": true, - "recentExplorations": false, + "recentExplorations": true, "recentlySaved": false, "jumpBackIn": false, "pocket": false diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt index 0903563cf7..5768118482 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt @@ -24,6 +24,7 @@ import org.mozilla.fenix.home.HomeScreenViewModel import org.mozilla.fenix.home.Mode import org.mozilla.fenix.home.OnboardingState import org.mozilla.fenix.home.recenttabs.RecentTab +import org.mozilla.fenix.onboarding.JumpBackInCFRDialog import org.mozilla.fenix.utils.Settings // This method got a little complex with the addition of the tab tray feature flag @@ -197,7 +198,13 @@ class SessionControlView( init { view.apply { adapter = sessionControlAdapter - layoutManager = LinearLayoutManager(containerView.context) + layoutManager = object : LinearLayoutManager(containerView.context) { + override fun onLayoutCompleted(state: RecyclerView.State?) { + super.onLayoutCompleted(state) + + JumpBackInCFRDialog(view).showIfNeeded() + } + } val itemTouchHelper = ItemTouchHelper( SwipeToDeleteCallback( diff --git a/app/src/main/java/org/mozilla/fenix/onboarding/HomeOnboardingDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/onboarding/HomeOnboardingDialogFragment.kt index f776bead06..ad078aadba 100644 --- a/app/src/main/java/org/mozilla/fenix/onboarding/HomeOnboardingDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/onboarding/HomeOnboardingDialogFragment.kt @@ -4,22 +4,14 @@ package org.mozilla.fenix.onboarding -import android.app.Dialog -import android.content.Context -import android.graphics.Color -import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.DialogFragment -import androidx.recyclerview.widget.RecyclerView import org.mozilla.fenix.R import org.mozilla.fenix.databinding.FragmentOnboardingHomeDialogBinding -import org.mozilla.fenix.databinding.OnboardingJumpBackInCfrBinding import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.home.recenttabs.view.RecentTabsHeaderViewHolder /** * Dialog displayed once when one or multiples of these sections are shown in the home screen @@ -45,65 +37,7 @@ class HomeOnboardingDialogFragment : DialogFragment() { context?.settings()?.let { settings -> settings.hasShownHomeOnboardingDialog = true } - showJumpCFR() dismiss() } } - - private fun showJumpCFR() { - val jumpBackInView = findJumpBackInView() - jumpBackInView?.let { - val crfDialog = createJumpCRF(anchor = jumpBackInView) - crfDialog?.show() - } - } - - private fun findJumpBackInView(): View? { - val list = activity?.findViewById(R.id.sessionControlRecyclerView) - val count = list?.adapter?.itemCount ?: return null - - for (index in 0..count) { - val viewHolder = list.findViewHolderForAdapterPosition(index) - if (viewHolder is RecentTabsHeaderViewHolder) { - return viewHolder.containerView - } - } - return null - } - - private fun createJumpCRF(anchor: View): Dialog? { - val context: Context = requireContext() - val anchorPosition = IntArray(2) - val popupBinding = OnboardingJumpBackInCfrBinding.inflate(LayoutInflater.from(context)) - val popup = Dialog(context) - - popup.apply { - setContentView(popupBinding.root) - setCancelable(false) - // removing title or setting it as an empty string does not prevent a11y services from assigning one - setTitle(" ") - } - popupBinding.closeInfoBanner.setOnClickListener { - popup.dismiss() - } - - anchor.getLocationOnScreen(anchorPosition) - val (x, y) = anchorPosition - - if (x == 0 && y == 0) { - return null - } - - popupBinding.root.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) - - popup.window?.apply { - val attr = attributes - setGravity(Gravity.START or Gravity.TOP) - attr.x = x - attr.y = y - popupBinding.root.measuredHeight - attributes = attr - setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - } - return popup - } } diff --git a/app/src/main/java/org/mozilla/fenix/onboarding/JumpBackInCFRDialog.kt b/app/src/main/java/org/mozilla/fenix/onboarding/JumpBackInCFRDialog.kt new file mode 100644 index 0000000000..31a87ae3a9 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/onboarding/JumpBackInCFRDialog.kt @@ -0,0 +1,89 @@ +/* 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.onboarding + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import org.mozilla.fenix.databinding.OnboardingJumpBackInCfrBinding +import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.home.recenttabs.view.RecentTabsHeaderViewHolder + +/** + * Dialog displayed once when the jump back in section is shown in the home screen. + */ +class JumpBackInCFRDialog(val recyclerView: RecyclerView) { + + /** + * Try to show the crf dialog if it hasn't been shown before. + */ + fun showIfNeeded() { + val jumpBackInView = findJumpBackInView() + jumpBackInView?.let { + val crfDialog = createJumpCRF(anchor = jumpBackInView) + crfDialog?.let { + val context = jumpBackInView.context + context.settings().shouldShowJumpBackInCFR = false + it.show() + } + } + } + + private fun findJumpBackInView(): View? { + val count = recyclerView.adapter?.itemCount ?: return null + + for (index in 0..count) { + val viewHolder = recyclerView.findViewHolderForAdapterPosition(index) + if (viewHolder is RecentTabsHeaderViewHolder) { + return viewHolder.containerView + } + } + return null + } + + private fun createJumpCRF(anchor: View): Dialog? { + val context: Context = recyclerView.context + if (!context.settings().shouldShowJumpBackInCFR) { + return null + } + val anchorPosition = IntArray(2) + val popupBinding = OnboardingJumpBackInCfrBinding.inflate(LayoutInflater.from(context)) + val popup = Dialog(context) + + popup.apply { + setContentView(popupBinding.root) + setCancelable(false) + // removing title or setting it as an empty string does not prevent a11y services from assigning one + setTitle(" ") + } + popupBinding.closeInfoBanner.setOnClickListener { + popup.dismiss() + } + + anchor.getLocationOnScreen(anchorPosition) + val (x, y) = anchorPosition + + if (x == 0 && y == 0) { + return null + } + + popupBinding.root.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) + + popup.window?.apply { + val attr = attributes + setGravity(Gravity.START or Gravity.TOP) + attr.x = x + attr.y = y - popupBinding.root.measuredHeight + attributes = attr + setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + } + return popup + } +} diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index a7b7459722..a930e80ac6 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -837,6 +837,14 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = true ) + /** + * Indicates if the jump back in CRF should be shown. + */ + var shouldShowJumpBackInCFR by booleanPreference( + appContext.getPreferenceKey(R.string.pref_key_should_show_jump_back_in_tabs_popup), + default = true + ) + fun getSitePermissionsPhoneFeatureAction( feature: PhoneFeature, default: Action = Action.ASK_TO_ALLOW diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index d3d2873fac..8dfec3df80 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -227,6 +227,8 @@ pref_key_should_show_inactive_tabs_popup + + pref_key_should_show_jump_back_in_tabs_popup pref_key_migrating_from_fenix_nightly_tip From 6d32b1c324279a4aa658d33114ca109cc598f52e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 6 Oct 2021 16:33:25 -0400 Subject: [PATCH 389/517] No issue: Small layout update for Pocket Stories (#21752) (cherry picked from commit 23e51c250aa4563db2a3c1862102f5c3400a2d73) Co-authored-by: Roger Yang --- .../viewholders/pocket/PocketStoriesComposables.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index d410cd8ad6..bc2a152b22 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -85,7 +85,7 @@ fun PocketStory( imageUrl = imageUrl, onClick = { onStoryClick(story) }, title = { - TabTitle(text = story.title, maxLines = 3) + TabTitle(text = story.title, maxLines = 2) }, subtitle = { if (isValidPublisher && isValidTimeToRead) { @@ -168,7 +168,7 @@ fun PocketStoriesCategories( ) { Box(modifier = modifier) { StaggeredHorizontalGrid( - horizontalItemsSpacing = 16.dp + horizontalItemsSpacing = 8.dp ) { categories.filter { it.name != POCKET_STORIES_DEFAULT_CATEGORY_NAME }.forEach { category -> SelectableChip(category.name, selections.map { it.name }.contains(category.name)) { From 01ee7e7cd8677eb8cd1efce6efd2abde20be9a71 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 6 Oct 2021 17:05:46 -0400 Subject: [PATCH 390/517] No issue: Update Pocket categories spacing to 16dp. (#21757) (cherry picked from commit 3632ed77d5d76c1ff47dd7fc2b319dd5092e7a01) Co-authored-by: Roger Yang --- .../viewholders/pocket/PocketStoriesComposables.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index bc2a152b22..8860ffb9e5 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -168,7 +168,8 @@ fun PocketStoriesCategories( ) { Box(modifier = modifier) { StaggeredHorizontalGrid( - horizontalItemsSpacing = 8.dp + horizontalItemsSpacing = 16.dp, + verticalItemsSpacing = 16.dp ) { categories.filter { it.name != POCKET_STORIES_DEFAULT_CATEGORY_NAME }.forEach { category -> SelectableChip(category.name, selections.map { it.name }.contains(category.name)) { From 18148f2d3c40b0f275845322ea9e4dad6674f487 Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Thu, 7 Oct 2021 10:48:29 -0400 Subject: [PATCH 391/517] For #21765 only activate pocket by default for the right audience (cherry picked from commit 5843fafbb6c40071fafdb97a2d8132e89cb10493) --- .../main/java/org/mozilla/fenix/experiments/NimbusFeatures.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/experiments/NimbusFeatures.kt b/app/src/main/java/org/mozilla/fenix/experiments/NimbusFeatures.kt index 79042b18b9..8636339c8b 100644 --- a/app/src/main/java/org/mozilla/fenix/experiments/NimbusFeatures.kt +++ b/app/src/main/java/org/mozilla/fenix/experiments/NimbusFeatures.kt @@ -62,7 +62,7 @@ class NimbusFeatures(private val context: Context) { */ fun toMap(context: Context): Map { return values().associate { section -> - val channelDefault = if (section == pocket) { + val channelDefault = if (section == pocket && Config.channel.isNightlyOrDebug) { FeatureFlags.isPocketRecommendationsFeatureEnabled(context) } else { Config.channel.isNightlyOrDebug From 05268d10aeba306573317dc5e7479e0ff54614c7 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Thu, 7 Oct 2021 23:20:44 +0000 Subject: [PATCH 392/517] Update to Android-Components 94.0.2. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 85792b443f..e42a0c3fa2 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.1" + const val VERSION = "94.0.2" } From afee07d38f045c6da06c5db8ceb7197e876a6320 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Fri, 8 Oct 2021 00:30:25 -0400 Subject: [PATCH 393/517] Issue #21794: Reverse chevron for inactive tabs (cherry picked from commit bcc40e8e468c196a196222db1989f585a2dc9729) --- app/src/main/res/layout/inactive_header_item.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/layout/inactive_header_item.xml b/app/src/main/res/layout/inactive_header_item.xml index f61c871acd..6ace4d6a6b 100644 --- a/app/src/main/res/layout/inactive_header_item.xml +++ b/app/src/main/res/layout/inactive_header_item.xml @@ -47,7 +47,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="12dp" - android:rotation="180" android:contentDescription="@string/tab_menu" app:layout_constraintBottom_toBottomOf="@id/inactive_title" app:layout_constraintStart_toEndOf="@id/inactive_title" From d5c5f6f9648ecc9d62dc9e08d3640d2a3493d475 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 9 Oct 2021 19:31:49 -0700 Subject: [PATCH 394/517] For #21313: Renew fission metrics expiring in December (#21821) --- app/metrics.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/metrics.yaml b/app/metrics.yaml index 4442b0ef99..9e10d0e8fb 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -4637,12 +4637,13 @@ engine_tab: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/17864 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21779#issuecomment-938089467 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - skaspari@mozilla.com - expires: "2021-12-31" + expires: "2022-07-01" kill_foreground_age: type: timing_distribution time_unit: millisecond @@ -4654,12 +4655,13 @@ engine_tab: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/17864 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21779#issuecomment-938089467 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - skaspari@mozilla.com - expires: "2021-12-31" + expires: "2022-07-01" kill_background_age: type: timing_distribution time_unit: millisecond @@ -4671,12 +4673,13 @@ engine_tab: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/17864 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21779#issuecomment-938089467 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - skaspari@mozilla.com - expires: "2021-12-31" + expires: "2022-07-01" foreground_metrics: type: event description: | @@ -4687,12 +4690,13 @@ engine_tab: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18747#issuecomment-815731764 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21779#issuecomment-938089467 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - skaspari@mozilla.com - expires: "2021-12-31" + expires: "2022-07-01" extra_keys: background_active_tabs: description: | From aca7cb074c91a6557e45138160a95921590372a7 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Sun, 10 Oct 2021 23:28:52 +0000 Subject: [PATCH 395/517] Update to Android-Components 94.0.3. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index e42a0c3fa2..f19b4149c9 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.2" + const val VERSION = "94.0.3" } From ce2d312cb9d93e838d6750d4858f4abc733bdd9e Mon Sep 17 00:00:00 2001 From: Mozilla Releng Treescript Date: Mon, 11 Oct 2021 01:51:06 +0000 Subject: [PATCH 396/517] Automatic version bump CLOSED TREE NO BUG a=release --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index f383d67f6b..e1e5fd1aff 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -94.0.0-beta.2 +94.0.0-beta.3 From 7e0146d08d170c63b3f63303f72f43de09611dee Mon Sep 17 00:00:00 2001 From: Mugurell Date: Fri, 8 Oct 2021 18:48:31 +0300 Subject: [PATCH 397/517] For #21806 - Set 1dp elevation for ListItemTabLarge To get smaller shadows. (cherry picked from commit abcc9dfc679405c411a4bd5ce1d7ec94b32a4b80) --- app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt | 2 +- .../org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt index 6d1eb8d776..ffcede086f 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt @@ -111,7 +111,7 @@ private fun ListItemTabSurface( modifier = modifier, shape = RoundedCornerShape(8.dp), backgroundColor = FirefoxTheme.colors.surface, - elevation = 6.dp + elevation = 1.dp ) { Row( modifier = Modifier.padding(16.dp) diff --git a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt index 16b912a3b3..850e25d960 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt @@ -50,7 +50,7 @@ fun ListItemTabLargePlaceholder( .clickable { onClick() }, shape = RoundedCornerShape(8.dp), backgroundColor = FirefoxTheme.colors.surface, - elevation = 6.dp, + elevation = 1.dp, ) { Column( modifier = Modifier From aa1bd6b7e467caab95bfb787a2ad3e853692ff64 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Thu, 7 Oct 2021 16:30:06 -0400 Subject: [PATCH 398/517] Issue #21686: Move submitList calls into TabsAdapter Co-authored-by: Roger Yang --- .../tabstray/browser/InactiveTabsAdapter.kt | 4 ---- .../fenix/tabstray/browser/TabGroupAdapter.kt | 4 ---- .../mozilla/fenix/tabstray/browser/TabSorter.kt | 9 --------- .../fenix/tabstray/browser/TabsAdapter.kt | 16 ++-------------- .../AbstractBrowserPageViewHolderTest.kt | 2 -- 5 files changed, 2 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt index 49dec1eeea..473950a7ec 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt @@ -105,10 +105,6 @@ class InactiveTabsAdapter( } override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false - override fun onTabsChanged(position: Int, count: Int) = Unit - override fun onTabsInserted(position: Int, count: Int) = Unit - override fun onTabsMoved(fromPosition: Int, toPosition: Int) = Unit - override fun onTabsRemoved(position: Int, count: Int) = Unit private object DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean { diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt index 1298f85e5d..17b000ada3 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabGroupAdapter.kt @@ -109,10 +109,6 @@ class TabGroupAdapter( * Not implemented; handled by nested [RecyclerView]. */ override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false - override fun onTabsChanged(position: Int, count: Int) = Unit - override fun onTabsInserted(position: Int, count: Int) = Unit - override fun onTabsMoved(fromPosition: Int, toPosition: Int) = Unit - override fun onTabsRemoved(position: Int, count: Int) = Unit private object DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Group, newItem: Group) = oldItem.title == newItem.title diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt index ddc8aba3e4..de9e1232b8 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt @@ -46,19 +46,10 @@ class TabSorter( // Normal tabs. val totalNormalTabs = (normalTabs + remainderTabs) val selectedTabIndex = totalNormalTabs.findSelectedIndex(selectedTabId) - - // N.B: For regular tabs, we cannot use submitList alone, because the `TabsAdapter` needs to have a reference - // to the new tabs in it. We considered moving the call within `updateTabs` but this would have the side-effect - // of notifying the adapter twice for private tabs which shared the `TabsAdapter`. concatAdapter.browserAdapter.updateTabs(Tabs(totalNormalTabs, selectedTabIndex)) - concatAdapter.browserAdapter.submitList(totalNormalTabs) } override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false - override fun onTabsChanged(position: Int, count: Int) = Unit - override fun onTabsInserted(position: Int, count: Int) = Unit - override fun onTabsMoved(fromPosition: Int, toPosition: Int) = Unit - override fun onTabsRemoved(position: Int, count: Int) = Unit } private fun List.findSelectedIndex(tabId: String?): Int { diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt index 77aa6509ca..6533009f76 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt @@ -37,6 +37,8 @@ abstract class TabsAdapter( override fun updateTabs(tabs: Tabs) { this.tabs = tabs + submitList(tabs.list) + notifyObservers { onTabsUpdated() } } @@ -47,23 +49,9 @@ abstract class TabsAdapter( holder.bind(tabs.list[position], isTabSelected(tabs, position), styling, this) } - override fun getItemCount(): Int = tabs?.list?.size ?: 0 - final override fun isTabSelected(tabs: Tabs, position: Int): Boolean = tabs.selectedIndex == position - final override fun onTabsChanged(position: Int, count: Int) = - notifyItemRangeChanged(position, count) - - final override fun onTabsInserted(position: Int, count: Int) = - notifyItemRangeInserted(position, count) - - final override fun onTabsMoved(fromPosition: Int, toPosition: Int) = - notifyItemMoved(fromPosition, toPosition) - - final override fun onTabsRemoved(position: Int, count: Int) = - notifyItemRangeRemoved(position, count) - private object DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Tab, newItem: Tab): Boolean { return oldItem.id == newItem.id diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/viewholders/AbstractBrowserPageViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/viewholders/AbstractBrowserPageViewHolderTest.kt index 7bbff9acc3..fc39a0ebd1 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/viewholders/AbstractBrowserPageViewHolderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/viewholders/AbstractBrowserPageViewHolderTest.kt @@ -51,7 +51,6 @@ class AbstractBrowserPageViewHolderTest { selectedIndex = 0 ) ) - adapter.onTabsInserted(0, 1) assertTrue(trayList.visibility == VISIBLE) assertTrue(emptyList.visibility == GONE) @@ -74,7 +73,6 @@ class AbstractBrowserPageViewHolderTest { selectedIndex = 0 ) ) - adapter.onTabsInserted(0, 0) assertTrue(trayList.visibility == GONE) assertTrue(emptyList.visibility == VISIBLE) From 97f8d1e12f5f6a255b86a051804d18719bc86899 Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Fri, 8 Oct 2021 16:27:11 -0400 Subject: [PATCH 399/517] Issue #21686: Stop using internally stored tabs list in adapters --- .../fenix/tabstray/browser/BrowserTabsAdapter.kt | 6 ++---- .../org/mozilla/fenix/tabstray/browser/TabsAdapter.kt | 11 ++++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapter.kt index a4e47a5a70..1bb5d03e2c 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/BrowserTabsAdapter.kt @@ -106,16 +106,14 @@ class BrowserTabsAdapter( * display itself. */ override fun onBindViewHolder(holder: AbstractBrowserTabViewHolder, position: Int, payloads: List) { - val tabs = tabs ?: return - - if (tabs.list.isEmpty()) return + if (currentList.isEmpty()) return if (payloads.isEmpty()) { onBindViewHolder(holder, position) return } - if (position == tabs.selectedIndex) { + if (position == selectedIndex) { if (payloads.contains(PAYLOAD_HIGHLIGHT_SELECTED_ITEM)) { holder.updateSelectedTabIndicator(true) } else if (payloads.contains(PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM)) { diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt index 6533009f76..7c2c29eee5 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabsAdapter.kt @@ -30,12 +30,12 @@ abstract class TabsAdapter( delegate: Observable = ObserverRegistry() ) : ListAdapter(DiffCallback), TabsTray, Observable by delegate { - protected var tabs: Tabs? = null + protected var selectedIndex: Int? = null protected var styling: TabsTrayStyling = TabsTrayStyling() @CallSuper override fun updateTabs(tabs: Tabs) { - this.tabs = tabs + this.selectedIndex = tabs.selectedIndex submitList(tabs.list) @@ -44,13 +44,10 @@ abstract class TabsAdapter( @CallSuper override fun onBindViewHolder(holder: T, position: Int) { - val tabs = tabs ?: return - - holder.bind(tabs.list[position], isTabSelected(tabs, position), styling, this) + holder.bind(getItem(position), selectedIndex == position, styling, this) } - final override fun isTabSelected(tabs: Tabs, position: Int): Boolean = - tabs.selectedIndex == position + override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false private object DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Tab, newItem: Tab): Boolean { From e0b2e3ab82bdd503dec50d3182a3a6febba10f43 Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Tue, 12 Oct 2021 15:35:29 -0400 Subject: [PATCH 400/517] Update to Android-Components 94.0.4. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index f19b4149c9..2d613117dd 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.3" + const val VERSION = "94.0.4" } From 0b072f75b540424d9c5015b329d3c4695331b335 Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Tue, 12 Oct 2021 12:46:48 -0400 Subject: [PATCH 401/517] For #21838 wait until experiments have been completely opt-out on the nimbus SDK. (cherry picked from commit 9439a65e1e09b29f2e1a2ddee797da3241a93b14) --- .../mozilla/fenix/settings/studies/StudiesInteractor.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesInteractor.kt b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesInteractor.kt index 46d4d87943..0a0d6e0af4 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/studies/StudiesInteractor.kt @@ -6,6 +6,7 @@ package org.mozilla.fenix.settings.studies import androidx.annotation.VisibleForTesting import mozilla.components.service.nimbus.NimbusApi +import org.mozilla.experiments.nimbus.NimbusInterface import org.mozilla.experiments.nimbus.internal.EnrolledExperiment import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity @@ -36,8 +37,14 @@ class DefaultStudiesInteractor( } override fun removeStudy(experiment: EnrolledExperiment) { + experiments.register(object : NimbusInterface.Observer { + override fun onUpdatesApplied(updated: List) { + // Wait until the experiment is unrolled from nimbus to restart. + killApplication() + } + }) experiments.optOut(experiment.slug) - killApplication() + experiments.applyPendingExperiments() } @VisibleForTesting From 48caf898548bd2589316e6ddb35d63586b17a692 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Oct 2021 21:23:09 -0400 Subject: [PATCH 402/517] Update to Android-Components 94.0.5. (#21874) Co-authored-by: MickeyMoz Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 2d613117dd..04f3fb3674 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.4" + const val VERSION = "94.0.5" } From e74c8aa618428d9837d2a5f3e9b9a9a9c7abc1f8 Mon Sep 17 00:00:00 2001 From: Mozilla Releng Treescript Date: Wed, 13 Oct 2021 02:18:29 +0000 Subject: [PATCH 403/517] Automatic version bump CLOSED TREE NO BUG a=release --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index e1e5fd1aff..4f94a615bc 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -94.0.0-beta.3 +94.0.0-beta.4 From 1b70be55edec6333dc8bddc41e0675b5ebe8c030 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Wed, 13 Oct 2021 18:02:12 +0000 Subject: [PATCH 404/517] Update to Android-Components 94.0.6. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 04f3fb3674..14b7ff3a97 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.5" + const val VERSION = "94.0.6" } From 135f8a363ce60af9575c282d6ef5179c43a5f68b Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Wed, 13 Oct 2021 01:22:03 -0400 Subject: [PATCH 405/517] Issue #21794: Remove rotation; rely on state activated It seems like we no longer need to use rotation for the chevron, since we are now using two different icons within the `ic_chevon` that change depending on the `state_activated`. (cherry picked from commit 722ab9f3cadaece6aa0a07b18d3f0b6c9dc7c543) --- .../org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt index 7ab9ea2c02..b7677b8867 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt @@ -46,7 +46,6 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite inactiveTabsInteractor.onHeaderClicked(newState) it.isActivated = newState - binding.chevron.rotation = ROTATION_DEGREE correctHeaderBorder(isActivated) } @@ -68,7 +67,6 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite companion object { const val LAYOUT_ID = R.layout.inactive_header_item - private const val ROTATION_DEGREE = 180F } } From a9e598b6b6c23b5fc5cd981fddf132b89fd0f209 Mon Sep 17 00:00:00 2001 From: Grisha Kruglov Date: Wed, 13 Oct 2021 01:54:30 -0700 Subject: [PATCH 406/517] Closes #21871 - Eagerly update UI state after search group removal Before this patch, this was the behavior - 'remove' button is clicked, we'd ask the storage to remove metadata (on its IO thread), then navigate to Home Screen. This resulted in a race we could end-up on the Home Screen before delete finishes, so the search groups do not appear to be removed (but, refreshing the Home Screen again shows that they are removed). This also resulted in an unnecessary navigation which felt very janky (screen will "scroll" to the top) and was way more work than necessary. After this patch, we: - dispatch two actions (on browserstore, on homefragmentstore) which remove the search groups from any relevant in-memory state; any UI bound to this state will be automatically "refreshed" - no longer navigate as part of the remove action, so the UI doesn't move and removal happens "in-place" --- .../controller/HistoryMetadataController.kt | 16 ++++++++++++---- .../java/org/mozilla/fenix/home/HomeFragment.kt | 4 +++- .../org/mozilla/fenix/home/HomeFragmentStore.kt | 4 ++++ .../mozilla/fenix/home/HomeFragmentStoreTest.kt | 12 ++++++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt index af8cd6f031..704db99aad 100644 --- a/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt @@ -9,12 +9,15 @@ import androidx.annotation.VisibleForTesting.PRIVATE import androidx.navigation.NavController import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import mozilla.components.browser.state.action.HistoryMetadataAction +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.storage.HistoryMetadataStorage import org.mozilla.fenix.R -import org.mozilla.fenix.browser.BrowserFragmentDirections import org.mozilla.fenix.historymetadata.HistoryMetadataGroup import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor +import org.mozilla.fenix.home.HomeFragmentAction import org.mozilla.fenix.home.HomeFragmentDirections +import org.mozilla.fenix.home.HomeFragmentStore import org.mozilla.fenix.library.history.toHistoryMetadata /** @@ -42,6 +45,8 @@ interface HistoryMetadataController { * The default implementation of [HistoryMetadataController]. */ class DefaultHistoryMetadataController( + private val store: BrowserStore, + private val homeStore: HomeFragmentStore, private val navController: NavController, private val storage: HistoryMetadataStorage, private val scope: CoroutineScope @@ -65,12 +70,15 @@ class DefaultHistoryMetadataController( } override fun handleRemoveGroup(searchTerm: String) { + // We want to update the UI right away in response to user action without waiting for the IO. + // First, dispatch actions that will clean up search groups in the two stores that have + // metadata-related state. + store.dispatch(HistoryMetadataAction.DisbandSearchGroupAction(searchTerm = searchTerm)) + homeStore.dispatch(HomeFragmentAction.DisbandSearchGroupAction(searchTerm = searchTerm)) + // Then, perform the expensive IO work of removing search groups from storage. scope.launch { storage.deleteHistoryMetadata(searchTerm) } - navController.navigate( - BrowserFragmentDirections.actionGlobalHome() - ) } @VisibleForTesting(otherwise = PRIVATE) diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index c2998bc21e..3bea7b450b 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -349,8 +349,10 @@ class HomeFragment : Fragment() { ), historyMetadataController = DefaultHistoryMetadataController( navController = findNavController(), + homeStore = homeFragmentStore, storage = components.core.historyStorage, - scope = viewLifecycleOwner.lifecycleScope + scope = viewLifecycleOwner.lifecycleScope, + store = components.core.store ), pocketStoriesController = DefaultPocketStoriesController( homeActivity = activity, diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt index dd18e0d121..61e597ac04 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt @@ -97,6 +97,7 @@ sealed class HomeFragmentAction : Action { data class RecentTabsChange(val recentTabs: List) : HomeFragmentAction() data class RecentBookmarksChange(val recentBookmarks: List) : HomeFragmentAction() data class HistoryMetadataChange(val historyMetadata: List) : HomeFragmentAction() + data class DisbandSearchGroupAction(val searchTerm: String) : HomeFragmentAction() data class SelectPocketStoriesCategory(val categoryName: String) : HomeFragmentAction() data class DeselectPocketStoriesCategory(val categoryName: String) : HomeFragmentAction() data class PocketStoriesShown(val storiesShown: List) : HomeFragmentAction() @@ -150,6 +151,9 @@ private fun homeFragmentStateReducer( is HomeFragmentAction.RecentTabsChange -> state.copy(recentTabs = action.recentTabs) is HomeFragmentAction.RecentBookmarksChange -> state.copy(recentBookmarks = action.recentBookmarks) is HomeFragmentAction.HistoryMetadataChange -> state.copy(historyMetadata = action.historyMetadata) + is HomeFragmentAction.DisbandSearchGroupAction -> state.copy( + historyMetadata = state.historyMetadata.filter { it.title.lowercase() != action.searchTerm.lowercase() } + ) is HomeFragmentAction.SelectPocketStoriesCategory -> { val updatedCategoriesState = state.copy( pocketStoriesCategoriesSelections = diff --git a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt index f5bb4db992..2d4c026514 100644 --- a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt @@ -128,6 +128,18 @@ class HomeFragmentStoreTest { assertEquals(historyMetadata, homeFragmentStore.state.historyMetadata) } + @Test + fun `Test disbanding search group in HomeFragmentStore`() = runBlocking { + val g1 = HistoryMetadataGroup(title = "test One") + val g2 = HistoryMetadataGroup(title = "test two") + val historyMetadata: List = listOf(g1, g2) + homeFragmentStore.dispatch(HomeFragmentAction.HistoryMetadataChange(historyMetadata)).join() + assertEquals(historyMetadata, homeFragmentStore.state.historyMetadata) + + homeFragmentStore.dispatch(HomeFragmentAction.DisbandSearchGroupAction("Test one")).join() + assertEquals(listOf(g2), homeFragmentStore.state.historyMetadata) + } + @Test fun `Test changing hiding collections placeholder`() = runBlocking { assertTrue(homeFragmentStore.state.showCollectionPlaceholder) From f58191db8fc9c72eddd05e98d730112ede243881 Mon Sep 17 00:00:00 2001 From: Grisha Kruglov Date: Wed, 13 Oct 2021 11:16:12 -0700 Subject: [PATCH 407/517] Fix up HistoryMetadataController tests --- .../HistoryMetadataControllerTest.kt | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/app/src/test/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataControllerTest.kt b/app/src/test/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataControllerTest.kt index f9d5a64f06..df5872ccb4 100644 --- a/app/src/test/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataControllerTest.kt @@ -14,6 +14,8 @@ import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineDispatcher import kotlinx.coroutines.test.TestCoroutineScope +import mozilla.components.browser.state.action.HistoryMetadataAction +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.storage.DocumentType import mozilla.components.concept.storage.HistoryMetadata import mozilla.components.concept.storage.HistoryMetadataKey @@ -24,9 +26,10 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.mozilla.fenix.R -import org.mozilla.fenix.browser.BrowserFragmentDirections import org.mozilla.fenix.historymetadata.HistoryMetadataGroup +import org.mozilla.fenix.home.HomeFragmentAction import org.mozilla.fenix.home.HomeFragmentDirections +import org.mozilla.fenix.home.HomeFragmentStore @OptIn(ExperimentalCoroutinesApi::class) class HistoryMetadataControllerTest { @@ -38,6 +41,8 @@ class HistoryMetadataControllerTest { private val navController = mockk(relaxed = true) private lateinit var storage: HistoryMetadataStorage + private lateinit var homeFragmentStore: HomeFragmentStore + private lateinit var store: BrowserStore private val scope = TestCoroutineScope() private lateinit var controller: DefaultHistoryMetadataController @@ -48,9 +53,13 @@ class HistoryMetadataControllerTest { every { id } returns R.id.homeFragment } storage = mockk(relaxed = true) + homeFragmentStore = mockk(relaxed = true) + store = mockk(relaxed = true) controller = spyk( DefaultHistoryMetadataController( + homeStore = homeFragmentStore, + store = store, navController = navController, scope = scope, storage = storage @@ -126,14 +135,15 @@ class HistoryMetadataControllerTest { controller.handleRemoveGroup(historyGroup.title) testDispatcher.advanceUntilIdle() + verify { + store.dispatch(HistoryMetadataAction.DisbandSearchGroupAction(searchTerm = historyGroup.title)) + } + verify { + homeFragmentStore.dispatch(HomeFragmentAction.DisbandSearchGroupAction(searchTerm = historyGroup.title)) + } coVerify { storage.deleteHistoryMetadata(historyGroup.title) } - verify { - navController.navigate( - BrowserFragmentDirections.actionGlobalHome() - ) - } } } From 5de19320b394554334842e53a375c9de3d2e5491 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Fri, 15 Oct 2021 00:53:11 +0000 Subject: [PATCH 408/517] Update to Android-Components 94.0.7. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 14b7ff3a97..e72e7050f3 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.6" + const val VERSION = "94.0.7" } From a5fa918e1f93f759593b8106b25e1873a60ce7b9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 15 Oct 2021 10:36:19 -0400 Subject: [PATCH 409/517] Close #21917: Update pocket stories card elevation to match other cards (#21956) (cherry picked from commit 2b928609661d4d94e1f3883c08f610a17e6d7dd7) Co-authored-by: Roger Yang --- app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt | 2 +- .../org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt index ffcede086f..6d1eb8d776 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLarge.kt @@ -111,7 +111,7 @@ private fun ListItemTabSurface( modifier = modifier, shape = RoundedCornerShape(8.dp), backgroundColor = FirefoxTheme.colors.surface, - elevation = 1.dp + elevation = 6.dp ) { Row( modifier = Modifier.padding(16.dp) diff --git a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt index 850e25d960..16b912a3b3 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/ListItemTabLargePlaceholder.kt @@ -50,7 +50,7 @@ fun ListItemTabLargePlaceholder( .clickable { onClick() }, shape = RoundedCornerShape(8.dp), backgroundColor = FirefoxTheme.colors.surface, - elevation = 1.dp, + elevation = 6.dp, ) { Column( modifier = Modifier From 85914a4232ae02d2b5fcc303b52b9fee79377fdd Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Thu, 14 Oct 2021 10:25:47 -0400 Subject: [PATCH 410/517] =?UTF-8?q?For=20#21906=20=E2=81=83=20Remove=20old?= =?UTF-8?q?=20copy=20form=20inactive=20tabs=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 25c784b684efaa46cdb16652b1a2bd8a777aed46) --- .../tabstray/browser/InactiveTabViewHolder.kt | 35 ++----------------- .../tabstray/browser/InactiveTabsAdapter.kt | 12 +++---- .../org/mozilla/fenix/tabstray/ext/Context.kt | 14 -------- .../main/res/layout/inactive_footer_item.xml | 28 +-------------- app/src/main/res/values/strings.xml | 6 ++-- 5 files changed, 10 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt index b7677b8867..d2d8671d58 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt @@ -5,7 +5,6 @@ package org.mozilla.fenix.tabstray.browser import android.view.View -import androidx.annotation.StringRes import androidx.core.view.updatePadding import androidx.recyclerview.widget.RecyclerView import mozilla.components.browser.toolbar.MAX_URI_LENGTH @@ -19,10 +18,6 @@ import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.dpToPx import org.mozilla.fenix.tabstray.TabsTrayInteractor -import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.Manual -import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.OneDay -import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.OneMonth -import org.mozilla.fenix.tabstray.browser.AutoCloseInterval.OneWeek sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { @@ -113,27 +108,8 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite class FooterHolder(itemView: View) : InactiveTabViewHolder(itemView) { - val binding = InactiveFooterItemBinding.bind(itemView) - - fun bind(interval: AutoCloseInterval) { - val context = itemView.context - val stringRes = when (interval) { - Manual, OneDay -> { - binding.inactiveDescription.visibility = View.GONE - binding.topDivider.visibility = View.GONE - null - } - OneWeek -> { - context.getString(interval.description) - } - OneMonth -> { - context.getString(interval.description) - } - } - if (stringRes != null) { - binding.inactiveDescription.text = - context.getString(R.string.inactive_tabs_description, stringRes) - } + init { + InactiveFooterItemBinding.bind(itemView) } companion object { @@ -141,10 +117,3 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite } } } - -enum class AutoCloseInterval(@StringRes val description: Int) { - Manual(0), - OneDay(0), - OneWeek(R.string.inactive_tabs_7_days), - OneMonth(R.string.inactive_tabs_30_days) -} diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt index 473950a7ec..11e94a3bb4 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt @@ -18,7 +18,6 @@ import org.mozilla.fenix.tabstray.TabsTrayInteractor import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.FooterHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.HeaderHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.TabViewHolder -import org.mozilla.fenix.tabstray.ext.autoCloseInterval import mozilla.components.support.base.observer.Observable as ComponentObservable /** @@ -67,11 +66,8 @@ class InactiveTabsAdapter( val item = getItem(position) as Item.Tab holder.bind(item.tab) } - is FooterHolder -> { - val item = getItem(position) as Item.Footer - holder.bind(item.interval) - } - is HeaderHolder -> { + + is HeaderHolder, is FooterHolder -> { // do nothing. } } @@ -99,7 +95,7 @@ class InactiveTabsAdapter( } val items = tabs.list.map { Item.Tab(it) } - val footer = Item.Footer(context.autoCloseInterval) + val footer = Item.Footer submitList(listOf(Item.Header) + items + listOf(footer)) } @@ -140,6 +136,6 @@ class InactiveTabsAdapter( * A footer for the inactive tab section. This may be seen only * when at least one inactive tab is present. */ - data class Footer(val interval: AutoCloseInterval) : Item() + object Footer : Item() } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/ext/Context.kt b/app/src/main/java/org/mozilla/fenix/tabstray/ext/Context.kt index 92ad59cbfb..e8082ac6e7 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/ext/Context.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/ext/Context.kt @@ -6,7 +6,6 @@ package org.mozilla.fenix.tabstray.ext import android.content.Context import org.mozilla.fenix.ext.components -import org.mozilla.fenix.tabstray.browser.AutoCloseInterval const val MIN_COLUMN_WIDTH_DP = 180 @@ -32,16 +31,3 @@ internal val Context.defaultBrowserLayoutColumns: Int 1 } } - -/** - * Returns the appropriate [AutoCloseInterval] based on user preferences. - */ -internal val Context.autoCloseInterval: AutoCloseInterval - get() = with(components.settings) { - when { - closeTabsAfterOneDay -> AutoCloseInterval.OneDay - closeTabsAfterOneWeek -> AutoCloseInterval.OneWeek - closeTabsAfterOneMonth -> AutoCloseInterval.OneMonth - else -> AutoCloseInterval.Manual - } - } diff --git a/app/src/main/res/layout/inactive_footer_item.xml b/app/src/main/res/layout/inactive_footer_item.xml index 955ff5b42a..ae13e8b318 100644 --- a/app/src/main/res/layout/inactive_footer_item.xml +++ b/app/src/main/res/layout/inactive_footer_item.xml @@ -15,37 +15,11 @@ android:paddingEnd="1dp" android:paddingBottom="1dp"> - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6039ed8ca6..e45f7462b5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1928,11 +1928,11 @@ Close all inactive tabs - Tabs are available here for %s. After that time, tabs will be automatically closed. + Tabs are available here for %s. After that time, tabs will be automatically closed. - 30 days + 30 days - 1 week + 1 week From 38b817cad109352d5270347aa8f1e674deab6e5e Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Fri, 15 Oct 2021 11:25:56 -0400 Subject: [PATCH 411/517] Close #21573: Add shadow to recent bookmarks section (#21959) --- .../drawable/recent_bookmark_background.xml | 16 --------------- .../main/res/layout/recent_bookmark_item.xml | 20 +++++++++---------- .../res/layout/recent_bookmarks_header.xml | 2 +- app/src/main/res/values/dimens.xml | 9 +++++++-- 4 files changed, 18 insertions(+), 29 deletions(-) delete mode 100644 app/src/main/res/drawable/recent_bookmark_background.xml diff --git a/app/src/main/res/drawable/recent_bookmark_background.xml b/app/src/main/res/drawable/recent_bookmark_background.xml deleted file mode 100644 index f62435f3c6..0000000000 --- a/app/src/main/res/drawable/recent_bookmark_background.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/recent_bookmark_item.xml b/app/src/main/res/layout/recent_bookmark_item.xml index f7533cbeaa..48d46366b4 100644 --- a/app/src/main/res/layout/recent_bookmark_item.xml +++ b/app/src/main/res/layout/recent_bookmark_item.xml @@ -8,13 +8,15 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/bookmark_item" android:layout_width="@dimen/recent_bookmark_item_width" - android:layout_height="@dimen/recent_bookmark_item_height" - android:padding="4dp"> + android:layout_height="wrap_content" > - + android:layout_gravity="center" /> - + diff --git a/app/src/main/res/layout/recent_bookmarks_header.xml b/app/src/main/res/layout/recent_bookmarks_header.xml index 955b0d31b1..e08ffdd189 100644 --- a/app/src/main/res/layout/recent_bookmarks_header.xml +++ b/app/src/main/res/layout/recent_bookmarks_header.xml @@ -9,7 +9,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="40dp"> + android:layout_marginTop="@dimen/recent_bookmark_header_margin_top"> 16dp - 136dp - 156dp + 40dp + 164dp + 156dp + 16dp + 8dp + 6dp 96dp 0dp 8dp + 4dp From 8906cfcc3fe979bb5263a1f19f54cad05fb7f96d Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Fri, 15 Oct 2021 14:35:17 -0700 Subject: [PATCH 412/517] For #21522: Wrap section titles on home (#21836) --- .../mozilla/fenix/compose/SectionHeader.kt | 2 +- .../pocket/PocketStoriesViewHolder.kt | 4 +++ .../res/layout/history_metadata_header.xml | 24 ++++++++------- .../res/layout/recent_bookmarks_header.xml | 23 ++++++++------- .../main/res/layout/recent_tabs_header.xml | 29 ++++++++++--------- 5 files changed, 46 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt b/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt index b53c87c48d..a358fcd1eb 100644 --- a/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt +++ b/app/src/main/java/org/mozilla/fenix/compose/SectionHeader.kt @@ -60,7 +60,7 @@ fun HomeSectionHeader( fontSize = 16.sp, lineHeight = 20.sp ), - maxLines = 1, + maxLines = 2, overflow = TextOverflow.Ellipsis, color = FirefoxTheme.colors.textPrimary ) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt index e3d357e2c3..35d3262705 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt @@ -11,8 +11,10 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy @@ -98,6 +100,7 @@ fun PocketStories( modifier = Modifier .fillMaxWidth() .padding(horizontal = horizontalPadding.dp) + .wrapContentHeight(align = Alignment.Top) ) Spacer(Modifier.height(17.dp)) @@ -111,6 +114,7 @@ fun PocketStories( modifier = Modifier .fillMaxWidth() .padding(horizontal = horizontalPadding.dp) + .wrapContentHeight(align = Alignment.Top) ) Spacer(Modifier.height(17.dp)) diff --git a/app/src/main/res/layout/history_metadata_header.xml b/app/src/main/res/layout/history_metadata_header.xml index 56f30d3844..138bfd42ca 100644 --- a/app/src/main/res/layout/history_metadata_header.xml +++ b/app/src/main/res/layout/history_metadata_header.xml @@ -8,18 +8,20 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="@dimen/home_item_horizontal_margin" - android:layout_marginTop="40dp"> + android:layout_marginTop="40dp" + android:layout_marginBottom="16dp"> diff --git a/app/src/main/res/layout/recent_bookmarks_header.xml b/app/src/main/res/layout/recent_bookmarks_header.xml index e08ffdd189..0c97c2285f 100644 --- a/app/src/main/res/layout/recent_bookmarks_header.xml +++ b/app/src/main/res/layout/recent_bookmarks_header.xml @@ -14,14 +14,15 @@ + android:text="@string/recently_saved_show_all" + android:textColor="@color/home_show_all_button_text" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/layout/recent_tabs_header.xml b/app/src/main/res/layout/recent_tabs_header.xml index 76a9e5820f..6876c48a30 100644 --- a/app/src/main/res/layout/recent_tabs_header.xml +++ b/app/src/main/res/layout/recent_tabs_header.xml @@ -7,35 +7,38 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="@dimen/home_item_horizontal_margin" - android:layout_marginTop="40dp"> + android:layout_marginTop="40dp" + android:layout_marginBottom="16dp"> + app:layout_constraintEnd_toStartOf="@id/show_all_button" + app:layout_constraintTop_toTopOf="parent" /> From 0f9469a2002923827ad9e320028a5b43d00f6400 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 17 Oct 2021 21:45:25 -0400 Subject: [PATCH 413/517] Update to Android-Components 94.0.8. (#21984) Co-authored-by: MickeyMoz --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index e72e7050f3..bddd4f4f57 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.7" + const val VERSION = "94.0.8" } From c5c3375267d390f8ae2c8187f913ba1c9195cb31 Mon Sep 17 00:00:00 2001 From: Mozilla Releng Treescript Date: Mon, 18 Oct 2021 03:10:18 +0000 Subject: [PATCH 414/517] Automatic version bump CLOSED TREE NO BUG a=release --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 4f94a615bc..03b23e9b39 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -94.0.0-beta.4 +94.0.0-beta.5 From 0e1271a15dcf6350a31c930a09b258fadce1510a Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Thu, 7 Oct 2021 19:55:44 -0400 Subject: [PATCH 415/517] For #21791 Adds tab auto-close prompt (cherry picked from commit 08256ac68c57a8bbc69278a7c251dee4566fca0a) --- .../fenix/tabstray/TrayPagerAdapter.kt | 2 +- .../tabstray/browser/InactiveTabViewHolder.kt | 22 ++++++ .../tabstray/browser/InactiveTabsAdapter.kt | 28 ++++++- .../InactiveTabsAutoCloseDialogController.kt | 44 +++++++++++ .../InactiveTabsAutoCloseDialogInteractor.kt | 22 ++++++ .../tabstray/browser/NormalBrowserTrayList.kt | 27 +++++-- .../java/org/mozilla/fenix/utils/Settings.kt | 21 +++++ ...ctive_tab_auto_close_border_background.xml | 14 ++++ .../res/layout/inactive_tabs_auto_close.xml | 79 +++++++++++++++++++ app/src/main/res/values/preference_keys.xml | 2 + app/src/main/res/values/strings.xml | 8 ++ ...activeTabsAutoCloseDialogInteractorTest.kt | 32 ++++++++ ...activeTabsAutoCloseDialogControllerTest.kt | 55 +++++++++++++ .../org/mozilla/fenix/utils/SettingsTest.kt | 30 +++++++ 14 files changed, 375 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogController.kt create mode 100644 app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogInteractor.kt create mode 100644 app/src/main/res/drawable/inactive_tab_auto_close_border_background.xml create mode 100644 app/src/main/res/layout/inactive_tabs_auto_close.xml create mode 100644 app/src/test/java/org/mozilla/fenix/tabstray/browser/DefaultInactiveTabsAutoCloseDialogInteractorTest.kt create mode 100644 app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogControllerTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TrayPagerAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TrayPagerAdapter.kt index 32844de463..e33b709c2a 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TrayPagerAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TrayPagerAdapter.kt @@ -40,7 +40,7 @@ class TrayPagerAdapter( */ private val normalAdapter by lazy { ConcatAdapter( - InactiveTabsAdapter(context, browserInteractor, interactor, INACTIVE_TABS_FEATURE_NAME), + InactiveTabsAdapter(context, browserInteractor, interactor, INACTIVE_TABS_FEATURE_NAME, context.settings()), TabGroupAdapter(context, browserInteractor, tabsTrayStore, TAB_GROUP_FEATURE_NAME), TitleHeaderAdapter(browserStore, context.settings()), BrowserTabsAdapter(context, browserInteractor, tabsTrayStore, TABS_TRAY_FEATURE_NAME) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt index d2d8671d58..a7b46f72da 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt @@ -13,6 +13,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.databinding.InactiveFooterItemBinding import org.mozilla.fenix.databinding.InactiveHeaderItemBinding import org.mozilla.fenix.databinding.InactiveTabListItemBinding +import org.mozilla.fenix.databinding.InactiveTabsAutoCloseBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.ext.toShortUrl @@ -65,6 +66,27 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite } } + class AutoCloseDialogHolder( + itemView: View, + interactor: InactiveTabsAutoCloseDialogInteractor + ) : InactiveTabViewHolder(itemView) { + private val binding = InactiveTabsAutoCloseBinding.bind(itemView) + + init { + binding.closeButton.setOnClickListener { + interactor.onCloseClicked() + } + + binding.action.setOnClickListener { + interactor.onEnabledAutoCloseClicked() + } + } + + companion object { + const val LAYOUT_ID = R.layout.inactive_tabs_auto_close + } + } + /** * A RecyclerView ViewHolder implementation for an inactive tab view. * diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt index 11e94a3bb4..afd033042d 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAdapter.kt @@ -15,9 +15,11 @@ import mozilla.components.concept.tabstray.TabsTray import mozilla.components.support.base.observer.ObserverRegistry import org.mozilla.fenix.components.Components import org.mozilla.fenix.tabstray.TabsTrayInteractor +import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.AutoCloseDialogHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.FooterHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.HeaderHolder import org.mozilla.fenix.tabstray.browser.InactiveTabViewHolder.TabViewHolder +import org.mozilla.fenix.utils.Settings import mozilla.components.support.base.observer.Observable as ComponentObservable /** @@ -43,16 +45,20 @@ class InactiveTabsAdapter( private val browserTrayInteractor: BrowserTrayInteractor, private val tabsTrayInteractor: TabsTrayInteractor, private val featureName: String, + private val settings: Settings, delegate: Observable = ObserverRegistry() ) : Adapter(DiffCallback), TabsTray, Observable by delegate { internal lateinit var inactiveTabsInteractor: InactiveTabsInteractor + internal lateinit var inactiveTabsAutoCloseDialogInteractor: InactiveTabsAutoCloseDialogInteractor + internal var inActiveTabsCount: Int = 0 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InactiveTabViewHolder { val view = LayoutInflater.from(parent.context) .inflate(viewType, parent, false) return when (viewType) { + AutoCloseDialogHolder.LAYOUT_ID -> AutoCloseDialogHolder(view, inactiveTabsAutoCloseDialogInteractor) HeaderHolder.LAYOUT_ID -> HeaderHolder(view, inactiveTabsInteractor, tabsTrayInteractor) TabViewHolder.LAYOUT_ID -> TabViewHolder(view, browserTrayInteractor, featureName) FooterHolder.LAYOUT_ID -> FooterHolder(view) @@ -67,7 +73,7 @@ class InactiveTabsAdapter( holder.bind(item.tab) } - is HeaderHolder, is FooterHolder -> { + is HeaderHolder, is AutoCloseDialogHolder, is FooterHolder -> { // do nothing. } } @@ -76,12 +82,19 @@ class InactiveTabsAdapter( override fun getItemViewType(position: Int): Int { return when (position) { 0 -> HeaderHolder.LAYOUT_ID + 1 -> if (settings.shouldShowInactiveTabsAutoCloseDialog(inActiveTabsCount)) { + AutoCloseDialogHolder.LAYOUT_ID + } else { + TabViewHolder.LAYOUT_ID + } itemCount - 1 -> FooterHolder.LAYOUT_ID else -> TabViewHolder.LAYOUT_ID } } override fun updateTabs(tabs: Tabs) { + inActiveTabsCount = tabs.list.size + // Early return with an empty list to remove the header/footer items. if (tabs.list.isEmpty()) { submitList(emptyList()) @@ -96,8 +109,12 @@ class InactiveTabsAdapter( val items = tabs.list.map { Item.Tab(it) } val footer = Item.Footer - - submitList(listOf(Item.Header) + items + listOf(footer)) + val headerItems = if (settings.shouldShowInactiveTabsAutoCloseDialog(items.size)) { + listOf(Item.Header, Item.AutoCloseMessage) + } else { + listOf(Item.Header) + } + submitList(headerItems + items + listOf(footer)) } override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false @@ -132,6 +149,11 @@ class InactiveTabsAdapter( */ data class Tab(val tab: TabsTrayTab) : Item() + /** + * A dialog for when the inactive tabs section reach 20 tabs. + */ + object AutoCloseMessage : Item() + /** * A footer for the inactive tab section. This may be seen only * when at least one inactive tab is present. diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogController.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogController.kt new file mode 100644 index 0000000000..137d7d3189 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogController.kt @@ -0,0 +1,44 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.tabstray.browser + +import androidx.annotation.VisibleForTesting +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.tabstray.TabsTray +import mozilla.components.feature.tabs.ext.toTabs +import org.mozilla.fenix.utils.Settings + +class InactiveTabsAutoCloseDialogController( + private val browserStore: BrowserStore, + private val settings: Settings, + private val tabFilter: (TabSessionState) -> Boolean, + private val tray: TabsTray +) { + /** + * Dismiss the auto-close dialog. + */ + fun close() { + settings.hasInactiveTabsAutoCloseDialogBeenDismissed = true + refeshInactiveTabsSecion() + } + + /** + * Enable the auto-close feature with the after a month setting. + */ + fun enableAutoClosed() { + settings.closeTabsAfterOneMonth = true + settings.closeTabsAfterOneWeek = false + settings.closeTabsAfterOneDay = false + settings.manuallyCloseTabs = false + refeshInactiveTabsSecion() + } + + @VisibleForTesting + internal fun refeshInactiveTabsSecion() { + val tabs = browserStore.state.toTabs { tabFilter.invoke(it) } + tray.updateTabs(tabs) + } +} diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogInteractor.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogInteractor.kt new file mode 100644 index 0000000000..ec3d930d99 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogInteractor.kt @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.tabstray.browser + +interface InactiveTabsAutoCloseDialogInteractor { + fun onCloseClicked() + fun onEnabledAutoCloseClicked() +} + +class DefaultInactiveTabsAutoCloseDialogInteractor( + private val controller: InactiveTabsAutoCloseDialogController +) : InactiveTabsAutoCloseDialogInteractor { + override fun onCloseClicked() { + controller.close() + } + + override fun onEnabledAutoCloseClicked() { + controller.enableAutoClosed() + } +} diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt index f24c5ce37f..0c3b39af2a 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt @@ -39,23 +39,35 @@ class NormalBrowserTrayList @JvmOverloads constructor( private val swipeDelegate = SwipeToDeleteDelegate() private val concatAdapter by lazy { adapter as ConcatAdapter } private val tabSorter by lazy { TabSorter(context, concatAdapter, context.components.core.store) } - private val inactiveTabsInteractor by lazy { - val tabFilter: (TabSessionState) -> Boolean = filter@{ - if (!context.settings().inactiveTabsAreEnabled) { - return@filter false - } - it.isNormalTabInactive(maxActiveTime) + private val inactiveTabsFilter: (TabSessionState) -> Boolean = filter@{ + if (!context.settings().inactiveTabsAreEnabled) { + return@filter false } + it.isNormalTabInactive(maxActiveTime) + } + + private val inactiveTabsInteractor by lazy { DefaultInactiveTabsInteractor( InactiveTabsController( context.components.core.store, - tabFilter, + inactiveTabsFilter, concatAdapter.inactiveTabsAdapter, context.components.analytics.metrics ) ) } + private val inactiveTabsAutoCloseInteractor by lazy { + DefaultInactiveTabsAutoCloseDialogInteractor( + InactiveTabsAutoCloseDialogController( + context.components.core.store, + context.settings(), + inactiveTabsFilter, + concatAdapter.inactiveTabsAdapter + ) + ) + } + override val tabsFeature by lazy { TabsFeature( tabSorter, @@ -81,6 +93,7 @@ class NormalBrowserTrayList @JvmOverloads constructor( super.onAttachedToWindow() concatAdapter.inactiveTabsAdapter.inactiveTabsInteractor = inactiveTabsInteractor + concatAdapter.inactiveTabsAdapter.inactiveTabsAutoCloseDialogInteractor = inactiveTabsAutoCloseInteractor tabsFeature.start() diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index a930e80ac6..7fbfc0e207 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -67,6 +67,7 @@ class Settings(private val appContext: Context) : PreferencesHolder { private const val CFR_COUNT_CONDITION_FOCUS_INSTALLED = 1 private const val CFR_COUNT_CONDITION_FOCUS_NOT_INSTALLED = 3 private const val APP_LAUNCHES_TO_SHOW_DEFAULT_BROWSER_CARD = 3 + private const val INACTIVE_TAB_MINIMUM_TO_SHOW_AUTO_CLOSE_DIALOG = 20 const val FOUR_HOURS_MS = 60 * 60 * 4 * 1000L const val ONE_DAY_MS = 60 * 60 * 24 * 1000L @@ -837,6 +838,26 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = true ) + /** + * Indicates if the auto-close dialog for inactive tabs has been dismissed before. + */ + var hasInactiveTabsAutoCloseDialogBeenDismissed by booleanPreference( + appContext.getPreferenceKey(R.string.pref_key_has_inactive_tabs_auto_close_dialog_dismissed), + default = false + ) + + /** + * Indicates if the auto-close dialog should be visible based on + * if the user has dismissed it before [hasInactiveTabsAutoCloseDialogBeenDismissed], + * if the minimum number of tabs has been accumulated [numbersOfTabs] + * and if the auto-close setting is already set to [closeTabsAfterOneMonth]. + */ + fun shouldShowInactiveTabsAutoCloseDialog(numbersOfTabs: Int): Boolean { + return !hasInactiveTabsAutoCloseDialogBeenDismissed && + numbersOfTabs >= INACTIVE_TAB_MINIMUM_TO_SHOW_AUTO_CLOSE_DIALOG && + !closeTabsAfterOneMonth + } + /** * Indicates if the jump back in CRF should be shown. */ diff --git a/app/src/main/res/drawable/inactive_tab_auto_close_border_background.xml b/app/src/main/res/drawable/inactive_tab_auto_close_border_background.xml new file mode 100644 index 0000000000..bc80384e33 --- /dev/null +++ b/app/src/main/res/drawable/inactive_tab_auto_close_border_background.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/inactive_tabs_auto_close.xml b/app/src/main/res/layout/inactive_tabs_auto_close.xml new file mode 100644 index 0000000000..e8a1a41331 --- /dev/null +++ b/app/src/main/res/layout/inactive_tabs_auto_close.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index 8dfec3df80..1404092d2d 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -227,6 +227,8 @@ pref_key_should_show_inactive_tabs_popup + + pref_key_has_inactive_tabs_auto_close_dialog_dismissed pref_key_should_show_jump_back_in_tabs_popup diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e45f7462b5..746403697f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,6 +110,14 @@ Tabs you haven’t viewed for two weeks get moved here. Turn off in settings + + Auto-close after one month? + + Firefox can close tabs you haven’t viewed over the past month. + + Close + + Turn on auto close diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/DefaultInactiveTabsAutoCloseDialogInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/DefaultInactiveTabsAutoCloseDialogInteractorTest.kt new file mode 100644 index 0000000000..110987efef --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/DefaultInactiveTabsAutoCloseDialogInteractorTest.kt @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.tabstray.browser + +import io.mockk.mockk +import io.mockk.verify +import org.junit.Test + +class DefaultInactiveTabsAutoCloseDialogInteractorTest { + + @Test + fun `WHEN onCloseClicked THEN close`() { + val controller: InactiveTabsAutoCloseDialogController = mockk(relaxed = true) + val interactor = DefaultInactiveTabsAutoCloseDialogInteractor(controller) + + interactor.onCloseClicked() + + verify { controller.close() } + } + + @Test + fun `WHEN onEnabledAutoCloseClicked THEN enableAutoClosed`() { + val controller: InactiveTabsAutoCloseDialogController = mockk(relaxed = true) + val interactor = DefaultInactiveTabsAutoCloseDialogInteractor(controller) + + interactor.onEnabledAutoCloseClicked() + + verify { controller.enableAutoClosed() } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogControllerTest.kt new file mode 100644 index 0000000000..6629c7e988 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/tabstray/browser/InactiveTabsAutoCloseDialogControllerTest.kt @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.tabstray.browser + +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.tabstray.TabsTray +import org.junit.Test +import org.mozilla.fenix.utils.Settings + +class InactiveTabsAutoCloseDialogControllerTest { + + @Test + fun `WHEN close THEN update settings and refresh`() { + val filter: (TabSessionState) -> Boolean = { !it.content.private } + val store = BrowserStore() + val settings: Settings = mockk(relaxed = true) + val tray: TabsTray = mockk(relaxed = true) + val controller = spyk(InactiveTabsAutoCloseDialogController(store, settings, filter, tray)) + + every { controller.refeshInactiveTabsSecion() } just Runs + + controller.close() + + verify { settings.hasInactiveTabsAutoCloseDialogBeenDismissed = true } + verify { controller.refeshInactiveTabsSecion() } + } + + @Test + fun `WHEN enableAutoClosed THEN update closeTabsAfterOneMonth settings and refresh`() { + val filter: (TabSessionState) -> Boolean = { !it.content.private } + val store = BrowserStore() + val settings: Settings = mockk(relaxed = true) + val tray: TabsTray = mockk(relaxed = true) + val controller = spyk(InactiveTabsAutoCloseDialogController(store, settings, filter, tray)) + + every { controller.refeshInactiveTabsSecion() } just Runs + + controller.enableAutoClosed() + + verify { settings.closeTabsAfterOneMonth = true } + verify { settings.closeTabsAfterOneWeek = false } + verify { settings.closeTabsAfterOneDay = false } + verify { settings.manuallyCloseTabs = false } + verify { controller.refeshInactiveTabsSecion() } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt index ba946d0ced..8a5ccdfcbf 100644 --- a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt +++ b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt @@ -753,4 +753,34 @@ class SettingsTest { // Then assertTrue(settings.inactiveTabsAreEnabled) } + + @Test + fun `GIVEN shouldShowInactiveTabsAutoCloseDialog WHEN the dialog has been dismissed before THEN no show the dialog`() { + val settings = spyk(settings) + every { settings.hasInactiveTabsAutoCloseDialogBeenDismissed } returns true + + assertFalse(settings.shouldShowInactiveTabsAutoCloseDialog(20)) + } + + @Test + fun `GIVEN shouldShowInactiveTabsAutoCloseDialog WHEN the inactive tabs are less than the minimum THEN no show the dialog`() { + assertFalse(settings.shouldShowInactiveTabsAutoCloseDialog(19)) + } + + @Test + fun `GIVEN shouldShowInactiveTabsAutoCloseDialog WHEN closeTabsAfterOneMonth is already selected THEN no show the dialog`() { + val settings = spyk(settings) + every { settings.closeTabsAfterOneMonth } returns true + + assertFalse(settings.shouldShowInactiveTabsAutoCloseDialog(19)) + } + + @Test + fun `GIVEN shouldShowInactiveTabsAutoCloseDialog WHEN the dialog has not been dismissed, with more inactive tabs than the queried and closeTabsAfterOneMonth not set THEN show the dialog`() { + val settings = spyk(settings) + every { settings.closeTabsAfterOneMonth } returns false + every { settings.hasInactiveTabsAutoCloseDialogBeenDismissed } returns false + + assertTrue(settings.shouldShowInactiveTabsAutoCloseDialog(20)) + } } From 2d8aceff32bb2fd2a98f63f768f5a7361a7a8e9a Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Wed, 20 Oct 2021 00:03:54 +0000 Subject: [PATCH 416/517] Update to Android-Components 94.0.9. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index bddd4f4f57..bf0b8b56f2 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.8" + const val VERSION = "94.0.9" } From 2124c76db0660a98c1b1a9d866cdf9401d18fa46 Mon Sep 17 00:00:00 2001 From: Christian Sadilek Date: Thu, 7 Oct 2021 17:17:42 -0400 Subject: [PATCH 417/517] Enable pocket stories in CA (cherry picked from commit 1b463a5c1791434f683054a77fdcd232b43ca9fd) --- app/src/main/java/org/mozilla/fenix/FeatureFlags.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index 165beb7822..9d514cc914 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -82,8 +82,9 @@ object FeatureFlags { * Show Pocket recommended stories on home. */ fun isPocketRecommendationsFeatureEnabled(context: Context): Boolean { - return "en-US" == LocaleManager.getCurrentLocale(context) + val langTag = LocaleManager.getCurrentLocale(context) ?.toLanguageTag() ?: getSystemDefault().toLanguageTag() + return listOf("en-US", "en-CA").contains(langTag) } /** From c10bfee4e53b7a97c8e4aa292daccf1bce3f4a20 Mon Sep 17 00:00:00 2001 From: Noah Bond <87384386+MozillaNoah@users.noreply.github.com> Date: Wed, 20 Oct 2021 17:08:16 -0700 Subject: [PATCH 418/517] MR2 Inactive tabs telemetry (#21908) (#22056) * For #21903 - Added telemetry for interacting with inactive tabs * For #21903 - Added missing inactive tab delete count event to delete all event * For #21903 - Added PR numbers to metrics * For #21903 - Updated broken unit tests. Resolved critical lint warning. * For #21903 - Fixed inactive tabs setting toggle metric * For #21903 - Updated FenixApp unit test * For #21903 - Updated newline character in Metrics. Set inactive tab metrics' lifetime to default. Updated expiration to Nov 2022. Refactored inactive tabs metric to be a single metric. * PR: addendum for last commit that missed a file * For #21903 - Changed logic check for reporting inactive tab count * PR: fixed merge conflict * For #21903 - Removed tab close tracking when the user closes ALL inactive tabs * For #21903 - Removed individual tab close metric verify from CLOSE ALL test * For #21903 - Updated inactive tabs toggle setting expiration to match the expiration of the other events Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- app/metrics.yaml | 137 +++++++++++++----- .../org/mozilla/fenix/FenixApplication.kt | 2 + .../mozilla/fenix/components/metrics/Event.kt | 6 + .../components/metrics/GleanMetricsService.kt | 13 ++ .../fenix/tabstray/TabsTrayController.kt | 1 + .../tabstray/browser/InactiveTabViewHolder.kt | 3 + .../tabstray/browser/NormalBrowserTrayList.kt | 10 +- .../fenix/tabstray/browser/TabSorter.kt | 8 + .../org/mozilla/fenix/FenixApplicationTest.kt | 2 + .../tabstray/DefaultTabsTrayControllerTest.kt | 23 +++ 10 files changed, 171 insertions(+), 34 deletions(-) diff --git a/app/metrics.yaml b/app/metrics.yaml index 9e10d0e8fb..1a0a1b3a3f 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -310,7 +310,7 @@ events: expires: "2021-12-01" synced_tab_opened: type: event - description: > + description: | An event that indicates that a synced tab was opened. bugs: - https://github.com/mozilla-mobile/fenix/issues/15369 @@ -1252,7 +1252,7 @@ metrics: customize_home: most_visited_sites: type: boolean - description: > + description: | An indication of whether the most visited sites are enabled to be displayed send_in_pings: @@ -1269,7 +1269,7 @@ customize_home: expires: "2022-09-20" jump_back_in: type: boolean - description: > + description: | An indication of whether the Jump back in section is enabled to be displayed send_in_pings: @@ -1286,7 +1286,7 @@ customize_home: expires: "2022-09-20" recently_saved: type: boolean - description: > + description: | An indication of whether the recently saved section is enabled to be displayed send_in_pings: @@ -1303,7 +1303,7 @@ customize_home: expires: "2022-09-20" recently_visited: type: boolean - description: > + description: | An indication of whether the Recently visited section is enabled to be displayed send_in_pings: @@ -1320,7 +1320,7 @@ customize_home: expires: "2022-09-20" pocket: type: boolean - description: > + description: | An indication of whether Pocket is enabled to be displayed send_in_pings: - metrics @@ -1336,7 +1336,7 @@ customize_home: expires: "2022-09-20" preference_toggled: type: event - description: > + description: | A user toggles the preference for the home screen items. extra_keys: preference_key: @@ -1362,7 +1362,7 @@ customize_home: preferences: search_suggestions_enabled: type: boolean - description: > + description: | Whether or not the user has search suggestions enabled default: true send_in_pings: @@ -1381,7 +1381,7 @@ preferences: expires: "2022-02-01" remote_debugging_enabled: type: boolean - description: > + description: | Whether or not the user has remote debugging enabled default: false send_in_pings: @@ -1400,7 +1400,7 @@ preferences: expires: "2022-02-01" telemetry_enabled: type: boolean - description: > + description: | Whether or not the user has telemetry enabled. Note we should never receive a "false" value for this since telemetry would not send in that case. @@ -1421,7 +1421,7 @@ preferences: expires: "2022-02-01" enhanced_tracking_protection: type: string - description: > + description: | What type of enhanced tracking protection the user has enabled. "standard," "strict," "custom," or "" (if disabled) default: "standard" @@ -1441,7 +1441,7 @@ preferences: expires: "2022-02-01" bookmarks_suggestion: type: boolean - description: > + description: | Whether or not the user has enabled bookmark search suggestions default: true send_in_pings: @@ -1460,7 +1460,7 @@ preferences: expires: "2022-02-01" browsing_history_suggestion: type: boolean - description: > + description: | Whether or not the user has enabled browsing history suggestions. default: true send_in_pings: @@ -1479,7 +1479,7 @@ preferences: expires: "2022-02-01" clipboard_suggestions_enabled: type: boolean - description: > + description: | Whether or not the user has enabled clipboard search suggestions. default: true send_in_pings: @@ -1498,7 +1498,7 @@ preferences: expires: "2022-02-01" search_shortcuts_enabled: type: boolean - description: > + description: | Whether or not the user has enabled search shortcuts. default: true send_in_pings: @@ -1517,7 +1517,7 @@ preferences: expires: "2022-02-01" signed_in_sync: type: boolean - description: > + description: | Whether or not the user is signed into FxA default: false send_in_pings: @@ -1536,7 +1536,7 @@ preferences: expires: "2022-02-01" sync_items: type: string_list - description: > + description: | The list of items the user has chosen to sync with FxA. default: "" if the user is signed out. Otherwise defaults to whatever is set in their FxA account. New accounts set: @@ -1557,7 +1557,7 @@ preferences: expires: "2022-02-01" voice_search_enabled: type: boolean - description: > + description: | Whether or not the user has enabled the voice search button. default: true send_in_pings: @@ -1576,7 +1576,7 @@ preferences: expires: "2022-02-01" toolbar_position_setting: type: string - description: > + description: | The position of the toolbar default: bottom (defaults to top if the user has accessibility services) send_in_pings: @@ -1595,7 +1595,7 @@ preferences: expires: "2022-02-01" accessibility_services: type: string_list - description: > + description: | Whether or not the user has touch exploration or switch services enabled. These are built into the Android OS, not Fenix prefs. default: "" @@ -1615,7 +1615,7 @@ preferences: expires: "2022-02-01" open_links_in_app_enabled: type: boolean - description: > + description: | Whether or not the user has the open links in apps feature enabled. default: false send_in_pings: @@ -1634,7 +1634,7 @@ preferences: expires: "2022-02-01" user_theme: type: string - description: > + description: | The theme the user has enabled. "light," "dark," "system," or "battery" default: "system" for API 28+, else "light" send_in_pings: @@ -1651,6 +1651,22 @@ preferences: notification_emails: - android-probes@mozilla.com expires: "2022-02-01" + inactive_tabs_enabled: + type: boolean + description: | + Whether or not the user has the inactive tabs feature enabled. + default: true + send_in_pings: + - metrics + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21903 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21908 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-11-01" search.default_engine: code: @@ -2784,6 +2800,61 @@ tabs_tray: notification_emails: - android-probes@mozilla.com expires: "2022-08-01" + has_inactive_tabs: + type: event + description: | + A boolean that indicates if the user has any INACTIVE tabs. + extra_keys: + inactive_tabs_count: + description: "The number of inactive tabs the user currently has." + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21903 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21908 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-11-01" + close_all_inactive_tabs: + type: event + description: | + A user tapped the close all inactive tabs button in the the tabs tray + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21903 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21908 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-11-01" + close_inactive_tab: + type: counter + description: | + A counter that indicates how many INACTIVE tabs a user has closed. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21903 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21908 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-11-01" + open_inactive_tab: + type: counter + description: | + A counter that indicates how many INACTIVE tabs a user has opened. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21903 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21908 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-11-01" collections: renamed: @@ -4190,7 +4261,7 @@ perf.awesomebar: - metrics type: timing_distribution time_unit: millisecond - description: > + description: | Duration of a history awesomebar suggestion query. bugs: - https://github.com/mozilla-mobile/android-components/issues/4992 @@ -4210,7 +4281,7 @@ perf.awesomebar: - metrics type: timing_distribution time_unit: millisecond - description: > + description: | Duration of a bookmarks awesomebar suggestion query. bugs: - https://github.com/mozilla-mobile/android-components/issues/4992 @@ -4230,7 +4301,7 @@ perf.awesomebar: - metrics type: timing_distribution time_unit: millisecond - description: > + description: | Duration of a search engine awesomebar suggestion query. bugs: - https://github.com/mozilla-mobile/android-components/issues/4992 @@ -4250,7 +4321,7 @@ perf.awesomebar: - metrics type: timing_distribution time_unit: millisecond - description: > + description: | Duration of a session awesomebar suggestion query. bugs: - https://github.com/mozilla-mobile/android-components/issues/4992 @@ -4270,7 +4341,7 @@ perf.awesomebar: - metrics type: timing_distribution time_unit: millisecond - description: > + description: | Duration of a synced tabs awesomebar suggestion query. bugs: - https://github.com/mozilla-mobile/android-components/issues/4992 @@ -4290,7 +4361,7 @@ perf.awesomebar: - metrics type: timing_distribution time_unit: millisecond - description: > + description: | Duration of a clipboard awesomebar suggestion query. bugs: - https://github.com/mozilla-mobile/android-components/issues/4992 @@ -4310,7 +4381,7 @@ perf.awesomebar: - metrics type: timing_distribution time_unit: millisecond - description: > + description: | Duration of a shortcuts awesomebar suggestion query. bugs: - https://github.com/mozilla-mobile/android-components/issues/4992 @@ -4368,7 +4439,7 @@ storage.stats: send_in_pings: - metrics type: timing_distribution - description: > + description: | How long it took to query the device for the StorageStats that contain the file size information. The docs say it may be expensive so we want to ensure it's not too expensive. This value is only available on Android @@ -4392,7 +4463,7 @@ storage.stats: send_in_pings: - metrics type: memory_distribution - description: > + description: | The size of the app's APK and related files as installed: this is expected to be larger than download size. This is the output of [StorageStats.getAppBytes](https://developer.android.com/reference/android/app/usage/StorageStats#getAppBytes()) @@ -4419,7 +4490,7 @@ storage.stats: send_in_pings: - metrics type: memory_distribution - description: > + description: | The size of all cached data in the app. This is the output of [StorageStats.getCacheBytes](https://developer.android.com/reference/android/app/usage/StorageStats#getCacheBytes()) so see that for details. This value is only available on Android 8+. @@ -4443,7 +4514,7 @@ storage.stats: send_in_pings: - metrics type: memory_distribution - description: > + description: | The size of all data minus `cache_bytes`. This is the output of [StorageStats.getDataBytes](https://developer.android.com/reference/android/app/usage/StorageStats#getDataBytes()) except we subtract the value of `cache_bytes` so the cache is not measured diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 7b2aae00ac..9ef01dfc9a 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -691,6 +691,8 @@ open class FenixApplication : LocaleAwareApplication(), Provider { else -> "" } ) + + inactiveTabsEnabled.set(settings.inactiveTabsAreEnabled) } reportHomeScreenMetrics(settings) } diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt index 6b1262f7e0..dd02deed94 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt @@ -164,6 +164,12 @@ sealed class Event { object TabsTrayRecentlyClosedPressed : Event() object TabsTrayInactiveTabsExpanded : Event() object TabsTrayInactiveTabsCollapsed : Event() + data class TabsTrayHasInactiveTabs(val count: Int) : Event() { + override val extras = mapOf(TabsTray.hasInactiveTabsKeys.inactiveTabsCount to count.toString()) + } + object TabsTrayCloseAllInactiveTabs : Event() + data class TabsTrayCloseInactiveTab(val amountClosed: Int = 1) : Event() + object TabsTrayOpenInactiveTab : Event() object ProgressiveWebAppOpenFromHomescreenTap : Event() object ProgressiveWebAppInstallAsShortcut : Event() diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt index b13afb8d45..6b518bdc58 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt @@ -590,6 +590,19 @@ private val Event.wrapper: EventWrapper<*>? is Event.TabsTrayInactiveTabsCollapsed -> EventWrapper( { TabsTray.inactiveTabsCollapsed.record(it) } ) + is Event.TabsTrayHasInactiveTabs -> EventWrapper( + { TabsTray.hasInactiveTabs.record(it) }, + { TabsTray.hasInactiveTabsKeys.valueOf(it) } + ) + is Event.TabsTrayCloseAllInactiveTabs -> EventWrapper( + { TabsTray.closeAllInactiveTabs.record(it) } + ) + is Event.TabsTrayCloseInactiveTab -> EventWrapper( + { TabsTray.closeInactiveTab.add(amountClosed) } + ) + is Event.TabsTrayOpenInactiveTab -> EventWrapper( + { TabsTray.openInactiveTab.add() } + ) is Event.AutoPlaySettingVisited -> EventWrapper( { Autoplay.visitedSetting.record(it) } ) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt index 8bdee189cd..7c6c388ef0 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt @@ -219,6 +219,7 @@ class DefaultTabsTrayController( } override fun handleDeleteAllInactiveTabs() { + metrics.track(Event.TabsTrayCloseAllInactiveTabs) browserStore.state.inactiveTabs.map { it.id }.let { tabsUseCases.removeTabs(it) } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt index a7b46f72da..073fd545d3 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/InactiveTabViewHolder.kt @@ -10,6 +10,7 @@ import androidx.recyclerview.widget.RecyclerView import mozilla.components.browser.toolbar.MAX_URI_LENGTH import mozilla.components.concept.tabstray.Tab import org.mozilla.fenix.R +import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.databinding.InactiveFooterItemBinding import org.mozilla.fenix.databinding.InactiveHeaderItemBinding import org.mozilla.fenix.databinding.InactiveTabListItemBinding @@ -108,6 +109,7 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite val url = tab.url.toShortUrl(components.publicSuffixList).take(MAX_URI_LENGTH) itemView.setOnClickListener { + components.analytics.metrics.track(Event.TabsTrayOpenInactiveTab) browserTrayInteractor.open(tab, featureName) } @@ -118,6 +120,7 @@ sealed class InactiveTabViewHolder(itemView: View) : RecyclerView.ViewHolder(ite R.drawable.mozac_ic_close, R.string.content_description_close_button ) { + components.analytics.metrics.track(Event.TabsTrayCloseInactiveTab()) browserTrayInteractor.close(tab, featureName) } } diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt index 0c3b39af2a..a9188b3144 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/NormalBrowserTrayList.kt @@ -38,7 +38,15 @@ class NormalBrowserTrayList @JvmOverloads constructor( private val swipeDelegate = SwipeToDeleteDelegate() private val concatAdapter by lazy { adapter as ConcatAdapter } - private val tabSorter by lazy { TabSorter(context, concatAdapter, context.components.core.store) } + private val tabSorter by lazy { + TabSorter( + context, + context.settings(), + context.components.analytics.metrics, + concatAdapter, + context.components.core.store + ) + } private val inactiveTabsFilter: (TabSessionState) -> Boolean = filter@{ if (!context.settings().inactiveTabsAreEnabled) { return@filter false diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt index de9e1232b8..02d770a981 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt @@ -14,9 +14,12 @@ import mozilla.components.feature.tabs.tabstray.TabsFeature import mozilla.components.support.base.observer.Observable import mozilla.components.support.base.observer.ObserverRegistry import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.tabstray.ext.browserAdapter import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter import org.mozilla.fenix.tabstray.ext.tabGroupAdapter +import org.mozilla.fenix.utils.Settings import kotlin.math.max /** @@ -24,6 +27,8 @@ import kotlin.math.max */ class TabSorter( private val context: Context, + private val settings: Settings, + private val metrics: MetricController, private val concatAdapter: ConcatAdapter, private val store: BrowserStore ) : TabsTray, Observable by ObserverRegistry() { @@ -36,6 +41,9 @@ class TabSorter( // Inactive tabs val selectedInactiveIndex = inactiveTabs.findSelectedIndex(selectedTabId) concatAdapter.inactiveTabsAdapter.updateTabs((Tabs(inactiveTabs, selectedInactiveIndex))) + if (settings.inactiveTabsAreEnabled) { + metrics.track(Event.TabsTrayHasInactiveTabs(inactiveTabs.size)) + } // Tab groups // We don't need to provide a selectedId, because the [TabGroupAdapter] has that built-in with support from diff --git a/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt b/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt index 92c21aee2a..dab743caa5 100644 --- a/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt +++ b/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt @@ -129,6 +129,7 @@ class FenixApplicationTest { every { settings.showPocketRecommendationsFeature } returns true every { settings.showPocketRecommendationsFeature } returns true every { application.reportHomeScreenMetrics(settings) } just Runs + every { settings.inactiveTabsAreEnabled } returns true application.setStartupMetrics(browserStore, settings, browsersCache, mozillaProductDetector) @@ -164,6 +165,7 @@ class FenixApplicationTest { assertEquals("fixed_top", Preferences.toolbarPositionSetting.testGetValue()) assertEquals("standard", Preferences.enhancedTrackingProtection.testGetValue()) assertEquals(listOf("switch", "touch exploration"), Preferences.accessibilityServices.testGetValue()) + assertEquals(true, Preferences.inactiveTabsEnabled.testGetValue()) // Verify that search engine defaults are NOT set. This test does // not mock most of the objects telemetry is collected from. diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt index c5dda16aef..025f5617ce 100644 --- a/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabstray/DefaultTabsTrayControllerTest.kt @@ -431,6 +431,29 @@ class DefaultTabsTrayControllerTest { } } + @Test + fun `WHEN handleDeleteAllInactiveTabs is called THEN Event#TabsTrayCloseAllInactiveTabs and Event#TabsTrayCloseInactiveTab are added to telemetry`() { + val inactiveTab: TabSessionState = mockk { + every { lastAccess } returns maxActiveTime + every { createdAt } returns 0 + every { id } returns "24" + every { content } returns mockk { + every { private } returns false + } + } + every { browserStore.state } returns mockk() + try { + mockkStatic("mozilla.components.browser.state.selector.SelectorsKt") + every { browserStore.state.inactiveTabs } returns listOf(inactiveTab) + + createController().handleDeleteAllInactiveTabs() + + verify { metrics.track(Event.TabsTrayCloseAllInactiveTabs) } + } finally { + unmockkStatic("mozilla.components.browser.state.selector.SelectorsKt") + } + } + private fun createController( navigateToHomeAndDeleteSession: (String) -> Unit = { }, selectTabPosition: (Int, Boolean) -> Unit = { _, _ -> }, From 51a55ca68c99908997e745132359d6ecae3f1975 Mon Sep 17 00:00:00 2001 From: Christian Sadilek Date: Thu, 14 Oct 2021 17:13:03 -0400 Subject: [PATCH 419/517] Closes #21944: Top sites rendered slowly on first load of HomeFragment (cherry picked from commit c3ef16de61efee6cf8805b4617ec13f684222f29) --- .../java/org/mozilla/fenix/FenixApplication.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 9ef01dfc9a..9ec08555fc 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -64,6 +64,7 @@ import org.mozilla.fenix.telemetry.TelemetryLifecycleObserver import org.mozilla.fenix.utils.BrowsersCache import java.util.concurrent.TimeUnit import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.storage.FrecencyThresholdOption import mozilla.components.feature.autofill.AutofillUseCases import mozilla.components.feature.search.ext.buildSearchUrl import mozilla.components.feature.search.ext.waitForSelectedOrDefaultSearchEngine @@ -248,6 +249,19 @@ open class FenixApplication : LocaleAwareApplication(), Provider { components.core.passwordsStorage.warmUp() components.core.autofillStorage.warmUp() + // Populate the top site cache to improve initial load experience + // of the home fragment when the app is launched to a tab. The actual + // database call is not expensive. However, the additional context + // switches delay rendering top sites when the cache is empty, which + // we can prevent with this. + components.core.topSitesStorage.getTopSites( + components.settings.topSitesMaxLimit, + if (components.settings.showTopFrecentSites) + FrecencyThresholdOption.SKIP_ONE_TIME_PAGES + else + null + ) + // This service uses `historyStorage`, and so we can only touch it when we know // it's safe to touch `historyStorage. By 'safe', we mainly mean that underlying // places library will be able to load, which requires first running Megazord.init(). From 9092ecca4a932e1511324703898e93fe09d8dafb Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:48 +0000 Subject: [PATCH 420/517] Strings - app/src/main/res/values-an/strings.xml --- app/src/main/res/values-an/strings.xml | 146 +++++++------------------ 1 file changed, 40 insertions(+), 106 deletions(-) diff --git a/app/src/main/res/values-an/strings.xml b/app/src/main/res/values-an/strings.xml index a8ccecc846..f501cf2f15 100644 --- a/app/src/main/res/values-an/strings.xml +++ b/app/src/main/res/values-an/strings.xml @@ -1,9 +1,10 @@ - + %s privau %s (nav. privada) + Mas opcions @@ -22,7 +23,7 @@ 1 pestanya ubierta. Toca pa cambiar de pestanya. - %1$s pestanyas ubiertas. Toca pa cambiar de pestanya. + %1$s pestanyas ubiertas. Toca pa cambiar de pestanya. %1$d seleccionaus @@ -70,14 +71,6 @@ No, gracias - - - Accede a Firefox mas rapido. Anyade un widget en a tuya pantalla d’inicio. - - Anyadir widget - - Agora no - Puetz configurar Firefox pa que ubra automaticament los vinclos en as aplicacions. @@ -106,7 +99,7 @@ Nueva pestanya privada - Puestos favoritos + Puestos favoritos @@ -120,7 +113,7 @@ Aturar - Marcapachinas + Marcapachinas Editar marcapachinas @@ -149,8 +142,6 @@ Mirar en a pachina Pestanya privada - - Nueva pestanya Alzar en a colección @@ -174,6 +165,7 @@ Ubrir en aplicación Apariencia + No se puede connectar. No se reconoix lo esquema d’URL. @@ -210,7 +202,6 @@ Saber-ne mas - Buscar @@ -285,7 +276,7 @@ Tema - Inicio + Inicio Cenyos @@ -402,9 +393,6 @@ Saber-ne mas - - Se desactivó en cheneral, veye a Achustes pa activar-lo. - Telemetría @@ -430,7 +418,7 @@ Activar Sync - Escaniar codigo de emparejamiento en a versión d’escritorio de Firefox + Escaniar codigo de emparejamiento en a versión d’escritorio de Firefox Iniciar sesión @@ -492,8 +480,6 @@ Atros marcapachinas Historial - - Pestanyas sincronizadas Lista de lectura @@ -518,7 +504,6 @@ No bi ha aquí pestanyas zarradas fa poco - Zarrar las pestanyas @@ -596,7 +581,7 @@ Ubrir pestanyas - Borrar + Borrar Borrar de l\'historial @@ -617,15 +602,15 @@ Limpiar - Copiar + Copiar - Compartir + Compartir - Ubrir en una nueva pestanya + Ubrir en una nueva pestanya - Ubrir en una pestanya privada + Ubrir en una pestanya privada - Borrar + Borrar %1$d triau(s) @@ -643,13 +628,11 @@ No i hai garra historial - - - No bi ha descargas aquí %1$d seleccionaus + Lo sentimos. %1$s no puede cargar ixa pachina. @@ -672,7 +655,7 @@ Menú d’o marcapachinas - Editar marcapachinas + Editar marcapachinas Triar carpeta @@ -683,8 +666,6 @@ S’ha borrau %1$s Adhibir carpeta - - S’ha creyau lo marcapachinas. S’ha alzau lo marcapachinas @@ -783,15 +764,15 @@ Activada Desactivada - + Permitir audio y vídeo Blocar audio y vídeo nomás con datos mobils L’audio y lo vídeo se reproducirán con Wi-Fi - + Blocar solo audio - + Blocar audio y vídeo Activau @@ -895,6 +876,8 @@ Borrar y ubrir Funciona con + + Colección borrada @@ -939,7 +922,7 @@ Borrar - Cancelar + Cancelar Accedendo a pantalla completa @@ -1026,7 +1009,7 @@ Firefox Nightly s’ha tresladau - ¶ + · Esta aplicación ya no va a recibir actualizacions de seguranza. Obtiene lo nuevo Nightly y deixa d’usar esta aplicación. · \n\nPara transferir los tuyos marcapachinas, inicios de sesión y historial a unatra aplicación, creya una cuenta Firefox. @@ -1038,72 +1021,44 @@ Te damos la bienvenida a %s! Ya tiens una cuenta? - - Conoixe a %s - Veyer las novedatz + Veyer las novedatz - Tiens preguntas sobre lo redisenyo de %s? Quiers saber qué ha cambiau? + Tiens preguntas sobre lo redisenyo de %s? Quiers saber qué ha cambiau? - Obtiene respuestas aquí - - Encomienza la sincronización d\'as adrezas d\'interés, las contrasenyas y muito mas con a tuya cuenta d\'o Firefox. - - Saber-ne mas + Obtiene respuestas aquí Sí, iniciar sesión Iniciando sesión… - - Iniciar sesión en Firefox Mantener-me desconnectau Sync ye activau Error en iniciar sesión - - Privacidat automatica - - La configuración de privacidat y seguranza bloca los elementos de seguimiento, los programas maliciosos y las companyías que te siguen. Standard (predeterminau) - - Bloca menos elementos de seguimiento. Las pachinas se cargarán con normalidat. Estricta (recomendada) Estricto - - Bloca mas elementos de seguimiento, anuncios y finestras emerchents. Las pachinas se cargan mas rapido, pero se puede perder bella funcionalidat. - - Prene una posición - - Preba la navegación con una sola man con a barra de ferramientas inferior u mueve-la a la parte superior. - Navega de forma privada + Navega de forma privada - Ubre una pestanya privada una vegada: Toca l’icono %s. + Ubre una pestanya privada una vegada: Toca l’icono %s. - Ubre siempre pestanyas privadas: Actualiza los achustes d’o tuyo navegador. + Ubre siempre pestanyas privadas: Actualiza los achustes d’o tuyo navegador. - Ubrir achustes + Ubrir achustes La tuya privacidat - - Hemos disenyau %s pa dar-te control sobre lo que compartes - en linia y lo que compartes con nusatros. Leye lo nuestro aviso de privacidat - Zarrar + Zarrar Prencipia a navegar @@ -1111,8 +1066,6 @@ Tría lo tuyo tema - - Cabida un poquet de batería y descansa la vista activando lo modo fosco. Automatico @@ -1164,14 +1117,10 @@ Leyer mas Standard (predeterminau) - - Bloca menos elementos de seguimiento. Las pachinas se cargarán con normalidat. Qué ye lo que ye blocau per la protección estandard contra lo seguimiento Estricto - - Bloca mas elementos de seguimiento, anuncios y finestras emerchents. Las pachinas se cargan mas rapido, pero se puede perder bella funcionalidat. Qué ye lo que ye blocau per la protección estricta contra lo seguimiento @@ -1203,6 +1152,7 @@ Criptominers Detectores de ditaladas + Blocau Permitiu @@ -1227,7 +1177,7 @@ Bloca la carga de anuncios externos, vídeos y conteniu que contienga codigo de seguimiento. Puede afectar a la funcionalidat d’o puesto web. - Cada vegada que lo escudo ye de color morau, %s ha blocau elementos de seguimiento en bell puesto. Toca pa mas información. + Cada vegada que lo escudo ye de color morau, %s ha blocau elementos de seguimiento en bell puesto. Toca pa mas información. Las proteccions son activadas pa este puesto @@ -1299,13 +1249,10 @@ No alzar nunca - Replenar automaticament + Replenar automaticament + Inicios de sesión sincronizaus - - Activau - - Desactivau Tornar a connectar @@ -1415,7 +1362,7 @@ Cadena de busqueda a usar - Substituyir la consulta con “%s”. Eixemplo:\n https://www.google.com/search?q=%s + Substituyir la consulta con “%s”. Eixemplo:\n https://www.google.com/search?q=%s Saber-ne mas @@ -1463,9 +1410,9 @@ %1$s]]> - Connexión segura + Connexión segura - Connexión insegura + Connexión insegura Seguro que quiers eliminar totz los permisos de totz los puestos? @@ -1505,7 +1452,7 @@ Descartar cambios Editar - + Fa falta clau Busqueda per voz @@ -1539,22 +1486,9 @@ Vale, entendiu - Amostrar los puestos mas visitaus + Amostrar los puestos mas visitaus Eliminar - - Quita-le lo millor provecho a %s. - - - Colecciona las cosetas que t’importan - - Agrupa busquedas, puestos y pestanyas semellants pa un acceso rapido mas tarde. - - Ya has iniciau sesión como %s en unatro navegador Firefox d’este telefono. Quiers iniciar sesión con esta cuenta? - - Puetz anyadir facilment este puesto web a la tuya pachina d’inicio pa tener-ie acceso instantanio y navegar rapidament como si estase una aplicación. - From bc39877e5a2f4d81fd3e0df7e65c2d6c4879f5b2 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:48 +0000 Subject: [PATCH 421/517] Strings - app/src/main/res/values-br/strings.xml --- app/src/main/res/values-br/strings.xml | 150 +++++++++++++++++++++---- 1 file changed, 129 insertions(+), 21 deletions(-) diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml index 31ac5c3042..4d1559c41a 100644 --- a/app/src/main/res/values-br/strings.xml +++ b/app/src/main/res/values-br/strings.xml @@ -50,7 +50,9 @@ Enrollet nevez ’zo - Merket evel sined nevez ’zo + Merket evel sined nevez ’zo + + Sinedoù nevez Sinedoù enrollet nevez ’zo @@ -105,6 +107,11 @@ Argas + + An ivinelloù n’ho peus ket gwelet e-pad div sizhun a vo dilec’hiet amañ. + + Diweredekaat en arventennoù + Ivinell nevez @@ -119,10 +126,32 @@ Diskouez pep tra + + Diskouez an holl afelloù ivinelloù nevez + + Ho klask evit \"%1$s\" + + + Lec’hiennoù: %1$s + - Merdeadurioù tremenet + Merdeadurioù tremenet + + + Gweladennet nevez ’zo + + Klaskoù nevesañ + + Dilemel + + Diskouez afelloù an holl verdeadennoù kent @@ -200,6 +229,8 @@ Personelaat an degemer + + Personelaat an degemer Skramm degemer @@ -246,6 +277,28 @@ Klask war-eeun eus ar varrenn chomlec’h + + + Petra zo nevez e Firefox + + Aesoc’h eo adstagañ lec’h m’ho peus paouezet. + + Pajenn degemer Firefox personelaet + + Mont d’hoc’h ivinelloù digor, sinedoù ha roll istor merdeiñ. + + Ivinelloù naet ha renket mat + + + Tennañ an ivinelloù diezhomm gant an aozadur gwellaet hag an ivinelloù a serr o-unan. + + Klaskoù nevesañ + + Adweladennit ho klaskoù nevesañ diwar ho pajenn degemer hag ivinelloù. + + + Gant ho pajenn Firefox personelaet eo aesoc’h da adstagañ lec’h m’ho peus paouezet. Klaskit e-touez hoc’h ivinelloù nevez, sinedoù ha disoc’hoù enklask. + Digeriñ un ivinell Firefox nevez @@ -398,9 +451,15 @@ Enrollet nevez ’zo - Merket evel sined nevez ’zo - - Gweladennet nevez ’zo + Merket evel sined nevez ’zo + + Ivinelloù nevez + + Gweladennet nevez ’zo + + Klaskoù nevesañ Pocket @@ -589,6 +648,13 @@ Serriñ + + Lec’hienn %d + + Lec’hiennoù %d + Ivinelloù serret nevez zo @@ -611,6 +677,10 @@ Roll Grid + + Klask er strolladoù + + Strollañ al lec’hiennoù heñvel Serriñ an ivinelloù @@ -622,6 +692,9 @@ Goude ur mizvezh + + Serriñ an ivinelloù digor ent emgefreek + Kregiñ gant ar skramm degemer @@ -640,6 +713,12 @@ Serriñ goude ur miz + + + Dilec’hiañ an ivinelloù kozh dioberiant + + An ivinelloù n’ho peus ket gwelet e-pad div sizhun a vo dilec’hiet er lodenn dioberiant. + Dilemel @@ -760,7 +839,10 @@ Enrollañ - All + All + + + Ivinelloù all @@ -774,15 +856,15 @@ Skarzhañ - Eilañ + Eilañ - Rannañ + Rannañ - Digeriñ en un ivinell nevez + Digeriñ en un ivinell nevez - Digeriñ war un ivinell brevez + Digeriñ war un ivinell brevez - Dilemel + Dilemel %1$d diuzet @@ -1257,12 +1339,12 @@ Ur gont hoc’h eus dija? - Gwelout pezh a zo nevez + Gwelout pezh a zo nevez - Goulennoù hoc’h eus a-zivout ar %s nevez? Fellout a ra deoc’h gouzout pezh a zo bet cheñchet? + Goulennoù hoc’h eus a-zivout ar %s nevez? Fellout a ra deoc’h gouzout pezh a zo bet cheñchet? - Amañ emañ ar respontoù + Amañ emañ ar respontoù Goubredañ Firefox etre an trevnadoù @@ -1302,14 +1384,14 @@ Lakait ar varrenn ostilhoù en ul lec’h aes da dizhout. Dalc’hit hi en traoñ pe diblasit anezhi e-krec’h. - Merdeiñ prevez + Merdeiñ prevez - Digorit un ivinell prevez hepken: stokit an arlun %s. + Digorit un ivinell prevez hepken: stokit an arlun %s. - Digeriñ ivinelloù prevez bewech: kemmit an arventennoù merdeiñ prevez. + Digeriñ ivinelloù prevez bewech: kemmit an arventennoù merdeiñ prevez. - Digeriñ an arventennoù + Digeriñ an arventennoù Ho puhez prevez Nullañ - - + + Ivinelloù dioberiant + + Serriñ an holl ivinelloù dioberiant An ivinelloù a zo hegerz amañ e-pad %s. Goude e vo serret an ivinelloù ent emgefreek. @@ -1901,6 +1985,30 @@ 1 sizhun + + + Serriñ emgefreek goude ur miz? + + Firefox a c’hall serriñ ivinelloù n’ho peus ket gwelet er miz diwezhañ. + + GWEREDEKAAT AR SERRIÑ EMGEFREEK + + + + Skoazellit ac’hanomp da welaat + + Perak ho peus diweredekaet an ivinelloù dioberiant? + + N’on ket dedennet er c’heweriuster + + Re hir eo ar padelezh dioberiantiz + + Re verr eo ar padelezh dioberiantiz + + Kas + + Serriñ + Digeriñ liammoù al lec’hiennoù, posteloù ha kemennadennoù e Firefox en un doare emgefreek. @@ -1916,4 +2024,4 @@ Serriñ - + From af74b2c6ee6ce16202efdd664390b0610526e79a Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:48 +0000 Subject: [PATCH 422/517] Strings - app/src/main/res/values-ca/strings.xml --- app/src/main/res/values-ca/strings.xml | 269 +++++++++++++++++++++++-- 1 file changed, 247 insertions(+), 22 deletions(-) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 97fa92b863..247e193979 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -1,5 +1,5 @@ - + @@ -52,7 +52,11 @@ - Desades recentment + Desades recentment + + Afegides recentment + + Adreces d’interès recents Adreces d’interès desades recentment @@ -103,6 +107,12 @@ Descarta + + Aquí s’hi mouen les pestanyes que no heu vist des de fa dues setmanes. + + + Desactiva-ho als paràmetres + Pestanya nova @@ -118,6 +128,31 @@ Mostra-ho tot + + Mostra el botó de totes les pestanyes recents + + La vostra cerca de «%1$s» + + Llocs: %1$s + + + + Exploracions passades + + Visitat recentment + + Cerques recents + + Elimina + + Mostra el botó de totes les exploracions anteriors + Pestanyes obertes @@ -193,6 +228,10 @@ Edita + + Personalitza l’inici + + Personalitza la pàgina d’inici Pantalla d’inici @@ -240,6 +279,28 @@ Cerca directament des de la barra d’adreces + + + Novetats del Firefox + + Ara és més fàcil continuar des d’on ho havíeu deixat. + + Pàgina d’inici del Firefox personalitzada + + Accediu a les pestanyes obertes, a les adreces d’interès i a l’historial de navegació. + + Pestanyes clares i organitzades + + Reduïu el caos de pestanyes amb un disseny millorat i tancament automàtic. + + Cerques recents + + + Recupereu les cerques recents des de la pàgina d’inici i des de les pestanyes. + + + Amb la pàgina d’inici del Firefox personalitzada, ara és més fàcil continuar des d’on ho havíeu deixat. Hi trobareu les pestanyes, les adreces d’interès i els resultats de cerca recents. + Obre una pestanya nova del Firefox @@ -388,6 +449,24 @@ S’ha modificat la col·lecció de complements. L’aplicació es tancarà per aplicar els canvis… + + + Torneu a la pestanya + + Desades recentment + + Afegides recentment + + Adreces d’interès recents + + Visitat recentment + + Cerques recents + + Pocket + El complement no és compatible @@ -573,6 +652,13 @@ Tanca + + %d lloc + + %d llocs + Pestanyes tancades recentment @@ -595,6 +681,10 @@ Llista Graella + + Cerca en grups + + Agrupa els llocs relacionats Tanca les pestanyes @@ -606,6 +696,9 @@ Al cap d’un mes + + Tanca automàticament les pestanyes obertes + Comença a la pantalla d’inici @@ -624,6 +717,30 @@ Tanca-les al cap d’un mes + + + Mou les pestanyes antigues a inactives + + Les pestanyes que no hàgiu vist des de fa dues setmanes es mouran a la secció d’inactives. + + + + Elimina + + Actius + + El Firefox pot instal·lar i executar estudis de tant en tant. + + Més informació + + L’aplicació es tancarà per aplicar els canvis + + D’acord + + Cancel·la + + S’està tancant l’aplicació per aplicar els canvis… + Pestanyes obertes @@ -651,6 +768,8 @@ Comparteix totes les pestanyes Pestanyes tancades recentment + + Tancades recentment Paràmetres del compte @@ -723,6 +842,11 @@ Desa + + Altres + + Altres pestanyes + Suprimeix l’historial @@ -735,15 +859,15 @@ Esborra - Copia + Copia - Comparteix + Comparteix - Obre en una pestanya nova + Obre en una pestanya nova - Obre en una pestanya privada + Obre en una pestanya privada - Suprimeix + Suprimeix %1$d seleccionats @@ -947,6 +1071,11 @@ Desactivat + + Activat + + Desactivat + Col·leccions @@ -1108,7 +1237,7 @@ Suprimeix - Cancel·la + Cancel·la Esteu en mode de pantalla completa @@ -1214,12 +1343,12 @@ Ja teniu un compte? - Descobriu les novetats + Descobriu les novetats - Teniu preguntes sobre el redisseny del %s? Voleu saber què ha canviat? + Teniu preguntes sobre el redisseny del %s? Voleu saber què ha canviat? - Vegeu les respostes aquí + Vegeu les respostes aquí Sincronitzeu el Firefox entre dispositius @@ -1259,14 +1388,14 @@ Tingueu la barra d’eines a mà. La podeu mantenir a la part inferior o moure-la a dalt. - Navegueu amb privadesa + Navegueu amb privadesa - Obriu una pestanya privada una sola vegada: toqueu la icona %s. + Obriu una pestanya privada una sola vegada: toqueu la icona %s. - Obriu pestanyes privades cada vegada: actualitzeu els paràmetres de navegació privada. + Obriu pestanyes privades cada vegada: actualitzeu els paràmetres de navegació privada. - Obre els paràmetres + Obre els paràmetres La vostra privadesa - Tanca + Tanca Comença a navegar @@ -1379,6 +1508,9 @@ Miners de criptomonedes Generadors d’empremtes digitals + + Detalls + Blocat Permès @@ -1404,7 +1536,7 @@ Impedeix que es carreguin anuncis, vídeos i altre contingut que contenen codi de seguiment. Pot afectar la funcionalitat del lloc web. - Quan l’escut és de color lila, el %s està blocant els elements de seguiment del lloc. Toqueu per obtenir més informació. + Quan l’escut és de color lila, el %s està blocant els elements de seguiment del lloc. Toqueu per obtenir més informació. S’han activat les proteccions per a aquest lloc @@ -1429,6 +1561,12 @@ Esborra les galetes definides per redireccions a llocs web que se sap que fan seguiment. + + Alguns elements de seguiment marcats a continuació s’han desblocat parcialment en aquesta pàgina perquè hi heu interactuat. + + Més informació + Assistència @@ -1486,7 +1624,19 @@ No els desis mai - Emplenament automàtic + Emplenament automàtic + + Emplena automàticament en el %1$s + + Emplena i desa noms d’usuari i contrasenyes en els llocs webs mentre utilitzeu el %1$s. + + Emplena automàticament en altres aplicacions + + Emplena noms d’usuari i contrasenyes en altres aplicacions del vostre dispositiu. + + + Afegeix un inici de sessió + Sincronitza els inicis de sessió @@ -1549,6 +1699,8 @@ Copia el nom d’usuari Esborra el nom d’usuari + + Esborra el nom del servidor Copia el lloc @@ -1715,9 +1867,13 @@ %1$s]]> - Connexió segura + La connexió és segura - Connexió insegura + La connexió no és segura + + Connexió segura + + Connexió insegura Segur que voleu esborrar tots els permisos de tots els llocs? @@ -1757,8 +1913,14 @@ Descarta els canvis Edita - + + Afegeix un inici de sessió nou + Cal una contrasenya + + Cal el nom d’usuari + + Cal el nom del servidor Cerca per veu @@ -1767,6 +1929,16 @@ Ja existeix un inici de sessió amb aquest nom d’usuari + + https://www.example.com + + L’adreça web ha de contenir «https://» o «http://» + + L’adreça web ha de contenir «https://» o «http://» + + + Cal un nom de servidor vàlid + Connecteu un altre dispositiu. @@ -1793,8 +1965,10 @@ Entesos + + Mostra els llocs principals més visitats - Mostra els llocs més visitats + Mostra els llocs més visitats Nom @@ -1805,6 +1979,42 @@ Cancel·la + + + Pestanyes inactives + + Tanca totes les pestanyes inactives + + Les pestanyes estaran disponibles aquí durant %s. Passat aquest temps, es tancaran automàticament. + + 30 dies + + 1 setmana + + + + Voleu tancar-les automàticament al cap d’un mes? + + El Firefox pot tancar les pestanyes que no hàgiu vist durant el darrer mes. + + ACTIVA EL TANCAMENT AUTOMÀTIC + + + + Ajudeu-nos a millorar + + Per què heu desactivat les pestanyes inactives? + + No m’interessa la funció + + El temps que cal per passar-les a inactives és massa llarg + + El temps que cal per passar-les a inactives és massa curt + + Envia + + Tanca + Feu que els enllaços dels llocs web, del correu electrònic i dels missatges s’obrin automàticament en el Firefox. @@ -1820,4 +2030,19 @@ Tanca + + + Articles suggerents + + Articles suggerents + + Articles per tema + + Descobriu-ne més + + Amb tecnologia del Pocket. + + Part de la família Firefox. %s + + Més informació From f2e5d7ad7e901b8cba0a7cc887a6330f4305cbc4 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 423/517] Strings - app/src/main/res/values-ckb/strings.xml --- app/src/main/res/values-ckb/strings.xml | 66 ++++++++++++++----------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml index e6804d187f..1417b54479 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -1,9 +1,10 @@ - + تایبەت %s %s (تایبەت) + هەڵبژاردەی زیاتر @@ -46,6 +47,18 @@ دیاریکرا + + + دوواین پاشەکەوتکراو + + دوواین دڵخوازکراو + + دوواین دڵخوازەکان + + دوواین دڵخوازکراوی پاشەکەوتکراو + + هەمووی پیشان بدە + %1$s بەرهەم هێنراوە لە لایەن مۆزیلاوە. @@ -86,11 +99,6 @@ پشتگوێخستن - - بڕۆ بۆ ڕێکخستن - - پشتگوێخستن - بازدەری نوێ @@ -534,8 +542,6 @@ بازدەری نوێ لە ناو پەڕە بگەڕێ - - بازدەرە هاوکاتپێکراوەکان لیستەی خوێندراوەکان @@ -699,15 +705,15 @@ پاککردنەوە - لەبەرگرتنەوە + لەبەرگرتنەوە - بڵاوکردنەوە + بڵاوکردنەوە - کردنەوە لە بازدەری نوێ + کردنەوە لە بازدەری نوێ - کردنەوە لە بازدەری تایبەت + کردنەوە لە بازدەری تایبەت - سڕینەوە + سڕینەوە %1$d دیاریکراوە @@ -1005,6 +1011,8 @@ سڕینەوە و کردنەوە پشتگیریکراوە لە لایەن + + کۆکەرەوە سڕایەوە @@ -1056,7 +1064,7 @@ سڕینەوە - پاشگەزبوونەوە + پاشگەزبوونەوە چوونە دۆخی پراوپڕی شاشە @@ -1149,12 +1157,12 @@ پێشتر هەژمارت هەبووە؟ - بزانە چی نوێ هەیە + بزانە چی نوێ هەیە - پرسیارت هەیە دەربارەی نوێکردنەوەی ڕووکاری %s؟ دەتەوێت بزانیت چی گۆڕاوە؟ + پرسیارت هەیە دەربارەی نوێکردنەوەی ڕووکاری %s؟ دەتەوێت بزانیت چی گۆڕاوە؟ - وەڵامەکان لێرە بەدەست بهێنە + وەڵامەکان لێرە بەدەست بهێنە فایەرفۆکس هاوکاتگەری پێبکە لە نێوان ئامێرەکان @@ -1191,14 +1199,14 @@ توڵامراز بکەرە شوێنێک زوو دەستت پێیبگات، لە خوارەوە، یان بیخە سەرەوە. - بگەڕێ بە تایبەتیی (شاراوەیی) + بگەڕێ بە تایبەتیی (شاراوەیی) - یەکسەر بازدەرێکی تایبەت بکەرەوە: پەنجە بنێ بە وێنۆچکەی %s. + یەکسەر بازدەرێکی تایبەت بکەرەوە: پەنجە بنێ بە وێنۆچکەی %s. - بازدەری تایبەت هەموو کات بکەرەوە: ڕێکخستنەکانی گەڕانی تایبەتی نوێبکەرەوە. + بازدەری تایبەت هەموو کات بکەرەوە: ڕێکخستنەکانی گەڕانی تایبەتی نوێبکەرەوە. - ڕێکخستنەکان بکەرەوە + ڕێکخستنەکان بکەرەوە تایبەتێتی بکەرەوە تێبینییەکانی تایبەتێتی بخوێنەوە - داخستن + داخستن دەستبکە بە گەڕان @@ -1306,6 +1314,7 @@ شیفرە-کانەکەر پەنجەمۆرەکان + بلۆککراوە ڕێگەپێدراوە @@ -1387,7 +1396,8 @@ هەرگیز پاشەکەوت مەکە - پڕکردنەوەی خۆکار + پڕکردنەوەی خۆکار + چوونەژوورەوەکان هاوکاتگەری پێبکە @@ -1563,9 +1573,9 @@ %1$s بجوڵێنە بۆ کاراکردن]]> - پەیوەندیگرتنی پارێزراو + پەیوەندیگرتنی پارێزراو - پەیوەندیگرتنێکی ناپارێزراو + پەیوەندیگرتنێکی ناپارێزراو تۆ دڵنیایت لە لابردنی هەموو دەسەڵاتەکان لە هەموو ماڵپەڕەکان؟ @@ -1600,7 +1610,7 @@ گۆڕانکارییەکان پشتگوێبخە دەستکاریکردن - + وشەی تێپڕبوون داواکراوە گەڕانی دەنگی @@ -1629,7 +1639,7 @@ باشە، تێگەیشتم - زۆترین ماڵپەڕە سەردانکراوەکان پیشان بدە + زۆترین ماڵپەڕە سەردانکراوەکان پیشان بدە ناو @@ -1651,4 +1661,4 @@ داخستن - + From 2f34c5f8baf37cd9be375ae2f6fdaa3534381300 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 424/517] Strings - app/src/main/res/values-co/strings.xml --- app/src/main/res/values-co/strings.xml | 209 ++++++++++++++++++++----- 1 file changed, 168 insertions(+), 41 deletions(-) diff --git a/app/src/main/res/values-co/strings.xml b/app/src/main/res/values-co/strings.xml index 6430adf3b5..845c90a3bd 100644 --- a/app/src/main/res/values-co/strings.xml +++ b/app/src/main/res/values-co/strings.xml @@ -24,7 +24,7 @@ 1 unghjetta aperta. Picchichjà per cambià d’unghjetta. - %1$s unghjette aperte. Picchichjà per cambià d’unghjetta. + %1$s unghjette aperte. Picchichjà per cambià d’unghjetta. %1$d selezziunatu(i) @@ -54,7 +54,9 @@ Arregistrate pocu fà - Indettati pocu fà + Indettati pocu fà + + Indette recente Indette arregistrate pocu fà @@ -71,10 +73,10 @@ - ¶ + · %1$s squassa e vostre cronolugie di ricerca è di navigazione da l’unghjette private quandu vò chjuditele o chitate l’appiecazione. Benchè quessu ùn vi rende micca anonimu nant’à i siti web nè da u vostru furnidore d’accessu à internet, vi permette di cunservà sicreta a vostra attività in linea per tutte l’altre persone chì impiegherianu u vostru apparechju. - ¶ - Idee precuncepite apprupositu di a navigazione privata¶ + + Idee precuncepite apprupositu di a navigazione privata Squassà a sessione @@ -109,6 +111,20 @@ Ricusà + + L’unghjette chì vo ùn avete micca viste dapoi duie settimane sò dispiazzate quì. + + Disattivà in e preferenze + + + Chjusura autumatica dopu un mese ? + + Firefox pò chjode l’unghjette chì vo ùn avete micca fighjate durante un mese scorsu. + + Chjode + + Attivà a chjusura autumatica + Nova unghjetta @@ -117,14 +133,23 @@ Nova unghjetta privata - Siti principale + Siti principale - Rivene in st’unghjetta + Rivene à st’unghjetta Tuttu affissà + + Buttone per affissà tutte l’indette recente + + A vostra ricerca di « %1$s » + + Siti: %1$s + @@ -132,11 +157,17 @@ - Visitati pocu fà + Visitati pocu fà + + Ricerche recente Caccià + + Buttone per affissà tutte l’esplurazioni recente + Unghjette aperte @@ -149,7 +180,7 @@ Piantà - Indetta + Indetta Mudificà l’indetta @@ -213,6 +244,8 @@ Persunalizà l’accolta + + Persunalizà a pagina d’accolta Screnu d’accolta @@ -258,6 +291,28 @@ Ricercà direttamente da a barra d’indirizzu + + + Ciò chì hè novu in Firefox + + Ora hè più faciule di rivene induve vo avete piantatu. + + Pagina d’accolta di Firefox persunalizata + + Saltà à e vostre unghjette aperte, à l’indette è à a cronolugia di navigazione. + + Unghjette chjare è organizate + + + Sbarazzà l’unghjette cù un accunciamentu più chjaru è a so chjusura autumatica. + + Ricerche recente + + Fighjate torna l’ultime ricerche da a vostra pagina d’accolta è e vostre unghjette. + + + A vostra pagina d’accolta Firefox persunalizata vi permette avà di rivene d’una manera faciule induve vo avete lasciatu. Ci truverete l’unghjette, l’indette, è i risultati di ricerca recente. + Apre una nova unghjetta in Firefox @@ -337,7 +392,9 @@ Tema - Accolta + Accolta + + Pagina d’accolta Mosse @@ -383,7 +440,7 @@ Compie autumaticamente l’indirizzi - Apre i liami in appiecazioni + Apre i liami in l’appiecazioni Ghjestiunariu esternu di scaricamentu @@ -410,13 +467,19 @@ - Rivene in st’unghjetta + Rivene à st’unghjetta Arregistrate pocu fà - Indettati pocu fà - - Visitati pocu fà + Indettati pocu fà + + Indette recente + + Visitati pocu fà + + Ricerche recente Pocket @@ -528,7 +591,7 @@ Attivà Sync - Numerizà u codice d’associu nant’à Firefox per l’urdinatore + Numerizà u codice d’associu nant’à Firefox per l’urdinatore Cunnettesi @@ -636,6 +699,10 @@ Lista Quadrittere + + Gruppi di ricerca + + Raggruppà i siti assuciati Chjode l’unghjette @@ -647,15 +714,26 @@ Dopu un mese - + + Chjusura autumatica di l’unghjette aperte + + - Principià da u screnu d’accolta + Principià da u screnu d’accolta + + Screnu d’apertura - Dopu quattru ore + Dopu quattru ore + + Pagina d’accolta - Sempre + Sempre + + Ultima unghjetta - Mai + Mai + + Pagina d’accolta dopu quattru ore d’inattività Chjusura manuale @@ -665,6 +743,12 @@ Chjusura dopu un mese + + + Dispiazzà e vechje unghjette in a sezzione inattiva + + L’unghjette chì vo ùn avete micca viste dapoi duie settimane sò dispiazzate in a sezzione inattiva. + Caccià @@ -707,13 +791,13 @@ Arregistrà in una cullezzione - Selezziunà + Selezziunà Sparte tutte l’unghjette Unghjette chjose pocu fà - Chjose pocu fà + Chjose pocu fà Preferenze di u contu @@ -787,7 +871,10 @@ Arregistrà - Altri + Altri + + + Altre unghjette @@ -832,10 +919,6 @@ Alcuna cronolugia - - Squassà a lista di i scaricamenti - - Site sicuru di vulè squassà a lista di i vostri scaricamenti ? Scaricamenti cacciati @@ -877,7 +960,7 @@ Listinu di l’indette - Mudificà l’indetta + Mudificà l’indetta Selezziunà un cartulare @@ -1254,8 +1337,8 @@ Avà Firefox Preview si chjama Firefox Nightly - ¶ - · Firefox Nightly cambia ogni notte è vene cù nove funzioni esperimentale.¶ + + · Firefox Nightly cambia ogni notte è vene cù nove funzioni esperimentale. · Sarrimanenti, pò esse menu stabule. Scaricate u nostru navigatore beta per una pratica più stabule. @@ -1265,8 +1348,8 @@ Firefox Nightly hà cambiatu - ¶ - · St’appiecazione ùn riciverà più mudificazione di sicurità. Fermate d’impiegalla è cambiate per u novu Nightly.¶ + + · St’appiecazione ùn riciverà più mudificazione di sicurità. Fermate d’impiegalla è cambiate per u novu Nightly. · \n\nPer trasferà e vostre indette, identificazioni di cunnessione, è a cronolugia ver di un’altra appiecazione, create un contu Firefox. Cambià ver di u novu Nightly @@ -1274,8 +1357,8 @@ Firefox Nightly hà cambiatu - ¶ - · St’appiecazione ùn riciverà più mudificazione di sicurità. Ottene u novu Nightly è fermà d’impiegà st’appiecazione.¶ + + · St’appiecazione ùn riciverà più mudificazione di sicurità. Ottene u novu Nightly è fermà d’impiegà st’appiecazione. · \n\nPer trasferà e vostre indette, identificazioni di cunnessione, è a cronolugia ver di un’altra appiecazione, create un contu Firefox. Ottene u novu Nightly @@ -1915,8 +1998,10 @@ Iè, aghju capitu + + Siti principale i più visitati - Affissà i siti principale i più visitati + Affissà i siti principale i più visitati Affissà i siti visitati aspessu @@ -1929,16 +2014,43 @@ Abbandunà - - + + Unghjette inattive + + Chjode tutte l’unghjette inattive - L’unghjette sò dispunibule quì durante %s. Dopu à stu tempu, seranu chjose autumaticamente. + L’unghjette sò dispunibule quì durante %s. Dopu à stu tempu, seranu chjose autumaticamente. - 30 ghjorni + 30 ghjorni - 1 settimana + 1 settimana + + + + Chjusura autumatica dopu un mese ? + + Firefox pò chjode l’unghjette chì vo ùn avete micca fighjate durante un mese scorsu. + + ATTIVÀ A CHJUSURA AUTUMATICA + + + + Per piacè, aiutateci à amendacci + + Perchè vo avete disattivatu l’unghjette inattive ? + + Sta funzione ùn mi garba + + + U tempu d’inattività hè troppu longu + + U tempu d’inattività hè troppu cortu + + Mandà + + Chjode Definisce chì e leie di i siti web, i currieri elettronichi è i messaghji s’aprinu autumaticamente in Firefox. @@ -1955,4 +2067,19 @@ Chjode + + + Storie chì fanu riflette + + Storie chì fanu riflette + + Storie da l’articulu + + Scoprene di più + + Funziuneghja grazia à Pocket. + + Parte di a famiglia Firefox. + + Sapene di più From 993410df38527156f675b8130ed6625304ebc746 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 425/517] Strings - app/src/main/res/values-cs/strings.xml --- app/src/main/res/values-cs/strings.xml | 159 ++++++++++++++++++++----- 1 file changed, 130 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index d7bfe76e30..4f0bca92e3 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -25,7 +25,7 @@ Jeden otevřený panel. Klepnutím panely přepnete. - %1$s otevřených panelů. Klepnutím přepnete panely. + %1$s otevřených panelů. Klepnutím přepnete panely. Vybráno panelů: %1$d @@ -54,7 +54,9 @@ Naposledy uložené - Naposledy přidané + Naposledy přidané + + Naposledy přidané Naposledy uložené záložky @@ -107,6 +109,20 @@ Ne, děkuji + + Sem se přesunou panely, které jste déle než dva týdny nepoužili. + + Vypnout v nastavení + + + Chcete automaticky zavírat měsíc staré neaktivní panely? + + Firefox může zavřít panely, které jste během posledního měsíce nepoužili. + + Nechci + + Zapnout automatické zavírání + Nový panel @@ -114,7 +130,7 @@ Nový anonymní panel - Top stránky + Top stránky @@ -122,6 +138,9 @@ Zobrazit vše + + Tlačítko pro zobrazení všech nedávných panelů + @@ -129,11 +148,17 @@ - Nedávno navštívené + Nedávno navštívené + + Nedávno vyhledávané Odstranit + + Tlačítko pro zobrazení vaší nedávné historie prohlížení + Otevřít panely @@ -146,7 +171,7 @@ Zastaví načítání stránky - Přidat do záložek + Přidat do záložek Upravit záložku @@ -213,6 +238,8 @@ Přizpůsobit + + Přizpůsobit Domovská obrazovka @@ -260,6 +287,25 @@ Vyhledat na webu přímo z adresního řádku + + + Co je ve Firefoxu nového + + Osobní domovská stránka Firefoxu + + Rychlý přístup k otevřeným panelům, vašim záložkám i historii. + + Úhledně srovnané panely + + Vylepšené zobrazení nebo automatické zavírání vás zbaví nepořádku v panelech. + + Nedávno vyhledávané + + Na domovské stránce najdete, co jste nedávno hledali. + + + Na své osobní domovské stránce Firefoxu vždy najdete, co jste dělali naposledy, ať už nedávno otevřené panely, záložky nebo výsledky vyhledávání. + Otevřít nový panel Firefoxu @@ -338,7 +384,7 @@ Vzhled - Domovská stránka + Domovská stránka Gesta @@ -413,9 +459,15 @@ Naposledy uložené - Naposledy přidané - - Nedávno navštívené + Naposledy přidané + + Naposledy přidané + + Nedávno navštívené + + Nedávno vyhledávané Pocket @@ -531,7 +583,7 @@ Zapnout synchronizaci - Naskenovat párovací kód zobrazený ve Firefoxu na počítači + Naskenovat párovací kód zobrazený ve Firefoxu na počítači Přihlásit se @@ -648,15 +700,18 @@ Po měsíci - + + Automatické zavírání panelů + + - Při spuštění zobrazit domovskou obrazovku + Při spuštění zobrazit domovskou obrazovku - Po pár hodinách od zavření + Po pár hodinách od zavření - Vždy + Vždy - Nikdy + Nikdy Zavírat ručně @@ -666,6 +721,12 @@ Zavírat po měsíci + + + Přesun neaktivních panelů + + Panely, které jste dva týdny neotevřeli, budou přesunuty do sekce pro neaktivní panely. + Odstranit @@ -708,13 +769,13 @@ Uložit do sbírky - Vybrat + Vybrat Sdílet všechny panely Nedávno zavřené panely - Nedávno zavřené + Nedávno zavřené Nastavení účtu @@ -795,7 +856,10 @@ Uložit - Ostatní + Ostatní + + + Další panely @@ -841,10 +905,6 @@ Zatím nemáte žádnou historii prohlížení - - Smazat stažené soubory - - Opravdu chcete smazat všechny stažené soubory? Stažené soubory smazány @@ -885,7 +945,7 @@ Nabídka záložek - Upravit záložku + Upravit záložku Vybrat složku @@ -1920,7 +1980,7 @@ OK, rozumím - Zobrazovat nejnavštěvovanější stránky + Zobrazovat nejnavštěvovanější stránky Zobrazovat nejnavštěvovanější stránky @@ -1933,15 +1993,41 @@ Zrušit - - + + Neaktivní panely + + Zavřít všechny neaktivní panely - Panely zde budou k dispozici po dobu %s. Poté se automaticky zavřou. + Panely zde budou k dispozici po dobu %s. Poté se automaticky zavřou. - 30 dnů + 30 dnů - jednoho týdne + jednoho týdne + + + + Chcete automaticky zavírat měsíc staré neaktivní panely? + + Firefox může zavřít panely, které jste během posledního měsíce nepoužili. + + ZAPNOUT AUTOMATICKÉ ZAVÍRÁNÍ + + + + Pomozte nám se zlepšit + + Proč jste vypnuli funkci neaktivních panelů? + + Tato funkce mě nezajímá + + Doba před označením panelů jako neaktivních je moc dlouhá + + Doba před označením panelů jako neaktivních je moc krátká + + Odeslat + + Zavřít Nastavte si automatické otevírání odkazů, e-mailů a zpráv ve Firefoxu. @@ -1958,4 +2044,19 @@ Zavřít + + + Zajímavé články + + Zajímavé články + + Články podle témat + + Objevte více + + Službu poskytuje Pocket. + + Součást rodiny Firefoxu. %s + + Zjistit více From b85ed65e3e4e58edcf9a3d578ad66d3e98b43784 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 426/517] Strings - app/src/main/res/values-cy/strings.xml --- app/src/main/res/values-cy/strings.xml | 190 ++++++++++++++++++++----- 1 file changed, 158 insertions(+), 32 deletions(-) diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index dfbaf7552f..b5f2e09f49 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -23,7 +23,7 @@ 1 tab agored. Tapio i newid tabiau. - %1$s tab agored. Tapio i newid tabiau. + %1$s tab agored. Tapio i newid tabiau. %1$d wedi’u dewis @@ -53,7 +53,9 @@ Cadwyd yn ddiweddar - Wedi gosod nod tudalen yn ddiweddar + Wedi gosod nod tudalen yn ddiweddar + + Nodau tudalen diweddar Nodau tudalen wedi’u cadw’n ddiweddar @@ -106,6 +108,20 @@ Cau + + Mae tabiau nad ydych chi wedi edrych arnyn nhw ers pythefnos yn cael eu symud yma. + + Eu diffodd yn y gosodiadau + + + Autogau ar ôl mis? + + Gall Firefox gau tabiau nad ydych wedi edrych arnyn nhw dros y mis diwethaf. + + Cau + + Troi autogau ymlaen + Tab newydd @@ -113,7 +129,7 @@ Tab preifat newydd - Hoff wefannau + Hoff wefannau @@ -121,6 +137,15 @@ Dangos y cyfan + + Dangos botwm yr holl dabiau diweddar + + Eich chwilio am \"%1$s\" + + Gwefannau: %1$s + @@ -128,11 +153,17 @@ - Ymwelwyd yn ddiweddar + Ymwelwyd yn ddiweddar + + Chwilio diweddar Tynnu + + Dangos botwm holl chwilio’r gorffennol + Tabiau Agored @@ -145,7 +176,7 @@ Atal - Nod Tudalen + Nod Tudalen Golygu nod tudalen @@ -165,7 +196,7 @@ Gwefan bwrdd gwaith - I’r sgrin Cartref + Ychwanegu i’r sgrin Cartref Gosod @@ -209,6 +240,8 @@ Cyfaddasu cartref + + Cyfaddasu’r dudalen cartref Sgrin cartref @@ -254,6 +287,27 @@ Chwilio’n uniongyrchol o’r bar cyfeiriad + + + Beth sy’n newydd yn Firefox + + Mae nawr yn haws ailgychwyn o’r lle y gwnaethoch chi adael. + + Tudalen cartref personoledig Firefox + + Symudwch i’ch tabiau agored, nodau tudalen, a’ch hanes pori. + + Tabiau glân, trefnus + + Cliriwch annibendod tabiau gyda gwell cynllun a thabiau cau awtomatig. + + Chwilio diweddar + + Ailedrych ar eich chwilio diweddaraf o’ch tudalen cartref a’ch tabiau. + + + Mae eich cartref personoledig Firefox bellach yn ei gwneud hi’n haws i fynd yn ôl i le wnaethoch chi adael. Dewch o hyd i’ch tabiau, nodau tudalen a’ch canlyniadau chwilio diweddar. + Agorwch tab Firefox newydd @@ -333,7 +387,9 @@ Thema - Cartref + Cartref + + Tudalen Cartref Ystumiau @@ -408,9 +464,15 @@ Cadwyd yn ddiweddar - Wedi gosod nod tudalen yn ddiweddar - - Ymwelwyd yn ddiweddar + Wedi gosod nod tudalen yn ddiweddar + + Nodau tudalen diweddar + + Ymwelwyd yn ddiweddar + + Chwilio diweddar Pocket @@ -522,7 +584,7 @@ Cychwyn Sync - Sganiwch god paru yn Firefox bwrdd gwaith + Sganiwch god paru yn Firefox bwrdd gwaith Mewngofnodi @@ -627,6 +689,10 @@ Rhestr Grid + + Chwilio grwpiau + + Casglu gwefannau cysylltiedig ynghyd Cau tabiau @@ -638,15 +704,26 @@ Ar ôl mis - + + Awtogau tabiau agored + + - Dechrau o gartref + Dechrau o gartref + + Sgrin agoriadol - Ar ôl pedair awr + Ar ôl pedair awr + + Tudalen Cartref - Bob tro + Bob tro + + Tab olaf - Byth + Byth + + Tudalen cartref ar ôl pedair awr segur Cau â llaw @@ -656,6 +733,13 @@ Cau ar ôl mis + + + Symud hen dabiau i anweithredol + + + Mae tabiau nad ydych chi wedi edrych arnyn nhw ers pythefnos yn cael eu symud i’r adran anweithredol. + Tynnu @@ -698,13 +782,13 @@ Cadw i Gasgliad - Dewis + Dewis Rhannu pob tab Tabiau wedi’u cau’n ddiweddar - Caewyd yn ddiweddar + Caewyd yn ddiweddar Gosodiadau cyfrif @@ -778,7 +862,10 @@ Cadw - Eraill + Eraill + + + Tabiau eraill @@ -823,10 +910,6 @@ Dim hanes yma - - Dileu llwythi - - Ydych chi’n siŵr eich bod eisiau clirio eich hanes? Llwythi Wedi’u Tynnu @@ -869,7 +952,7 @@ Dewislen nodau tudalen - Golygu nod tudalen + Golygu nod tudalen Dewis ffolder @@ -1532,7 +1615,7 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad URL wedi ei gopïo i’r clipfwrdd - I’r sgrin Cartref + Ychwanegu i’r sgrin Cartref Diddymu @@ -1820,7 +1903,7 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Ydych chi’n siŵr eich bod am ddileu’r nod tudalen yma? - Cadw fel Hoff wefan + Ychwanegu i Hoff wefan Dilyswyd gan: %1$s @@ -1897,8 +1980,10 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Iawn, Wedi deall! + + Dangos yr hoff wefannau yr ymwelwyd â nhw amlaf - Dangos yr Hoff wefannau yr ymwelwyd â nhw amlaf + Dangos yr Hoff wefannau yr ymwelwyd â nhw amlaf Dangos y gwefannau yr ymwelwyd â nhw amlaf @@ -1911,15 +1996,41 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Diddymu - - + + Tabiau anweithredol + + Caewch bob tab anweithredol - Mae tabiau ar gael yma am %s. Wedi hynny, bydd y tabiau’n cael eu cau’n awtomatig. + Mae tabiau ar gael yma am %s. Wedi hynny, bydd y tabiau’n cael eu cau’n awtomatig. - 30 diwrnod + 30 diwrnod - 1 wythnos + 1 wythnos + + + + Autogau ar ôl mis? + + Gall Firefox gau tabiau nad ydych wedi edrych arnyn nhw dros y mis diwethaf. + + TROI AWTOGAU YMLAEN + + + + Helpwch ni i wella, os gwelwch yn dda + + Pam wnaethoch chi analluogi tabiau anweithredol? + + Dim diddordeb yn y nodwedd + + Mae’r amser i anweithredol yn rhy hir + + Mae’r amser i anweithredol yn rhy fyr + + Anfon + + Cau Gosod dolenni o wefannau, e-byst, a negeseuon i agor yn awtomatig yn Firefox. @@ -1936,4 +2047,19 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad Cau + + + Straeon sy’n procio’r meddwl + + Straeon sy’n procio’r meddwl + + Straeon yn ôl pwnc + + Darganfod rhagor + + Wedi’i bweru gan Pocket. + + Rhan o deulu Firefox. %s + + Dysgu rhagor From 8b660555edb6953f687407a9178339e4c12035b2 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 427/517] Strings - app/src/main/res/values-da/strings.xml --- app/src/main/res/values-da/strings.xml | 214 +++++++++++++++++++++---- 1 file changed, 187 insertions(+), 27 deletions(-) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 1c8a6909f9..907eb01270 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -23,7 +23,7 @@ 1 åbent faneblad. Tryk for at skifte faneblade. - %1$s åbne faneblade. Tryk for at skifte faneblade. + %1$s åbne faneblade. Tryk for at skifte faneblade. %1$d valgt @@ -53,6 +53,10 @@ Gemt for nylig + + Seneste bogmærker + + Seneste bogmærker Bogmærker gemt for nylig @@ -69,7 +73,7 @@ %1$s rydder din søge- og browserhistorik fra private faneblade, når du lukker dem, eller når du afslutter programmet. Det gør det nemmere at holde din færden på nettet for dig selv, hvis andre bruger den samme computer. Websteder og din internetudbyder kan dog stadig finde ud af, hvad du foretager dig. - Almindelige myter om privat browsing + Udbredte myter om privat browsing Slet session @@ -103,6 +107,11 @@ Afvis + + Faneblade, du ikke har set i to uger, flyttes hertil. + + Slå fra i indstillinger + Nyt faneblad @@ -110,7 +119,7 @@ Nyt privat faneblad - Foretrukne websteder + Foretrukne websteder @@ -118,6 +127,15 @@ Vis alle + + Knap til visning af alle seneste faneblade + + Din søgning efter \"%1$s\" + + Websteder: %1$s + @@ -125,7 +143,16 @@ - Besøgt for nylig + Besøgt for nylig + + + Seneste søgninger + + Fjern + + Knap til visning af alle tidligere udforsket @@ -139,7 +166,7 @@ Stop - Bogmærk + Bogmærk Rediger bogmærke @@ -201,6 +228,10 @@ Rediger + + Tilpas startside + + Tilpas startside Startskærm @@ -246,6 +277,27 @@ Søg direkte fra adressefeltet + + + Nyheder i Firefox + + Det er blevet lettere at fortsætte, hvor du slap. + + Tilpasset startside i Firefox + + Hop til dine åbne faneblade, bogmærker og browserhistorik. + + Overskuelige faneblade + + Fjern distraherende faneblade med forbedret layout og automatisk lukning af faneblade. + + Seneste søgninger + + Besøg dine seneste søgninger igen fra din startside og faneblade. + + + Din personlige startside i Firefox gør det nu lettere at fortsætte, hvor du slap. Find dine seneste faneblade, bogmærker og søgeresultater. + Åbn et nyt Firefox-faneblad @@ -324,7 +376,7 @@ Tema - Startside + Startside Bevægelser @@ -391,10 +443,24 @@ Tilføjelses-samling ændret. Afslutter applikationen for at anvende ændringerne… + + + Hop tilbage til Gemt for nylig - - Besøgt for nylig + + Seneste bogmærker + + Seneste bogmærker + + Besøgt for nylig + + + Seneste søgninger + + Pocket @@ -503,7 +569,7 @@ Aktiver Sync - Skan parrings-koden i Firefox på din computer + Skan parrings-koden i Firefox på din computer Log ind @@ -555,7 +621,7 @@ Bogmærker - Skrivebords-bogmærker + Bogmærker fra din computer Bogmærke-menu @@ -580,6 +646,13 @@ Luk + + %d websted + + %d websteder + Nyligt lukkede faneblade @@ -602,6 +675,10 @@ Liste Gitter + + Søgegrupper + + Gruppér relaterede websteder Luk faneblade @@ -613,15 +690,18 @@ Efter en måned - + + Luk automatisk åbne faneblade + + - Begynd på startsiden + Begynd på startsiden - Efter fire timer + Efter fire timer - Altid + Altid - Aldrig + Aldrig Luk manuelt @@ -631,6 +711,12 @@ Luk efter en måned + + + Flyt gamle faneblade til afsnittet Inaktive faneblade + + Faneblade, du ikke har set i to uger, flyttes til afsnittet Inaktive faneblade. + Fjern @@ -672,13 +758,13 @@ Gem til samling - Vælg + Vælg Del alle faneblade Nyligt lukkede faneblade - Nyligt lukkede + Nyligt lukkede Kontoindstillinger @@ -750,6 +836,11 @@ Gem + + Andre + + Andre faneblade + Slet historik @@ -793,10 +884,6 @@ Ingen historik - - Ryd filhentninger - - Er du sikker på, at du vil rydde dine filhentninger? Filhentninger fjernet @@ -837,7 +924,7 @@ Bogmærke-menu - Rediger bogmærke + Rediger bogmærke Vælg mappe @@ -1457,6 +1544,12 @@ Rydder cookies, der sættes af omdirigeringer til websteder, der er kendt for at spore deres brugere. + + Blokeringen af nogle af sporings-mekanismerne angivet nedenfor er blevet delvist ophævet, fordi du har interageret med dem *. + + Læs mere + Hjælp @@ -1523,6 +1616,9 @@ Udfyld brugernavne og adgangskoder i andre apps på din enhed. + + Tilføj login + Synkroniser logins @@ -1585,6 +1681,8 @@ Kopier brugernavn Ryd brugernavn + + Ryd værtsnavn Kopier websted @@ -1751,6 +1849,10 @@ %1$s TIL]]> + + Forbindelsen er sikker + + Forbindelsen er ikke sikker Sikker forbindelse @@ -1794,8 +1896,14 @@ Fortryd ændringer Rediger + + Tilføj nyt login Adgangskode påkrævet + + Brugernavn påkrævet + + Værtsnavn påkrævet Stemme-søgning @@ -1803,6 +1911,15 @@ Et login med dette brugernavn findes allerede + + https://www.eksempel.dk + + Webadressen skal indeholde \“https://\“ eller \“http://\“ + + Webadressen skal indeholde \“https://\“ eller \“http://\“ + + Gyldigt værtsnavn påkrævet + Opret forbindelse til en ny enhed. @@ -1828,6 +1945,8 @@ Ok, forstået + + Vis mest besøgte websteder Vis mest besøgte websteder @@ -1840,15 +1959,41 @@ Annuller - - + + Inaktive faneblade + + Luk alle inaktive faneblade - Faneblade er tilgængelige her i %s. Herefter lukkes fanebladene automatisk. + Faneblade er tilgængelige her i %s. Herefter lukkes fanebladene automatisk. - 30 dage + 30 dage - 1 uge + 1 uge + + + + Luk automatisk efter en måned? + + Firefox kan lukke faneblade, du ikke har set i løbet af den seneste måned. + + SLÅ AUTOMATISK LUKNING TIL + + + + Hjælp os med at gøre det bedre + + Hvorfor deaktiverede du inaktive faner? + + Ikke interesseret i funktionen + + Det tager for lang tid, inden de bliver inaktive + + Det tager for kort tid, inden de bliver inaktive + + Send + + Lukker Indstil links fra websteder, mails og beskeder til automatisk at blive åbnet i Firefox. @@ -1865,4 +2010,19 @@ Luk + + + Tankevækkende historier + + Tankevækkende historier + + Historier efter emne + + Opdag mere + + Leveret af Pocket. + + En del af Firefox-familien. %s + + Læs mere From b4f9da032a29d2cfd612e305b0e4f36de21d72aa Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 428/517] Strings - app/src/main/res/values-de/strings.xml --- app/src/main/res/values-de/strings.xml | 183 +++++++++++++++++++++---- 1 file changed, 154 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0adb5c0912..1d78649874 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -26,7 +26,7 @@ 1 offener Tab. Antippen, um Tabs zu wechseln. - %1$s offene Tabs. Antippen, um Tabs zu wechseln. + %1$s offene Tabs. Antippen, um Tabs zu wechseln. %1$d ausgewählt @@ -55,7 +55,9 @@ Kürzlich gespeichert - Kürzlich als Lesezeichen gesetzt + Kürzlich als Lesezeichen gesetzt + + Neueste Lesezeichen Kürzlich gespeicherte Lesezeichen @@ -110,6 +112,20 @@ Schließen + + Tabs, die Sie zwei Wochen lang nicht angesehen haben, werden hierher verschoben. + + In den Einstellungen deaktivieren + + + Nach einem Monat automatisch schließen? + + Firefox kann Tabs schließen, die Sie im letzten Monat nicht angesehen haben. + + Schließen + + Automatisches Schließen aktivieren + Neuer Tab @@ -117,7 +133,7 @@ Neuer privater Tab - Wichtige Seiten + Wichtige Seiten @@ -125,6 +141,15 @@ Alle anzeigen + + Schaltfläche „Alle zuletzt geöffneten Tabs“ anzeigen + + Ihre Suche nach \"%1$s\" + + Websites: %1$s + @@ -132,11 +157,17 @@ - Kürzlich besucht + Kürzlich besucht + + Letzte Suchanfragen Entfernen + + Schaltfläche „Alle vergangenen Erkundungen“ anzeigen + Offene Tabs @@ -149,7 +180,7 @@ Anhalten - Zu Lesezeichen hinzufügen + Zu Lesezeichen hinzufügen Lesezeichen bearbeiten @@ -215,6 +246,8 @@ Startbildschirm anpassen + + Startbildschirm anpassen Startbildschirm @@ -261,6 +294,27 @@ Direkt aus der Adressleiste suchen + + + Was neu ist bei Firefox + + Es ist jetzt einfacher, dort weiterzumachen, wo Sie aufgehört haben. + + Personalisierte Firefox-Startseite + + Wechseln Sie zu Ihren geöffneten Tabs, Lesezeichen und Ihrer Surf-Chronik. + + Übersichtliche, organisierte Tabs + + Dank verbessertem Layout und automatisch schließenden Tabs ist Schluss mit Unordnung. + + Letzte Suchanfragen + + Rufen Sie Ihre letzten Suchanfragen von Ihrer Startseite und Ihren Tabs aus erneut auf. + + + Ihre personalisierte Firefox-Startseite macht es jetzt einfacher, dort weiterzumachen, wo Sie aufgehört haben. Finden Sie Ihre letzten Tabs, Lesezeichen und Suchergebnisse. + In einem neuen Firefox-Tab öffnen @@ -340,7 +394,9 @@ Theme - Startseite + Startseite + + Startseite Gesten @@ -415,9 +471,15 @@ Kürzlich gespeichert - Kürzlich als Lesezeichen gesetzt - - Kürzlich besucht + Kürzlich als Lesezeichen gesetzt + + Neueste Lesezeichen + + Kürzlich besucht + + Letzte Suchanfragen Pocket @@ -532,7 +594,7 @@ Sync aktivieren - Scannen Sie den Pairing-Code in Firefox + Scannen Sie den Pairing-Code in Firefox Anmelden @@ -639,6 +701,10 @@ Liste Raster + + Gruppen durchsuchen + + Zusammengehörige Websites gruppieren Tabs schließen @@ -650,15 +716,26 @@ Nach einem Monat - + + Offene Tabs automatisch schließen + + - Am Startbildschirm neu beginnen + Am Startbildschirm neu beginnen + + Startbildschirm - Nach vier Stunden + Nach vier Stunden + + Startseite - Immer + Immer + + Letzter Tab - Nie + Nie + + Startseite nach vier Stunden Inaktivität Manuell schließen @@ -668,6 +745,12 @@ Nach einem Monat schließen + + + Alte Tabs zu „inaktiv“ verschieben + + Tabs, die Sie zwei Wochen lang nicht angesehen haben, werden in den inaktiven Bereich verschoben. + Entfernen @@ -711,13 +794,13 @@ In Sammlung speichern - Auswählen + Auswählen Alle Tabs teilen Kürzlich geschlossene Tabs - Kürzlich geschlossen + Kürzlich geschlossen Kontoeinstellungen @@ -792,7 +875,10 @@ Speichern - Andere + Andere + + + Andere Tabs @@ -838,10 +924,6 @@ Keine Chronik vorhanden - - Downloads löschen - - Sollen Ihre Downloads wirklich gelöscht werden? Downloads entfernt @@ -883,7 +965,7 @@ Lesezeichen-Menü - Lesezeichen bearbeiten + Lesezeichen bearbeiten Ordner auswählen @@ -1932,8 +2014,10 @@ Ok, verstanden + + Meistbesuchte wichtige Seiten - Meistbesuchte Seiten anzeigen + Meistbesuchte Seiten anzeigen Meistbesuchte Seiten anzeigen @@ -1946,15 +2030,41 @@ Abbrechen - - + + Inaktive Tabs + + Alle inaktiven Tabs schließen - Tabs sind hier für %s Tage verfügbar. Danach werden Tabs automatisch geschlossen. + Tabs sind hier für %s Tage verfügbar. Danach werden Tabs automatisch geschlossen. - 30 Tage + 30 Tage - 1 Woche + 1 Woche + + + + Nach einem Monat automatisch schließen? + + Firefox kann Tabs schließen, die Sie im letzten Monat nicht angesehen haben. + + AUTOMATISCHES SCHLIESSEN AKTIVIEREN + + + + Bitte helfen Sie uns, besser zu werden + + Warum haben Sie die inaktiven Tabs deaktiviert? + + Kein Interesse an der Funktion + + Die Zeit bis zur Inaktivität ist zu lang + + Die Zeit bis zur Inaktivität ist zu kurz + + Absenden + + Schließen Stellen Sie Links von Websites, E-Mails und Nachrichten so ein, dass sie in Firefox automatisch geöffnet werden. @@ -1971,4 +2081,19 @@ Schließen + + + Geschichten, die zum Nachdenken anregen + + Geschichten, die zum Nachdenken anregen + + Geschichten nach Thema + + Mehr entdecken + + Bereitgestellt von Pocket + + Teil der Firefox-Familie. %s + + Weitere Informationen From b1da610c6d4ea654c64edfd27b0da3d756b87be6 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 429/517] Strings - app/src/main/res/values-dsb/strings.xml --- app/src/main/res/values-dsb/strings.xml | 186 ++++++++++++++++++++---- 1 file changed, 157 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-dsb/strings.xml b/app/src/main/res/values-dsb/strings.xml index c31c2480db..02153ef102 100644 --- a/app/src/main/res/values-dsb/strings.xml +++ b/app/src/main/res/values-dsb/strings.xml @@ -23,7 +23,7 @@ Wócynjone rejtariki: %1$s. Pótusniśo, aby rejtariki pśešaltował. - Wócynjone rejtariki: %1$s. Pótusniśo, aby rejtariki pśešaltował. + Wócynjone rejtariki: %1$s. Pótusniśo, aby rejtariki pśešaltował. Wubrane: %1$d @@ -52,7 +52,9 @@ Njedawno skłaźone - Njedawno ako cytańske znamjenja wótpołožone + Njedawno ako cytańske znamjenja wótpołožone + + Nejnowše cytańske znamjenja Njedawno skłaźone cytańske znamjenja @@ -106,6 +108,20 @@ Zachyśiś + + Rejtariki, kótarež njejsćo se woglědał dwa tyźenja, se sem pśesunu. + + W nastajenjach znjemóžniś + + + Pó jadnem mjasecu awtomatiski zacyniś? + + Firefox móžo rejtariki zacyniś, kótarež njejsćo se woglědał zajźony mjasec. + + Zacyniś + + Awtomatiske zacynjanje zmóžniś + Nowy rejtarik @@ -113,7 +129,7 @@ Nowy priwatny rejtarik - Nejcesćej woglědane sedła + Nejcesćej woglědane sedła @@ -121,6 +137,15 @@ Wšykne pokazaś + + Tłocašk „Njedawno wócynjone rejtariki“ pokazaś + + Wašo pytanje za \"%1$s\" + + Sedła: %1$s + @@ -128,11 +153,17 @@ - Pśed krotkim woglědane + Pśed krotkim woglědane + + Nejnowše pytanja Wótwónoźeś + + Tłocašk „Wšykne zajźone wuslěźenja“ pokazaś + Wócynjone rejtariki @@ -145,7 +176,7 @@ Zastajiś - Ako cytańske znamje składowaś + Ako cytańske znamje składowaś Cytańske znamje wobźěłaś @@ -209,6 +240,8 @@ Startowu wobrazowku pśiměriś + + Startowy bok pśiměriś Startowa wobrazowka @@ -254,6 +287,28 @@ Direktnje z adresowego póla pytaś + + + Nowe funkcije a změny we Firefox + + Jo něnto lažčej tam pókšacowaś, źož sćo pśestał. + + Personalizěrowany startowy bok Firefox + + Pśejźćo k swójim wócynjonym rejtarikam, cytańskim znamjenjam a pśeglědowańskej historiji. + + Pśeglědne, organizěrowane rejtariki + + Wótwónoźćo rejtarikowy barłog pśez pólěpšone wugótowanje a awtomatiski zacynjajuce rejtariki. + + Nejnowše pytanja + + + Wótwołśo znowego swóje nejnowše pytanja ze swójogo startowego boka a rejtarikow. + + + Waša personalizěrowany startowy bok Firefox něnto wólažcujo tam pókšacowaś, źož sćo pśestał. Namakajśo swóje nejnowše rejtariki, cytańske znamjenja a pytańske wuslědki. + Nowy rejtarik Firefox wócyniś @@ -333,7 +388,9 @@ Drastwa - Startowy bok + Startowy bok + + Startowy bok Gesty @@ -407,9 +464,15 @@ Njedawno skłaźone - Njedawno ako cytańske znamjenja wótpołožone - - Pśed krotkim woglědane + Njedawno ako cytańske znamjenja wótpołožone + + Nejnowše cytańske znamjenja + + Pśed krotkim woglědane + + Nejnowše pytanja Pocket @@ -521,7 +584,7 @@ Synchronizaciju zmóžniś - Pórowański kod w desktopowem Firefox skannowaś + Pórowański kod w desktopowem Firefox skannowaś Pśizjawiś @@ -627,6 +690,10 @@ Lisćina Kśidno + + Pytańske kupki + + Gromadu słušajuce sedła rědowaś Rejtariki zacyniś @@ -638,15 +705,26 @@ Pó jadnom mjasecu - + + Rejtariki awtomatiski zacyniś + + - Na startowej wobrazowce zachopiś + Na startowej wobrazowce zachopiś + + Startowa wobrazowka - Pó styrich góźinach + Pó styrich góźinach + + Startowy bok - Pśecej + Pśecej + + Slědny rejtarik - Nigda + Nigda + + Startowy bok pó styrich góźinach inaktiwnosći Manuelnje zacyniś @@ -656,6 +734,13 @@ Pó jadnom mjasecu zacyniś + + + Stare rejtariki do „inaktiwne“ pśesunuś + + + Rejtariki, kótarež njejsćo se woglědał dwa tyźenja, se do inaktiwnego wótrězka pśesunu. + Wótwónoźeś @@ -698,13 +783,13 @@ Do zběrki składowaś - Wubraś + Wubraś Wšykne rejtariki źěliś Rowno zacynjone rejtariki - Njedawno zacynjone + Njedawno zacynjone Kontowe nastajenja @@ -778,7 +863,10 @@ Składowaś - Druge + Druge + + + Druge rejtariki @@ -823,10 +911,6 @@ How žedna historija njejo - - Ześěgnjenja lašowaś - - Cośo napšawdu swóje ześěgnjenja lašowaś? Ześěgnjenja wótwónoźone @@ -869,7 +953,7 @@ Meni cytańskich znamjenjow - Cytańske znamje wobźěłaś + Cytańske znamje wobźěłaś Zarědnik wubraś @@ -1899,8 +1983,10 @@ W pórěźe, som zrozměł + + Nejcesćej woglědane sedła pokazaś - Nejcesćej woglědane sedła pokazaś + Nejcesćej woglědane sedła pokazaś Nejcesćej woglědane sedła pokazaś @@ -1913,15 +1999,42 @@ Pśetergnuś - - + + Inaktiwne rejtariki + + Wšykne inaktiwne rejtariki zacyniś - Rejtariki su how za %s k dispoziciji. Pó toś tom casu se rejtariki awtomatiski zacyniju. + Rejtariki su how za %s k dispoziciji. Pó toś tom casu se rejtariki awtomatiski zacyniju. - 30 dnjow + 30 dnjow - 1 tyźeń + 1 tyźeń + + + + Pó jadnem mjasecu awtomatiski zacyniś? + + Firefox móžo rejtariki zacyniś, kótarež njejsćo se woglědał zajźony mjasec. + + AWTOMATISKE ZACYNJANJE ZMÓŽNIŚ + + + + Pšosym pomagajśo nam, aby my se pólěpšyli + + + Cogodla sćo znjemóžnił inaktiwne rejtariki? + + Žeden zajm na toś tej funkciji + + Cas do „inaktiwny“ jo pśedłujki + + Cas do „inaktiwny“ jo pśekrotki + + Pósłaś + + Zacyniś Nastajśo wótkaze z websedłow, mejlkow a powěsćow, aby se awtomatiski we Firefox wócynili. @@ -1938,4 +2051,19 @@ Zacyniś + + + Tšojeńka, kótarež k rozmyslowanju pógnuwaju + + Tšojeńka, kótarež k rozmyslowanju pógnuwaju + + Tšojeńka pó temje + + Wěcej wuslěźiś + + Wót Pocket spěchowany + + Źěl swójźby Firefox. %s + + From 6b10cc6e32a3f09d267cfc6da9c4a31a33398ebd Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 430/517] Strings - app/src/main/res/values-el/strings.xml --- app/src/main/res/values-el/strings.xml | 184 +++++++++++++++++++++---- 1 file changed, 154 insertions(+), 30 deletions(-) diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index fb8eb91f17..4999caf0a3 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -26,7 +26,7 @@ 1 ανοικτή καρτέλα. Πατήστε για εναλλαγή καρτελών. - %1$s ανοικτές καρτέλες. Πατήστε για εναλλαγή καρτελών. + %1$s ανοικτές καρτέλες. Πατήστε για εναλλαγή καρτελών. %1$d επιλογή(ές) @@ -59,7 +59,9 @@ Πρόσφατα αποθηκευμένα - Πρόσφατοι σελιδοδείκτες + Πρόσφατοι σελιδοδείκτες + + Πρόσφατοι σελιδοδείκτες Πρόσφατα αποθηκευμένοι σελιδοδείκτες @@ -114,6 +116,20 @@ Απόρριψη + + Οι καρτέλες που δεν έχετε προβάλει για δύο εβδομάδες μετακινούνται εδώ. + + Απενεργοποίηση στις ρυθμίσεις + + + Αυτόματο κλείσιμο μετά από έναν μήνα; + + Το Firefox μπορεί να κλείσει τις καρτέλες που δεν έχετε προβάλει τον περασμένο μήνα. + + Κλείσιμο + + Ενεργοποίηση αυτόματου κλεισίματος + Νέα καρτέλα @@ -121,7 +137,7 @@ Νέα ιδιωτική καρτέλα - Κορυφαίοι ιστότοποι + Κορυφαίοι ιστότοποι @@ -129,6 +145,15 @@ Εμφάνιση όλων + + Κουμπί «Εμφάνιση όλων των πρόσφατων καρτελών» + + Η αναζήτησή σας για «%1$s» + + Ιστότοποι: %1$s + @@ -136,11 +161,17 @@ - Πρόσφατες επισκέψεις + Πρόσφατες επισκέψεις + + Πρόσφατες αναζητήσεις Αφαίρεση + + Κουμπί «Εμφάνιση όλων των προηγούμενων εξερευνήσεων» + Ανοικτές καρτέλες @@ -153,7 +184,7 @@ Διακοπή - Αποθήκευση σελιδοδείκτη + Αποθήκευση σελιδοδείκτη Επεξεργασία σελιδοδείκτη @@ -218,6 +249,8 @@ Προσαρμογή αρχικής + + Προσαρμογή αρχικής σελίδας Αρχική οθόνη @@ -264,6 +297,27 @@ Αναζήτηση απευθείας από τη γραμμή διευθύνσεων + + + Τι νέο υπάρχει στο Firefox + + Τώρα είναι ευκολότερο να επιστρέψετε εκεί όπου σταματήσατε. + + Εξατομικευμένη αρχική σελίδα Firefox + + Μεταβείτε στις ανοικτές καρτέλες, τους σελιδοδείκτες και το ιστορικό περιήγησής σας. + + Σαφείς, οργανωμένες καρτέλες + + Κλείστε περιττές καρτέλες με τη βελτιωμένη διάταξη και το αυτόματο κλείσιμο καρτελών. + + Πρόσφατες αναζητήσεις + + Επισκεφθείτε ξανά τις τελευταίες αναζητήσεις σας από την αρχική σελίδα και τις καρτέλες σας. + + + Η εξατομικευμένη αρχική σελίδα του Firefox διευκολύνει την επιστροφή στο σημείο που σταματήσατε. Βρείτε τις πρόσφατες καρτέλες, τους σελιδοδείκτες και τα αποτελέσματα αναζήτησής σας. + Άνοιγμα νέας καρτέλας Firefox @@ -344,7 +398,9 @@ Θέμα - Αρχική σελίδα + Αρχική σελίδα + + Αρχική σελίδα Χειρονομίες @@ -418,9 +474,15 @@ Πρόσφατα αποθηκευμένα - Πρόσφατοι σελιδοδείκτες - - Πρόσφατες επισκέψεις + Πρόσφατοι σελιδοδείκτες + + Πρόσφατοι σελιδοδείκτες + + Πρόσφατες επισκέψεις + + Πρόσφατες αναζητήσεις Pocket @@ -533,7 +595,7 @@ Ενεργοποίηση Sync - Σάρωση κωδικού σύζευξης στο Firefox για υπολογιστή + Σάρωση κωδικού σύζευξης στο Firefox για υπολογιστή Σύνδεση @@ -640,6 +702,10 @@ Λίστα Πλέγμα + + Αναζήτηση ομάδων + + Ομαδοποίηση σχετικών ιστοτόπων Κλείσιμο καρτελών @@ -652,15 +718,26 @@ Μετά από έναν μήνα - + + Αυτόματο κλείσιμο ανοικτών καρτελών + + - Εκκίνηση στην αρχική οθόνη + Εκκίνηση στην αρχική οθόνη + + Οθόνη εκκίνησης - Μετά από τέσσερις ώρες + Μετά από τέσσερις ώρες + + Αρχική σελίδα - Πάντα + Πάντα + + Τελευταία καρτέλα - Ποτέ + Ποτέ + + Αρχική σελίδα μετά από τέσσερις ώρες αδράνειας Χειροκίνητο κλείσιμο @@ -670,6 +747,12 @@ Κλείσιμο μετά από έναν μήνα + + + Μετακίνηση παλαιών καρτελών στις ανενεργές + + Οι καρτέλες που δεν έχετε προβάλει για δύο εβδομάδες μετακινούνται στην ενότητα «Ανενεργές». + Αφαίρεση @@ -712,13 +795,13 @@ Αποθήκευση στη συλλογή - Επιλογή + Επιλογή Κοινή χρήση όλων των καρτελών Πρόσφατα κλεισμένες καρτέλες - Έκλεισαν πρόσφατα + Έκλεισαν πρόσφατα Ρυθμίσεις λογαριασμού @@ -791,7 +874,10 @@ Αποθήκευση - Άλλες + Άλλες + + + Άλλες καρτέλες @@ -837,11 +923,6 @@ Δεν υπάρχει ιστορικό - - Διαγραφή λήψεων - - Θέλετε σίγουρα να διαγράψετε τις λήψεις σας; - Οι λήψεις διαγράφηκαν @@ -883,7 +964,7 @@ Μενού σελιδοδεικτών - Επεξεργασία σελιδοδείκτη + Επεξεργασία σελιδοδείκτη Επιλογή φακέλου @@ -1925,8 +2006,10 @@ OK, το κατάλαβα + + Πιο συχνοί κορυφαίοι ιστότοποι - Εμφάνιση πιο συχνών κορυφαίων ιστοτόπων + Εμφάνιση πιο συχνών κορυφαίων ιστοτόπων Εμφάνιση πιο συχνών ιστοτόπων @@ -1939,15 +2022,41 @@ Ακύρωση - - + + Ανενεργές καρτέλες + + Κλείσιμο όλων των ανενεργών καρτελών - Οι καρτέλες θα είναι διαθέσιμες εδώ για %s. Έπειτα, οι καρτέλες θα κλείσουν αυτόματα. + Οι καρτέλες θα είναι διαθέσιμες εδώ για %s. Έπειτα, οι καρτέλες θα κλείσουν αυτόματα. - 30 ημέρες + 30 ημέρες - 1 εβδομάδα + 1 εβδομάδα + + + + Αυτόματο κλείσιμο μετά από έναν μήνα; + + Το Firefox μπορεί να κλείσει τις καρτέλες που δεν έχετε προβάλει τον περασμένο μήνα. + + ΕΝΕΡΓΟΠΟΙΗΣΗ ΑΥΤΟΜΑΤΟΥ ΚΛΕΙΣΙΜΑΤΟΣ + + + + Παρακαλούμε βοηθήστε μας να βελτιωθούμε + + Γιατί απενεργοποιήσατε τις ανενεργές καρτέλες; + + Δεν με ενδιαφέρει η λειτουργία + + Ο χρόνος αδράνειας είναι πολύ μεγάλος + + Ο χρόνος αδράνειας είναι πολύ μικρός + + Αποστολή + + Κλείσιμο Αυτόματο άνοιγμα συνδέσμων από ιστοτόπους, email και μηνύματα στο Firefox. @@ -1964,4 +2073,19 @@ Κλείσιμο + + + Άρθρα που σας βάζουν σε σκέψεις + + Άρθρα που σας βάζουν σε σκέψεις + + Άρθρα ανά θέμα + + Ανακαλύψτε περισσότερα + + Με την υποστήριξη του Pocket. + + Μέρος της οικογένειας του Firefox. %s + + Μάθετε περισσότερα From 08899469f2d6b4bb6038a47c5d62ecf22f3aa998 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 431/517] Strings - app/src/main/res/values-en-rCA/strings.xml --- app/src/main/res/values-en-rCA/strings.xml | 156 ++++++++++++++++++++- 1 file changed, 149 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-en-rCA/strings.xml b/app/src/main/res/values-en-rCA/strings.xml index 40ac30cffa..751043b085 100644 --- a/app/src/main/res/values-en-rCA/strings.xml +++ b/app/src/main/res/values-en-rCA/strings.xml @@ -53,7 +53,9 @@ Recently saved - Recently bookmarked + Recently bookmarked + + Recent bookmarks Recently saved bookmarks @@ -106,6 +108,11 @@ Dismiss + + Tabs you haven’t viewed for two weeks get moved here. + + Turn off in settings + New tab @@ -121,6 +128,15 @@ Show all + + Show all recent tabs button + + Your search for \"%1$s\" + + Sites: %1$s + @@ -128,11 +144,17 @@ - Recently visited + Recently visited + + Recent searches Remove + + Show all past explorations button + Open Tabs @@ -209,6 +231,8 @@ Customize home + + Customize homepage Home screen @@ -254,6 +278,27 @@ Search directly from the address bar + + + What’s new in Firefox + + It’s now easier to pick back up where you left off. + + Personalized Firefox homepage + + Jump to your open tabs, bookmarks and browsing history. + + Clean, organized tabs + + Clear away tab clutter with improved layout and auto-closing tabs. + + Recent searches + + Revisit your latest searches from your homepage and tabs. + + + Your personalized Firefox homepage now makes it easier to pick up where you left off. Find your recent tabs, bookmarks and search results. + Open a new Firefox tab @@ -406,9 +451,15 @@ Recently saved - Recently bookmarked - - Recently visited + Recently bookmarked + + Recent bookmarks + + Recently visited + + Recent searches Pocket @@ -624,6 +675,10 @@ List Grid + + Search groups + + Group related sites together Close tabs @@ -635,6 +690,9 @@ After one month + + Auto-close open tabs + Start on home @@ -653,6 +711,12 @@ Close after one month + + + Move old tabs to inactive + + Tabs you haven’t viewed for two weeks get moved to the inactive section. + Remove @@ -773,6 +837,11 @@ Save + + Other + + Other tabs + Delete history @@ -1477,6 +1546,12 @@ Clears cookies set by redirects to known tracking websites. + + Some trackers marked below have been partially unblocked on this page because you interacted with them *. + + Learn more + Support @@ -1543,6 +1618,9 @@ Fill usernames and passwords in other apps on your device. + + Add login + Sync logins @@ -1605,6 +1683,8 @@ Copy username Clear username + + Clear hostname Copy site @@ -1771,6 +1851,10 @@ %1$s to ON]]> + + Connection is secure + + Connection is not secure Secure Connection @@ -1814,8 +1898,14 @@ Discard changes Edit + + Add new login Password required + + Username required + + Hostname required Voice search @@ -1824,6 +1914,15 @@ A login with that username already exists + + https://www.example.com + + Web address must contain "https://" or "http://" + + Web address must contain "https://" or "http://" + + Valid hostname required + Connect another device. @@ -1849,6 +1948,8 @@ OK, Got It + + Show most visited top sites Show most visited sites @@ -1861,9 +1962,11 @@ Cancel - - + + Inactive tabs + + Close all inactive tabs Tabs are available here for %s. After that time, tabs will be automatically closed. @@ -1871,6 +1974,30 @@ 1 week + + + Auto-close after one month? + + Firefox can close tabs you haven’t viewed over the past month. + + TURN ON AUTO CLOSE + + + + Please help us to improve + + Why did you disable inactive tabs? + + Not interested in the feature + + Time to inactive is too long + + Time to inactive is too short + + Send + + Close + Set links from websites, emails, and messages to open automatically in Firefox. @@ -1886,4 +2013,19 @@ Close + + + Thought provoking stories + + Thought-provoking stories + + Stories by topic + + Discover more + + Powered by Pocket. + + Part of the Firefox family. %s + + Learn more From 8f13b30ee9ebb0b9302b617ee886c5ab09ba81fc Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 432/517] Strings - app/src/main/res/values-en-rGB/strings.xml --- app/src/main/res/values-en-rGB/strings.xml | 184 +++++++++++++++++---- 1 file changed, 155 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml index b8c461ae63..399d99e3c3 100644 --- a/app/src/main/res/values-en-rGB/strings.xml +++ b/app/src/main/res/values-en-rGB/strings.xml @@ -23,7 +23,7 @@ 1 open tab. Tap to switch tabs. - %1$s open tabs. Tap to switch tabs. + %1$s open tabs. Tap to switch tabs. %1$d selected @@ -52,7 +52,9 @@ Recently saved - Recently bookmarked + Recently bookmarked + + Recent bookmarks Recently saved bookmarks @@ -105,6 +107,21 @@ Dismiss + + Tabs you haven’t viewed for two weeks get moved here. + + Turn off in settings + + + Auto-close after one month? + + Firefox can close tabs you haven’t viewed over the past month. + + Close + + + Turn on auto close + New tab @@ -112,7 +129,7 @@ New private tab - Top sites + Top sites @@ -120,6 +137,15 @@ Show all + + Show all recent tabs button + + Your search for \"%1$s\" + + Sites: %1$s + @@ -127,11 +153,17 @@ - Recently visited + Recently visited + + Recent searches Remove + + Show all past explorations button + Open Tabs @@ -144,7 +176,7 @@ Stop - Bookmark + Bookmark Edit bookmark @@ -208,6 +240,8 @@ Customise home + + Customise homepage Home screen @@ -253,6 +287,27 @@ Search directly from the address bar + + + What’s new in Firefox + + It’s now easier to pick back up where you left off. + + Personalised Firefox homepage + + Jump to your open tabs, bookmarks and browsing history. + + Clean, organised tabs + + Clear away tab clutter with improved layout and auto-closing tabs. + + Recent searches + + Revisit your latest searches from your homepage and tabs. + + + Your personalised Firefox homepage now makes it easier to pick up where you left off. Find your recent tabs, bookmarks and search results. + Open a new Firefox tab @@ -332,7 +387,9 @@ Theme - Home + Home + + Homepage Gestures @@ -406,9 +463,15 @@ Recently saved - Recently bookmarked - - Recently visited + Recently bookmarked + + Recent bookmarks + + Recently visited + + Recent searches Pocket @@ -519,7 +582,7 @@ Turn on Sync - Scan pairing code in desktop Firefox + Scan pairing code in desktop Firefox Sign in @@ -624,6 +687,10 @@ List Grid + + Search groups + + Group related sites together Close tabs @@ -636,15 +703,26 @@ After one month - + + Auto-close open tabs + + - Start on home + Start on home + + Opening screen - After four hours + After four hours + + Homepage - Always + Always + + Last tab - Never + Never + + Homepage after four hours of inactivity Close manually @@ -654,6 +732,12 @@ Close after one month + + + Move old tabs to inactive + + Tabs you haven’t viewed for two weeks get moved to the inactive section. + Remove @@ -696,13 +780,13 @@ Save to collection - Select + Select Share all tabs Recently closed tabs - Recently closed + Recently closed Account settings @@ -776,7 +860,10 @@ Save - Other + Other + + + Other tabs @@ -821,10 +908,6 @@ No history here - - Delete downloads - - Are you sure you want to clear your downloads? Downloads Removed @@ -865,7 +948,7 @@ Bookmark menu - Edit bookmark + Edit bookmark Select folder @@ -1886,8 +1969,10 @@ OK, Got It + + Most visited top sites - Show most visited top sites + Show most visited top sites Show most visited sites @@ -1900,15 +1985,41 @@ Cancel - - + + Inactive tabs + + Close all inactive tabs - Tabs are available here for %s. After that time, tabs will be automatically closed. + Tabs are available here for %s. After that time, tabs will be automatically closed. - 30 days + 30 days - 1 week + 1 week + + + + Auto-close after one month? + + Firefox can close tabs you haven’t viewed over the past month. + + TURN ON AUTO CLOSE + + + + Please help us to improve + + Why did you disable inactive tabs? + + Not interested in the feature + + Time to inactive is too long + + Time to inactive is too short + + Send + + Closes Set links from web sites, emails, and messages to open automatically in Firefox. @@ -1925,4 +2036,19 @@ Close + + + Thought provoking stories + + Thought-provoking stories + + Stories by topic + + Discover more + + Powered by Pocket + + Part of the Firefox family. %s + + Learn more From 307cf4723a7f64f347b3d39d9b004cde92681b44 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 433/517] Strings - app/src/main/res/values-es/strings.xml --- app/src/main/res/values-es/strings.xml | 54 +++++++++++++++----------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3a6dd3b6f9..731245a8e1 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -55,7 +55,11 @@ - Guardados recientemente + Guardados recientemente + + Añadidos recientemente + + Marcadores recientes Marcadores guardados recientemente @@ -128,7 +132,7 @@ - Exploraciones pasadas + Exploraciones pasadas @@ -254,6 +258,9 @@ Busca directamente desde la barra de direcciones + + Página de inicio de Firefox personalizada + Abrir una nueva pestaña de Firefox @@ -402,9 +409,10 @@ Colección de complementos modificada. Cerrando la aplicación para aplicar los cambios… - Guardados recientemente - - Visitados recientemente + Guardados recientemente + + Visitados recientemente Pocket @@ -617,6 +625,8 @@ Lista Cuadrícula + + Buscar en grupos Cerrar pestañas @@ -768,7 +778,7 @@ Guardar - Otro + Otro @@ -782,15 +792,15 @@ Limpiar - Copiar + Copiar - Compartir + Compartir - Abrir en una nueva pestaña + Abrir en una nueva pestaña - Abrir en una pestaña privada + Abrir en una pestaña privada - Eliminar + Eliminar %1$d seleccionados @@ -1286,12 +1296,12 @@ ¿Ya tienes una cuenta? - Ver las novedades + Ver las novedades - ¿Tienes preguntas sobre el rediseño de %s? ¿Quieres saber qué ha cambiado? + ¿Tienes preguntas sobre el rediseño de %s? ¿Quieres saber qué ha cambiado? - Obtén respuestas aquí + Obtén respuestas aquí Sincronizar Firefox entre dispositivos @@ -1332,15 +1342,15 @@ Pon la barra de herramientas a tu alcance. Mantenla abajo, o muévela hacia arriba. - Navegar de forma privada + Navegar de forma privada - Abre una pestaña privada una vez: Toca el icono %s. + Abre una pestaña privada una vez: Toca el icono %s. - Abre siempre pestañas privadas: Actualiza los ajustes de tu navegador. + Abre siempre pestañas privadas: Actualiza los ajustes de tu navegador. - Abrir ajustes + Abrir ajustes Tu privacidad https://www.example.com - La dirección web debe contener \“https://\“ o \“http://\“ + La dirección web debe contener \“https://\“ o \“http://\“ Se requiere nombre de servidor válido @@ -1914,8 +1924,8 @@ Cancelar - - + + Pestañas inactivas Las pestañas están disponibles aquí durante %s. Después de ese tiempo, las pestañas se cerrarán automáticamente. @@ -1939,4 +1949,4 @@ Cerrar - + From cc626070d04816abe23e75dab8b9c9d9b64c7d4f Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:49 +0000 Subject: [PATCH 434/517] Strings - app/src/main/res/values-es-rAR/strings.xml --- app/src/main/res/values-es-rAR/strings.xml | 192 +++++++++++++++++---- 1 file changed, 159 insertions(+), 33 deletions(-) diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml index 3a487309c3..df399ab5ec 100644 --- a/app/src/main/res/values-es-rAR/strings.xml +++ b/app/src/main/res/values-es-rAR/strings.xml @@ -24,7 +24,7 @@ 1 pestaña abierta. Tocá para cambiar de pestaña. - %1$s pestañas abiertas. Tocá para cambiar de pestaña. + %1$s pestañas abiertas. Tocá para cambiar de pestaña. Se seleccionó %1$d @@ -53,7 +53,9 @@ Guardados recientemente - Recientemente marcados + Recientemente marcados + + Marcadores recientes Marcadores guardados recientemente @@ -107,6 +109,20 @@ Descartar + + Las pestañas que no se vieron por dos semanas se mueven acá. + + Desactivar en configuración + + + ¿Cerrar automáticamente después de un mes? + + Firefox puede cerrar pestañas que no abriste durante el último mes. + + Cerrar + + Activar cierre automático + Nueva pestaña @@ -114,7 +130,7 @@ Nueva pestaña privada - Sitios importantes + Sitios importantes @@ -122,6 +138,15 @@ Mostrar todas + + Botón mostrar todas las pestañas recientes + + La búsqueda de \"%1$s\" + + Sitios: %1$s + @@ -129,11 +154,17 @@ - Visitados recientemente + Visitados recientemente + + Búsquedas recientes Eliminar + + Mostrar el botón de todas las exploraciones pasadas + Pestañas abiertas @@ -146,7 +177,7 @@ Detener - Marcador + Marcador Editar marcador @@ -212,6 +243,8 @@ Personalizar inicio + + Personalizar inicio Pantalla de inicio @@ -260,6 +293,28 @@ Buscar directamente desde la barra de direcciones + + + Que hay de nuevo en Firefox + + Ahora es más fácil continuar donde lo dejaste. + + Página de inicio personalizada de Firefox + + Accedé a tus pestañas abiertas, marcadores y al historial de navegación. + + Pestañas claras y organizadas + + Eliminá el desorden de las pestañas con un diseño mejorado y cierre automático. + + Búsquedas recientes + + + Revisá tus últimas búsquedas desde tu página de inicio y pestañas. + + + Tu página de inicio de Firefox personalizada hace que ahora sea más fácil continuar donde lo habías dejado. Encontrá tus pestañas, marcadores y resultados de búsqueda recientes. + Abrir una nueva pestaña de Firefox @@ -338,7 +393,9 @@ Tema - Inicio + Inicio + + Página de inicio Gestos @@ -413,9 +470,15 @@ Guardados recientemente - Recientemente marcados - - Visitados recientemente + Recientemente marcados + + Marcadores recientes + + Visitados recientemente + + Búsquedas recientes Pocket @@ -532,7 +595,7 @@ Activar Sync - Escanear código de emparejamiento en la versión de escritorio de Firefox + Escanear código de emparejamiento en la versión de escritorio de Firefox Iniciar sesión @@ -562,7 +625,7 @@ Oscuro - Establecido por el ahorrador de batería + Establecido por ahorro de batería Usar el tema del dispositivo @@ -640,11 +703,15 @@ Lista Cuadrícula + + Buscar en grupos + + Agrupar sitios relacionados Cerrar pestañas - Manualmente + Nunca Después de un día @@ -652,15 +719,26 @@ Después de un mes - + + Cerrar automáticamente pestañas abiertas + + - Arrancar en inicio + Arrancar en inicio + + Pantalla de apertura - Después de cuatro horas + Después de cuatro horas + + Página de inicio - Siempre + Siempre + + Última pestaña - Nunca + Nunca + + Página de inicio después de cuatro horas de inactividad Cerrar manualmente @@ -670,6 +748,12 @@ Cerrar después de un mes + + + Mover pestañas antiguas a inactivas + + Las pestañas que no viste durante dos semanas se mueven a la sección inactiva. + Eliminar @@ -712,13 +796,13 @@ Guardar en colección - Seleccionar + Seleccionar Compartir todas las pestañas Pestañas recientemente cerradas - Recientemente cerrados + Recientemente cerrados Configuración de la cuenta @@ -795,7 +879,10 @@ Guardar - Otro + Otro + + + Otras pestañas @@ -841,10 +928,6 @@ Aquí no hay historial - - Eliminar descargas - - ¿Seguro que quieres eliminar las descargas? Descargas eliminadas @@ -885,7 +968,7 @@ Menú de marcadores - Editar marcador + Editar marcador Seleccionar carpeta @@ -1823,9 +1906,9 @@ %1$s a Habilitado]]> - Conexión segura + La conexión es segura - Conexión no segura + La conexión no es segura Conexión segura @@ -1920,8 +2003,10 @@ Listo, lo entendí. + + Sitios frecuentes más visitados - Mostrar los sitios frecuentes más visitados + Mostrar los sitios frecuentes más visitados Mostrar sitios más visitados @@ -1934,16 +2019,42 @@ Cancelar - - + + Pestañas inactivas + + Cerrar todas las pestañas inactivas - Las pestañas están disponibles aquí por %s. Después de ese tiempo, las pestañas se cerrarán automáticamente. + Las pestañas están disponibles aquí por %s. Después de ese tiempo, las pestañas se cerrarán automáticamente. - 30 días + 30 días - 1 semana + 1 semana + + + + ¿Cerrar automáticamente después de un mes? + + Firefox puede cerrar pestañas que no viste durante el último mes. + + ACTIVAR EL CIERRE AUTOMÁTICO + + + + Ayudanos a mejorar + + ¿Por qué desactivaste las pestañas inactivas? + + No me interesa la función + + El tiempo para pasarlas a inactivas es demasiado largo + + El tiempo para pasarlas a inactivas es demasiado corto + + Enviar + + Cerrar Configurar enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Firefox. @@ -1960,4 +2071,19 @@ Cerrar + + + Historias que te hacen reflexionar + + Historias que te hacen reflexionar + + Historias por tema + + Descubrir más + + Desarrollado por Pocket. + + Parte de la familia de Firefox. %s + + Conocer más From 5b6a7b9e5724e527259c0f9ee0905621af786493 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 435/517] Strings - app/src/main/res/values-es-rCL/strings.xml --- app/src/main/res/values-es-rCL/strings.xml | 184 +++++++++++++++++---- 1 file changed, 155 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-es-rCL/strings.xml b/app/src/main/res/values-es-rCL/strings.xml index 606b2e8ce3..dca1122d09 100644 --- a/app/src/main/res/values-es-rCL/strings.xml +++ b/app/src/main/res/values-es-rCL/strings.xml @@ -23,7 +23,7 @@ 1 pestaña abierta. Toca para cambiar de pestaña. - %1$s pestañas abiertas. Toca para cambiar de pestaña. + %1$s pestañas abiertas. Toca para cambiar de pestaña. %1$d seleccionadas @@ -52,7 +52,9 @@ Guardados recientemente - Añadidos recientemente + Añadidos recientemente + + Marcadores recientes Marcadores guardados recientemente @@ -105,6 +107,21 @@ Ocultar + + Las pestañas que no ha visto durante dos semanas se mueven aquí. + + Desactivar en ajustes + + + ¿Cerrar automáticamente después de un mes? + + + Firefox puede cerrar pestañas que no has visto durante el último mes. + + Cerrar + + Activar cierre automático + Nueva pestaña @@ -112,7 +129,7 @@ Nueva pestaña privada - Sitios frecuentes + Sitios frecuentes @@ -120,6 +137,15 @@ Mostrar todo + + Botón mostrar todas las pestañas recientes + + Tu búsqueda de \"%1$s\" + + Sitios: %1$s + @@ -127,11 +153,17 @@ - Visitados recientemente + Visitados recientemente + + Búsquedas recientes Eliminar + + Botón mostrar todas las exploraciones pasadas + Pestañas abiertas @@ -144,7 +176,7 @@ Detener - Añadir marcador + Añadir marcador Editar marcador @@ -208,6 +240,8 @@ Personalizar inicio + + Personalizar página de inicio Pantalla de inicio @@ -253,6 +287,27 @@ Buscar directamente desde la barra de direcciones + + + Que hay de nuevo en Firefox + + Ahora es más fácil retomar desde donde quedaste. + + Página de inicio personalizada de Firefox + + Salta a tus pestañas abiertas, marcadores e historial de navegación. + + Pestañas limpias y organizadas + + Elimina el desorden de pestañas con un diseño mejorado y pestañas con cierre automático. + + Búsquedas recientes + + Revise tus últimas búsquedas desde tu página de inicio y pestañas. + + + Tu página de inicio personalizada de Firefox ahora hace que sea mucho más fácil continuar desde donde quedaste. Encuentra tus pestañas, marcadores y resultados de búsqueda recientes. + Abrir una nueva pestaña de Firefox @@ -332,7 +387,9 @@ Tema - Inicio + Inicio + + Página de inicio Gestos @@ -406,9 +463,15 @@ Guardados recientemente - Añadidos recientemente - - Visitados recientemente + Añadidos recientemente + + Marcadores recientes + + Visitados recientemente + + Búsquedas recientes Pocket @@ -520,7 +583,7 @@ Activar la sincronización - Escanea el código de emparejamiento en Firefox para escritorio + Escanea el código de emparejamiento en Firefox para escritorio Conectarse @@ -626,6 +689,10 @@ Lista Cuadrícula + + Buscar en grupos + + Agrupar sitios relacionados Cerrar pestañas @@ -637,15 +704,26 @@ Después de un mes - + + Cerrar automáticamente pestañas + + - Empezar en la pantalla de inicio + Empezar en la pantalla de inicio + + Pantalla de apertura - Tras cuatro horas + Tras cuatro horas + + Página de inicio - Siempre + Siempre + + Última pestaña - Nunca + Nunca + + Página de inicio después de cuatro horas de inactividad Cerrar manualmente @@ -655,6 +733,12 @@ Cerrar después de un mes + + + Mover pestañas antiguas a inactivas + + Las pestañas que no ha visto durante dos semanas se mueven a la sección de inactivas. + Eliminar @@ -697,13 +781,13 @@ Guardar en la colección - Seleccionar + Seleccionar Compartir todas las pestañas Pestañas cerradas recientemente - Cerrados recientemente + Cerrados recientemente Ajustes de la cuenta @@ -777,7 +861,10 @@ Guardar - Otro + Otro + + + Otras pestañas @@ -823,10 +910,6 @@ Sin historial aquí - - Eliminar descargas - - ¿De verdad quieres limpiar tus descargas? Descargas removidas @@ -867,7 +950,7 @@ Menú de marcadores - Editar marcador + Editar marcador Seleccionar carpeta @@ -1893,8 +1976,10 @@ Ok, ¡ya caché! + + Sitios frecuentes más visitados - Mostrar los sitios frecuentes más visitados + Mostrar los sitios frecuentes más visitados Mostrar los sitios más visitados @@ -1907,15 +1992,41 @@ Cancelar - - + + Pestañas inactivas + + Cerrar todas las pestañas inactivas - Las pestañas están disponibles aquí por %s. Después de ese tiempo, las pestañas se cerrarán automáticamente. + Las pestañas están disponibles aquí por %s. Después de ese tiempo, las pestañas se cerrarán automáticamente. - 30 días + 30 días - 1 semana + 1 semana + + + + ¿Cerrar automáticamente después de un mes? + + Firefox puede cerrar pestañas que no has visto durante el último mes. + + ACTIVAR CIERRE AUTOMÁTICO + + + + Por favor, ayúdanos a mejorar + + ¿Por qué desactivaste pestañas inactivas? + + No me interesa la funcionalidad + + El tiempo para la inactividad es demasiado largo + + El tiempo para la inactividad es demasiado corto + + Enviar + + Cerrar Configura enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Firefox. @@ -1932,4 +2043,19 @@ Cerrar + + + Historias que provocan reflexión + + Historias que provocan reflexión + + Historias por tema + + Descubrir más + + Impulsado por Pocket. + + Parte de la familia Firefox. %s + + Aprender más From 1ab7e29e7e8729a9d256f063a349b6ac0e8913a4 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 436/517] Strings - app/src/main/res/values-es-rES/strings.xml --- app/src/main/res/values-es-rES/strings.xml | 185 +++++++++++++++++---- 1 file changed, 156 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 6254ceefab..db1a3ab9ec 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -26,7 +26,7 @@ 1 pestaña abierta. Toca para cambiar de pestaña. - %1$s pestañas abiertas. Toca para cambiar de pestaña. + %1$s pestañas abiertas. Toca para cambiar de pestaña. %1$d seleccionadas @@ -56,7 +56,9 @@ Guardado recientemente - Añadidos recientemente + Añadidos recientemente + + Marcadores recientes Marcadores guardados recientemente @@ -109,6 +111,21 @@ Descartar + + Aquí se mueven las pestañas que no has visto durante dos semanas. + + + Desactivar en los ajustes + + + ¿Cerrar automáticamente después de un mes? + + Firefox puede cerrar pestañas que no has visto durante el último mes. + + Cerrar + + Activar el cierre automático + Nueva pestaña @@ -116,7 +133,7 @@ Nueva pestaña privada - Sitios favoritos + Sitios favoritos @@ -124,6 +141,15 @@ Mostrar todas + + Mostrar el botón de todas las pestañas recientes + + Tu búsqueda por \"%1$s\" + + Sitios: %1$s + @@ -131,11 +157,17 @@ - Visitados recientemente + Visitados recientemente + + Búsquedas recientes Eliminar + + Mostrar el botón de todas las exploraciones anteriores + Pestañas abiertas @@ -148,7 +180,7 @@ Detener - Marcador + Marcador Editar marcador @@ -214,6 +246,8 @@ Personalizar inicio + + Personalizar la página de inicio Pantalla de inicio @@ -260,6 +294,27 @@ Buscar directamente desde la barra de direcciones + + + Novedades de Firefox + + Ahora es más fácil continuar donde lo habías dejado. + + Página de inicio de Firefox personalizada + + Accede a tus pestañas abiertas, marcadores y al historial de navegación. + + Pestañas claras y organizadas + + Elimina el desorden de pestañas con un diseño mejorado y con cierre automático. + + Búsquedas recientes + + Revisa tus últimas búsquedas desde tu página de inicio y pestañas. + + + Tu página de inicio de Firefox personalizada hace que ahora sea más fácil continuar donde lo habías dejado. Encuentra tus pestañas, marcadores y resultados de búsqueda recientes. + Abrir una nueva pestaña de Firefox @@ -340,7 +395,9 @@ Tema - Inicio + Inicio + + Página de inicio Gestos @@ -415,9 +472,15 @@ Guardados recientemente - Añadidos recientemente - - Visitados recientemente + Añadidos recientemente + + Marcadores recientes + + Visitados recientemente + + Búsquedas recientes Pocket @@ -533,7 +596,7 @@ Activar Sync - Escanear código de emparejamiento en la versión de escritorio de Firefox + Escanear código de emparejamiento en la versión de escritorio de Firefox Iniciar sesión @@ -639,6 +702,10 @@ Lista Cuadrícula + + Buscar en grupos + + Agrupar sitios relacionados Cerrar pestañas @@ -650,15 +717,26 @@ Después de un mes - + + Cerrar automáticamente pestañas abiertas + + - Empezar desde el inicio + Empezar desde el inicio + + Pantalla de apertura - Tras cuatro horas + Tras cuatro horas + + Página de inicio - Siempre + Siempre + + Última pestaña - Nunca + Nunca + + Página de inicio después de cuatro horas de inactividad Cerrar manualmente @@ -668,6 +746,12 @@ Cerrar después de un mes + + + Mover las pestañas antiguas a inactivas + + Las pestañas que no has visto durante dos semanas se mueven a la sección inactiva. + Eliminar @@ -709,13 +793,13 @@ Guardar en la colección - Seleccionar + Seleccionar Compartir todas las pestañas Pestañas cerradas recientemente - Cerradas recientemente + Cerradas recientemente Ajustes de la cuenta @@ -790,7 +874,10 @@ Guardar - Otro + Otro + + + Otras pestañas @@ -836,10 +923,6 @@ No hay ningún historial - - Eliminar descargas - - ¿Seguro que quieres eliminar tus descargas? Descargas eliminadas @@ -881,7 +964,7 @@ Menú del marcador - Editar marcador + Editar marcador Seleccionar carpeta @@ -1937,8 +2020,10 @@ Vale, entendido + + Sitios favoritos más visitados - Mostrar los sitios favoritos más visitados + Mostrar los sitios favoritos más visitados Mostrar los sitios más visitados @@ -1951,15 +2036,42 @@ Cancelar - - + + Pestañas inactivas + + Cerrar todas las pestañas inactivas - Las pestañas están disponibles aquí durante %s. Después de ese tiempo, las pestañas se cerrarán automáticamente. + Las pestañas están disponibles aquí durante %s. Después de ese tiempo, las pestañas se cerrarán automáticamente. - 30 días + 30 días - 1 semana + 1 semana + + + + ¿Cerrar automáticamente después de un mes? + + Firefox puede cerrar pestañas que no has visto durante el último mes. + + ACTIVAR EL CIERRE AUTOMÁTICO + + + + Ayúdanos a mejorar + + ¿Por qué has desactivado las pestañas inactivas? + + No me interesa la función + + + El tiempo para pasarlas a inactivas es demasiado largo + + El tiempo para pasarlas a inactivas es demasiado corto + + Enviar + + Cerrar Configura enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Firefox. @@ -1976,4 +2088,19 @@ Cerrar + + + Historias que te hacen reflexionar + + Historias que te hacen reflexionar + + Historias por tema + + Descubrir más + + Desarrollado por Pocket. + + Parte de la familia Firefox. %s + + Saber más From fffdd3673ccf5d2c61ac7ced1d83e585847fe76a Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 437/517] Strings - app/src/main/res/values-es-rMX/strings.xml --- app/src/main/res/values-es-rMX/strings.xml | 169 ++++++++++++++++----- 1 file changed, 129 insertions(+), 40 deletions(-) diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index 5861ba1818..317f92ff89 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -23,7 +23,7 @@ 1 pestaña abierta. Tocar para cambiar de pestaña. - %1$s pestañas abiertas. Tocar para cambiar de pestaña. + %1$s pestañas abiertas. Tocar para cambiar de pestaña. %1$d seleccionadas @@ -50,7 +50,11 @@ - Guardado recientemente + Guardado recientemente + + Marcados recientemente + + Marcadores recientes Marcadores recién guardados @@ -101,6 +105,14 @@ Descartar + + Las pestañas que no has visto durante dos semanas se mueven aquí. + + Desactivar en los ajustes + + + Cerrar + Nueva pestaña @@ -108,7 +120,7 @@ Nueva pestaña privada - Sitios frecuentes + Sitios frecuentes @@ -116,10 +128,25 @@ Mostrar todo + + Sitios: %1$s + - Exploraciones pasadas + Exploraciones pasadas + + + Visitados recientemente + + Búsquedas recientes + + + Eliminar @@ -133,7 +160,7 @@ Detener - Marcador + Marcador Editar marcador @@ -243,6 +270,12 @@ Buscar directamente desde la barra de direcciones + + + Novedades en Firefox + + Búsquedas recientes + Abrir una nueva pestaña de Firefox @@ -322,7 +355,7 @@ Tema - Inicio + Inicio Gestos @@ -390,6 +423,21 @@ Colección de complementos modificada. Cerrando la aplicación para aplicar los cambios… + + + Saltar hacia atrás + + Guardados recientemente + + Visitados recientemente + + Búsquedas recientes + + + Pocket + Complemento no compatible @@ -497,7 +545,7 @@ Activar Sync - Escanear código de emparejamiento en la versión de escritorio de Firefox + Escanear código de emparejamiento en la versión de escritorio de Firefox Iniciar sesión @@ -574,6 +622,13 @@ Cerrar + + %d sitio + + %d sitios + Pestañas recientemente cerradas @@ -596,6 +651,8 @@ Lista Cuadrícula + + Agrupar sitios relacionados Cerrar pestañas @@ -607,15 +664,17 @@ Después de un mes - + - Comenzar en la página de inicio + Comenzar en la página de inicio - Después de cuatro horas + Después de cuatro horas - Siempre + Siempre + + Última pestaña - Nunca + Nunca Cerrar manualmente @@ -666,13 +725,13 @@ Guardar en colección - Seleccionar + Seleccionar Compartir todas las pestañas Pestañas recientemente cerradas - Cerradas recientemente + Cerradas recientemente Ajustes de la cuenta @@ -744,6 +803,11 @@ Guardar + + Otro + + Otras pestañas + Eliminar historial @@ -756,15 +820,15 @@ Limpiar - Copiar + Copiar - Compartir + Compartir - Abrir en una nueva pestaña + Abrir en una nueva pestaña - Abrir en una pestaña privada + Abrir en una pestaña privada - Eliminar + Eliminar %1$d seleccionado(s) @@ -787,10 +851,6 @@ No hay ningún historial - - Eliminar descargas - - ¿Seguro que quieres eliminar tus descargas? Descargas removidas @@ -830,7 +890,7 @@ Menú de marcadores - Editar marcador + Editar marcador Seleccionar carpeta @@ -1236,12 +1296,12 @@ ¿Ya tienes una cuenta? - Ver las novedades + Ver las novedades - ¿Tienes preguntas sobre el rediseño de %s? ¿Quieres saber qué ha cambiado? + ¿Tienes preguntas sobre el rediseño de %s? ¿Quieres saber qué ha cambiado? - Obtén respuestas aquí + Obtén respuestas aquí Sincronizar Firefox entre dispositivos @@ -1282,15 +1342,15 @@ Coloca la barra de herramientas al alcance de la mano. Mantenla en la parte inferior o muévela hacia arriba. - Navegar de forma privada + Navegar de forma privada - Abre una pestaña privada una vez: Toca el ícono %s. + Abre una pestaña privada una vez: Toca el ícono %s. - Abre siempre pestañas privadas: Actualiza los ajustes de tu navegador. + Abre siempre pestañas privadas: Actualiza los ajustes de tu navegador. - Abrir ajustes + Abrir ajustes Tu privacidad Limpia las cookies creadas por redirecciones a sitios web de seguimiento conocidos. + + Saber más + Ayuda @@ -1522,6 +1585,9 @@ Completar los nombres de usuario y contraseñas en otras aplicaciones de tu dispositivo. + + Agregar inicio de sesión + Sincronizar inicios de sesión @@ -1750,10 +1816,12 @@ %1$s a activado]]> - Conexión segura + Conexión segura + + Conexión segura - Conexión insegura + Conexión insegura ¿Seguro que quieres borrar todos los permisos de todos los sitios? @@ -1793,7 +1861,7 @@ Descartar cambios Editar - + Se requiere contraseña Búsqueda por voz @@ -1802,6 +1870,9 @@ Ya existe un inicio de sesión con ese nombre de usuario + + https://www.example.com + Conectar otro dispositivo. @@ -1828,7 +1899,7 @@ Vale, entendido - Mostrar los sitios más visitados + Mostrar los sitios más visitados Nombre @@ -1839,15 +1910,23 @@ Cancelar - - + + Pestañas inactivas - Las pestañas están disponibles aquí durante %s. Después de ese tiempo, las pestañas se cerrarán automáticamente. + Las pestañas están disponibles aquí durante %s. Después de ese tiempo, las pestañas se cerrarán automáticamente. - 30 días + 30 días - 1 semana + 1 semana + + + No me interesa la función + + Enviar + + + Cerrar Configura enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Firefox. @@ -1864,4 +1943,14 @@ Cerrar + + Historias por tema + + Descubrir más + + Desarrollado por Pocket. + + Parte de la familia Firefox. %s + + Saber más From 055cbe91bd703fd3f05cb0f12919bd61bb706cf9 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 438/517] Strings - app/src/main/res/values-eu/strings.xml --- app/src/main/res/values-eu/strings.xml | 268 +++++++++++++++++++++---- 1 file changed, 228 insertions(+), 40 deletions(-) diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 906105adf6..bdc6732667 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -24,7 +24,7 @@ Irekitako fitxa bat. Sakatu fitxaz aldatzeko. - Irekitako %1$s fitxa. Sakatu fitxaz aldatzeko. + Irekitako %1$s fitxa. Sakatu fitxaz aldatzeko. %1$d hautatuta @@ -52,7 +52,11 @@ - Gordetako azkenak + Gordetako azkenak + + Azken laster-markak + + Azken laster-markak Gordetako azken laster-markak @@ -106,6 +110,20 @@ Baztertu + + Azken bi asteetan ikusi ez dituzun fitxak hona eramaten dira. + + Desaktibatu ezarpenetan + + + Automatikoki itxi hilabete ondoren? + + Azken hilabetean ikusi ez dituzun fitxak itxi ditzake Firefoxek. + + Itxi + + Aktibatu automatikoki ixtea + Fitxa berria @@ -113,7 +131,7 @@ Fitxa pribatu berria - Gune erabilienak + Gune erabilienak @@ -121,10 +139,31 @@ Erakutsi denak + + Azken fitxa guztiak erakusteko botoia + + Zure bilaketa: \"%1$s\" + + Guneak: %1$s + - Iraganeko esplorazioak + Iraganeko esplorazioak + + + Bisitatutako azkenak + + Azken bilaketak + + Kendu + + Iraganeko esplorazio guztiak erakusteko botoia @@ -138,7 +177,7 @@ Gelditu - Egin laster-marka + Egin laster-marka Editatu laster-marka @@ -203,6 +242,10 @@ Editatu + + Pertsonalizatu hasiera + + Pertsonalizatu hasiera-orria Hasiera-pantaila @@ -249,6 +292,27 @@ Bilatu helbide-barratik zuzenean + + + Firefoxen berrikuntzak + + Errazagoa da orain utzi zenuen tokitik jarraitzea. + + Firefoxen hasiera-orri pertsonalizatua + + Saltatu irekitako zure fitxa, laster-marka eta nabigazio-historiara. + + Fitxa txukun eta antolatuak + + Kendu soberan dagoena hobetutako diseinu eta automatikoki ixten diren fitxekin. + + Azken bilaketak + + Itzuli zure azken bilaketetara zure hasiera-orri eta fitxetatik. + + + Errazagoa da utzitako lekutik jarraitzea Firefoxen pertsonalizatutako hasiera-orriarekin. Aurkitu zure azken fitxak, laster-markak eta bilaketa-emaitzak. + Ireki Firefoxen fitxa berri bat @@ -327,7 +391,9 @@ Itxura - Hasierako pantaila + Hasierako pantaila + + Hasiera-orria Keinuak @@ -396,6 +462,24 @@ Gehigarrien bilduma aldatu egin da. Aplikaziotik irteten aldaketak aplikatzeko… + + + Itzuli zeudenera + + Gordetako azkenak + + Azken laster-markak + + Azken laster-markak + + Bisitatutako azkenak + + Azken bilaketak + + Pocket + Gehigarria ez da onartzen @@ -508,7 +592,7 @@ Gaitu sinkronizazioa - Eskaneatu parekatzeko kodea mahaigaineko Firefoxen + Eskaneatu parekatzeko kodea mahaigaineko Firefoxen Hasi saioa @@ -586,6 +670,13 @@ Itxi + + Gune bat + + %d gune + Itxitako azken fitxak @@ -608,6 +699,10 @@ Zerrenda Sareta + + Bilaketa-taldeak + + Taldekatu antzerako guneak Itxi fitxak @@ -619,15 +714,26 @@ Hilabete ondoren - + + Automatikoki itxi irekitako fitxak + + - Hasi hasierako pantailan + Hasi hasierako pantailan + + Irekitze-pantaila - Lau ordu ondoren + Lau ordu ondoren + + Hasiera-orria - Beti + Beti + + Azken fitxa - Inoiz ez + Inoiz ez + + Hasiera-orria, lau orduz inaktibo egon ondoren Itxi eskuz @@ -637,6 +743,12 @@ Itxi hilabete ondoren + + + Eraman fitxa zaharrak inaktiboetara + + Azken bi asteetan ikusi ez dituzun fitxak inaktiboen atalera eramaten dira. + Kendu @@ -677,13 +789,13 @@ Gorde bilduman - Hautatu + Hautatu Partekatu fitxa guztiak Itxitako azken fitxak - Itxitako azkenak + Itxitako azkenak Kontu-ezarpenak @@ -757,6 +869,11 @@ Gorde + + Bestelakoak + + Beste fitxak + Ezabatu historia @@ -769,15 +886,15 @@ Garbitu - Kopiatu + Kopiatu - Partekatu + Partekatu - Ireki fitxa berrian + Ireki fitxa berrian - Ireki fitxa pribatuan + Ireki fitxa pribatuan - Ezabatu + Ezabatu %1$d hautatuta @@ -800,10 +917,6 @@ Historiarik ez hemen - - Ezabatu deskargak - - Ziur zaude zure deskargak garbitu nahi dituzula? Deskargak kenduta @@ -845,7 +958,7 @@ Laster-marken menua - Editatu laster-marka + Editatu laster-marka Hautatu karpeta @@ -1253,12 +1366,12 @@ Dagoeneko baduzu kontu bat? - Ikusi nobedadeak + Ikusi nobedadeak - Birdiseinatutako %s(r)i buruzko galderak dituzu? Zer aldatu den jakin nahi duzu? + Birdiseinatutako %s(r)i buruzko galderak dituzu? Zer aldatu den jakin nahi duzu? - Eskuratu erantzunak hemen + Eskuratu erantzunak hemen Sinkronizatu Firefox gailuen artean @@ -1299,14 +1412,14 @@ Izan tresna-barra esku-eskura. Manten ezazu behean edo eraman ezazu gora. - Nabigatu modu pribatuan + Nabigatu modu pribatuan - Ireki fitxa pribatua behin: sakatu %s ikonoa. + Ireki fitxa pribatua behin: sakatu %s ikonoa. - Ireki fitxa pribatuak aldiro: eguneratu zure nabigatze pribatuko ezarpenak. + Ireki fitxa pribatuak aldiro: eguneratu zure nabigatze pribatuko ezarpenak. - Ireki ezarpenak + Ireki ezarpenak Zure pribatutasuna Webgune jakinetara egindako birbideraketek ezarritako jarraipen-cookieak garbitzen ditu. + + Behean markatutako zenbait jarraipen-elementu erdizka desblokeatu dira orri honetan beraiekin elkarreragin duzulako *. + + Argibide gehiago + Laguntza @@ -1538,6 +1657,9 @@ Bete erabiltzaile-izen eta pasahitzak zure gailuko beste aplikazioetan. + + Gehitu saio-hasiera + Sinkronizatu saio-hasierak @@ -1600,6 +1722,8 @@ Kopiatu erabiltzaile-izena Garbitu erabiltzaile-izena + + Garbitu ostalaria Kopiatu gunea @@ -1768,9 +1892,13 @@ %1$s piztuta egon dadin]]> - Konexio segurua + Konexioa segurua da - Konexio ez-segurua + Konexioa ez da segurua + + Konexio segurua + + Konexio ez-segurua Ziur zaude gune guztietako baimen guztiak garbitu nahi dituzula? @@ -1810,8 +1938,14 @@ Baztertu aldaketak Editatu - + + Gehitu saio-hasiera berria + Pasahitza behar da + + Erabiltzaile-izena behar da + + Ostalari-izena behar da Ahots bidezko bilaketa @@ -1820,6 +1954,15 @@ Erabiltzaile-izen hori badago lehendik ere + + https://www.adibidea.eus + + Web helbideak "https://" edo "http://" izan behar du + + Web helbideak "https://" edo "http://" izan behar du + + Baliozko ostalari-izena behar da + Konektatu beste gailu bat. @@ -1846,8 +1989,12 @@ Ados, ulertuta + + Gehien bisitatutako guneak + + Erakutsi gehien bisitatutako guneak - Erakutsi gehien bisitatutako guneak + Erakutsi gehien bisitatutako guneak Izena @@ -1858,15 +2005,41 @@ Utzi - - + + Fitxa inaktiboak + + Itxi fitxa inaktibo guztiak - Fitxak %s egongo dira erabilgarri. Denbora horren ondoren, automatikoki itxiko dira. + Fitxak %s egongo dira erabilgarri. Denbora horren ondoren, automatikoki itxiko dira. - 30 egunez + 30 egunez - astebetez + astebetez + + + + Automatikoki itxi hilabete ondoren? + + Azken hilabetean ikusi ez dituzun fitxak itxi ditzake Firefoxek. + + AKTIBATU AUTOMATIKOKI IXTEA + + + + Lagun iezaguzu hobetzen + + Zergatik desgaitu dituzu fitxa inaktiboak? + + Ez daukat eginbide honen interesik + + Desaktibatzeko denbora luzeegia da + + Desaktibatzeko denbora laburregia da + + Bidali + + Itxi Ireki webgune, posta elektroniko eta mezuetako loturak Firefoxen automatikoki. @@ -1883,4 +2056,19 @@ Itxi + + + Hausnartzeko moduko istorioak + + Hausnartzeko moduko istorioak + + Gaiaren araberako istorioak + + Aurkitu gehiago + + Pocket-ek hornitua. + + Firefoxen familiakoa. %s + + Argibide gehiago From c68b8752400b3d01556288ca38868ba6665c5166 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 439/517] Strings - app/src/main/res/values-fi/strings.xml --- app/src/main/res/values-fi/strings.xml | 184 +++++++++++++++++++++---- 1 file changed, 155 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index d967fe8e23..1a84a47e95 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -25,7 +25,7 @@ 1 avoin välilehti. Napauta vaihtaaksesi. - %1$s avointa välilehteä. Napauta vaihtaaksesi. + %1$s avointa välilehteä. Napauta vaihtaaksesi. %1$d valittu @@ -54,7 +54,9 @@ Äskettäin tallennettu - Viimeksi kirjanmerkityt + Viimeksi kirjanmerkityt + + Viimeisimmät kirjanmerkit Äskettäin tallennetut kirjanmerkit @@ -107,6 +109,20 @@ Hylkää + + Välilehdet, joita et ole katsonut kahteen viikkoon, siirretään tänne. + + Sammuta asetuksista + + + Suljetaanko automaattisesti kuukauden jälkeen? + + Firefox voi sulkea välilehdet, joita et ole katsonut viimeisen kuukauden aikana. + + Sulje + + Ota automaattinen sulkeminen käyttöön + Uusi välilehti @@ -114,7 +130,7 @@ Uusi yksityinen välilehti - Ykkössivustot + Ykkössivustot @@ -122,6 +138,15 @@ Näytä kaikki + + Näytä "Kaikki viimeisimmät välilehdet"-painike + + Hakusi ehdoilla \"%1$s\" + + Sivustot: %1$s + @@ -129,11 +154,17 @@ - Äskettäin vierailtu + Äskettäin vierailtu + + Viimeisimmät haut Poista + + Näytä "Kaikki aiemmat tutkimukset"-painike + Avoimet välilehdet @@ -146,7 +177,7 @@ Pysäytä - Lisää kirjanmerkki + Lisää kirjanmerkki Muokkaa kirjanmerkkiä @@ -213,6 +244,8 @@ Mukauta kotia + + Mukauta kotisivua Aloitusnäkymä @@ -259,6 +292,27 @@ Hae suoraan osoitepalkista + + + Mitä uutta Firefoxissa + + Nyt on aiempaa helpompaa jatkaa siitä, mihin jäit. + + Personoitu Firefox-kotisivu + + Siirry avoimiin välilehtiisi, kirjanmerkkeihisi ja selaushistoriaasi. + + Siistit, järjestetyt välilehdet + + Estä välilehtien sotku parannetulla asettelulla ja automaattisesti sulkeutuvilla välilehdillä. + + Viimeisimmät haut + + Palaa viimeisimpiin hakuihin etusivultasi ja välilehdiltäsi. + + + Sinulle mukautetun Firefox-kotisivun avulla on helpompi jatkaa siitä, mihin jäit. Löydä viimeisimmät välilehdet, kirjanmerkit ja hakutulokset. + Avaa uusi Firefox-välilehti @@ -339,7 +393,9 @@ Teema - Koti + Koti + + Kotisivu Eleet @@ -414,9 +470,15 @@ Äskettäin tallennettu - Viimeksi kirjanmerkityt - - Äskettäin vierailtu + Viimeksi kirjanmerkityt + + Viimeisimmät kirjanmerkit + + Äskettäin vierailtu + + Viimeisimmät haut Pocket @@ -530,7 +592,7 @@ Ota Sync käyttöön - Skannaa parituskoodi Firefoxin työpöytäversiossa + Skannaa parituskoodi Firefoxin työpöytäversiossa Kirjaudu sisän @@ -636,6 +698,10 @@ Lista Ruudukko + + Hakuryhmät + + Ryhmitä toisiinsa liittyvät sivustot yhteen Sulje välilehdet @@ -648,15 +714,26 @@ Yhden kuukauden jälkeen - + + Sulje automaattisesti avoimet välilehdet + + - Käynnistä aloitusnäkymästä + Käynnistä aloitusnäkymästä + + Avautuva näkymä - Neljän tunnin jälkeen + Neljän tunnin jälkeen + + Kotisivu - Aina + Aina + + Viimeisin välilehti - Ei koskaan + Ei koskaan + + Kotisivu neljän tunnin käyttämättömyyden jälkeen Sulje manuaalisesti @@ -666,6 +743,12 @@ Sulje kuukauden kuluttua + + + Siirrä vanhat välilehdet passiivisiksi + + Välilehdet, joita et ole katsonut kahteen viikkoon, siirretään passiivisten osioon. + Poista @@ -708,13 +791,13 @@ Tallenna kokoelmaan - Valitse + Valitse Jaa kaikki välilehdet Viimeksi suljetut välilehdet - Viimeksi suljettu + Viimeksi suljettu Tilin asetukset @@ -789,7 +872,10 @@ Tallenna - Muut + Muut + + + Muut välilehdet @@ -835,10 +921,6 @@ Ei historiaa täällä - - Poista lataukset - - Haluatko varmasti tyhjentää lataukset? Lataukset poistettu @@ -879,7 +961,7 @@ Kirjanmerkkivalikko - Muokkaa kirjanmerkkiä + Muokkaa kirjanmerkkiä Valitse kansio @@ -1915,8 +1997,10 @@ Selvä + + Vierailluimmat ykkössivustot - Näytä vierailluimmat ykkössivustot + Näytä vierailluimmat ykkössivustot Näytä vierailluimmat sivustot @@ -1929,15 +2013,42 @@ Peruuta - - + + Passiiviset välilehdet + + Sulje kaikki passiiviset välilehdet - Välilehdet pysyvät täällä %s. Sen jälkeen välilehdet suljetaan automattisesti. + Välilehdet pysyvät täällä %s. Sen jälkeen välilehdet suljetaan automattisesti. - 30 päivää + 30 päivää - 1 viikon + 1 viikon + + + + Suljetaanko automaattisesti kuukauden kuluttua? + + Firefox voi sulkea välilehdet, joita et ole katsonut viimeisen kuukauden aikana. + + KÄYTÄ AUTOMAATTISTA SULKEMISTA + + + + Auta meitä parantamaan + + Miksi poistit passiiviset välilehdet käytöstä? + + Ominaisuus ei kiinnosta + + Aika passiiviseksi asettamiseksi on liian pitkä + + + Aika passiiviseksi asettamiseksi on liian lyhyt + + Lähetä + + Sulje Aseta verkkosivustojen, sähköpostien ja viestien linkit avautumaan automaattisesti Firefoxissa. @@ -1954,4 +2065,19 @@ Sulje + + + Ajatuksia herättäviä tarinoita + + Ajatuksia herättäviä tarinoita + + Tarinoita aiheittain + + Löydä lisää + + Taustavoimana Pocket. + + Osa Firefox-perhettä. %s + + Lue lisää From f69012f91591778de4d882b0f25230eece7e4d52 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 440/517] Strings - app/src/main/res/values-fr/strings.xml --- app/src/main/res/values-fr/strings.xml | 178 +++++++++++++++++++++---- 1 file changed, 149 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ade59e495f..f816205056 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -26,7 +26,7 @@ 1 onglet ouvert. Appuyez pour changer d’onglet. - %1$s onglets ouverts. Appuyez pour changer d’onglet. + %1$s onglets ouverts. Appuyez pour changer d’onglet. %1$d sélectionnés @@ -55,7 +55,9 @@ Récemment enregistrés - Marqués récemment + Marqués récemment + + Marque-pages récents Marque-pages récemment enregistrés @@ -107,6 +109,20 @@ Ignorer + + Les onglets que vous n’avez pas consultés depuis deux semaines sont déplacés ici. + + Désactiver dans les paramètres + + + Fermeture automatique après un mois ? + + Firefox peut fermer les onglets que vous n’avez pas consultés au cours du mois dernier. + + Fermer + + Activer la fermeture automatique + Nouvel onglet @@ -114,7 +130,7 @@ Nouvel onglet nav. privée - Sites principaux + Sites principaux @@ -122,6 +138,16 @@ Tout afficher + + Afficher le bouton des onglets récents + + Votre recherche de « %1$s » + + + Sites : %1$s + @@ -129,11 +155,17 @@ - Visités récemment + Visités récemment + + Recherches récentes Supprimer + + Afficher le bouton des navigations passées + Onglets ouverts @@ -146,7 +178,7 @@ Arrêter - Marque-page + Marque-page Modifier le marque-page @@ -212,6 +244,8 @@ Personnaliser l’accueil + + Personnaliser la page d’accueil Écran d’accueil @@ -258,6 +292,28 @@ Recherchez directement depuis la barre d’adresse + + + Les nouveautés de Firefox + + Il est désormais plus facile de reprendre là où vous en étiez. + + Accueil personnalisé de Firefox + + Accédez à vos onglets ouverts, vos marque-pages et votre historique de navigation. + + Onglets nets et organisés + + Éliminez le fouillis d’onglets grâce à une présentation améliorée et des onglets qui se ferment automatiquement. + + Recherches récentes + + + Consultez vos dernières recherches depuis votre page d’accueil et vos onglets. + + + L’accueil personnalisé de Firefox permet désormais de reprendre plus facilement là où vous en étiez. Retrouvez vos onglets, marque-pages et résultats de recherche récents. + Ouvrir un nouvel onglet dans Firefox @@ -337,7 +393,9 @@ Thème - Accueil + Accueil + + Page d’accueil Mouvements @@ -412,9 +470,15 @@ Enregistrés récemment - Marqués récemment - - Visités récemment + Marqués récemment + + Marque-pages récents + + Visités récemment + + Recherches récentes Pocket @@ -530,7 +594,7 @@ Activer Sync - Scanner le code d’association depuis Firefox pour ordinateur + Scanner le code d’association depuis Firefox pour ordinateur Connexion @@ -637,6 +701,10 @@ Liste Grille + + Groupes de recherches + + Regrouper les sites liés Fermer les onglets @@ -648,15 +716,24 @@ Après un mois - + + Fermeture automatique des onglets ouverts + + - Démarrer par l’écran d’accueil + Démarrer par l’écran d’accueil - Après quatre heures + Après quatre heures + + Page d’accueil - Toujours + Toujours + + Dernier onglet - Jamais + Jamais + + Page d’accueil après quatre heures d’inactivité Fermeture manuelle @@ -666,6 +743,9 @@ Fermeture après un mois + + Les onglets que vous n’avez pas consultés depuis deux semaines sont déplacés vers la section inactive. + Supprimer @@ -708,13 +788,13 @@ Enregistrer dans une collection - Sélectionner + Sélectionner Partager tous les onglets Onglets récemment fermés - Récemment fermés + Récemment fermés Paramètres du compte @@ -789,7 +869,10 @@ Enregistrer - Autres + Autres + + + Autres onglets @@ -835,10 +918,6 @@ Pas d’historique - - Effacer la liste des téléchargements - - Voulez-vous vraiment effacer la liste de vos téléchargements ? Téléchargements supprimés @@ -880,7 +959,7 @@ Menu des marque-pages - Modifier le marque-page + Modifier le marque-page Sélectionner un dossier @@ -1930,8 +2009,10 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n J’ai compris + + Les sites les plus visités - Afficher les sites les plus visités + Afficher les sites les plus visités Afficher les sites les plus visités @@ -1944,15 +2025,43 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n Annuler - - + + Onglets inactifs + + Fermer tous les onglets inactifs - Les onglets sont disponibles ici pendant %s jours. Après ce délai, les onglets seront automatiquement fermés. + Les onglets sont disponibles ici pendant %s jours. Après ce délai, les onglets seront automatiquement fermés. - 30 jours + 30 jours - 1 semaine + 1 semaine + + + + Fermeture automatique après un mois ? + + Firefox peut fermer les onglets que vous n’avez pas consultés au cours du mois dernier. + + + ACTIVER LA FERMETURE AUTOMATIQUE + + + + Merci de nous aider à nous améliorer + + Pourquoi avez-vous désactivé les onglets inactifs ? + + + La fonctionnalité ne m’intéresse pas + + Le temps d’inactivité est trop long + + Le temps d’inactivité est trop court + + Envoyer + + Fermer Faites en sorte que les liens des sites web, des courriels et des messages s’ouvrent automatiquement dans Firefox. @@ -1969,4 +2078,15 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n Fermer + + + Des articles qui font réfléchir + + Des articles qui font réfléchir + + Articles par sujet + + En découvrir davantage + + En savoir plus From 171e63d9fac1c4dfe99602b5c94cf315b0bbe123 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 441/517] Strings - app/src/main/res/values-fy-rNL/strings.xml --- app/src/main/res/values-fy-rNL/strings.xml | 183 +++++++++++++++++---- 1 file changed, 154 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-fy-rNL/strings.xml b/app/src/main/res/values-fy-rNL/strings.xml index 52b8949ccf..60159235dd 100644 --- a/app/src/main/res/values-fy-rNL/strings.xml +++ b/app/src/main/res/values-fy-rNL/strings.xml @@ -24,7 +24,7 @@ 1 iepen ljepblêd. Tik om tusken ljepblêden te wikseljen. - %1$s iepen ljepblêden. Tik om tusken ljepblêden te wikseljen. + %1$s iepen ljepblêden. Tik om tusken ljepblêden te wikseljen. %1$d selektearre @@ -53,7 +53,9 @@ Koartlyn bewarre - Resint oanmakke blêdwizers + Resint oanmakke blêdwizers + + Resinte blêdwizers Resint bewarre blêdwizers @@ -108,6 +110,20 @@ Slute + + Ljepblêden dy\'t jo twa wiken net besjoen hawwe, wurde hjirhinne ferpleatst. + + Utskeakelje yn ynstellingen + + + Automatysk slute nei in moanne? + + Firefox kin ljepblêden dy\'t jo de ôfrûne moanne net besjoen hawwe slute. + + Slute + + Automatysk slute ynskeakelje + Nij ljepblêd @@ -115,7 +131,7 @@ Nij priveeljepblêd - Topwebsites + Topwebsites @@ -123,6 +139,15 @@ Alles toane + + Knop Alle resinte ljepblêden toane + + Jo sykopdracht nei ‘%1$s’ + + Websites: %1$s + @@ -130,11 +155,17 @@ - Koartlyn besocht + Koartlyn besocht + + Resinte sykopdrachten Fuortsmite + + Knop Alle eardere sykopdrachten toane + Iepen ljepblêden @@ -147,7 +178,7 @@ Stopje - Blêdwizer meitsje + Blêdwizer meitsje Blêdwizer bewurkje @@ -212,6 +243,8 @@ Startside oanpasse + + Startside oanpasse Startskerm @@ -258,6 +291,27 @@ Streekrjocht fan de adresbalke út sykje + + + Wat is der nij yn Firefox + + It is no ienfâldiger fierder te gean wêr\'t jo bleaun wiene. + + Personalisearre Firefox-startside + + Spring nei jo iepen ljepblêden, blêdwizers en navigaasjeskiednis. + + Kreaze, oardere ljepblêden + + Smyt ljepblêdrommel fuort mei in ferbettere opmaak en automatysk slutende ljepblêden. + + Resinte sykopdrachten + + Besjoch jo lêste sykopdrachten opnij fan jo startside en ljepblêden ôf. + + + Jo personalisearre Firefox-startside makket it no ienfâldiger om fierder te gean wêr\'t jo bleaun wiene. Fyn jo resinte ljepblêden, blêdwizers, en sykresultaten. + In nij Firefox-ljepblêd iepenje @@ -336,7 +390,9 @@ Tema - Startside + Startside + + Startside Bewegingen @@ -411,9 +467,15 @@ Koartlyn bewarre - Resint oanmakke blêdwizers - - Koartlyn besocht + Resint oanmakke blêdwizers + + Resinte blêdwizers + + Koartlyn besocht + + Resinte sykopdrachten Pocket @@ -524,7 +586,7 @@ Sync ynskeakelje - Keppelingskoade scanne yn desktop-Firefox + Keppelingskoade scanne yn desktop-Firefox Oanmelde @@ -629,6 +691,10 @@ List Roaster + + Groepen sykje + + Relatearre websites groepearje Ljepblêden slute @@ -640,15 +706,26 @@ Nei ien moanne - + + Iepen ljepblêden automatysk slute + + - Op startskerm begjinne + Op startskerm begjinne + + Iepeningsskerm - Nei fjouwer oer + Nei fjouwer oer + + Startside - Altyd + Altyd + + Lêste ljepblêd - Nea + Nea + + Startside nei fjouwer oeren ynaktiviteit Hânmjittich slute @@ -658,6 +735,12 @@ Nei in moanne slute + + + Alde ljepblêden nei ynaktyf ferpleatse + + Ljepblêden dy\'t jo twa wiken net besjoen hawwe, wurde ferpleatst nei de seksje Ynaktyf. + Fuortsmite @@ -698,13 +781,13 @@ Yn kolleksje bewarje - Selektearje + Selektearje Alle ljepblêden diele Koartlyn sluten ljepblêden - Koartlyn sluten + Koartlyn sluten Accountynstellingen @@ -778,7 +861,10 @@ Bewarje - Oars + Oars + + + Oare ljeplêden @@ -823,10 +909,6 @@ Gjin skiednis hjir - - Downloads wiskje - - Binne jo wis dat jo jo downloads wiskje wolle? Downloads fuortsmiten @@ -867,7 +949,7 @@ Blêdwizermenu - Blêdwizer bewurkje + Blêdwizer bewurkje Map selektearje @@ -1897,8 +1979,10 @@ OK, begrepen + + Meast besochte topwebsites - Meast besochte topwebsites toane + Meast besochte topwebsites toane Meast besochte websites toane @@ -1912,15 +1996,41 @@ Annulearje - - + + Ynaktive ljepblêden + + Alle ynaktive ljepblêden slute - Ljepblêden binne hjir %s beskikber. Dêrnei wurde ljepblêden automatysk sluten. + Ljepblêden binne hjir %s beskikber. Dêrnei wurde ljepblêden automatysk sluten. - 30 dagen + 30 dagen - 1 wike + 1 wike + + + + Automatysk slute nei in moanne? + + Firefox kin ljepblêden dy\'t jo de ôfrûne moanne net besjoen hawwe slute. + + AUTOMATYSK SLUTE YNSKEAKELJE + + + + Help ús te ferbetterjen + + Wêrom hawwe jo ynaktive ljepblêden útskeakele? + + Net ynteressearre yn de funksje + + It duorret te lang eardat ljepblêden ynaktyf wurde + + Ljepblêden wurde te fluch ynaktyf + + Ferstjoere + + Slute Keppelingen fan websites, e-mail en berjochten automatysk yn Firefox iepenje. @@ -1937,4 +2047,19 @@ Slute + + + Ferhalen dy\'t ta neitinken stimme + + Ferhalen dy\'t ta neitinken stimme + + Ferhalen op ûnderwerp + + Mear ûntdekke + + Mooglik makke troch Pocket. + + Underdiel fan de Firefox-famylje. %s + + Mear ynfo From 3d3469669d63e55e9cc4422977398df98fcdd4d7 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 442/517] Strings - app/src/main/res/values-gd/strings.xml --- app/src/main/res/values-gd/strings.xml | 96 +++++++++++++++++++------- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/app/src/main/res/values-gd/strings.xml b/app/src/main/res/values-gd/strings.xml index 62ee0a60b0..d48bdf34d7 100644 --- a/app/src/main/res/values-gd/strings.xml +++ b/app/src/main/res/values-gd/strings.xml @@ -1,5 +1,5 @@ - + %s prìobhaideach @@ -48,7 +48,11 @@ - Air a shàbhaladh o chionn goirid + Air a shàbhaladh o chionn goirid + + Comharran-lìn ùra + + Comharran-lìn ùra Comharran-lìn a shàbhail thu o chionn goirid @@ -101,6 +105,11 @@ Leig seachad + + Thèid tabaichean nach dug thu sùil orra fad cola-deug a ghluasad an-seo. + + Cuir dheth sna roghainnean + Taba ùr @@ -116,6 +125,31 @@ Seall na h-uile + + Am putan airson gach taba a bha fosgailte o chionn goirid a shealltainn + + Rinn thu lorg airson “%1$s” + + Làraichean: %1$s + + + + Na rùraich thu roimhe + + Air tadhal orra o chionn goirid + + Luirg o chionn goirid + + Thoir air falbh + + Am putan a sheallas na rùraich thu roimhe + Tabaichean fosgailte @@ -190,6 +224,10 @@ Deasaich + + Gnàthaich an dachaigh + + Gnàthaich an duilleag-dhachaigh Sgrìn dachaigh @@ -235,6 +273,14 @@ Lorg sa bhad on bhàr-sheòlaidh + + + Na tha ùr ann am Firefox + + Tha e nas fhasa a-nis leantainn ort far an robh thu roimhe. + + Duilleag-dhachaigh Firefox ghnàthaichte + Fosgail taba Firefox ùr @@ -727,15 +773,15 @@ Falamhaich - Dèan lethbhreac + Dèan lethbhreac - Co-roinn + Co-roinn - Fosgail ann an taba ùr + Fosgail ann an taba ùr - Fosgail ann an taba prìobhaideach + Fosgail ann an taba prìobhaideach - Sguab às + Sguab às %1$d air an taghadh @@ -1096,7 +1142,7 @@ Sguab às - Sguir dheth + Sguir dheth A’ fosgladh modh na làn-sgrìn @@ -1201,12 +1247,12 @@ A bheil cunntas agad mu thràth? - Faic na tha ùr + Faic na tha ùr - A bheil ceist agad mun dealbhadh ur aig %s? Airson fiosrachadh na tha air atharrachadh? + A bheil ceist agad mun dealbhadh ur aig %s? Airson fiosrachadh na tha air atharrachadh? - Faigh freagairtean an-seo + Faigh freagairtean an-seo Sioncronaich Firefox eadar uidheaman @@ -1247,14 +1293,14 @@ Cuir am bàr-inneal ri do làimh. Cùm e aig a’ bhonn no gluais e gun bhàrr. - Dèan brabhsadh prìobhaideach + Dèan brabhsadh prìobhaideach - Fosgail taba prìobhaideach aon turas: Thoir gnogag air an ìomhaigheag %s. + Fosgail taba prìobhaideach aon turas: Thoir gnogag air an ìomhaigheag %s. - Fosgail na tabaichean prìobhaideach gach turas: Ùraich roghainnean a’ bhrabhsaidh phrìobhaidich agad. + Fosgail na tabaichean prìobhaideach gach turas: Ùraich roghainnean a’ bhrabhsaidh phrìobhaidich agad. - Fosgail na roghainnean + Fosgail na roghainnean Do phrìobhaideachd - Dùin + Dùin Tòisich air brabhsadh @@ -1367,6 +1413,7 @@ Criopto-mhèinneadairean Lorgaichean-meur + Bacte Ceadaichte @@ -1391,7 +1438,7 @@ Cuiridh seo stad air luchdadh de shanasachd, videothan is susbaint eile on taobh a-muigh anns a bheil còd tracaidh. Dh’fhaoidte gum bi droch-bhuaidh aige seo air obrachadh de chuid a làraichean-lìn. - Gach turas a bhios an sgiath purpaidh, bhac %s tracaichean air làrach. Thoir gnogag airson barrachd fiosrachaidh. + Gach turas a bhios an sgiath purpaidh, bhac %s tracaichean air làrach. Thoir gnogag airson barrachd fiosrachaidh. Tha an dìon AIR air an làrach seo @@ -1473,7 +1520,8 @@ Na sàbhail idir - Fèin-lìonadh + Fèin-lìonadh + Sioncronaich na clàraidhean a-steach @@ -1653,7 +1701,7 @@ An t-sreang luirg a thèid a chleachdadh - Cuir “%s” an àite na ceist. Ball-eisimpleir:\nhttps://www.google.com/search?q=%s + Cuir “%s” an àite na ceist. Ball-eisimpleir:\nhttps://www.google.com/search?q=%s Barrachd fiosrachaidh @@ -1702,9 +1750,9 @@ %1$s AIR]]> - Ceangal tèarainte + Ceangal tèarainte - Ceangal nach eil tèarainte + Ceangal nach eil tèarainte A bheil thu cinnteach gu bheil thu airson na ceadan air gach làrach fhalamhachadh? @@ -1745,7 +1793,7 @@ Tilg air falbh na h-atharraichean Deasaich - + Tha feum air facal-faire Lorg-gutha @@ -1779,7 +1827,7 @@ Ceart, tha mi agaibh - Seall na làraichean air a do thadhail thu as trice + Seall na làraichean air a do thadhail thu as trice Ainm @@ -1805,4 +1853,4 @@ Dùin - + From ffe2d1350d272b2885c7152df8f34840822cffa0 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 443/517] Strings - app/src/main/res/values-gn/strings.xml --- app/src/main/res/values-gn/strings.xml | 186 +++++++++++++++++++++---- 1 file changed, 157 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-gn/strings.xml b/app/src/main/res/values-gn/strings.xml index 6faa270508..e22071decb 100644 --- a/app/src/main/res/values-gn/strings.xml +++ b/app/src/main/res/values-gn/strings.xml @@ -25,7 +25,7 @@ 1 embojuruja tendayke. Eikutu emoambue hag̃ua tendayke. - %1$s tendayke ijurujáva. Eikutu emoambue hag̃ua tendayke. + %1$s tendayke ijurujáva. Eikutu emoambue hag̃ua tendayke. %1$d poravopyre @@ -54,7 +54,9 @@ Oñeñongaturamóva - Oñembokurusu ramóva + Oñembokurusu ramóva + + Techaukaha ramoguáva Techaukaha oñeñongaturamóva @@ -108,6 +110,20 @@ Mboyke + + Ko’ápe oku’e umi tendayke ehecha’ỹa mokõi arapokõindýpe. + + Eipe’aite pytyvõhápe + + + ¿Omboty ijehegui peteĩ jasy rire? + + Firefox ombotykuaa tendayke ehecha’ỹakuri jasy ohasaramóvape. + + Mboty + + Emyandy mboty jehegui + Tendayke pyahu @@ -115,7 +131,7 @@ Tendayke pyahu ñemigua - Tenda jehayhuvéva + Tenda jehayhuvéva @@ -123,6 +139,15 @@ Techaukapaite + + Ehechauka votõ opaite tendayke ramoguápe + + Eheka \"%1$s\" rupive + + Tendakuéra: %1$s + @@ -130,11 +155,17 @@ - Ojeikeramohague + Ojeikeramohague + + Ojeheka ramóva Mboguete + + Ehechauka votõ opaite nde jeheka rapykuere + Embojuruja tendayke @@ -147,7 +178,7 @@ Pyta - Techaukaha + Techaukaha Techaukaha mbosako’i @@ -213,6 +244,8 @@ Emboava moñepyrũ + + Emboava kuatiarogue ñepyrũgua Mba’erechaha ñepyrũgua @@ -259,6 +292,28 @@ Eheka kundaharape renda guive + + + Firefox mba’epyahu + + Ko’ág̃a ndahasyive eku’ejeývo eheja haguégui. + + Firefox kuatiarogue ñepyrũgua mboavapyre + + Eike ne rendayke ijurujáva, techaukaha ha tembiasakue rembiasakue. + + Tendayke hesakã ha hendaporãva + + Emboguete tendayke oĩvaipáva oikoporãvéva ndive ha oñembotýva ha’eño. + + Ojeheka ramóva + + + Ehecha eheka ramovéva kuatiarogue ñepyrũ ha tendayke guive. + + + Nde kuatiarogue ñepyrũgua mboavapyre Firefox pegua nombohasyive eñepyrũjeývo eheja haguégui. Ejuhu ne rendayke, techaukaha ha ehekaramovéva. + Embojuruja Firefox rendayke pyahu @@ -338,7 +393,9 @@ Téma - Ñepyrũ + Ñepyrũ + + Kuatiarogue ñepyrũ Ñeha’ãnga @@ -412,9 +469,15 @@ Oñeñongaturamóva - Oñembokurusu ramóva - - Ojeikeramohague + Oñembokurusu ramóva + + Techaukaha ramoguáva + + Ojeikeramohague + + Ojeheka ramóva Pocket @@ -528,7 +591,7 @@ Emyandy Sync - Emoha’ãnga mbojojaha ayvu Firefox mohendaha reheguaitépe + Emoha’ãnga mbojojaha ayvu Firefox mohendaha reheguaitépe Eñepyrũ tembiapo @@ -636,6 +699,10 @@ Tysýi Kora’ieta + + Eheka atýhápe + + Ambyaty tenda ojueheguáva Emboty tendayke @@ -647,15 +714,26 @@ Peteĩ jasy rire - + + Omboty ijehegui tendayke ijurujáva + + - Eñepyrũ yvyetégui + Eñepyrũ yvyetégui + + Mba’erechaha ñepyrũha - Irundy aravo rire + Irundy aravo rire + + Kuatiarogue ñepyrũha - Tapiaite + Tapiaite + + Tendayke pahagua - Araka’eve + Araka’eve + + Kuatiarrogue ñepyrũha irundy aravo ojeheja rire Emboty nde pópe @@ -666,6 +744,13 @@ Emboty peteĩ jasy rire + + + Eguerova tendayke ituja ha ojepuru’ỹva + + + Umi tendayke ehecha’ỹa mokõi arapokõindýpe ohóma tenda ojepuru’ỹvape. + Mboguete @@ -710,13 +795,13 @@ Eñongatu atyhápe - Poravo + Poravo Emoherakuã opaite tendayke Tendayke oñemboty ramóva - Oñembotyramovéva + Oñembotyramovéva Mba’ete ñemboheko @@ -790,7 +875,10 @@ Ñongatu - Ambue + Ambue + + + Ambue tendayke @@ -837,10 +925,6 @@ Ndaipóri tembiasakue - - Emboguete ñemboguejy - - ¿Añetehápe emboguetese umi ñemboguejy? Ñemboguejy Mboguetepyre @@ -882,7 +966,7 @@ Techaukaha poravorã - Embosako’i techaukaha + Embosako’i techaukaha Ñongatuha jeporavo @@ -1934,8 +2018,10 @@ Oĩma, aikumby + + Tendakuéra ojeikeveha - Ehechauka tendakuéra ojeikeveha + Ehechauka tendakuéra ojeikeveha Ehechauka tenda ojeikeveha @@ -1948,15 +2034,42 @@ Heja - - + + Tendayke ojepuru’ỹva + + Embotypaite tendayke ojepuru’ỹva - Tendaykekuéra eipurukuaa ápe %s ndive. Uperire, tendaykekuéra oñembotýta ijehegui. + Tendaykekuéra eipurukuaa ápe %s ndive. Uperire, tendaykekuéra oñembotýta ijehegui. - 30 ára + 30 ára - 1 arapokõindy + 1 arapokõindy + + + + ¿Omboty ijehegui peteĩ jasy rire? + + + Firefox ombotykuaa tendayke ehecha’ỹakuri jasy ohasaramóvape. + + EMYANDY ÑEMBOTY IJEHEGUÍVA + + + + Orepytyvõ rojapoporãvévo + + ¿Mba’ére emboty tendayke ojepuru’ỹva? + + Ndaikuaaséi hembiapoite + + Arapa’ũ ohasa hag̃ua jepuru’ỹme ipukueterei + + Arapa’ũ ohasa hag̃ua jepuru’ỹme mbykyeterei + + Mondo + + Mboty Emboheko ñanduti renda juajuha, ñanduti veve ha ñe’ẽmondo ijuruja hag̃ua ijehegui Firefox-pe. @@ -1973,4 +2086,19 @@ Mboty + + + Tembiasakue nemyakãngetáva + + Tembiasakue nemyakãngetáva + + Tembiasakue téma rehegua + + Ejuhukuaave + + Pocket omoheñoipyre. + + Firefox reheguaite. %s + + Kuaave From 6269e51fd675403777af8fa2b0f6b7e70fe191e0 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 444/517] Strings - app/src/main/res/values-iw/strings.xml --- app/src/main/res/values-iw/strings.xml | 181 +++++++++++++++++++++---- 1 file changed, 152 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 43f96c28f6..cdabcface3 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -23,7 +23,7 @@ לשונית אחת פתוחה. יש להקיש כדי להחליף לשוניות. - %1$s לשוניות פתוחות. יש להקיש כדי להחליף לשוניות. + %1$s לשוניות פתוחות. יש להקיש כדי להחליף לשוניות. %1$d נבחרו @@ -54,7 +54,9 @@ נשמרו לאחרונה - סימניות שנוספו לאחרונה + סימניות שנוספו לאחרונה + + סימניות אחרונות סימניות שנשמרו לאחרונה @@ -108,6 +110,20 @@ סגירה + + לשוניות שלא צפית בהם במשך שבועיים עוברות לכאן. + + כיבוי בהגדרות + + + לסגור באופן אוטומטי לאחר חודש אחד? + + ‏Firefox יכול לסגור לשוניות שלא נצפו במהלך החודש האחרון. + + סגירה + + הפעלת סגירה אוטומטית + לשונית חדשה @@ -115,14 +131,29 @@ לשונית פרטית חדשה - אתרים מובילים + אתרים מובילים + + + המשך גלישה הצגת הכול + + כפתור להצגת כל הלשוניות האחרונות + + החיפוש שלך עבור ״%1$s״ + + אתרים: %1$s + - נצפו לאחרונה + נצפו לאחרונה + + חיפושים אחרונים הסרה @@ -139,7 +170,7 @@ עצירה - הוספה לסימניות + הוספה לסימניות עריכת סימנייה @@ -203,6 +234,8 @@ התאמה אישית של מסך הבית + + התאמה אישית של מסך הבית מסך הבית @@ -248,6 +281,23 @@ חיפוש ישירות משורת הכתובת + + + מה חדש ב־Firefox + + עכשיו קל יותר לחזור למקום שבו הפסקת. + + מסך בית מותאם אישית של Firefox + + ניתן לעבור ללשוניות הפתוחות שלך, לסימניות ולהיסטוריית הגלישה שלך. + + לשוניות נקיות ומאורגנות + + חיפושים אחרונים + + + כעת קל יותר להמשיך מהמקום שבו הפסקת, במסך הבית של Firefox המותאם אישית שלך. ניתן למצוא את הלשוניות האחרונות, הסימניות ותוצאות החיפוש שלך. + פתיחת לשונית חדשה ב־Firefox @@ -326,7 +376,9 @@ ערכת נושא - בית + בית + + מסך הבית מחוות @@ -396,13 +448,22 @@ אוסף התוספות השתנה. היישום ייסגר לצורך להחלת השינויים… + + + המשך גלישה נשמרו לאחרונה - סימניות שנוספו לאחרונה - - נצפו לאחרונה + סימניות שנוספו לאחרונה + + סימניות אחרונות + + נצפו לאחרונה + + חיפושים אחרונים Pocket @@ -514,7 +575,7 @@ הפעלת Sync - סריקת קוד צימוד ב־Firefox לשולחן העבודה + סריקת קוד צימוד ב־Firefox לשולחן העבודה כניסה @@ -620,6 +681,8 @@ רשימה רשת + + קיבוץ אתרים קשורים סגירת לשוניות @@ -631,15 +694,26 @@ אחרי חודש אחד - + + סגירה אוטומטית של לשוניות פתוחות + + - להתחיל ממסך הבית + להתחיל ממסך הבית + + מסך הפתיחה - לאחר 4 שעות + לאחר 4 שעות + + דף הבית - תמיד + תמיד + + הלשונית האחרונה - לעולם לא + לעולם לא + + מסך הבית לאחר 4 שעות של חוסר פעילות סגירה באופן ידני @@ -649,6 +723,12 @@ סגירה לאחר חודש אחד + + + העברת לשוניות ישנות ללא פעילות + + לשוניות שלא צפית בהם במשך שבועיים עוברות למקטע הלא פעיל. + הסרה @@ -692,13 +772,13 @@ שמירה לאוסף - בחירה + בחירה שיתוף כל הלשוניות לשוניות שנסגרו לאחרונה - נסגרו לאחרונה + נסגרו לאחרונה הגדרות חשבון @@ -772,7 +852,10 @@ שמירה - אחר + אחר + + + לשוניות אחרות @@ -817,10 +900,6 @@ אין היסטוריה כאן - - מחיקת הורדות - - האם ברצונך לנקות את ההורדות שלך? ההורדות הוסרו @@ -861,7 +940,7 @@ תפריט סימניות - עריכת סימנייה + עריכת סימנייה בחירת תיקייה @@ -1892,8 +1971,10 @@ בסדר, הבנתי + + האתרים הנצפים ביותר - הצגת רוב האתרים הנצפים ביותר + הצגת האתרים הנצפים ביותר הצגת האתרים הנצפים ביותר @@ -1906,15 +1987,42 @@ ביטול - - + + לשוניות לא פעילות + + סגירת כל הלשוניות שאינן פעילות - לשוניות זמינות כאן למשך %s. לאחר משך זמן זה, לשוניות ייסגרו באופן אוטומטי. + לשוניות זמינות כאן למשך %s. לאחר משך זמן זה, לשוניות ייסגרו באופן אוטומטי. - 30 ימים + 30 ימים - שבוע אחד + שבוע אחד + + + + לסגור באופן אוטומטי לאחר חודש אחד? + + + ‏Firefox יכול לסגור לשוניות שלא נצפו במהלך החודש האחרון. + + הפעלת סגירה אוטומטית + + + + אנא עזרו לנו להשתפר + + מדוע השבתת את הלשוניות הלא פעילות? + + איני מעוניין בתכונה זו + + הזמן שבו לוקח ללשונית להפוך ללא פעילה ארוך מידי + + הזמן שבו לוקח ללשונית להפוך ללא פעילה קצר מידי + + שליחה + + סגירה הגדרת קישורים מאתרים, מהודעות דוא״ל ומהודעות לפתיחה אוטומטית ב־Firefox. @@ -1931,4 +2039,19 @@ סגירה + + + סיפורים מעוררי מחשבה + + סיפורים מעוררי מחשבה + + סיפורים לפי נושאים + + לגלות עוד + + מופעל באמצעות Pocket + + חלק ממשפחת Firefox. %s + + מידע נוסף From f24e7f2e8b8150ebfb5ed32101dfff19db508678 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 445/517] Strings - app/src/main/res/values-hr/strings.xml --- app/src/main/res/values-hr/strings.xml | 183 +++++++++++++++++++++---- 1 file changed, 154 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 4ce938b3dd..8872672239 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -24,7 +24,7 @@ 1 otvorena kartica. Dodirni za prebacivanje kartica. - %1$s otvorene kartice. Dodirni za prebacivanje kartica. + %1$s otvorene kartice. Dodirni za prebacivanje kartica. Odabrano: %1$d @@ -54,7 +54,9 @@ Nedavno spremljeno - Nedavno zabilježeno + Nedavno zabilježeno + + Nedavne zabilješke Nedavno spremljene zabilješke @@ -105,6 +107,20 @@ Odbaci + + Ovdje premještamo kartice koje niste otvorili dva tjedna. + + Isključi u postavkama + + + Automatski zatvoriti nakon jednog mjeseca? + + Firefox može zatvoriti kartice koje nisu otvorene posljednjih mjesec dana. + + Zatvori + + Uključi automatsko zatvaranje + Nova kartica @@ -112,7 +128,7 @@ Nova privatna kartica - Omiljene stranice + Omiljene stranice @@ -120,6 +136,15 @@ Prikaži sve + + Tipka za prikaz svih nedavnih kartica + + Tvoja pretraga za \"%1$s\" + + Stranice: %1$s + @@ -127,11 +152,17 @@ - Nedavno posjećeno + Nedavno posjećeno + + Nedavne pretrage Ukloni + + Tipka za prikaz svih nedavnih istraživanja + Otvorene kartice @@ -144,7 +175,7 @@ Zaustavi - Zabilješka + Zabilješka Uredi zabilješku @@ -208,6 +239,8 @@ Prilagodi početnu + + Prilagodi početnu Početni zaslon @@ -254,6 +287,27 @@ Pretraži izravno iz adresne trake + + + Što je novo u Firefoxu + + Sada je lakše nastaviti pregledavati web. + + Firefoxova personalizirana početna stranica + + Skoči na svoje otvorene kartice, zabilješke i povijest pregledavanja. + + Čiste, organizirane kartice + + Očisti nered među karticama pomoću poboljšanog rasporeda i automatskog zatvaranja kartica. + + Nedavne pretrage + + Ponovno pregledaj svoja posljednja pretraživanja na početnoj stranici i karticama. + + + Tvoja personalizirana Firefoxova početna stranica sada ti olakšava da nastaviš pretraživati. Pronađi svoje nedavne kartice, zabilješke i rezultate pretraživanja. + Otvori novu karticu u Firefoxu @@ -332,7 +386,9 @@ Tema - Početna + Početna + + Početna stranica Geste @@ -407,9 +463,15 @@ Nedavno spremljeno - Nedavno zabilježeno - - Nedavno posjećeno + Nedavno zabilježeno + + Nedavne zabilješke + + Nedavno posjećeno + + Nedavne pretrage Pocket @@ -523,7 +585,7 @@ Uključi sinkronizaciju - Skeniraj kôd za uparivanje u Firefoxu za računalo + Skeniraj kôd za uparivanje u Firefoxu za računalo Prijavi se @@ -627,6 +689,10 @@ Popis Mreža + + Grupe pretraživanja + + Grupiraj povezane web-stranice Zatvori kartice @@ -638,15 +704,26 @@ Nakon jednog mjeseca - + + Automatski zatvaraj otvorene kartice + + - Počni na početnoj stranici + Počni na početnoj stranici + + Zaslon kod otvaranja aplikacije - Nakon četiri sata + Nakon četiri sata + + Početna stranica - Uvijek + Uvijek + + Posljednja kartica - Nikad + Nikad + + Početna stranica nakon četiri sata neaktivnosti Zatvori ručno @@ -656,6 +733,12 @@ Zatvori nakon jednog mjeseca + + + Premjesti stare kartice u neaktivne + + Kartice koje nisu pregledavane dva tjedna premještene su u neaktivnu sekciju. + Ukloni @@ -698,13 +781,13 @@ Spremi u zbirku - Odaberi + Odaberi Podijeli sve kartice Nedavno zatvorene kartice - Nedavno zatvoreno + Nedavno zatvoreno Postavke računa @@ -778,7 +861,10 @@ Spremi - Ostalo + Ostalo + + + Ostale kartice @@ -826,10 +912,6 @@ Nema povijesti - - Izbriši preuzimanja - - Sigurno želiš očistiti svoja preuzimanja? Uklonjena preuzimanja @@ -871,7 +953,7 @@ Izbornik za zabilješke - Uredi zabilješku + Uredi zabilješku Odaberi mapu @@ -1909,8 +1991,10 @@ U redu, shvaćam + + Naposjećenije omiljene stranice - Prikaži najposjećenije omiljene stranice + Prikaži najposjećenije omiljene stranice Prikaži najposjećenije stranice @@ -1923,15 +2007,41 @@ Odustani - - + + Neaktivne kartice + + Zatvori sve neaktivne kartice - Kartice su dostupne ovdje %s. Nakon tog vremena, kartice će automatski biti zatvorene. + Kartice su dostupne ovdje %s. Nakon tog vremena, kartice će automatski biti zatvorene. - 30 dana + 30 dana - 1 tjedan + 1 tjedan + + + + Automatski zatvoriti nakon jednog mjeseca? + + Firefox može zatvoriti kartice koje nisu pregledane posljednjih mjesec dana. + + UKLJUČI AUTOMATSKO ZATVARANJE + + + + Pomozi nam da se poboljšamo + + Zašto ste isključili neaktivne kartice? + + Ne zanima me ova značajka + + Vrijeme do neaktivnosti je predugo + + Vrijeme do neaktivnosti je prekratko + + Pošalji + + Zatvori Postavi automatsko otvaranje poveznica web stranica, e-pošte i poruka u Firefoxu. @@ -1948,4 +2058,19 @@ Zatvori + + + Priče koje potiču na razmišljanje + + Priče koje potiču na razmišljanje + + Priče po temama + + Otkrij više + + Pokreće Pocket. + + Dio Firefoxove obitelji. %s + + Saznaj više From 856bdd24a469b4927082d27b09e11dfa5d046f84 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:50 +0000 Subject: [PATCH 446/517] Strings - app/src/main/res/values-hsb/strings.xml --- app/src/main/res/values-hsb/strings.xml | 186 ++++++++++++++++++++---- 1 file changed, 157 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-hsb/strings.xml b/app/src/main/res/values-hsb/strings.xml index 6c962d3c1c..ea3519a563 100644 --- a/app/src/main/res/values-hsb/strings.xml +++ b/app/src/main/res/values-hsb/strings.xml @@ -23,7 +23,7 @@ Wočinjene rajtarki: %1$s. Podótkńće so, zo byšće rajtarki přepinał. - Wočinjene rajtarki: %1$s. Podótkńće so, zo byšće rajtarki přepinał. + Wočinjene rajtarki: %1$s. Podótkńće so, zo byšće rajtarki přepinał. Wubrane: %1$d @@ -52,7 +52,9 @@ Njedawno składowane - Njedawno jako zapołožka wotpołoženy + Njedawno jako zapołožka wotpołoženy + + Najnowše zapołožki Njedawno składowane zapołožki @@ -107,6 +109,20 @@ Zaćisnyć + + Rajtarki, kotrež njejsće sej dwě njedźeli wobhladał, so sem přesunu. + + W nastajenjach znjemóžnić + + + Po jednym měsacu awtomatisce začinić? + + Firefox móže rajtarki začinić, kotrež njejsće sej zańdźeny měsac wobhladał. + + Začinić + + Awtomatiske začinjenje zmóžnić + Nowy rajtark @@ -114,7 +130,7 @@ Nowy priwatny rajtark - Najhusćišo wopytane sydła + Najhusćišo wopytane sydła @@ -122,6 +138,15 @@ Wšě pokazać + + Tłóčatko „Njedawno wočinjene rajtarki“ pokazać + + Waše pytanje za \"%1$s\" + + Sydła: %1$s + @@ -129,11 +154,17 @@ - Njedawno wopytane + Njedawno wopytane + + Najnowše pytanja Wotstronić + + Tłóčatko „Wšě zańdźene wuslědźenja“ pokazać + Wočinjene rajtarki @@ -146,7 +177,7 @@ Zastajić - Zapołožkam přidać + Zapołožkam přidać Zapołožku wobdźěłać @@ -210,6 +241,8 @@ Startowu wobrazowku přiměrić + + Startowu stronu přiměrić Startowa wobrazowka @@ -255,6 +288,28 @@ Direktnje z adresoweho pola pytać + + + Nowe funkcije a změny we Firefox + + Je nětko lóšo tam pokročować, hdźež sće přestał. + + Personalizowana startowa strona Firefox + + Přeńdźće k swojim wočinjenym rajtarkam, zapołožkam a přehladowanskej historiji. + + Přehladne, organizowane rajtarki + + Wotstrońće rajtarkowu šmjatańcu přez polěpšene wuhotowanje a awtomatisce začinjace rajtarki. + + Najnowše pytanja + + + Wotwołće znowa swoje najnowše pytanja ze swojeje startoweje strony a rajtarkow. + + + Waša personalizowana startowa strona Firefox nětko wosnadnja tam pokročować, hdźež sće přestał. Namakajće swoje najnowše rajtarki, zapołožki a pytanske wuslědki. + Nowy rajtark Firefox wočinić @@ -334,7 +389,9 @@ Drasta - Startowa strona + Startowa strona + + Startowa strona Gesty @@ -408,9 +465,15 @@ Njedawno składowane - Njedawno jako zapołožka wotpołoženy - - Njedawno wopytane + Njedawno jako zapołožka wotpołoženy + + Najnowše zapołožki + + Njedawno wopytane + + Najnowše pytanja Pocket @@ -523,7 +586,7 @@ Synchronizaciju zmóžnić - Porowanski kod w desktopowym Firefox skenować + Porowanski kod w desktopowym Firefox skenować Přizjewić @@ -630,6 +693,10 @@ Lisćina Lěsyca + + Pytanske skupiny + + Přiwuzne sydła zeskupić Rajtarki začinić @@ -641,15 +708,26 @@ Po jednym měsacu - + + Wočinjene rajtarki awtomatisce začinić + + - Na startowej wobrazowce započeć + Na startowej wobrazowce započeć + + Startowa wobrazowka - Po štyrjoch hodźinach + Po štyrjoch hodźinach + + Startowa strona - Přeco + Přeco + + Posledni rajtark - Ženje + Ženje + + Startowa strona po štyrjoch hodźinach inaktiwnosće Manuelnje začinić @@ -660,6 +738,13 @@ Po jednym měsacu začinić + + + Stare rajtarki do „inaktiwne“ přesunyć + + + Rajtarki, kotrež njejsće sej dwě njedźeli wobhladał, so do inaktiwneho wotrězka přesunu. + Wotstronić @@ -702,13 +787,13 @@ Do zběrki składować - Wubrać + Wubrać Wšě rajtarki dźělić Runje začinjene rajtarki - + Kontowe nastajenja @@ -783,7 +868,10 @@ Składować - Druhe + Druhe + + + Druhe rajtarki @@ -828,10 +916,6 @@ Tu žana historija njeje - - Sćehnjenja zhašeć - - Chceće woprawdźe swoje sćehnjenja zhašeć? Sćehnjenja wotstronjene @@ -872,7 +956,7 @@ Meni zapołožkow - Zapołožku wobdźěłać + Zapołožku wobdźěłać Rjadowak wubrać @@ -1903,8 +1987,10 @@ W porjadku, sym zrozumił + + Najhusćišo wopytane sydła pokazać - Najhusćišo wopytowane sydła pokazać + Najhusćišo wopytowane sydła pokazać Najhusćišo wopytane sydła pokazać @@ -1917,15 +2003,42 @@ Přetorhnyć - - + + Inaktiwne rajtarki + + Wšě inaktiwne rajtarki začinić - Rajtarki su tu za %s k dispoziciji. Po tutym času so rajtarki awtomatisce začinja. + Rajtarki su tu za %s k dispoziciji. Po tutym času so rajtarki awtomatisce začinja. - 30 dnjow + 30 dnjow - 1 tydźeń + 1 tydźeń + + + + Po jednym měsacu awtomatisce začinić? + + Firefox móže rajtarki začinić, kotrež njejsće sej zańdźeny měsac wobhladał. + + AWTOMATISKE ZAČINJENJE ZMÓŽNIĆ + + + + Prošu pomhajće nam, zo bychmy so polěpšili + + Čehodla sće inaktiwne rajtarki znjemóžnił? + + Žadyn zajim na tutej funkciji + + + Čas do „inaktiwny“ je předołhi + + Čas do „inaktiwny“ je překrótki + + Pósłać + + Začinić Nastajće wotkazy z websydłow, mejlkow a powěsćow, zo bychu so awtomatisce we Firefox wočinili. @@ -1942,4 +2055,19 @@ Začinić + + + Stawiznički, kotrež k přemyslowanju pohonjeja + + Stawiznički, kotrež k přemyslowanju pohonjeja + + Stawiznički po temje + + Wjace wotkryć + + Wot Pocket spěchowany + + Dźěl swójby Firefox. %s + + Dalše informacije From 7087012fc51082fd5fa946dc6560af826a82fc3e Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 447/517] Strings - app/src/main/res/values-hu/strings.xml --- app/src/main/res/values-hu/strings.xml | 185 +++++++++++++++++++++---- 1 file changed, 156 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 4a5d2fc29d..7851838044 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -24,7 +24,7 @@ 1 nyitott lap. Koppintson a lapváltáshoz. - %1$s nyitott lap. Koppintson a lapváltáshoz. + %1$s nyitott lap. Koppintson a lapváltáshoz. %1$d kiválasztva @@ -53,7 +53,9 @@ Nemrég mentett - Nemrég könyvjelzőzött + Nemrég könyvjelzőzött + + Friss könyvjelzők Legutóbb mentett könyvjelzők @@ -106,6 +108,20 @@ Eltüntetés + + Azok a lapok, melyeket két hete nem nézett meg, ide kerülnek. + + Kikapcsolás a beállításokban + + + Automatikus bezárás egy hónap után? + + A Firefox bezárhatja azokat a lapokat, amelyeket az elmúlt hónapban nem nézett meg. + + Bezárás + + Automatikus bezárás bekapcsolása + Új lap @@ -113,7 +129,7 @@ Új privát lap - Kedvenc oldalak + Kedvenc oldalak @@ -121,6 +137,15 @@ Összes megjelenítése + + Az összes legutóbbi lap megjelenítése gomb + + Az Ön keresése erre: „%1$s” + + Webhelyek: %1$s + @@ -128,11 +153,17 @@ - Nemrég felkeresett + Nemrég felkeresett + + Legutóbbi keresések Eltávolítás + + Az összes korábbi felfedezés megjelenítése gomb + Nyitott lapok @@ -145,7 +176,7 @@ Leállítás - Könyvjelzőzés + Könyvjelzőzés Könyvjelző szerkesztése @@ -211,6 +242,8 @@ Kezdőoldal testreszabása + + Kezdőoldal testreszabása Kezdőképernyő @@ -257,6 +290,29 @@ Keresés közvetlenül a címsávból + + + Újdonságok a Firefoxban + + + Mostantól könnyebb ott folytatni, ahol abbahagyta. + + Személyre szabott Firefox kezdőoldal + + Ugrás a megnyitott lapokra, könyvjelzőkre és az előzményekre. + + Tiszta, rendszerezett lapok + + Számoljon le a lapok rendezetlenségével a jobb elrendezés és az automatikusan bezáródó lapok segítségével. + + Legutóbbi keresések + + + Tekintse meg újra a legutóbbi kereséseit a kezdőlapról és a lapokról. + + + A személyre szabott Firefox kezdőlapja megkönnyíti, hogy ott folytassa, ahol abbahagyta. Találja meg a legutóbbi lapjait, könyvjelzőit és keresési találatait. + Új Firefox-lap megnyitása @@ -335,7 +391,9 @@ Téma - Kezdőlap + Kezdőlap + + Kezdőlap Kézmozdulatok @@ -410,9 +468,15 @@ Nemrég mentett - Nemrég könyvjelzőzött - - Nemrég felkeresett + Nemrég könyvjelzőzött + + Friss könyvjelzők + + Nemrég felkeresett + + Legutóbbi keresések Pocket @@ -523,7 +587,7 @@ Sync bekapcsolása - Olvassa le a párosítási kódot az asztali Firefoxban + Olvassa le a párosítási kódot az asztali Firefoxban Bejelentkezés @@ -632,6 +696,10 @@ Lista Rács + + Keresési csoportok + + Kapcsolódó webhelyek csoportosítása Lapok bezárása @@ -643,15 +711,26 @@ Egy hónap után - + + Nyitott lapok automatikus bezárása + + - Kezdés a kezdőképernyőn + Kezdés a kezdőképernyőn + + Nyitóképernyő - Négy óra után + Négy óra után + + Kezdőlap - Mindig + Mindig + + Legutóbbi lap - Soha + Soha + + Kezdőlap négy óra tétlenség után Bezárás kézzel @@ -661,6 +740,12 @@ Bezárás egy hónap után + + + Régi lapok áthelyezése az inaktívak közé + + Azok a lapok, melyeket két hete nem nézett meg, átkerülnek az inaktív részbe. + Eltávolítás @@ -703,13 +788,13 @@ Gyűjteménybe mentés - Kijelölés + Kijelölés Az összes lap megosztása Nemrég bezárt lapok - Nemrég bezárt + Nemrég bezárt Fiókbeállítások @@ -784,7 +869,10 @@ Mentés - Egyéb + Egyéb + + + Más lapok @@ -829,10 +917,6 @@ Nincsenek előzmények - - Letöltések törlése - - Biztos, hogy törli a letöltéseket? Letöltések eltávolítva @@ -873,7 +957,7 @@ Könyvjelző menü - Könyvjelző szerkesztése + Könyvjelző szerkesztése Válasszon mappát @@ -1910,8 +1994,10 @@ Rendben, értem + + Leglátogatottabb oldalak megjelenítése - A leglátogatottabb oldalak megjelenítése + A leglátogatottabb oldalak megjelenítése A leglátogatottabb oldalak megjelenítése @@ -1924,15 +2010,41 @@ Mégse - - + + Inaktív lapok + + Összes inaktív lap bezárása - A lapok %s állnak itt rendelkezésre. Ezután a lapok automatikusan bezárásra kerülnek. + A lapok %s állnak itt rendelkezésre. Ezután a lapok automatikusan bezárásra kerülnek. - 30 napig + 30 napig - 1 hétig + 1 hétig + + + + Automatikus bezárás egy hónap után? + + A Firefox bezárhatja azokat a lapokat, amelyeket az elmúlt hónapban nem nézett meg. + + AUTOMATIKUS BEZÁRÁS BEKAPCSOLÁSA + + + + Segítsen nekünk a fejlesztésben + + Miért tiltotta le az inaktív lapokat? + + Nem érdekli a funkció + + Az inaktívvá tétel túl sokáig tart + + Az inaktívvá tétel túl rövid ideig tart + + Küldés + + Bezárás Állítsa be a webhelyek, e-mailek és üzenetek hivatkozásait, hogy azok automatikusan a Firefoxban nyíljanak meg. @@ -1949,4 +2061,19 @@ Bezárás + + + Elgondolkodtató történetek + + Elgondolkodtató történetek + + Történetek téma szerint + + Folytassa a felfedezést + + A motorháztető alatt: Pocket. + + A Firefox család tagja. %s + + További tudnivalók From f0075a46ae597066ec099751970a08428837f359 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 448/517] Strings - app/src/main/res/values-hy-rAM/strings.xml --- app/src/main/res/values-hy-rAM/strings.xml | 124 +++++++++++++++++++-- 1 file changed, 116 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-hy-rAM/strings.xml b/app/src/main/res/values-hy-rAM/strings.xml index 9ad90b664e..454bd07961 100644 --- a/app/src/main/res/values-hy-rAM/strings.xml +++ b/app/src/main/res/values-hy-rAM/strings.xml @@ -53,7 +53,9 @@ Վերջերս պահպանված - Վերջերս էջանշված + Վերջերս էջանշված + + Վերջին Էջանիշերը Վերջերս պահպանված էջանիշները @@ -105,6 +107,11 @@ Բաց թողնել + + Երկու շաբաթ չնայած էջանիշերը տեղափոխվում են այստեղ: + + Անջատել կարգավորումներում + Նոր ներդիր @@ -121,6 +128,15 @@ Ցուցադրել բոլորը + + Ցույց տալ բոլոր վերջին ներդիրների կոճակը + + Ձեր որոնումը \"%1$s\"-ի համար + + Կայքեր՝ %1$s + @@ -128,11 +144,17 @@ - Վերջերս այցելած + Վերջերս այցելած + + Վերջին որոնումները Հեռացնել + + Ցույց տալ բոլոր անցած հետազոտությունների կոճակը + Բաց ներդիրներ @@ -210,6 +232,8 @@ Հարմարեցնել տնայինը + + Հարմարեցնել տնայինը Հիմնական էկրան @@ -255,6 +279,27 @@ Որոնել ուղղակիրոեն հասցեագոտուց + + + Ինչն է նոր Firefox-ում + + Այժմ ավելի հեշտ է շարունակել այն տեղից, որտեղ կանգ եք առել: + + Անհատականացված Firefox-ի տնային էջ + + Անցեք ձեր բաց ներդիրներին, էջանիշերին և զննման պատմությանը: + + Մաքուր, կազմակերպված ներդիրներ + + Կարգի բերեք ներդիրները դասավորությունը լավարկելու և ինքնափակելու միջոցով: + + Վերջին որոնումները + + Վերանայեք ձեր վերջին որոնումները ձեր տնային էջից և ներդիրներից: + + + Ձեր անհատականացված Firefox-ի տնային էջը այժմ հեշտացնում է աշխատանքը շարունակելու այն կետից, որում կանգնել եք: Գտեք ձեր վերջին ներդիրները, էջանիշերը և որոնման արդյունքները: + Բացել Firefox-ի նոր ներդիր @@ -408,9 +453,15 @@ Վերջերս պահված - Վերջերս էջանշված - - Վերջերս այցելած + Վերջերս էջանշված + + Վերջին Էջանիշերը + + Վերջերս այցելած + + Վերջին որոնումները Pocket @@ -627,6 +678,10 @@ Ցանկ Ցանց + + Որոնել խմբեր + + Խմբավորել առնչվող կայքերը միասին Փակել ներդիրները @@ -638,6 +693,9 @@ Մեկ ամիս անց + + Ինքնափակել բաց ներդիրները + Սկսել տնից @@ -656,6 +714,12 @@ Փակել մեկ ամիս անց + + + Տեղափոխել հին ներդիրները անգործուն + + Ներդիրները, որոնք չեք դիտել երկու շաբաթ, տեղափոխվել են անգործուն հատված: + Հեռացնել @@ -776,7 +840,10 @@ Պահպանել - Այլ + Այլ + + + Այլ ներդիրներ @@ -1910,9 +1977,11 @@ Չեղարկել - - + + Ոչ ակտիվ ներդիրներ + + Փակել բոլոր անգործուն ներդիրները Ներդիրներն այստեղ հասանելի են %s: Այդ ժամանակից հետո ներդիրներն ինքնաբար կփակվեն: @@ -1920,6 +1989,30 @@ 1 շաբաթ + + + Ինքնափակե՞լ մեկ ամսից: + + Firefox-ը կարող է փակել ներդիրները, որոնք չեք դիտել վերջին ամսում: + + ՄԻԱՑՆԵԼ ԻՆՔՆԱՓԱԿՈՒՄԸ + + + + Օգնեք մեզ բարելավել + + Ինչու՞ եք անջատել անգործուն ներդիրները: + + Հետաքրքրված չեմ + + Անգործունության ժամանակը երկար է + + Անգործունության ժամանակը կարճ է + + Ուղարկել + + Փակել + Կայեք հղումներ կայքերից, էլ. նամակներից և հաղորդագրություններից, որոնք ինքնաբար կերպով կբացվեն Firefox-ում: @@ -1935,4 +2028,19 @@ Փակել + + + Մտահանգման պատմություններ + + Մտահանգման պատմություններով + + Պատմություններ ըստ թեմայի + + Բացահայտի՛ր ավելին + + Pocket-ի կողմից: + + Firefox ընտանիքի մի մասը: %s + + Իմանալ ավելին From 5b7f0e94fd04099a1ac7988c1205234fbdc79815 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 449/517] Strings - app/src/main/res/values-ia/strings.xml --- app/src/main/res/values-ia/strings.xml | 219 +++++++++++++++++++------ 1 file changed, 173 insertions(+), 46 deletions(-) diff --git a/app/src/main/res/values-ia/strings.xml b/app/src/main/res/values-ia/strings.xml index 1da50cee65..dfa18b86dd 100644 --- a/app/src/main/res/values-ia/strings.xml +++ b/app/src/main/res/values-ia/strings.xml @@ -25,7 +25,7 @@ 1 scheda aperte. Tocca pro commutar schedas. - %1$s schedas aperte. Tocca pro mutar schedas. + %1$s schedas aperte. Tocca pro mutar schedas. %1$d seligite @@ -53,7 +53,9 @@ Salvate recentemente - Marcapaginas recente + Marcapaginas recente + + Marcapaginas recente Marcapaginas recentemente salvate @@ -93,7 +95,7 @@ Dimitter - Accesso al camera necessari. Vade a parametros de Android, tocca permissos e tocca permitter. + Accesso al camera necessari. Vade al parametros de Android, tocca Permissiones e tocca Permitter. Vade a parametros @@ -107,13 +109,27 @@ Dimitter + + Le schedas que tu non visualisava desde duo septimanas es displaciate hic. + + Disactivar in parametros + + + Auto-clauder post un mense? + + Firefox pote clauder le schedas que tu non ha visualisate durante le mense passate. + + Clauder + + Activar auto-clausura + Nove scheda Nove scheda private - Sitos popular + Sitos popular @@ -121,6 +137,15 @@ Monstrar toto + + Monstrar tote le recente button de schedas + + Tu recerca de \"%1$s\" + + Sitos: %1$s + @@ -128,11 +153,17 @@ - Visitate recentemente + Visitate recentemente + + Recercas recente Remover + + Monstrar tote le passate buttones de exploration + Schedas aperte @@ -145,7 +176,7 @@ Stoppar - Marcapaginas + Marcapaginas Rediger le marcapaginas @@ -164,7 +195,7 @@ Bibliotheca - Sito del scriptorio + Sito de scriptorio Adder al pagina initial @@ -211,6 +242,8 @@ Personalisar pagina initial + + Personalisar pagina initial Pagina initial @@ -257,6 +290,28 @@ Cercar directemente per le barra de adresse + + + Que novas in Firefox + + Ora il es plus veloce seliger retro ubi tu abandonava. + + Pagina principal de Firefox personalisate + + Salta a tu schedas aperte, marcapaginas e chronologia de navigation. + + Schedas clar, organisate + + Elimina le disordine del schedas con le disposition meliorate e le auto-clausura. + + Recercas recente + + + Revisita tu ultime recercas ab tu pagina principal e schedas. + + + Tu pagina principal personalisate de Firefox ora simplifica reprender de ubi tu lassava. Trova tu recente schedas, marcapaginas e resultatos del recerca. + Aperir un nove scheda Firefox @@ -306,7 +361,7 @@ Confidentialitate e securitate - Permissos del sito + Permissiones del sito @@ -336,7 +391,9 @@ Thema - Initio + Initio + + Pagina initial Gestos @@ -412,9 +469,15 @@ Salvate recentemente - Marcapaginas recente - - Visitate recentemente + Marcapaginas recente + + Marcapaginas recente + + Visitate recentemente + + Recercas recente Pocket @@ -535,7 +598,7 @@ Activar Sync - Scannar le codice de accopulamento in Firefox scriptorio + Scannar le codice de accopulamento in Firefox scriptorio @@ -650,6 +713,10 @@ Lista Grillia + + Cercar gruppos + + Gruppar insimul le sitos correlate Clauder le schedas @@ -661,15 +728,26 @@ Depost un mense - + + Auto-clauder schedas aperte + + - Initiar al pagina initial + Initiar al pagina initial + + Aperiente schermo - Post quatro horas + Post quatro horas + + Pagina initial - Sempre + Sempre + + Ultime scheda - Nunquam + Nunquam + + Pagina initial post 4 horas de inactivitate Clauder manualmente @@ -679,6 +757,13 @@ Clauder depost un mense + + + Move le schedas vetule a inactive + + + Le schedas que tu non visualisava desde duo septimanas es displaciate al section inactive. + Remover @@ -723,13 +808,13 @@ Salvar al collection - Seliger + Seliger Compartir tote le schedas Schedas claudite recentemente - Recentemente claudite + Recentemente claudite Parametros de conto @@ -808,13 +893,16 @@ Salvar - Altere + Altere + + + Altere schedas Deler le chronologia - Desira tu vermente clarar tu chronologia? + Desira tu vermente vacuar tu chronologia? Chronologia delite @@ -855,10 +943,6 @@ Nulle chronologia hic - - Deler le discargamentos - - Desira tu vermente vacuar tu discargamentos? Discargamentos removite @@ -896,7 +980,7 @@ Menu de marcapaginas - Rediger le marcapaginas + Rediger le marcapaginas Selige un dossier @@ -967,7 +1051,7 @@ - Permissos + Permissiones Ir a configurationes Recommendate - Gere le permissos de sito + Gerer le permissiones de sito - Clarar permissos + Revocar permissiones - Clarar permisso + Revocar permission - Clarar permissos sur tote le sitos + Revocar permissiones sur tote le sitos Autoreproduction @@ -1250,7 +1334,7 @@ Libera spatio de immagazinage - Permissos del sito + Permissiones del sito Discargamentos @@ -1676,13 +1760,13 @@ Copiar le contrasigno - Clarar contrasigno + Vacuar contrasigno Copiar le nomine de usator - Clarar nomine de usator + Vacuar nomine de usator - Clarar nomine de servitor + Vacuar nomine de servitor Copiar sito @@ -1849,7 +1933,7 @@ 1. Va a Parametros de Android - Permissos]]> + Permissiones]]> %1$s a Active]]> @@ -1862,11 +1946,11 @@ Connexion non secur - Es tu secur que tu vole clarar tote le permissos sur tote le sitos? + Es tu secur que tu vole rader tote le permissiones sur tote le sitos? - Es tu secur que tu vole clarar tote le permissos pro iste sito? + Es tu secur que tu vole rader tote le permissiones pro iste sito? - Es tu secur que tu vole clarar iste permisso pro iste sito? + Es tu secur que tu vole rader iste permission pro iste sito? Nulle exceptiones sito @@ -1950,8 +2034,10 @@ Pro adder un altere sito principal, remove un. Tocca e retene le sito e selige remover. De accordo + + Sitos principal plus visitate - Monstrar le sitos principal plus visitate + Monstrar le sitos principal plus visitate Monstrar le sitos plus visitate @@ -1963,15 +2049,41 @@ Cancellar - - + + Schedas inactive + + Clauder tote le schedas inactive - Le schedas es disponibile hic pro %s. Post ille tempore, le schedas se claudera automaticamente. + Le schedas es disponibile hic pro %s. Post ille tempore, le schedas se claudera automaticamente. - 30 dies + 30 dies - 1 septimana + 1 septimana + + + + Auto-clauder post un mense? + + Firefox pote clauder le schedas que tu non ha visualisate durante le mense passate. + + ACTIVAR LE CLAUSURA AUTOMATIC + + + + Adjuta nos a meliorar + + Proque tu disactiva le schedas inactive? + + Non interessate al functionalitate + + Le tempore a inactive es troppo longe + + Le tempore a inactive es troppo breve + + Inviar + + Claude Stabilir qual ligamines de sitos web, e-mails e messages se aperi automaticamente in Firefox. @@ -1988,4 +2100,19 @@ Clauder + + + Historias hastive + + Historias hastive + + Historia per argumento + + Discoperi plus + + Potentiate per Pocket + + Parte del familia de Firefox. %s + + Pro saper plus From 2093e232144d85c05754ccffe415e55fdf4d3170 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 450/517] Strings - app/src/main/res/values-in/strings.xml --- app/src/main/res/values-in/strings.xml | 269 +++++++++++++++++++++---- 1 file changed, 229 insertions(+), 40 deletions(-) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 630e7ca754..064341a6a1 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -26,7 +26,7 @@ 1 tab terbuka. Ketuk untuk beralih tab. - %1$s tab terbuka. Ketuk untuk beralih tab. + %1$s tab terbuka. Ketuk untuk beralih tab. %1$d terpilih @@ -53,7 +53,11 @@ - Baru saja disimpan + Baru saja disimpan + + Baru saja dimarkahi + + Markah terbaru Markah yang baru saja disimpan @@ -106,6 +110,20 @@ Tutup + + Tab yang belum Anda lihat selama dua minggu dipindahkan ke sini. + + Matikan di pengaturan + + + Tutup otomatis setelah satu bulan? + + Firefox dapat menutup tab yang belum Anda lihat selama sebulan terakhir. + + Tutup + + Nyalakan Tutup-Otomatis + Tab baru @@ -113,7 +131,7 @@ Tab pribadi baru - Situs teratas + Situs teratas @@ -121,10 +139,31 @@ Tampilkan semua + + Tampilkan tombol semua tab terbaru + + Pencarian Anda untuk \"%1$s\" + + Situs: %1$s + - Penjelajahan terakhir + Penjelajahan terakhir + + + Baru dikunjungi + + Pencarian terkini + + Hapus + + Tampilkan tombol semua eksplorasi lampau @@ -138,7 +177,7 @@ Hentikan - Markah + Markah Edit markah @@ -203,6 +242,10 @@ Ubah + + Ubahsuai beranda + + Ubahsuai halaman beranda Layar beranda @@ -250,6 +293,28 @@ Cari langsung dari bilah alamat + + + Apa yang baru di Firefox + + Sekarang lebih mudah untuk melanjutkan dari sesi sebelumnya. + + Beranda Firefox yang dipersonalisasi + + Lompat ke tab terbuka, markah, dan riwayat penjelajahan Anda. + + Tab bersih dan terorganisir + + + Hilangkan tab berantakan dengan tata letak baru dan tab yang menutup otomasi. + + Pencarian terkini + + Kunjungi kembali penelusuran terkini dari beranda dan tab Anda. + + + Sekarang, halaman beranda Firefox yang dipersonalisasi memudahkan Anda melanjutkan pekerjaan dari sesi sebelumnya. Temukan tab, markah, dan hasil pencarian Anda yang terkini. + Buka tab Firefox baru @@ -330,7 +395,9 @@ Tema - Beranda + Beranda + + Beranda Gestur @@ -399,6 +466,24 @@ Koleksi pengaya dimodifikasi. Keluar dari aplikasi untuk menerapkan pengubahan… + + + Lompat kembali + + Baru saja disimpan + + Baru saja dimarkahi + + Markah terbaru + + Baru dikunjungi + + Pencarian terkini + + Pocket + Pengaya tidak didukung @@ -510,7 +595,7 @@ Aktifkan Sinkronisasi - Pindai kode pemasangan di Firefox desktop + Pindai kode pemasangan di Firefox desktop Masuk @@ -588,6 +673,13 @@ Tutup + + %d situs + + %d situs + Tab yang baru saja ditutup @@ -610,6 +702,10 @@ Daftar Grid + + Cari grup + + Kelompokkan situs terkait bersama-sama Tutup tab @@ -621,15 +717,26 @@ Setelah satu bulan - + + Tutup otomatis tab terbuka + + - Mulai dari Beranda + Mulai dari Beranda + + Layar pembuka - Setelah empat jam + Setelah empat jam + + Beranda - Selalu + Selalu + + Tab terakhir - Tidak Pernah + Tidak Pernah + + Beranda setelah empat jam tidak aktif Tutup secara manual @@ -639,6 +746,12 @@ Tutup setelah satu bulan + + + Pindahkan tab lama menjadi nonaktif + + Tab yang belum Anda lihat selama dua minggu dipindahkan ke bagian nonaktif. + Hapus @@ -679,13 +792,13 @@ Simpan ke koleksi - Pilih + Pilih Bagikan semua tab Tab yang baru saja ditutup - Baru saja ditutup + Baru saja ditutup Setelan akun @@ -759,6 +872,11 @@ Simpan + + Lainnya + + Tab lainnya + Hapus riwayat @@ -771,15 +889,15 @@ Bersihkan - Salin + Salin - Bagikan + Bagikan - Buka di tab baru + Buka di tab baru - Buka di tab pribadi + Buka di tab pribadi - Hapus + Hapus %1$d terpilih @@ -803,10 +921,6 @@ Tidak ada riwayat di sini - - Hapus unduhan - - Yakin akan membersihkan unduhan Anda? Unduhan Dihapus @@ -848,7 +962,7 @@ Menu markah - Edit markah + Edit markah Pilih folder @@ -1259,12 +1373,12 @@ Sudah memiliki akun? - Lihat apa yang baru + Lihat apa yang baru - Punya pertanyaan mengenai desain ulang %s? Ingin tahu apa saja yang berubah? + Punya pertanyaan mengenai desain ulang %s? Ingin tahu apa saja yang berubah? - Dapatkan jawabannya di sini + Dapatkan jawabannya di sini Sinkronkan Firefox antar perangkat @@ -1304,14 +1418,14 @@ Letakkan bilah alat agar mudah dijangkau. Tetap di bawah, atau pindahkan ke atas. - Menjelajah dengan privat + Menjelajah dengan privat - Buka tab pribadi sekali: Ketuk ikon %s. + Buka tab pribadi sekali: Ketuk ikon %s. - Selalu buka tab pribadi: Perbarui pengaturan penjelajahan pribadi Anda. + Selalu buka tab pribadi: Perbarui pengaturan penjelajahan pribadi Anda. - Buka pengaturan + Buka pengaturan Privasi Anda Menghapus kuki yang ditetapkan oleh pengalihan ke situs web pelacakan yang dikenal. + + Beberapa pelacak yang ditandai di bawah telah diblokir sebagian pada laman ini karena Anda berinteraksi dengan mereka *. + + Pelajari lebih lanjut + Dukungan @@ -1543,6 +1663,9 @@ Isi nama pengguna dan sandi di aplikasi lain pada perangkat Anda. + + Tambahkan log masuk + Sinkronkan info masuk @@ -1605,6 +1728,8 @@ Salin nama pengguna Hapus nama pengguna + + Hapus nama host Salin situs @@ -1769,9 +1894,13 @@ %1$s ke NYALA]]> - Sambungan Aman + Sambungan aman - Sambungan Tidak Aman + Sambungan tidak aman + + Sambungan Aman + + Sambungan Tidak Aman Yakin ingin menghapus semua izin di semua situs? @@ -1811,8 +1940,14 @@ Batalkan perubahan Ubah - + + Tambahkan log masuk baru + Kata sandi diperlukan + + Nama pengguna diperlukan + + Nama host diperlukan Pencarian suara @@ -1821,6 +1956,15 @@ Sebuah info masuk dengan nama pengguna tersebut sudah ada + + https://www.example.com + + Alamat web harus berisi "https://" atau "http://" + + Alamat web harus berisi "https://" atau "http://" + + Nama host yang valid diperlukan + Hubungkan perangkat lain @@ -1847,8 +1991,12 @@ Oke, Paham! + + Situs teratas yang paling banyak dikunjungi + + Tampilkan situs teratas yang paling sering dikunjungi - Tampilkan situs yang paling sering dikunjungi + Tampilkan situs yang paling sering dikunjungi Nama @@ -1859,15 +2007,41 @@ Batal - - + + Tab nonaktif + + Hapus semua tab nonaktif - Tab tersedia di sini selama %s. Setelah itu, tab akan otomatis ditutup. + Tab tersedia di sini selama %s. Setelah itu, tab akan otomatis ditutup. - 30 hari + 30 hari - 1 pekan + 1 pekan + + + + Tutup otomatis setelah satu bulan? + + Firefox dapat menutup tab yang belum Anda lihat selama sebulan terakhir. + + AKTIFKAN TUTUP OTOMATIS + + + + Bantu kami menjadi lebih baik + + Mengapa Anda mematikan fitur tab nonaktif? + + Tidak tertarik dengan fiturnya + + Waktu nonaktif terlalu lama + + Waktu nonaktif terlalu singkat + + Kirim + + Tutup Setel tautan dari situs web, surel, dan pesan untuk secara otomatis dibuka di Firefox. @@ -1884,4 +2058,19 @@ Tutup + + + Cerita yang menggugah pikiran + + Cerita yang menggugah pikiran + + Cerita berdasarkan topik + + Temukan lebih banyak + + Diberdayakan oleh Pocket. + + Bagian dari keluarga Firefox. %s + + Pelajari lebih lanjut From f6d8284246451041c255b71821d04e3082cd31c3 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 451/517] Strings - app/src/main/res/values-is/strings.xml --- app/src/main/res/values-is/strings.xml | 166 +++++++------------------ 1 file changed, 47 insertions(+), 119 deletions(-) diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 9a715a1e56..448ab770dc 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -1,9 +1,10 @@ - + Huliðs %s %s (Huliðs) + Fleiri stillingar @@ -22,7 +23,7 @@ 1 opinn flipi. Snertu til að skipta um flipa. - %1$s opnir flipar. Snertu til að skipta um flipa. + %1$s opnir flipar. Snertu til að skipta um flipa. %1$s er framleitt af Mozilla. @@ -32,10 +33,10 @@ Þú ert í huliðsglugga - ¶ + · %1$s hreinsar leitar- og vafraferilinn þinn þegar þú lokar forritinu eða lokar öllum einkaflipum og gluggum. Þó að þetta sé ekki nafnlaust gagnvart vefsíðum eða þjónustuveitanda þínum, þá gerir þetta þér auðveldara að halda því sem þú gerir á netinu huldu gagnvart einkaaðilum sem nota þetta tæki. - ¶ - Algengar mýtur um huliðsleit¶ + + Algengar mýtur um huliðsleit Eyða setu @@ -47,14 +48,6 @@ Nei takk - - - Komast fyrr í Firefox. Bættu við græju á ræsisíðuna þína. - - Bæta græju við - - Ekki núna - Nýr flipi @@ -62,7 +55,7 @@ Nýr einkaflipi - Vinsælustu svæðin + Vinsælustu svæðin @@ -76,7 +69,7 @@ Stöðva - Bókamerki + Bókamerki Breyta bókamerki @@ -104,8 +97,6 @@ Finna á síðu Einkaflipi - - Nýr flipi Vista í safn @@ -148,14 +139,8 @@ Skanna - - Flýtileiðir Leitarvélastillingar - - Leita með - - Að þessu sinni leita með: Afrita tengil frá klemmuspjaldi @@ -169,7 +154,6 @@ Fræðast meira - Leita @@ -263,8 +247,6 @@ Verkfærakista forritarans Kembt með fjartengingu með USB tengi - - Sýna flýtivísanir fyrir leit Sýna leitartillögur @@ -350,9 +332,6 @@ Fræðast meira - - Slökkt á víðvært, farðu í Stillingar til að til að virkja aftur. - Fjarmælingar @@ -379,7 +358,7 @@ Kveikja á samstillingu - Skannaðu pörunarkóða úr Firefox á tölvunni + Skannaðu pörunarkóða úr Firefox á tölvunni Innskráning @@ -431,8 +410,6 @@ Önnur bókamerki Ferill - - Samstilltir flipar Lestrarlisti @@ -507,7 +484,7 @@ Opna flipa - Fjarlægja + Fjarlægja %1$s (huliðshamur) @@ -524,15 +501,15 @@ Hreinsa - Afrita + Afrita - Deila + Deila - Opna nýjan flipa + Opna nýjan flipa - Opna í einkaflipa + Opna í einkaflipa - Eyða + Eyða %1$d valið @@ -550,6 +527,7 @@ Ekkert ferli hér + Því miður getur %1$s ekki hlaðið þessa síðu. @@ -572,7 +550,7 @@ Bókarmerkja valmynd - Breyta bókamerki + Breyta bókamerki Veldu möppu @@ -581,8 +559,6 @@ Eyddi %1$s Bæta við möppu - - Bókamerki bætt við. Bókamerki vistað! @@ -631,7 +607,7 @@ Eyddi %1$s - + Bókamerki fjarlægð AFTURKALLA @@ -680,15 +656,15 @@ Virkt Óvirkt - + Leyfa hljóð og myndskeið Loka á hljóð og myndskeið eingöngu á farsímanetstengingum Hljóð og myndbönd munu spila á þráðlausum netum - + Lokaðu eingöngu fyrir hljóð - + Loka á hljóð og myndskeið Virkt @@ -700,10 +676,6 @@ Söfn Valmynd safns - - Safnaðu því sem skiptir þig máli - - Hópaðu saman svipaðar leitir, síður og flipa svo auðvelt sé að opna seinna. Veldu flipa @@ -793,6 +765,8 @@ Eyða og opna Knúið af + + Safni eytt @@ -834,7 +808,7 @@ Eyða - Hætta við + Hætta við Fara í fullan skjá @@ -905,8 +879,8 @@ Firefox Preview heitir nú Firefox Nightly - ¶ - · Firefox Nightly er uppfærður á hverju kvöldi og er með virkni sem verið er að prófa sig áfram með.¶ + + · Firefox Nightly er uppfærður á hverju kvöldi og er með virkni sem verið er að prófa sig áfram með. · Hins vegar gæti hann verið óstöðugari fyrir vikið. Sæktu betu útgáfu af vafranum ef þú vilt stöðugri vafra. @@ -915,8 +889,8 @@ Firefox Nightly er kominn á nýjan stað - ¶ - · Þetta forrit mun ekki lengur fá öryggisuppfærslur. Hættu að nota þetta forrit og skiptu yfir í nýja Nightly.¶ + + · Þetta forrit mun ekki lengur fá öryggisuppfærslur. Hættu að nota þetta forrit og skiptu yfir í nýja Nightly. · \n\nÞú ættir að búa til Firefox reikning til að búa til bókamerki, innskráningu og sögu í annað forrit. Skiptu yfir í nýja Nightly @@ -924,8 +898,8 @@ Firefox Nightly er kominn á nýjan stað - ¶ - · Þetta forrit mun ekki lengur fá öryggisuppfærslur. Sæktu nýja Nightly og hættu að nota þetta forrit.¶ + + · Þetta forrit mun ekki lengur fá öryggisuppfærslur. Sæktu nýja Nightly og hættu að nota þetta forrit. · \n\nÞú ættir að búa til Firefox reikning til að búa til bókamerki, innskráningu og sögu í annað forrit. Sæktu nýja Nightly @@ -936,78 +910,47 @@ Velkomin í %s! Ertu þegar með reikning? - - Kynntu þér %s - Hvað er nýtt! + Hvað er nýtt! - Ertu með spurningar um hið endurhannaða %s? Viltu vita hvað hefur breyst? + Ertu með spurningar um hið endurhannaða %s? Viltu vita hvað hefur breyst? - Fáðu svör hér - - Fáðu sem mest út úr %s. + Fáðu svör hér - - Þú ert skráð/ur inn sem %s í öðrum Firefox vafra á þessum síma. Viltu skrá þig inn með þessum reikningi? Já, skráðu mig inn Skrái inn… - - Firefox innskráning Vera útskráður Kveikt er á samstillingu Innskráning mistókst - - Sjálfvirkt næði - - Persónuverndar- og öryggisstillingar loka á rekjara, spilliforrit og fyrirtæki sem fylgja þér. Staðlað (sjálfgefið) - - Loka á færri rekjara. Síður hlaðast venjulega. Strangt (mælt með) Strangt - - Lokar á fleiri rekjara, auglýsingar og sprettiglugga. Síður hlaðast hraðar en sum virkni gæti verið í ólagi. - - Taktu afstöðu - - Prófaðu að vafra með einni hönd með neðri tækjastikunni eða færðu hana efst. - Huliðsvöfrun + Huliðsvöfrun - Opna huliðsflipa einu sinni: Snertu %s táknið. + Opna huliðsflipa einu sinni: Snertu %s táknið. - Opna huliðsflipa í hvert sinn: Uppfærðu huliðsstillingarnar þínar. + Opna huliðsflipa í hvert sinn: Uppfærðu huliðsstillingarnar þínar. - Opna stillingar + Opna stillingar Persónuvernd þín - - Við hönnuðum %s til að gefa þér stjórn yfir því hverju þú deilir¶ - á netinu og því sem þú deilir með okkur.¶ Lestu persónuverndartilkynningu okkar - Loka + Loka Fara að vafra @@ -1015,8 +958,6 @@ Veldu þema - - Sparaðu rafhlöðu og verndaðu sjónina með því að virkja dökka haminn. Sjálfvirk @@ -1069,14 +1010,10 @@ Fræðast meira Staðlað (sjálfgefið) - - Loka á færri rekjara. Síður hlaðast venjulega. Hvað er lokað af með staðlaðri rekjaravernd Strangt - - Lokar á fleiri rekjara, auglýsingar og sprettiglugga. Síður hlaðast hraðar en sum virkni gæti verið í ólagi. Hvað er lokað af með strangari rekjaravernd @@ -1108,6 +1045,7 @@ Dulritunaraðilar Fingraför + Lokað á Leyft @@ -1132,7 +1070,7 @@ Stöðvar utanaðkomandi auglýsingar myndbönd og annað efni sem inniheldur kóða til að rekja hegðun frá því að hlaðast. Getur haft áhrif á virkni vefsetra. - Í hvert skipti sem skjöldurinn er fjólublár þá hefur %s lokað fyrir rekjara á vefsetrinu. Snertu til að fá meiri upplýsingar. + Í hvert skipti sem skjöldurinn er fjólublár þá hefur %s lokað fyrir rekjara á vefsetrinu. Snertu til að fá meiri upplýsingar. Kveikt er á vörn fyrir þetta vefsvæði @@ -1198,9 +1136,6 @@ Heiti á flýtivísun - - Þú getur á auðveldan hátt bætt þessu vefsvæði á ræsiskjáinn þinn fyrir auðveldari aðgengi og hraðari vöfrun. - Innskráning og lykilorð @@ -1210,13 +1145,10 @@ Aldrei vista - Sjálfvirk útfylling + Sjálfvirk útfylling + Samstilla innskráningar - - Virkt - - Óvirkt Endurtengja @@ -1321,7 +1253,7 @@ Leitarstrengur sem á að nota - Skipta út fyrirspurninni með “%s”. Dæmi:\nhttps://www.google.com/search?q=%s + Skipta út fyrirspurninni með “%s”. Dæmi:\nhttps://www.google.com/search?q=%s Fræðast meira @@ -1370,9 +1302,9 @@ %1$s yfir í VIRKT]]> - Örugg tenging + Örugg tenging - Óörugg tenging + Óörugg tenging Ertu viss um að þú viljir hreinsa allar heimildir á öllum vefsvæðum? @@ -1412,7 +1344,7 @@ Hætta við breytingar Breyta - + Lykilorðs krafist Raddleit @@ -1423,8 +1355,6 @@ Innskráning með þessu notandanafni er þegar til - - Tengjast við Firefox reikning. Tengja annað tæki. @@ -1441,9 +1371,7 @@ Ekki hægt að bæta fleiri vefsetrum við efstu vefsvæði - - Fjarlægðu eina til að bæta við nýju efstu vefsvæði. Ýttu lengi á síðuna og veldu fjarlægja. Í lagi, ég skil - + From fc60b7a39ca6a7b3237fa3fc002080accdc38a2b Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 452/517] Strings - app/src/main/res/values-it/strings.xml --- app/src/main/res/values-it/strings.xml | 188 +++++++++++++++++++++---- 1 file changed, 158 insertions(+), 30 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 34ec594c8d..d9e8938480 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -26,7 +26,7 @@ Aperta 1 scheda. Tocca per cambiare scheda. - Aperte %1$s schede. Tocca per cambiare scheda. + Aperte %1$s schede. Tocca per cambiare scheda. %1$d selezionate @@ -55,7 +55,9 @@ Aggiunti di recente - Aggiunte ai segnalibri di recente + Aggiunte ai segnalibri di recente + + Segnalibri recenti Segnalibri aggiunti di recente @@ -106,6 +108,20 @@ Chiudi + + Le schede che non visualizzi da due settimane vengono spostate qui. + + Disattiva nelle impostazioni + + + Chiudi automaticamente dopo un mese? + + Firefox può chiudere le schede che non hai visualizzato nell’ultimo mese. + + Chiudi + + Attiva chiusura automatica + Nuova scheda @@ -113,7 +129,7 @@ Nuova scheda anonima - Siti principali + Siti principali @@ -121,6 +137,15 @@ Visualizza tutte + + Pulsante per mostrare tutte le schede recenti + + La tua ricerca per “%1$s” + + Siti: %1$s + @@ -128,11 +153,17 @@ - Visitati di recente + Visitati di recente + + Ricerche recenti Rimuovi + + Pulsante per mostrare tutti gli argomenti già esplorati + Schede aperte @@ -145,7 +176,7 @@ Interrompi - Aggiungi ai segnalibri + Aggiungi ai segnalibri Modifica segnalibro @@ -212,6 +243,8 @@ Personalizza pagina iniziale + + Personalizza pagina iniziale Schermata principale @@ -258,6 +291,29 @@ Cerca direttamente dalla barra degli indirizzi + + + Novità di Firefox + + Ora è più facile riprendere da dove avevi interrotto. + + Pagina iniziale di Firefox personalizzata + + Passa alle schede aperte, ai segnalibri e alla cronologia di navigazione. + + + Schede chiare e organizzate + + Riduci il caos generato da troppe schede aperte con un layout migliorato e chiusura automatica. + + Ricerche recenti + + + Rivedi le tue ultime ricerche effettuate dalla pagina iniziale e dalle schede. + + + La pagina iniziale personalizzata di Firefox rende più semplice riprendere da dove avevi interrotto. Puoi trovare le schede recenti, i segnalibri e i risultati delle ricerche. + Apri una nuova scheda in Firefox @@ -338,7 +394,9 @@ Tema - Pagina iniziale + Pagina iniziale + + Pagina iniziale Gesti @@ -415,9 +473,15 @@ Aggiunti di recente - Aggiunte ai segnalibri di recente - - Visitati di recente + Aggiunte ai segnalibri di recente + + Segnalibri recenti + + Visitati di recente + + Ricerche recenti Pocket @@ -533,7 +597,7 @@ Attiva Sync - Scansiona il codice di associazione in Firefox desktop + Scansiona il codice di associazione in Firefox desktop Accedi @@ -563,7 +627,7 @@ Scuro - Impostato da risparmio batteria + Impostato da Risparmio energetico Usa il tema del dispositivo @@ -641,6 +705,10 @@ Elenco Griglia + + Gruppi di ricerca + + Raggruppa siti correlati Chiudi schede @@ -652,15 +720,26 @@ Dopo un mese - + + Chiudi automaticamente le schede aperte + + - Inizia dalla schermata principale + Inizia dalla schermata principale + + Schermata di apertura - Dopo quattro ore + Dopo quattro ore + + Pagina iniziale - Sempre + Sempre + + Ultima scheda - Mai + Mai + + Pagina iniziale dopo quattro ore di inattività Chiudi manualmente @@ -670,6 +749,12 @@ Chiudi dopo un mese + + + Sposta le vecchie schede nella sezione Inattive + + La schede che non visualizzi da due settimane vengono spostate nella sezione Inattive. + Rimuovi @@ -713,13 +798,13 @@ Salva in una raccolta - Seleziona + Seleziona Condividi tutte le schede Schede chiuse di recente - Chiuse di recente + Chiuse di recente Impostazioni account @@ -794,7 +879,10 @@ Salva - Altre + Altre + + + Altre schede @@ -840,10 +928,6 @@ Nessuna cronologia disponibile. - - Cancella download - - Cancellare i download? Download eliminati @@ -885,7 +969,7 @@ Menu segnalibri - Modifica segnalibro + Modifica segnalibro Seleziona cartella @@ -1939,8 +2023,10 @@ OK, tutto chiaro + + Siti principali più visitati - Mostra i siti principali più visitati + Mostra i siti principali più visitati Mostra i siti più visitati @@ -1953,15 +2039,42 @@ Annulla - - + + Schede inattive + + Chiudi tutte le schede inattive - Le schede saranno disponibili qui per %s. Trascorso questo tempo, verranno chiuse automaticamente. + Le schede saranno disponibili qui per %s. Trascorso questo tempo, verranno chiuse automaticamente. - 30 giorni + 30 giorni - 1 settimana + 1 settimana + + + + Chiudi automaticamente dopo un mese? + + Firefox può chiudere le schede che non hai visualizzato nell’ultimo mese. + + + ATTIVA CHIUSURA AUTOMATICA + + + + Aiutaci a migliorare + + Perché hai disattivato le schede inattive? + + Non sono interessato alla funzione + + Il tempo di inattività è troppo lungo + + Il tempo di inattività è troppo breve + + Invia + + Chiudi Apri link da siti web, email e messaggi in Firefox per impostazione predefinita. @@ -1978,4 +2091,19 @@ Chiudi + + + Storie che fanno riflettere + + Storie che fanno riflettere + + Storie per argomento + + Scopri altre storie + + Sviluppato con tecnologia Pocket + + Parte della famiglia Firefox. %s + + Ulteriori informazioni From 25695eaa1be202190bb80670a6ffac8b8c4e39c7 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 453/517] Strings - app/src/main/res/values-ja/strings.xml --- app/src/main/res/values-ja/strings.xml | 125 +++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 2ab525e585..16fdcef221 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -56,7 +56,9 @@ 最近保存 - 最近追加したブックマーク + 最近追加したブックマーク + + 最近追加したブックマーク 最近保存したブックマーク @@ -110,6 +112,11 @@ 閉じる + + 2 週間以上表示していないタブをここに移動します。 + + 設定でオフにする + 新しいタブ @@ -125,6 +132,15 @@ すべて表示 + + 最近のタブをすべて表示するボタンです + + \"%1$s\" についての検索 + + サイト数: %1$s + @@ -132,11 +148,17 @@ - 最近訪れたサイト + 最近訪れたサイト + + 最近の検索 削除 + + 過去の検索をすべて表示するボタンです + タブを開きます @@ -215,6 +237,8 @@ カスタマイズホーム + + ホームページをカスタマイズ ホーム画面 @@ -261,6 +285,28 @@ アドレスバーから直接検索します + + + Firefox の新機能 + + + 前回のページから再開するのが簡単になりました。 + + 個人に合わせた Firefox ホームページ + + 開いているタブ、ブックマーク、ブラウジング履歴にジャンプします。 + + きれいに整理されたタブ + + 改善されたレイアウトとタブを自動的に閉じる機能で散らかったタブを片付けます。 + + 最近の検索 + + ホームページとタブから最近の検索を確認できます。 + + + 個人に合わせた Firefox ホームページにより、前回のページから簡単に再開できるようになりました。最近のタブ、ブックマーク、検索結果を見つけられます。 + 新しい Firefox タブを開く @@ -416,9 +462,15 @@ 最近保存したサイト - 最近追加したブックマーク - - 最近訪れたサイト + 最近追加したブックマーク + + 最近追加したブックマーク + + 最近訪れたサイト + + 最近の検索 Pocket @@ -638,6 +690,10 @@ リスト グリッド + + タブグループを検索 + + 関連サイトをグループ化します タブを閉じる期間 @@ -649,6 +705,9 @@ 1 か月後 + + 開いたタブを自動的に閉じる + ホーム画面で開始 @@ -667,6 +726,12 @@ 1 か月後に閉じる + + + 古いタブを休止中に移動する + + 2 週間以上表示していないタブを休止中セクションに移動します。 + 削除 @@ -790,7 +855,10 @@ 保存 - その他 + その他 + + + 他のタブ @@ -1935,9 +2003,11 @@ キャンセル - - + + 休止中のタブ + + 休止中のタブをすべて閉じます これらのタブは %sを過ぎると自動的に閉じられます。 @@ -1945,6 +2015,30 @@ 1 週間 + + + 1 か月後に自動的に閉じますか? + + Firefox は 1 か月以上表示していないタブを閉じることができます。 + + 自動的に閉じる + + + + 改善にご協力ください + + タブの休止を無効にしたのはなぜですか? + + この機能に興味がない + + 休止になるまでの時間が長すぎる + + 休止になるまでの時間が短すぎる + + 送信 + + 閉じる + ウェブサイトやメール、メッセージのリンクを自動的に Firefox で開きます。 @@ -1960,4 +2054,19 @@ 閉じる + + + 示唆に富むストーリー + + 示唆に富むストーリー + + トピック別のストーリー + + より詳しく + + Powered by Pocket. + + Firefox ファミリーの一員です。 %s + + 詳細情報 From 11f85940f3d558cf80faa8182ed063cf1d665931 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 454/517] Strings - app/src/main/res/values-kab/strings.xml --- app/src/main/res/values-kab/strings.xml | 175 ++++++++++++++++++++---- 1 file changed, 146 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml index c5495957e3..48140fab21 100644 --- a/app/src/main/res/values-kab/strings.xml +++ b/app/src/main/res/values-kab/strings.xml @@ -24,7 +24,7 @@ 1 yiccer i yeldin. Sit akken ad tbeddleḍ iccer. - %1$s waccaren yeldin. Sit akken ad tettbeddileḍ gar waccaren. + %1$s waccaren yeldin. Sit akken ad tettbeddileḍ gar waccaren. %1$d yettwafren @@ -56,7 +56,9 @@ Yettwasekles melmi kan - Yettwacreḍ melmi kan + Yettwacreḍ melmi kan + + Ticraḍ n yisebtar n melmi kan Ticraḍ n yisebtar yettwaskelsen melmi kan @@ -109,6 +111,20 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Zgel + + Accaren ur tesneqdeḍ ara snat ledwaṛ ad ttusnekzen ɣer da. + + Sens-it deg yiɣewwaren + + + Amdal awurman seld yiwen wayyur? + + Firefox izmer ad imdel accaren i twalaḍ aggur iɛeddan. + + Mdel + + + Iccer amaynut @@ -116,7 +132,7 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Iccer uslig amaynut - Ismal ufrinen + Ismal ufrinen @@ -124,6 +140,15 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Sken kullec + + Sken tqeffalt n meṛṛa accaren n melmi kan + + Anadi-inek•inem i \"%1$s\" + + Ismal: %1$s + @@ -131,11 +156,17 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara - Yemmẓer melmi kan + Yemmẓer melmi kan + + Inadiyen n melmi kan Kkes + + Sken taqeffalt n usnirem iɛeddan + Ldi icarren @@ -148,7 +179,7 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Seḥbes - Creḍ asebter-agi + Creḍ asebter-agi Beddel tacreḍt n usebter @@ -213,6 +244,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Sagen asebter agejdan + + Sagen asebter agejdan Agilal agejdan @@ -258,6 +291,29 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Nadi srid seg ufeggag n tansiwin + + + D acu i d amaynut deg Firefox + + + Akka tura fessus ad tkemmleḍ ansi akken i tḥebseḍ. + + Asebter agejdan n Firefox yettwasagnen + + Ɛeddi ɣer waccaren yeldin, ticraḍ n yisebtar, d uzray n tunigin. + + Accaren zeddigen, yuddsen + + + Sfeḍ accaren ur tesriḍ ara s taneɣruft igerrzen akked waccaren n umdal awurman. + + Inadiyen imaynuten + + Ales rzu ɣer yinadiyen ineggura seg usebtar-inek•inem d waccaren. + + + Asebter-inek•inem n Firefox udmawan yettarra tura fessus ugar akemmel seg wanda akken i tḥebseḍ. Af accaren-ik•im n melmi kan, ticraḍ n yisebtar, d yigmaḍ n unadi. + Ldi iccer amaynut n Firefox @@ -337,7 +393,7 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Asentel - Asebter agejdan + Asebter agejdan Isillifen @@ -413,9 +469,15 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Yettwasekles melmi kan - Yettwacreḍ melmi kan - - Yemmẓer melmi kan + Yettwacreḍ melmi kan + + Ticraḍ n yisebtar n melmi kan + + Yemmẓer melmi kan + + Inadiyen n melmi kan Pocket @@ -529,7 +591,7 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Rmed Synch - Ḍumm-d tangalt n uciddi seg Firefox i uselkim + Ḍumm-d tangalt n uciddi seg Firefox i uselkim Qqen @@ -636,6 +698,10 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Tabdart Iẓiki + + Igrawen n unadi + + Ismal n yinagrawen i d-yittuɣalen lwaḥid Mdel iccaren @@ -647,15 +713,18 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Seld yiwen n waggur - + + Amdal awurman n waccaren yeldin + + - Bdu seg ugilal agejdan + Bdu seg ugilal agejdan - Seld rebεa n yisragen + Seld rebεa n yisragen - Yal tikkelt + Yal tikkelt - Urǧin + Urǧin Mdel s ufus @@ -667,6 +736,13 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Mdel seld yiwen wayyur + + + Senkez accaren iqburen ɣer irurmiden + + + Accaren ur tesneqdeḍ ara snat ledwaṛ ad ttusnekzen ɣer teggayt tarurmidt. + Kkes @@ -708,13 +784,13 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Sekles ɣer tagrumma - Fren + Fren Bḍu akk accaren Iccaren imedlen melmi kan - Yettwamdel melmi kan + Yettwamdel melmi kan Iɣewwaṛen n umiḍan @@ -788,7 +864,10 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Sekles - Wayeḍ + Wayeḍ + + + Accaren wiyaḍ @@ -834,10 +913,6 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Ulac amazray dagi - - Kkes isadaren - - D tidet tebɣiḍ ad tsefḍeḍ isadaren? Isadaren ttwakksen @@ -879,7 +954,7 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Umuɣ n ticraḍ n isebtar - Beddel tacreḍt n usebter + Beddel tacreḍt n usebter Fren akaram @@ -1915,7 +1990,7 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara IH, awi-t-id - Sken ismal yemmeẓren s waṭas + Sken ismal yemmeẓren s waṭas Sken-d ismal ittwarzan aṭas @@ -1928,15 +2003,42 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Sefsex - - + + Accaren arurmiden + + Mdel akk accaren irurmiden - Accaren llan da i %s. Seld aya, accaren ad uɣalen ad medlen s wudem awurman. + Accaren llan da i %s. Seld aya, accaren ad uɣalen ad medlen s wudem awurman. - 30 wussan + 30 wussan - 1dduṛt + 1dduṛt + + + + Amdal awurman seld yiwen wayyur? + + + Firefox izmer ad imdel accaren i twalaḍ aggur iɛeddan. + + RMED AMDAL AWURMAN + + + + Ttxil-k·m mudd-aɣ-d tallalt ad neddu ɣer sdat + + Ayɣer i tsenseḍ accaren irurmiden? + + Ur cliɛ ara deg tmahilt-a + + Akud n arurmid ɣezzif aṭas + + Akud n arurmid wezzil aṭas + + Azen + + Mdel Sbadu iseɣwan seg yismal web, seg yimaylen d yiznan i twaledyawt s wudem awurman deg Firefox. @@ -1953,4 +2055,19 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Mdel + + + Tiqsiḍin i ijebbden lwelha + + Tiqsiḍin i ijebbden lwelha + + Smizzwer s usentel + + Issin ugar + + Sɣur Pocket. + + D aḥric seg twacult Firefox. %s + + Issin ugar From 598c7f88a3b01d87172577313425c05cffffffc7 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 455/517] Strings - app/src/main/res/values-kk/strings.xml --- app/src/main/res/values-kk/strings.xml | 103 ++++++++++++++++++------- 1 file changed, 73 insertions(+), 30 deletions(-) diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 7c3032a443..1f3b708fe5 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -23,7 +23,7 @@ 1 ашық бет. Беттерді ауыстыру үшін шертіңіз. - %1$s ашық бет. Беттерді ауыстыру үшін шертіңіз. + %1$s ашық бет. Беттерді ауыстыру үшін шертіңіз. %1$d таңдалды @@ -53,7 +53,9 @@ Жуырда сақталған - Жуырдағы бетбелгілер + Жуырдағы бетбелгілер + + Жуырдағы бетбелгілер Жуырда сақталған бетбелгілер @@ -105,13 +107,27 @@ Тайдыру + + Сіз екі апта бойы қарамаған беттер осында жылжытылады. + + + Баптауларда сөндіру + + Бір айдан кейін автожабу керек пе? + + Firefox соңғы айда қаралмаған беттерді жаба алады. + + Жабу + + Автожабуды іске қосу + Жаңа бет Жаңа жекелік беті - Үздік сайттар + Үздік сайттар @@ -119,6 +135,15 @@ Барлығын көрсету + + Барлық соңғы беттерді көрсету батырмасы + + \"%1$s\" бойынша іздеуіңіз + + Сайттар: %1$s + @@ -126,7 +151,10 @@ - Жуырда қаралған + Жуырда қаралған + + Жуырдағы іздеулер Өшіру @@ -143,7 +171,7 @@ Тоқтату - Бетбелгілерге қосу + Бетбелгілерге қосу Бетбелгіні түзету @@ -206,6 +234,8 @@ Үй бетін баптау + + Үй бетін баптау Бастапқы экран @@ -252,6 +282,10 @@ Адрестік жолақтан тікелей іздеу + + + Firefox ішінде не жаңалық + Жаңа Firefox бетін ашу @@ -330,7 +364,9 @@ Тема - Үйге + Үйге + + Үй парағы Ым қимылдар @@ -403,9 +439,15 @@ Жуырда сақталған - Жуырдағы бетбелгілер - - Жуырда қаралған + Жуырдағы бетбелгілер + + Жуырдағы бетбелгілер + + Жуырда қаралған + + Жуырдағы іздеулер Pocket @@ -516,7 +558,7 @@ Синхрондауды іске қосу - Жұмыс үстеліндегі Firefox-та жұптау кодын сканерлеңіз + Жұмыс үстеліндегі Firefox-та жұптау кодын сканерлеңіз Кіру @@ -632,15 +674,15 @@ Бір айдан кейін - + - Бастапқы экраннан бастау + Бастапқы экраннан бастау - Төрт сағаттан кейін + Төрт сағаттан кейін - Әрқашан + Әрқашан - Ешқашан + Ешқашан Қолмен жабу @@ -691,13 +733,13 @@ Жинаққа сақтау - Таңдау + Таңдау Барлық беттермен бөлісу Жуырда жабылған беттер - Жақында жабылған + Жақында жабылған Тіркелгі баптаулары @@ -771,7 +813,7 @@ Сақтау - Басқа + Басқа @@ -816,10 +858,6 @@ Осында тарих жоқ - - Жүктемелерді тазарту - - Жүктемелеріңізді өшіруді шынымен қалайсыз ба? Жүктемелер өшірілді @@ -860,7 +898,7 @@ Бетбелгілер мәзірі - Бетбелгіні түзету + Бетбелгіні түзету Буманы таңдау @@ -1893,7 +1931,7 @@ Жақсы, түсіндім - Ең көп қаралған үздік сайттарды көрсету + Ең көп қаралған үздік сайттарды көрсету Ең көп қаралған сайттарды көрсету @@ -1906,15 +1944,20 @@ Бас тарту - - + + Белсенді емес беттер - Беттер осында %s ішінде қолжетімді. Осы уақыттан кейін беттер автоматты түрде жабылады. + Беттер осында %s ішінде қолжетімді. Осы уақыттан кейін беттер автоматты түрде жабылады. - 30 күн + 30 күн - 1 апта + 1 апта + + + Жіберу + + Жабу Веб-сайттар, эл. пошта хаттары және хабарламалардан сілтемелерді Firefox-та автоматты түрде ашылатындай етіп баптау. @@ -1931,4 +1974,4 @@ Жабу - + From 8712ac200aa79d0525dacebb4f153ca47109215b Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 456/517] Strings - app/src/main/res/values-kn/strings.xml --- app/src/main/res/values-kn/strings.xml | 169 ++++++++++--------------- 1 file changed, 65 insertions(+), 104 deletions(-) diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index f325017630..ecc7c217a1 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1,10 +1,11 @@ - + ಖಾಸಗಿ %s %s (ಖಾಸಗಿ) + ಮತ್ತಷ್ಟು ಆಯ್ಕೆಗಳು @@ -44,14 +45,6 @@ ಪರವಾಗಿಲ್ಲ - - - ಫೈರ್‌ಫಾಕ್ಸ್‌ಗೆ ವೇಗವಾಗಿ ಹೋಗಿ. ನಿಮ್ಮ ಮುಖಪುಟಕ್ಕೆ ವಿಜೆಟ್ ಸೇರಿಸಿ. - - ವಿಜೆಟ್ ಸೇರಿಸಿ - - ಈಗ ಬೇಡ - ಹೊಸ ಟ್ಯಾಬ್ @@ -99,8 +92,6 @@ ಪುಟದಲ್ಲಿ ಹುಡುಕು ಖಾಸಗಿ ಟ್ಯಾಬ್‌ - - ಹೊಸ ಟ್ಯಾಬ್ ಸಂಗ್ರಹಕ್ಕೆ ಉಳಿಸಿ @@ -124,6 +115,7 @@ ಅನ್ವಯಿಕದಲ್ಲಿ ತೆರೆಯಿರಿ ಗೋಚರಿಕೆ + ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಗುರುತಿಸಲಾಗದ URL ಯೋಜನೆ. @@ -141,8 +133,6 @@ ಸ್ಕ್ಯಾನ್ - - ಸರ್ಚ್ ಎಂಜಿನ್ ಸರ್ಚ್ ಎಂಜಿನ್ ಸಿದ್ದತೆಗಳು @@ -158,7 +148,6 @@ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ - ಹುಡುಕು @@ -334,9 +323,6 @@ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ - - ಜಾಗತಿಕವಾಗಿ ಆಫ್ ಮಾಡಲಾಗಿದೆ, ಅದನ್ನು ಆನ್ ಮಾಡಲು ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ. - ಟೆಲಿಮೆಟ್ರಿ @@ -413,8 +399,6 @@ ಇತರೆ ಪುಟಗುರುತುಗಳು ಇತಿಹಾಸ - - ಸಿಂಕ್ ಮಾಡಿದ ಹಾಳೆಗಳು ಓದಬೇಕಿರುವ ಪಟ್ಟಿ @@ -488,7 +472,7 @@ ತೆರೆದ ಟ್ಯಾಬ್‌ಗಳು - ತೆಗೆದು ಹಾಕು + ತೆಗೆದು ಹಾಕು %1$s (ಖಾಸಗಿ ಮೋಡ್) @@ -507,15 +491,15 @@ ಅಳಿಸು - ಪ್ರತಿ ಮಾಡು + ಪ್ರತಿ ಮಾಡು - ಹಂಚು + ಹಂಚು - ಹೊಸ ಹಾಳೆಯಲ್ಲಿ ತೆರೆ + ಹೊಸ ಹಾಳೆಯಲ್ಲಿ ತೆರೆ - ಹೊಸ ಖಾಸಗಿ ಹಾಳೆಯಲ್ಲ ತೆರೆ + ಹೊಸ ಖಾಸಗಿ ಹಾಳೆಯಲ್ಲ ತೆರೆ - ಅಳಿಸು + ಅಳಿಸು %1$d ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ @@ -534,6 +518,7 @@ ಇಲ್ಲಿ ಇತಿಹಾಸವಿಲ್ಲ + ಕ್ಷಮಿಸಿ. %1$s ಆ ಪುಟವನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. @@ -565,8 +550,6 @@ %1$s ಅಳಿಸಲಾಗಿದೆ ಫೋಲ್ಡರ್ ಸೇರಿಸು - - ಬುಕ್‌ಮಾರ್ಕ್ ಪರಿಷ್ಕರಿಸಲಾಗಿದೆ ಪುಟಗುರುತು ಉಳಿಸಲಾಗಿದೆ! @@ -663,15 +646,15 @@ ಆನ್ ಆಫ್ - + ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊವನ್ನು ಅನುಮತಿಸಿ ಸೆಲ್ಯುಲಾರ್ ಡೇಟಾದಲ್ಲಿ ಮಾತ್ರ ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊವನ್ನು ನಿರ್ಬಂಧಿಸಿ ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊ ವೈ-ಫೈನಲ್ಲಿ ಪ್ಲೇ ಆಗುತ್ತದೆ - + ಆಡಿಯೊವನ್ನು ಮಾತ್ರ ನಿರ್ಬಂಧಿಸಿ - + ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊವನ್ನು ನಿರ್ಬಂಧಿಸಿ ಆನ್ @@ -683,10 +666,6 @@ ಸಂಗ್ರಹಗಳು ಸಂಗ್ರಹ ಮೆನು - - ನಿಮಗೆ ಮುಖ್ಯವಾದ ವಿಷಯಗಳನ್ನು ಸಂಗ್ರಹಿಸಿ - - ನಂತರ ತ್ವರಿತ ಪ್ರವೇಶಕ್ಕಾಗಿ ಒಂದೇ ರೀತಿಯ ಹುಡುಕಾಟಗಳು, ಸೈಟ್‌ಗಳು ಮತ್ತು ಟ್ಯಾಬ್‌ಗಳನ್ನು ಒಟ್ಟುಗೂಡಿಸಿ. ಎಲ್ಲವನ್ನೂ ಆರಿಸು @@ -778,6 +757,8 @@ ಅಳಿಸಿ ಮತ್ತು ತೆರೆಯಿರಿ ಇದರಿಂದ ಸಾಮರ್ಥ್ಯ ಪಡೆದಿದೆ + + ಸಂಗ್ರಹವನ್ನು ಅಳಿಸಲಾಗಿದೆ @@ -820,7 +801,7 @@ ಅಳಿಸು - ರದ್ದು ಮಾಡು + ರದ್ದು ಮಾಡು ಪೂರ್ಣ ಪರದೆ ಮೋಡ್‌ಗೆ ಪ್ರವೇಶಿಸಲಾಗುತ್ತಿದೆ @@ -918,76 +899,45 @@ %sಗೆ ಸುಸ್ವಾಗತ! ಈಗಾಗಲೇ ಖಾತೆಯನ್ನು ಹೊಂದಿದ್ದೀರಾ? - - %s ಅನ್ನು ತಿಳಿದುಕೊಳ್ಳಿ - ಹೊಸತೇನಿದೆಯೆಂದು ನೋಡಿ + ಹೊಸತೇನಿದೆಯೆಂದು ನೋಡಿ - ಮರುವಿನ್ಯಾಸಗೊಳಿಸಲಾದ %s ಬಗ್ಗೆ ಪ್ರಶ್ನೆಗಳಿವೆಯೇ? ಏನನ್ನು ಬದಲಾಯಿಸಲಾಗಿದೆ ಎಂದು ತಿಳಿಯಲು ಬಯಸುವಿರಾ? + ಮರುವಿನ್ಯಾಸಗೊಳಿಸಲಾದ %s ಬಗ್ಗೆ ಪ್ರಶ್ನೆಗಳಿವೆಯೇ? ಏನನ್ನು ಬದಲಾಯಿಸಲಾಗಿದೆ ಎಂದು ತಿಳಿಯಲು ಬಯಸುವಿರಾ? - ಉತ್ತರಗಳನ್ನು ಇಲ್ಲಿ ಪಡೆಯಿರಿ - - %s ನಿಂದ ಹೆಚ್ಚಿನದನ್ನು ಪಡೆಯಿರಿ. - - ಈ ಫೋನ್‌ನಲ್ಲಿ ನೀವು ಮತ್ತೊಂದು ಫೈರ್‌ಫಾಕ್ಸ್ ಬ್ರೌಸರ್‌ನಲ್ಲಿ %s ಆಗಿ ಸೈನ್ ಇನ್ ಆಗಿದ್ದೀರಿ. ಈ ಖಾತೆಯೊಂದಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ನೀವು ಬಯಸುವಿರಾ? + ಉತ್ತರಗಳನ್ನು ಇಲ್ಲಿ ಪಡೆಯಿರಿ ಹೌದು, ನನ್ನನ್ನು ಸೈನ್ ಇನ್ ಮಾಡಿ ಸೈನ್ ಇನ್ ಮಾಡಲಾಗುತ್ತಿದೆ… - - ಫೈರ್‌ಫಾಕ್ಸ್‌ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ ಸೈನ್ ಔಟ್ ಆಗಿರಿ ಸಿಂಕ್ ಆನ್ ಆಗಿದೆ ಸೈನ್-ಇನ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ - - ಸ್ವಯಂಚಾಲಿತ ಗೌಪ್ಯತೆ - - ಗೌಪ್ಯತೆ ಮತ್ತು ಭದ್ರತಾ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಟ್ರ್ಯಾಕರ್‌ಗಳು, ಮಾಲ್‌ವೇರ್ ಮತ್ತು ನಿಮ್ಮನ್ನು ಅನುಸರಿಸುವ ಕಂಪನಿಗಳನ್ನು ನಿರ್ಬಂಧಿಸುತ್ತವೆ. ಪ್ರಮಾಣಿತ (ಡೀಫಾಲ್ಟ್) - - ಕಡಿಮೆ ಟ್ರ್ಯಾಕರ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸುತ್ತದೆ. ಪುಟಗಳು ಸಾಮಾನ್ಯವಾಗಿ ಲೋಡ್ ಆಗುತ್ತವೆ. ಕಟ್ಟುನಿಟ್ಟಾದ (ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ) ಕಡ್ಡಾಯ - - ಹೆಚ್ಚಿನ ಟ್ರ್ಯಾಕರ್‌ಗಳು, ಜಾಹೀರಾತುಗಳು ಮತ್ತು ಪಾಪ್‌ಅಪ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸುತ್ತದೆ. ಪುಟಗಳು ವೇಗವಾಗಿ ಲೋಡ್ ಆಗುತ್ತವೆ, ಆದರೆ ಕೆಲವು ಕಾರ್ಯಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೆ ಇರಬಹುದು. - - ದೇಣಿಗೆಯನ್ನು ನೀಡಿ - - ಕೆಳಗಿನ ಟೂಲ್‌ಬಾರ್‌ನೊಂದಿಗೆ ಒಂದು ಕೈಯ ಬ್ರೌಸಿಂಗ್ ಅನ್ನು ಪ್ರಯತ್ನಿಸಿ ಅಥವಾ ಅದನ್ನು ಮೇಲಕ್ಕೆ ಸರಿಸಿ. - ಮುಕ್ತವಾಗಿ ಜಾಲಾಡಿ + ಮುಕ್ತವಾಗಿ ಜಾಲಾಡಿ - ಖಾಸಗಿ ಟ್ಯಾಬ್ ಅನ್ನು ಒಮ್ಮೆ ತೆರೆಯಿರಿ: %s ಐಕಾನ್ ಟ್ಯಾಪ್ ಮಾಡಿ. + ಖಾಸಗಿ ಟ್ಯಾಬ್ ಅನ್ನು ಒಮ್ಮೆ ತೆರೆಯಿರಿ: %s ಐಕಾನ್ ಟ್ಯಾಪ್ ಮಾಡಿ. - ಪ್ರತಿ ಬಾರಿಯೂ ಖಾಸಗಿ ಟ್ಯಾಬ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ: ನಿಮ್ಮ ಖಾಸಗಿ ಬ್ರೌಸಿಂಗ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ನವೀಕರಿಸಿ. + ಪ್ರತಿ ಬಾರಿಯೂ ಖಾಸಗಿ ಟ್ಯಾಬ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ: ನಿಮ್ಮ ಖಾಸಗಿ ಬ್ರೌಸಿಂಗ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ನವೀಕರಿಸಿ. - ಸಿದ್ಧತೆಗಳನ್ನು ತೆರೆ + ಸಿದ್ಧತೆಗಳನ್ನು ತೆರೆ ನಿಮ್ಮ ಗೌಪ್ಯತೆ - - ನೀವು ಹಂಚಿಕೊಳ್ಳುವ ವಿಷಯದ ಮೇಲೆ ನಿಯಂತ್ರಣವನ್ನು ನೀಡಲು ನಾವು %s ಅನ್ನು ವಿನ್ಯಾಸಗೊಳಿಸಿದ್ದೇವೆ - ಆನ್‌ಲೈನ್ ಮತ್ತು ನೀವು ನಮ್ಮೊಂದಿಗೆ ಏನು ಹಂಚಿಕೊಳ್ಳುತ್ತೀರಿ. ನಮ್ಮ ಗೌಪ್ಯತೆ ಪ್ರಕಟಣೆಯನ್ನು ಓದಿ - ಮುಚ್ಚು + ಮುಚ್ಚು ಜಾಲವೀಕ್ಷಣೆಯನ್ನು ಆರಂಭಿಸಿ @@ -995,8 +945,6 @@ ನಿಮ್ಮ ಥೀಮ್ ಆಯ್ಕೆಮಾಡಿ - - ಡಾರ್ಕ್ ಮೋಡ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುವ ಮೂಲಕ ಸ್ವಲ್ಪ ಬ್ಯಾಟರಿ ಮತ್ತು ನಿಮ್ಮ ದೃಷ್ಟಿ ಉಳಿಸಿ. ಸ್ವಯಂಚಾಲಿತ @@ -1048,14 +996,10 @@ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ ಪ್ರಮಾಣಿತ (ಡೀಫಾಲ್ಟ್) - - ಕಡಿಮೆ ಟ್ರ್ಯಾಕರ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸುತ್ತದೆ. ಪುಟಗಳು ಸಾಮಾನ್ಯವಾಗಿ ಲೋಡ್ ಆಗುತ್ತವೆ. ಸ್ಟ್ಯಾಂಡರ್ಡ್ ಟ್ರ್ಯಾಕಿಂಗ್ ರಕ್ಷಣೆಯಿಂದ ಏನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ ಕಡ್ಡಾಯ - - ಹೆಚ್ಚಿನ ಟ್ರ್ಯಾಕರ್‌ಗಳು, ಜಾಹೀರಾತುಗಳು ಮತ್ತು ಪಾಪ್‌ಅಪ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸುತ್ತದೆ. ಪುಟಗಳು ವೇಗವಾಗಿ ಲೋಡ್ ಆಗುತ್ತವೆ, ಆದರೆ ಕೆಲವು ಕಾರ್ಯಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೆ ಇರಬಹುದು. ಕಟ್ಟುನಿಟ್ಟಾದ ಟ್ರ್ಯಾಕಿಂಗ್ ರಕ್ಷಣೆಯಿಂದ ಏನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ @@ -1087,6 +1031,7 @@ ಕ್ರಿಪ್ಟೋಗಣಿಗಾರರು ಬೆರಳಚ್ಚುಗಳು + ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ ಅನುಮತಿಸಲಾದ‍ @@ -1111,7 +1056,7 @@ ಟ್ರ್ಯಾಕಿಂಗ್ ಕೋಡ್ ಹೊಂದಿರುವ ಲೋಡ್ ಮಾಡುವುದರಿಂದ ಜಾಹೀರಾತುಗಳು, ವೀಡಿಯೊಗಳು ಮತ್ತು ಇತರ ವಿಷಯವನ್ನು ಹೊರಗೆ ನಿಲ್ಲಿಸುತ್ತದೆ. ಕೆಲವು ವೆಬ್‌ಸೈಟ್ ಕ್ರಿಯಾತ್ಮಕತೆಯ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರಬಹುದು. - ಗುರಾಣಿ ನೇರಳೆ ಬಣ್ಣದ್ದಾಗಲೆಲ್ಲಾ, %s ಸೈಟ್‌ನಲ್ಲಿ ಟ್ರ್ಯಾಕರ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸುತ್ತದೆ. ಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ. + ಗುರಾಣಿ ನೇರಳೆ ಬಣ್ಣದ್ದಾಗಲೆಲ್ಲಾ, %s ಸೈಟ್‌ನಲ್ಲಿ ಟ್ರ್ಯಾಕರ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸುತ್ತದೆ. ಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ. ಈ ಸೈಟ್‌ಗಾಗಿ ರಕ್ಷಣೆಗಳು ಆನ್ ಆಗಿವೆ @@ -1174,8 +1119,6 @@ ಶಾರ್ಟ್ಕಟ್ ಹೆಸರು - - ನೀವು ಸುಲಭವಾಗಿ ಈ ಜಾಲತಾಣವನ್ನು ನಿಮ್ಮ ಫೋನಿನ ಮುಖ್ಯ ಪರದೆಗೆ ಸೇರಿಸಿ ತಕ್ಷಣದ ಪ್ರವೇಶ ಪಡೆಯಬಹುದು ಮತ್ತು ಆಪ್‍ನಲ್ಲಿಯೇ ಶೀಘ್ರವಾಗಿ ಜಾಲಾಡುವ ಅನುಭವ ಪಡೆಯಬಹುದು. ಲಾಗಿನ್‌ಗಳು ಮತ್ತು ಪಾಸ್‌ವರ್ಡ್‌ಗಳು @@ -1186,13 +1129,10 @@ ಎಂದಿಗೂ ಉಳಿಸಬೇಡಿ - ಸ್ವಯಂತುಂಬುವಿಕೆ + ಸ್ವಯಂತುಂಬುವಿಕೆ + ಲಾಗಿನ್‌ಗಳನ್ನು ಸಿಂಕ್ ಮಾಡಿ - - ಆನ್ - - ಆಫ್ ಮರುಸಂಪರ್ಕಿಸು @@ -1276,6 +1216,18 @@ ಲಾಗಿನ್‌ಗಳ ಮೆನುವನ್ನು ವಿಂಗಡಿಸಿ + + + ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್‌ಗಳು + + ಕಾರ್ಡ್‌ಗಳನ್ನು ಉಳಿಸಿ ಮತ್ತು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ತುಂಬಿಸಿ + + ಡೇಟಾವನ್ನು ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ + + ಸಾಧನಗಳಲ್ಲಿ ಕಾರ್ಡ್‌ಗಳನ್ನು ಸಿಂಕ್ ಮಾಡಿ + + ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸೇರಿಸಿ + ಹುಡುಕು ಸಾಧನವನ್ನು ಸೇರಿಸಿ @@ -1296,7 +1248,7 @@ ಬಳಸಲು ಸ್ಟ್ರಿಂಗ್ ಹುಡುಕಿ - ಪ್ರಶ್ನೆಯನ್ನು “%s” ನೊಂದಿಗೆ ಬದಲಾಯಿಸಿ. ಉದಾಹರಣೆ: \n https://www.google.com/search?q=%s + ಪ್ರಶ್ನೆಯನ್ನು “%s” ನೊಂದಿಗೆ ಬದಲಾಯಿಸಿ. ಉದಾಹರಣೆ: \n https://www.google.com/search?q=%s ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ @@ -1344,9 +1296,9 @@ %1$s ಅನ್ನು ಆನ್‌ಗೆ ಟಾಗಲ್ ಮಾಡಿ]]> - ಸುರಕ್ಷಿತ ಸಂಪರ್ಕ + ಸುರಕ್ಷಿತ ಸಂಪರ್ಕ - ಅಸುರಕ್ಷಿತ ಸಂಪರ್ಕ + ಅಸುರಕ್ಷಿತ ಸಂಪರ್ಕ ಎಲ್ಲಾ ಸೈಟ್‌ಗಳಲ್ಲಿನ ಎಲ್ಲಾ ಅನುಮತಿಗಳನ್ನು ತೆರವುಗೊಳಿಸಲು ನೀವು ಬಯಸುತ್ತೀರಾ? @@ -1387,7 +1339,7 @@ ಬದಲಾವಣೆಗಳನ್ನು ತ್ಯಜಿಸಿ ಸಂಪಾದಿಸಿ - + ಗುಪ್ತಪದದ ಅಗತ್ಯವಿದೆ ಧ್ವನಿ ಹುಡುಕಾಟ @@ -1398,8 +1350,6 @@ ಆ ಬಳಕೆದಾರ ಹೆಸರಿನಲ್ಲಿನ ಲಾಗಿನ್ ಈಗಾಗಲೇ ಅಸ್ತಿತ್ವದಲ್ಲಿದೆ - - ಫೈರ್‌ಫಾಕ್ಸ್ ಖಾತೆಯೊಂದಿಗೆ ಸಂಪರ್ಕ ಸಾಧಿಸಿ. ಮತ್ತೊಂದು ಸಾಧನಕ್ಕೆ ಸಂಪರ್ಕವನ್ನು ಸಾಧಿಸಿ. @@ -1413,21 +1363,32 @@ ಸಿಂಕ್ ಮಾಡಲು ಸೈನ್ ಇನ್ ಆಗಿ + + ಯಾವುದೆ ತೆರೆದ ಟ್ಯಾಬ್‌ಗಳಿಲ್ಲ + ಉನ್ನತ ಸೈಟ್ ಮಿತಿಯನ್ನು ತಲುಪಿದೆ - ಹೊಸ ಉನ್ನತ ತಾಣ ಸೇರಿಸಲು, ಒಂದನ್ನು ತೆಗೆದುಹಾಕಿ. ತಾಣವನ್ನು ದೀರ್ಘಕಾಲ ಒತ್ತಿ ಮತ್ತು ತೆಗೆದುಹಾಕಿ ಆಯ್ಕೆಮಾಡಿ. + ಹೊಸ ಉನ್ನತ ಸೈಟ್ ಸೇರಿಸಲು, ಒಂದನ್ನು ತೆಗೆದುಹಾಕಿ. ಸೈಟ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ ಮತ್ತು ತೆಗೆದುಹಾಕಿ ಆಯ್ಕೆಮಾಡಿ. ಸರಿ, ಗೊತ್ತಾಯಿತು - - - ಕಿರುಹಾದಿಗಳು - - ಇದರೊಂದಿಗೆ ಹುಡುಕು - - ಈ ಸಮಯದಲ್ಲಿ, ಇದರೊಂದಿಗೆ ಹುಡುಕಿ: - - ಹುಡುಕಾಟ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ತೋರಿಸಿ + + ಹೆಚ್ಚು ಭೇಟಿ ನೀಡಿದ ಸೈಟ್‌ಗಳನ್ನು ತೋರಿಸಿ + + ಹೆಸರು + + ಟಾಪ್ ಸೈಟ್ ಹೆಸರು + + ಸರಿ + + ರದ್ದು ಮಾಡಿ + + + ತೆಗೆದು ಹಾಕಿ + + + ಹೆಚ್ಚಿನ ವಿವರಗಳಿಗಾಗಿ ಕ್ಲಿಕ್ ಮಾಡಿ + From 898b5888c7cf73600e36bc04633d020c0ef92cc0 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:51 +0000 Subject: [PATCH 457/517] Strings - app/src/main/res/values-ko/strings.xml --- app/src/main/res/values-ko/strings.xml | 186 +++++++++++++++++++++---- 1 file changed, 156 insertions(+), 30 deletions(-) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index b81b90e7ba..af26e7ca98 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -26,7 +26,7 @@ 열린 탭 1개. 탭을 전환하려면 누르세요. - 열린 탭 %1$s개. 탭을 전환하려면 누르세요. + 열린 탭 %1$s개. 탭을 전환하려면 누르세요. %1$d개 선택됨 @@ -58,7 +58,9 @@ 최근에 저장됨 - 최근에 북마크됨 + 최근에 북마크됨 + + 최근 북마크 최근에 저장된 북마크 @@ -113,6 +115,20 @@ 닫기 + + 2주 동안 보지 않은 탭은 여기로 이동됩니다. + + 설정에서 끄기 + + + 한 달 후 자동으로 닫으시겠습니까? + + Firefox는 지난 한 달 동안 보지 않은 탭을 닫을 수 있습니다. + + 닫기 + + 자동 닫기 켜기 + 새 탭 @@ -120,7 +136,7 @@ 새 사생활 보호 탭 - 상위 사이트 + 상위 사이트 @@ -128,6 +144,16 @@ 모두 표시 + + 모든 최근 탭 표시 버튼 + + + \"%1$s\"에 대한 검색 + + 사이트: %1$s개 + @@ -135,11 +161,17 @@ - 최근에 방문함 + 최근에 방문함 + + 최근 검색 삭제 + + 모든 지난 탐색 표시 버튼 + 탭 열기 @@ -152,7 +184,7 @@ 중지 - 북마크 + 북마크 북마크 편집 @@ -164,7 +196,7 @@ 도움말 - 새로운 기능 + 새 기능 설정 @@ -219,6 +251,8 @@ 홈 사용자 지정 + + 홈페이지 개인화 홈 화면 @@ -265,6 +299,27 @@ 주소 표시줄에서 직접 검색 + + + Firefox의 새 기능 + + 이제 이전에 사용하던 부분을 쉽게 찾을 수 있습니다. + + 개인화된 Firefox 홈페이지 + + 열린 탭, 북마크 및 방문 기록으로 이동합니다. + + 깔끔하게 정리된 탭 + + 향상된 레이아웃과 자동 탭 닫기로 탭을 깔끔하게 정리하세요. + + 최근 검색 + + 홈페이지와 탭에서 최근 검색을 다시 방문하세요. + + + 이제 개인화된 Firefox 홈페이지에서 이전에 사용하던 부분을 쉽게 찾을 수 있습니다. 최근 탭, 북마크 및 검색 결과를 찾으세요. + 새 Firefox 탭 열기 @@ -344,7 +399,9 @@ 테마 - + + + 홈페이지 제스처 @@ -419,9 +476,15 @@ 최근에 저장됨 - 최근에 북마크됨 - - 최근에 방문함 + 최근에 북마크됨 + + 최근 북마크 + + 최근에 방문함 + + 최근 검색 포켓 @@ -536,7 +599,7 @@ Sync 켜기 - 데스크톱 Firefox에서 페어링 코드를 스캔하세요 + 데스크톱 Firefox에서 페어링 코드를 스캔하세요 로그인 @@ -643,6 +706,10 @@ 목록 그리드 + + 그룹 검색 + + 관련된 사이트를 서로 그룹화 탭 닫기 @@ -654,15 +721,26 @@ 한 달 후 - + + 열린 탭 자동으로 닫기 + + - 홈에서 시작 + 홈에서 시작 + + 오프닝 화면 - 4시간 후 + 4시간 후 + + 홈페이지 - 항상 + 항상 + + 마지막 탭 - 안 함 + 안 함 + + 4시간 동안 사용하지 않을 때의 홈페이지 수동으로 닫기 @@ -672,6 +750,12 @@ 한 달 후 닫기 + + + 이전 탭을 비활성으로 이동 + + 2주 동안 보지 않은 탭은 비활동 섹션으로 이동됩니다. + 삭제 @@ -714,13 +798,13 @@ 모음집에 저장 - 선택 + 선택 모든 탭 공유 최근에 닫은 탭 - 최근에 닫음 + 최근에 닫음 계정 설정 @@ -796,7 +880,10 @@ 저장 - 기타 + 기타 + + + 기타 탭 @@ -842,10 +929,6 @@ 기록 없음 - - 다운로드 삭제 - - 다운로드를 삭제하시겠습니까? 다운로드 삭제됨 @@ -888,7 +971,7 @@ 북마크 메뉴 - 북마크 편집 + 북마크 편집 폴더 선택 @@ -1950,8 +2033,10 @@ 확인 + + 자주 방문한 상위 사이트 - 자주 방문한 상위 사이트 표시 + 자주 방문한 상위 사이트 표시 자주 방문한 사이트 표시 @@ -1965,15 +2050,41 @@ 취소 - - + + 비활성 탭 + + 모든 비활성 탭 닫기 - 탭은 여기에 %s 동안 유지됩니다. 그 이후에는 자동으로 닫힙니다. + 탭은 여기에 %s 동안 유지됩니다. 그 이후에는 자동으로 닫힙니다. - 30일 + 30일 - 1주 + 1주 + + + + 한 달 후 닫으시겠습니까? + + Firefox는 지난 한 달 동안 보지 않은 탭을 닫을 수 있습니다. + + 자동 닫기 켜기 + + + + 개선할 수 있도록 도와주세요 + + 비활성 탭 기능을 사용 안하시는 이유는 무엇입니까? + + 기능에 관심이 없음 + + 비활성 시간이 너무 김 + + 비활성 시간이 너무 짧음 + + 보내기 + + 닫기 Firefox에서 자동으로 열리도록 웹 사이트, 이메일 및 메시지의 링크를 설정합니다. @@ -1990,4 +2101,19 @@ 닫기 + + + 생각하게 하는 이야기 + + 생각하게 하는 이야기 + + 주제별 이야기 + + 더 발견하기 + + 포켓 제공 + + Firefox 제품군의 일부입니다. %s + + 더 알아보기 From c4e2f08ff3a227b28d919cb357e1ee4c0e80067f Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 458/517] Strings - app/src/main/res/values-lt/strings.xml --- app/src/main/res/values-lt/strings.xml | 269 +++++++++++++++++++++---- 1 file changed, 229 insertions(+), 40 deletions(-) diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index f5303c418c..c7fe9a22d5 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -24,7 +24,7 @@ 1 atverta kortelė. Bakstelėkite, norėdami pereiti tarp kortelių. - %1$s atvertos kortelės. Bakstelėkite, norėdami pereiti tarp kortelių. + %1$s atvertos kortelės. Bakstelėkite, norėdami pereiti tarp kortelių. Pasirinkta %1$d @@ -51,7 +51,11 @@ - Paskiausiai įtraukta + Paskiausiai įtraukta + + Paskiausi adresyno įrašai + + Paskiausi adresyno įrašai Paskiausiai įtraukti adresyno įrašai @@ -103,6 +107,20 @@ Paslėpti + + Čia perkeliamos paskutines dvi savaites nenaudotos kortelės. + + Išjunkite nustatymuose + + + Užverti automatiškai po mėnesio? + + „Firefox“ gali užverti korteles, kuriomis paskutinį mėnesį nesinaudojote. + + Užverti + + Įjungti automatinį užvėrimą + Nauja kortelė @@ -110,7 +128,7 @@ Nauja privačioji kortelė - Lankomiausios svetainės + Lankomiausios svetainės @@ -118,10 +136,32 @@ Rodyti viską + + Mygtukas visų paskiausių kortelių rodymui + + + Jūsų paieška „%1$s“ + + Svetainės: %1$s + - Ankstesni tyrinėjimai + Ankstesni tyrinėjimai + + + Paskiausiai lankyta + + Paskiausios paieškos + + Pašalinti + + Rodyti visų ankstesnių tyrinėjimų mygtuką @@ -135,7 +175,7 @@ Stabdyti - Įtraukti į adresyną + Įtraukti į adresyną Redaguoti adresyno įrašą @@ -198,6 +238,10 @@ Keisti + + Tvarkyti pradžią + + Tvarkyti pradžios tinklalapį Pradžios ekranas @@ -243,6 +287,27 @@ Ieškokite tiesiai iš adreso lauko + + + Kas naujo „Firefox“ + + Dabar lengviau tęsti nuo ten, kur baigėte. + + Suasmenintas „Firefox“ pradžios tinklalapis + + Pereikite prie atvertų kortelių, adresyno, ir naršymo istorijos. + + Švarios, tvarkingos kortelės + + Išvalykite kortelių netvarką su pagerintu išdėstymu ir automatiniu kortelių užvėrimu. + + Paskiausios paieškos + + Peržiūrėkite savo paskiausias paieškas iš pradžios tinklalapio ir kortelių. + + + Suasmenintame „Firefox“ pradžios tinklalapyje dabar lengviau tęsti nuo ten, kur baigėte. Raskite paskiausias korteles, adresyno įrašus, ir paieškos rezultatus. + Atverti naują „Firefox“ kortelę @@ -321,7 +386,9 @@ Grafinis apvalkalas - Pradžia + Pradžia + + Pradžios tinklalapis Gestai @@ -389,6 +456,24 @@ Pakeistas priedų rinkinys. Išeinama iš programos, kad būtų pritaikyti pakeitimai… + + + Sugrįžkite + + Paskiausiai įtraukta + + Paskiausi adresyno įrašai + + Paskiausi adresyno įrašai + + Paskiausiai lankyta + + Paskiausios paieškos + + Pocket + Priedas nepalaikomas @@ -500,7 +585,7 @@ Įjungti „Sync“ - Nuskaityti susiejimo kodą per kompiuterio „Firefox“ + Nuskaityti susiejimo kodą per kompiuterio „Firefox“ Prisijungti @@ -577,6 +662,13 @@ Užverti + + %d svetainė + + %d svetainės + Paskiausiai užvertos kortelės @@ -599,6 +691,10 @@ Sąrašas Tinklelis + + Paieškos grupės + + Grupuoti susijusias svetaines kartu Užverti korteles @@ -610,15 +706,26 @@ Po vieno mėnesio - + + Automatiškai užverti korteles + + - Įjungus rodyti pradžios ekraną + Įjungus rodyti pradžios ekraną + + Vaizdas po paleidimo - Po keturių valandų + Po keturių valandų + + Pradžios tinklalapis - Visada + Visada + + Paskutinė kortelė - Niekada + Niekada + + Pradžios tinklalapis po keturių valandų neveiklumo Užverti rankiniu būdu @@ -628,6 +735,12 @@ Užverti po mėnesio + + + Perkelti senas korteles prie neaktyvių + + Kortelės, kurių paskutines dvi savaites nenaudojote, bus perkeltos į neaktyvių skiltį. + Pašalinti @@ -668,13 +781,13 @@ Įtraukti į rinkinį - Pasirinkti + Pasirinkti Dalintis visomis kortelėmis Paskiausiai užvertos kortelės - Paskiausiai užverta + Paskiausiai užverta Paskyros nuostatos @@ -747,6 +860,11 @@ Įrašyti + + Kita + + Kitos kortelės + Išvalyti žurnalą @@ -760,15 +878,15 @@ Išvalyti - Kopijuoti + Kopijuoti - Dalintis + Dalintis - Atverti naujoje kortelėje + Atverti naujoje kortelėje - Atverti privačiojoje kortelėje + Atverti privačiojoje kortelėje - Pašalinti + Pašalinti Pasirinkta: %1$d @@ -791,10 +909,6 @@ Žurnalų įrašų nėra - - Pašalinti atsiuntimus - - Ar tikrai norite išvalyti savo atsiuntimus? Atsiuntimai pašalinti @@ -836,7 +950,7 @@ Adresyno meniu - Redaguoti adresyno įrašą + Redaguoti adresyno įrašą Pasirinkite aplanką @@ -1244,13 +1358,13 @@ Jau turite paskyrą? - Susipažinkite su naujovėmis + Susipažinkite su naujovėmis - Turite klausimų apie naująją „%s“? Norite sužinoti, kas pasikeitė? + Turite klausimų apie naująją „%s“? Norite sužinoti, kas pasikeitė? - Atsakymus rasite čia + Atsakymus rasite čia Sinchronizuokite „Firefox“ duomenis tarp įrenginių @@ -1292,14 +1406,14 @@ Pasidėkite priemonių juostą patogiai. Palikite ją apačioje, arba perkelkite į viršų. - Naršykite privačiai + Naršykite privačiai - Atverti privačiąją kortelę kartą: bakstelėkite %s piktogramą + Atverti privačiąją kortelę kartą: bakstelėkite %s piktogramą - Atverti privačiąsias korteles visada: pakeiskite savo privačiojo naršymo nuostatas + Atverti privačiąsias korteles visada: pakeiskite savo privačiojo naršymo nuostatas - Atverti nuostatas + Atverti nuostatas Jūsų privatumas Išvalo slapukus, įrašomus nukreipiant į žinomas stebėjimo svetaines. + + Kai kurie žemiau pažymėti stebėjimo elementai yra dalinai neblokuojami šiame tinklalapyje, nes su jais atlikote veiksmų *. + + Sužinoti daugiau + Žinynas @@ -1533,6 +1653,9 @@ Užpildyti prisijungimus kitose programose jūsų įrenginyje. + + Pridėti prisijungimą + Sinchronizuoti prisijungimus @@ -1596,6 +1719,8 @@ Kopijuoti naudotojo vardą Išvalyti naudotojo vardą + + Išvalyti serverio vardą Kopijuoti svetainę @@ -1769,9 +1894,13 @@ %1$s]]> - Saugus ryšys + Ryšys yra saugus - Nesaugus ryšys + Ryšys nėra saugus + + Saugus ryšys + + Nesaugus ryšys Ar tikrai norite išvalyti visus leidimus visose svetainėse? @@ -1811,8 +1940,14 @@ Atsisakyti pakeitimų Keisti - + + Pridėti naują prisijungimą + Reikalingas slaptažodis + + Naudotojo vardas privalomas + + Serverio vardas privalomas Paieška balsu @@ -1821,6 +1956,15 @@ Prisijungimas su tokiu naudotojo vardu jau yra + + https://www.example.com + + Svetainės adresas privalo turėti „https://“ arba „http://“ + + Svetainės adresas privalo turėti „https://“ arba „http://“ + + Privalomas tinkamas serverio vardas + Susiekite kitą įrenginį. @@ -1847,8 +1991,12 @@ Gerai, supratau + + Lankomiausios svetainės + + Rodyti lankomiausias svetaines - Rodyti lankomiausias svetaines + Rodyti lankomiausias svetaines Pavadinimas @@ -1859,15 +2007,41 @@ Atsisakyti - - + + Neaktyvios kortelės + + Užverti visas neaktyvias korteles - Kortelės čia bus laikomos %s. Po šio laiko jos bus automatiškai užvertos. + Kortelės čia bus laikomos %s. Po šio laiko jos bus automatiškai užvertos. - 30 dienų + 30 dienų - 1 savaitę + 1 savaitę + + + + Užverti automatiškai po mėnesio? + + „Firefox“ gali užverti korteles, kuriomis paskutinį mėnesį nesinaudojote. + + ĮJUNGTI AUTOMATINĮ UŽVĖRIMĄ + + + + Padėkite mums tobulėti + + Kodėl išjungėte neaktyvias korteles? + + Ši funkcija nereikalinga + + Per ilgas laikas iki perkėlimo prie neaktyvių + + Per trumpas laikas iki perkėlimo prie neaktyvių + + Siųsti + + Užverti Leiskite saitams iš svetainių, el. laiškų, ir žinučių būti automatiškai atvertiems per „Firefox“. @@ -1884,4 +2058,19 @@ Užverti + + + Susimąstyti verčiančios istorijos + + Susimąstyti verčiančios istorijos + + Straipsniai pagal temas + + Atrasti daugiau + + Veikia su „Pocket“ + + „Firefox“ šeimos dalis. %s + + Sužinoti daugiau From 280ed36fafc298dd66e5ef7da2fa78624a85babd Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 459/517] Strings - app/src/main/res/values-nb-rNO/strings.xml --- app/src/main/res/values-nb-rNO/strings.xml | 214 +++++++++++++++++++-- 1 file changed, 194 insertions(+), 20 deletions(-) diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 251b054643..192e2c3b5a 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -51,7 +51,11 @@ - Nylig lagret + Nylig lagret + + Nylig bokmerket + + Nylige bokmerker Nylig lagrede bokmerker @@ -106,6 +110,11 @@ Ignorer + + Faner du ikke har vist på to uker, blir flyttet hit. + + Slå av i innstillinger + Ny fane @@ -121,10 +130,33 @@ Vis alle + + Vis alle nylige faner-knappen + + + Du søkte etter «%1$s» + + Nettsteder: %1$s + - Tidligere utforskninger + Tidligere utforskninger + + + Nylig besøkt + + Nylige søk + + Fjern + + + Vis alle tidligere utforskninger-knappen @@ -201,6 +233,10 @@ Rediger + + Tilpass hjem + + Tilpass startsiden Startskjerm @@ -247,6 +283,28 @@ Søk direkte fra adresselinjen + + + Hva er nytt i Firefox + + Det er nå lettere å komme tilbake der du sluttet. + + Tilpasset Firefox-startside + + Gå til de åpne fanene, bokmerkene og nettleserhistorikken. + + Rene, organiserte faner + + Fjern rot i fanene med forbedret layout og faner som lukker seg automatisk. + + Nylige søk + + + Gå tilbake til de siste søkene dine fra startsiden og fanene. + + + Din personlige Firefox-startside gjør det nå lettere å fortsette der du sluttet. Finn de siste fanene, bokmerkene og søkeresultatene. + Åpne en ny Firefox-fane @@ -394,6 +452,24 @@ Tilleggssamling endret. Avslutter applikasjonen for å bruke endringer… + + + Hopp inn igjen + + Nylig lagret + + Nylig bokmerket + + Nylige bokmerker + + Nylig besøkt + + Nylige søk + + Pocket + Tillegget støttes ikke @@ -583,6 +659,13 @@ Lukk + + %d nettsted + + %d nettsteder + Nylig lukkede faner @@ -605,6 +688,10 @@ Liste Rutenett + + Søk i grupper + + Grupper relaterte nettsteder sammen Lukk faner @@ -616,6 +703,9 @@ Etter en måned + + Lukk åpne faner automatisk + Start på starskjerm @@ -634,6 +724,12 @@ Lukk etter en måned + + + Flytt gamle faner til inaktive + + Faner du ikke har vist på to uker, flyttes til den inaktive delen. + Fjern @@ -753,6 +849,11 @@ Lagre + + Annen + + Andre faner + Slett historikk @@ -765,15 +866,15 @@ Tøm - Kopier + Kopier - Del + Del - Åpne i ny fane + Åpne i ny fane - Åpne i privat fane + Åpne i privat fane - Slett + Slett %1$d valgt @@ -1250,13 +1351,13 @@ Har du allerede en konto? - Se hva som er nytt + Se hva som er nytt - Har du spørsmål om redesignet av %s? Vil du vite hva som er endret? + Har du spørsmål om redesignet av %s? Vil du vite hva som er endret? - Få svar her + Få svar her Synkroniser Firefox mellom enheter @@ -1299,15 +1400,15 @@ Plassere verktøylinjen innenfor rekkevidde. Ha den på bunn eller flytt den til toppen. - Surf privat + Surf privat - Åpne en privat fane en gang: Trykk på %s-ikonet. + Åpne en privat fane en gang: Trykk på %s-ikonet. - Åpne private faner hver gang: Oppdater dine innstillinger for privat nettlesing. + Åpne private faner hver gang: Oppdater dine innstillinger for privat nettlesing. - Åpne innstillinger + Åpne innstillinger Ditt personvern @@ -1477,6 +1578,12 @@ Fjerner infokapsler satt av omdirigeringer til kjente sporingsnettsteder. + + Noen sporere som er merket nedenfor, er delvis blitt avblokkert på denne siden fordi du samhandlet med dem *. + + Les mer + Brukerstøtte @@ -1544,6 +1651,9 @@ Fyll inn brukernavn og passord i andre apper på enheten din. + + Legg til innlogging + Synkroniser innlogginger @@ -1608,6 +1718,8 @@ Kopier brukernavn Tøm brukernavn + + Tøm servernavnet Kopier nettsted @@ -1780,9 +1892,13 @@ %1$s PÅ]]> - Sikker tilkobling + Tilkoblingen er sikker + + Tilkoblingen er ikke sikker + + Sikker tilkobling - Usikker tilkobling + Usikker tilkobling Er du sikker på at du vil fjerne alle tillatelsene på alle nettsteder? @@ -1822,8 +1938,14 @@ Forkast endringene Rediger - + + Legg til ny innlogging + Passord kreves + + Brukernavn påkrevd + + Servernavn påkrevd Stemmesøk @@ -1832,6 +1954,15 @@ En innlogging med det brukernavnet eksisterer allerede + + https://www.example.com + + Nettadressen må inneholde "https://" eller "http://" + + Nettadressen må inneholde "https://" eller "http://" + + Gyldig servernavn påkrevd + Koble til en annen enhet. @@ -1857,8 +1988,10 @@ OK, jeg skjønner + + Vis mest besøkte nettsteder - Vis mest besøkte nettsteder + Vis mest besøkte nettsteder Navn @@ -1869,9 +2002,11 @@ Avbryt - - + + Inaktive faner + + Lukk alle inaktive faner Faner er tilgjengelige her i %s. Etter det lukkes fanene automatisk. @@ -1879,6 +2014,30 @@ 1 uke + + + Lukk automatisk etter en måned? + + Firefox kan lukke faner du ikke har sett den siste måneden. + + SLÅ PÅ AUTOLUKKING + + + + Hjelp oss med å forbedre + + Hvorfor deaktiverte du inaktive faner? + + Ikke interessert i funksjonen + + Tiden til inaktiv er for lang + + Tiden til inaktiv er for kort + + Send + + Lukk + Angi at lenker fra nettsteder, e-postmeldinger og meldinger skal åpnes automatisk i Firefox. @@ -1894,4 +2053,19 @@ Lukk + + + Tankevekkende artikler + + Tankevekkende artikler + + Artikler etter emne + + Oppdag mer + + Drevet av Pocket. + + Del av Firefox-familien. %s + + Les mer From 34305877ba97a9f507216b2d7e3f48ef7a332053 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 460/517] Strings - app/src/main/res/values-nl/strings.xml --- app/src/main/res/values-nl/strings.xml | 184 +++++++++++++++++++++---- 1 file changed, 155 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index b05fb71588..dd66017c7b 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -26,7 +26,7 @@ 1 open tabblad. Tik om tussen tabbladen te wisselen. - %1$s open tabbladen. Tik om tussen tabbladen te wisselen. + %1$s open tabbladen. Tik om tussen tabbladen te wisselen. %1$d geselecteerd @@ -56,7 +56,9 @@ Onlangs opgeslagen - Recent aangemaakte bladwijzers + Recent aangemaakte bladwijzers + + Recente bladwijzers Recent opgeslagen bladwijzers @@ -111,6 +113,20 @@ Sluiten + + Tabbladen die u twee weken niet hebt bekeken, worden hierheen verplaatst. + + Uitschakelen in instellingen + + + Automatisch sluiten na een maand? + + Firefox kan tabbladen die u de afgelopen maand niet hebt bekeken sluiten. + + Sluiten + + Automatisch sluiten inschakelen + Nieuw tabblad @@ -118,7 +134,7 @@ Nieuw privétabblad - Topwebsites + Topwebsites @@ -126,6 +142,15 @@ Alles tonen + + Knop Alle recente tabbladen tonen + + Uw zoekopdracht naar ‘%1$s’ + + Websites: %1$s + @@ -133,11 +158,17 @@ - Onlangs bezocht + Onlangs bezocht + + Recente zoekopdrachten Verwijderen + + Knop Alle eerdere zoekopdrachten tonen + Open tabbladen @@ -150,7 +181,7 @@ Stoppen - Bladwijzer maken + Bladwijzer maken Bladwijzer bewerken @@ -216,6 +247,8 @@ Startpagina aanpassen + + Startpagina aanpassen Startscherm @@ -262,6 +295,28 @@ Rechtstreeks vanuit de adresbalk zoeken + + + Wat is er nieuw in Firefox + + Het is nu eenvoudiger om verder te gaan waar u was gebleven. + + Gepersonaliseerde Firefox-startpagina + + Spring naar uw open tabbladen, bladwijzers en navigatiegeschiedenis. + + Nette, geordende tabbladen + + Verwijder tabbladrommel met een verbeterde opmaak en automatisch sluitende tabbladen. + + Recente zoekopdrachten + + + Bekijk uw laatste zoekopdrachten opnieuw vanaf uw startpagina en tabbladen. + + + Uw gepersonaliseerde Firefox-startpagina maakt het nu eenvoudiger om verder te gaan waar u was gebleven. Vind uw recente tabbladen, bladwijzers en zoekresultaten. + Een nieuw Firefox-tabblad openen @@ -340,7 +395,9 @@ Thema - Startpagina + Startpagina + + Startpagina Bewegingen @@ -415,9 +472,15 @@ Onlangs opgeslagen - Recent aangemaakte bladwijzers - - Onlangs bezocht + Recent aangemaakte bladwijzers + + Recente bladwijzers + + Onlangs bezocht + + Recente zoekopdrachten Pocket @@ -528,7 +591,7 @@ Sync inschakelen - Koppelingscode scannen in desktop-Firefox + Koppelingscode scannen in desktop-Firefox Aanmelden @@ -636,6 +699,10 @@ Lijst Raster + + Groepen zoeken + + Gerelateerde websites groeperen Tabbladen sluiten @@ -647,15 +714,26 @@ Na een maand - + + Open tabbladen automatisch sluiten + + - Op startscherm beginnen + Op startscherm beginnen + + Openingsscherm - Na vier uur + Na vier uur + + Startpagina - Altijd + Altijd + + Laatste tabblad - Nooit + Nooit + + Startpagina na vier uur inactiviteit Handmatig sluiten @@ -665,6 +743,12 @@ Na een maand sluiten + + + Oude tabbladen naar inactief verplaatsen + + Tabbladen die u twee weken niet hebt bekeken, worden verplaatst naar de sectie Inactief. + Verwijderen @@ -707,13 +791,13 @@ In collectie opslaan - Selecteren + Selecteren Alle tabbladen delen Onlangs gesloten tabbladen - Onlangs gesloten + Onlangs gesloten Accountinstellingen @@ -788,7 +872,10 @@ Opslaan - Overig + Overig + + + Overige tabbladen @@ -833,10 +920,6 @@ Geen geschiedenis hier - - Downloads verwijderen - - Weet u zeker dat u uw downloads wilt wissen? Downloads verwijderd @@ -877,7 +960,7 @@ Bladwijzermenu - Bladwijzer bewerken + Bladwijzer bewerken Map selecteren @@ -1907,8 +1990,10 @@ OK, begrepen + + Meest bezochte topwebsites - Meest bezochte topwebsites tonen + Meest bezochte topwebsites tonen Meest bezochte websites tonen @@ -1921,15 +2006,41 @@ Annuleren - - + + Inactieve tabbladen + + Alle inactieve tabbladen sluiten - Tabbladen zijn hier %s beschikbaar. Daarna worden tabbladen automatisch gesloten. + Tabbladen zijn hier %s beschikbaar. Daarna worden tabbladen automatisch gesloten. - 30 dagen + 30 dagen - 1 week + 1 week + + + + Automatisch sluiten na een maand? + + Firefox kan tabbladen die u de afgelopen maand niet hebt bekeken sluiten. + + AUTOMATISCH SLUITEN INSCHAKELEN + + + + Help ons te verbeteren + + Waarom hebt u inactieve tabbladen uitgeschakeld? + + Niet geïnteresseerd in de functie + + Het duurt te lang voordat tabbladen inactief worden + + Tabbladen worden te snel inactief + + Verzenden + + Sluiten Koppelingen van websites, e-mail en berichten automatisch in Firefox openen. @@ -1946,4 +2057,19 @@ Sluiten + + + Verhalen die tot nadenken stemmen + + Verhalen die tot nadenken stemmen + + Verhalen op onderwerp + + Meer ontdekken + + Mogelijk gemaakt door Pocket. + + Onderdeel van de Firefox-familie. %s + + Meer info From 2cda6ace40159c1642d633f417c6e085b08761c3 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 461/517] Strings - app/src/main/res/values-nn-rNO/strings.xml --- app/src/main/res/values-nn-rNO/strings.xml | 89 ++++++++++++++++++++-- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-nn-rNO/strings.xml b/app/src/main/res/values-nn-rNO/strings.xml index 3c64e05ee4..1562fabc74 100644 --- a/app/src/main/res/values-nn-rNO/strings.xml +++ b/app/src/main/res/values-nn-rNO/strings.xml @@ -57,7 +57,9 @@ Nyleg lagra - Nyleg bokmerkte + Nyleg bokmerkte + + Nylege bokmerke Nylig lagra bokmerke @@ -109,6 +111,11 @@ Ignorer + + Faner du ikkje har vist på to veker vert flytta hit. + + Slå av i innstillingar + Ny fane @@ -124,6 +131,15 @@ Vis alle + + Vis alle nylege faner-knappen + + Du søkte etter «%1$s» + + Nettstadar: %1$s + @@ -131,11 +147,17 @@ - Nyleg besøkte + Nyleg besøkte + + Nylege søk Fjern + + Vis alle tidlegare utforskningar-knappen + Opne faner @@ -212,6 +234,8 @@ Tilpass heime + + Tilpasse startsida Startskjerm @@ -257,6 +281,27 @@ Søk direkte frå adresselinja + + + Kva er nytt i Firefox + + + Det er no enklare å fortsetje der du slutta. + + Tilpassa Firefox-startside + + + Gå til dei opne fanene, bokmerka og nettlesarhistorikken. + + Reine, organiserte faner + + Fjern rot i fanene med forbetra layout og faner som automatisk lèt seg att. + + Nylege søk + + + Gå tilbake til dei siste søka dine frå startsida og fanene. + Opne ei ny Firefox-fane @@ -405,8 +450,21 @@ Tilleggssamling endra. Avsluttar applikasjonen for å bruke endringar… - - Nyleg besøkte + + + Hopp inn igjen + + Nyleg lagra + + Nyleg bokmerkte + + Nylege bokmerke + + Nyleg besøkte + + Nylege søk Pocket @@ -620,6 +678,8 @@ Liste Rutenett + + Søkjegrupper Lat att faner @@ -772,7 +832,7 @@ Lagre - Anna + Anna @@ -1632,7 +1692,7 @@ Tøm brukarnamn - Tøm brukarnamn + Tøm servarnamn Kopier nettstad @@ -1907,8 +1967,8 @@ Avbryt - - + + Inaktive faner Faner er tilgjengelege her i %s. Etter det vert fanene automatisk attlatne. @@ -1917,6 +1977,11 @@ 1 veke + + Send + + Lat att + Vel at lenker frå nettstadar, e-postmeldingar og meldingar skal opnast automatisk i Firefox. @@ -1932,4 +1997,12 @@ Lat att + + Oppdag meir + + Levert av Pocket. + + Del av Firefox-familien. %s + + Les meir From c5f6be7d6f8962dc6e8ce5ce29566e8ff06e6c9f Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 462/517] Strings - app/src/main/res/values-oc/strings.xml --- app/src/main/res/values-oc/strings.xml | 196 +++++++++++++++++++++---- 1 file changed, 168 insertions(+), 28 deletions(-) diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml index a80240e214..b922df0bb5 100644 --- a/app/src/main/res/values-oc/strings.xml +++ b/app/src/main/res/values-oc/strings.xml @@ -24,7 +24,7 @@ 1 onglet dobèrt. Tocatz per i bascular. - %1$s onglets dobèrts. Tocatz per cambiar d’onglet. + %1$s onglets dobèrts. Tocatz per cambiar d’onglet. %1$d seleccionats @@ -53,7 +53,9 @@ Salvats recentament - Marcats recentament + Marcats recentament + + Marcats recentament Marcats recentament @@ -105,6 +107,21 @@ Ignorar + + Los onglets qu’avètz pas consultats fa dos setmanas son desplaçats aicí. + + Desactivar los paramètres + + + Tampadura automatica aprèp un mes ? + + Firefox pòot tampar los onglets qu’avètz pas consultats pendent lo mes passat. + + Tampar + + + Activar la tampadura auto + Onglet novèl @@ -113,7 +130,7 @@ Novèl onglet de nav. privada - Mai visitats + Mai visitats @@ -121,6 +138,15 @@ O afichar tot + + Afichar lo boton dels onglets recents + + Vòstre recèrca per \« %1$s \ » + + Sites : %1$s + @@ -128,11 +154,17 @@ - Visitats fa res + Visitats fa res + + Recèrcas recentas Suprimir + + Afichar lo boton de las navegacions passadas + Onglets dobèrts @@ -145,7 +177,7 @@ Arrestar - Marcapagina + Marcapagina Modificar lo marcapagina @@ -210,6 +242,8 @@ Personalizar l’acuèlh + + Personalizar la pagina d’acuèlh Ecran d’acuèlh @@ -255,6 +289,28 @@ Recèrcas dirèctament dins la barra d’adreça + + + Qu’es nòu dins Firefox + + Ara es mai facil de contunhar d’ont aviatz arrestat. + + Acuèlh personalizat de Firefox + + Accedissètz a vòstres onglets dobèrts, vòstres marcapaginas e vòstre istoric de navegacion. + + Onglets clars e organizats + + Eliminatz lo bazar amb un agençament melhorat e d’onglets que se tampan solets. + + Recèrcas recentas + + + Revisitatz vòstras darrièras recèrcas a partir de la pagina d’acuèlh e vòstres onglets. + + + L’acuèlh personalizat de Firefox permet ara de tornar mai facilament ont èretz. Trapatz vòstres onglets, marcapaginas e recèrcas recentas. + Dobrir dins un onglet Firefox novèl @@ -334,7 +390,9 @@ Tèma - Acuèlh + Acuèlh + + Pagina d’acuèlh Gèstes @@ -404,12 +462,21 @@ Colleccion de moduls complementaris modificada. Tampadura de l’aplicacion per aplicar las modificacions… + + + Tornar a aqueste onglet Salvats recentament - Marcats recentament - - Visitats fa res + Marcats recentament + + Marcats recentament + + Visitats fa res + + Recèrcas recentas Pocket @@ -523,7 +590,7 @@ Activar la sincronizacion - Numerizar lo còdi d’associacion del Firefox de burèu estant + Numerizar lo còdi d’associacion del Firefox de burèu estant Connexion @@ -630,6 +697,10 @@ Lista Grasilha + + Cercar en grops + + Agropar los sites ligats Tampar los onglets @@ -642,15 +713,24 @@ Aprèp un mes - + + Auto-tampadura d’onglets dubèrts + + - Començar per l’ecran d’acuèlh + Començar per l’ecran d’acuèlh - Aprèp 4 oras + Aprèp 4 oras + + Pagina d’acuèlh - Totjorn + Totjorn + + Darrièr onglet - Jamai + Jamai + + Pagina d’acuèlh aprèp quatre oras d’inactivitat Tampar manualament @@ -660,6 +740,13 @@ Tampar aprèp un mes + + + Passar los onglets ancians a inactius + + + Los onglets qu’avètz pas consultats fa doas setmanas aniràn a la seccion dels inactius. + Suprimir @@ -702,13 +789,13 @@ Salvar a la colleccion - Seleccionar + Seleccionar Partejar totes los onglets Onglets tampats recentament - Tampaduras recentas + Tampaduras recentas Paramètres del compte @@ -782,7 +869,10 @@ Enregistrar - Autres + Autres + + + Autres dubèrts @@ -828,10 +918,6 @@ Cap d’istoric - - Suprimir los telecargaments - - Volètz vertadièrament escafar los telecargaments ? Telecargament suprimits @@ -873,7 +959,7 @@ Menú dels marcapaginas - Modificar lo marcapagina + Modificar lo marcapagina Seleccionar un dossièr @@ -1892,6 +1978,13 @@ https://www.exemple.com + + L’adreça web deu conténer « https:// » o « http:// ». + + L’adreça web deu conténer « https:// » o « http:// ». + + Un nom d’òste valid es requerit + Connectatz un autre periferic. @@ -1917,6 +2010,10 @@ Òc, plan comprés + + Sites mai visitats + + Mostrar los sites mai visitats Mostrar los sites mai visitats @@ -1929,15 +2026,43 @@ Anullar - - + + Onglets inactius + + Tampar los onglets inactius - Los onglets son disponible aicí pendent %s jorns. Aprèp, seràn automaticament tampats. + Los onglets son disponible aicí pendent %s jorns. Aprèp, seràn automaticament tampats. - 30 jorns + 30 jorns - 1 setmana + 1 setmana + + + + Tampadura automatica aprèp un mes ? + + Firefox pòot tampar los onglets qu’avètz pas consultats pendent lo mes passat. + + + ACTIVAR LA TAMPADURA AUTO + + + + Mercés de vos ajudar a melhorar + + Perqué avètz desactivat los onglets inactius ? + + Pas interessat per aquesta foncionalitat + + Lo temps d’inactivitat es tròp long + + Lo temps d’inactivitat es tròp cort + + Mandar + + + Tampar Causir de dobrir los sites web, los corrièls e messatges automaticament dins Firefox. @@ -1954,4 +2079,19 @@ Tampar + + + Articles suggerits + + Articles suggerits + + Articles per tèma + + Ne descobrir mai + + Propulsat per Pocket. + + Membre de la familha Firefox. %s + + Ne saber mai From d822da8ccbf340cb73756f42b1e10e3cf025c14a Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 463/517] Strings - app/src/main/res/values-pa-rIN/strings.xml --- app/src/main/res/values-pa-rIN/strings.xml | 59 ++++++++++------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/app/src/main/res/values-pa-rIN/strings.xml b/app/src/main/res/values-pa-rIN/strings.xml index 0e20b0ae59..fcc1ffb47b 100644 --- a/app/src/main/res/values-pa-rIN/strings.xml +++ b/app/src/main/res/values-pa-rIN/strings.xml @@ -24,7 +24,7 @@ 1 ਟੈਬ ਖੁੱਲ੍ਹੀ। ਟੈਬਾਂ ਲਈ ਸਵਿੱਚ ਕਰਨ ਵਾਸਤੇ ਟੈਪ ਕਰੋ। - %1$s ਟੈਬਾਂ ਖੁੱਲ੍ਹੀ। ਟੈਬਾਂ ਲਈ ਸਵਿੱਚ ਕਰਨ ਵਾਸਤੇ ਟੈਪ ਕਰੋ। + %1$s ਟੈਬਾਂ ਖੁੱਲ੍ਹੀ। ਟੈਬਾਂ ਲਈ ਸਵਿੱਚ ਕਰਨ ਵਾਸਤੇ ਟੈਪ ਕਰੋ। %1$d ਚੁਣੀਆਂ @@ -54,7 +54,7 @@ ਤਾਜ਼ਾ ਸੰਭਾਲੇ - ਤਾਜ਼ਾ ਬੁੱਕਮਾਰਕ ਕੀਤੇ + ਤਾਜ਼ਾ ਬੁੱਕਮਾਰਕ ਕੀਤੇ ਤਾਜ਼ਾ ਸੰਭਾਲੇ ਬੁੱਕਮਾਰਕ @@ -70,7 +70,7 @@ ਤੁਸੀਂ ਪ੍ਰਾਈਵੇਟ ਸ਼ੈਸ਼ਨ ‘ਚ ਹੋ - ¶ + ਜਦੋਂ ਤੁਸੀਂ ਸਾਰੀਆਂ ਪ੍ਰਾਈਵੇਟ ਟੈਬਾਂ ਨੂੰ ਬੰਦ ਕਰਦੇ ਹੋ ਜਾਂ ਐਪ ਤੋਂ ਬਾਹਰ ਜਾਂਦੇ ਹੋ ਤਾਂ %1$s ਤੁਹਾਡੀ ਖੋਜ ਅਤੇ ਬਰਾਊਜ਼ਿੰਗ ਅਤੀਤ ਨੂੰ ਸਾਫ ਕਰਦਾ ਹੈ। ਹਾਲਾਂਕਿ ਇਹ ਤੁਹਾਨੂੰ ਵੈਬਸਾਈਟਾਂ ਜਾਂ ਤੁਹਾਡੇ ਇੰਟਰਨੈੱਟ ਦੇਣ ਵਾਲੇ ਲਈ ਅਣਪਛਾਤਾ ਨਹੀਂ ਬਣਾਉਂਦਾ, ਪਰ ਇਸ ਨਾਲ ਇਸ ਡਿਵਾਈਸ ਨੂੰ ਵਰਤੇ ਵਾਲੇ ਕਿਸੇ ਤੋਂ ਵੀ ਤੁਹਾਡੇ ਵਲੋਂ ਆਨਲਾਈਨ ਕੀਤੇ ਨੂੰ ਪ੍ਰਾਈਵੇਟ ਰੱਖਣਾ ਹੋਰ ਸੌਖਾ ਹੋ ਜਾਂਦਾ ਹੈ। @@ -118,7 +118,7 @@ ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਟੈਬ - ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ + ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ @@ -132,7 +132,7 @@ ਪਿਛਲੀਆਂ ਖੋਜਾਂ - ਹਾਲ ਦੇ ਖੋਲ੍ਹੀਆਂ ਗਈਆਂ + ਹਾਲ ਦੇ ਖੋਲ੍ਹੀਆਂ ਗਈਆਂ ਹਟਾਓ @@ -149,7 +149,7 @@ ਰੋਕੋ - ਬੁੱਕਮਾਰਕ + ਬੁੱਕਮਾਰਕ ਬੁੱਕਮਾਰਕ ਸੋਧੋ @@ -339,7 +339,7 @@ ਥੀਮ - ਮੁੱਖ ਸਫ਼ਾ + ਮੁੱਖ ਸਫ਼ਾ ਇਸ਼ਾਰੇ @@ -416,9 +416,10 @@ ਤਾਜ਼ਾ ਸੰਭਾਲੇ - ਤਾਜ਼ਾ ਬੁੱਕਮਾਰਕ ਕੀਤੇ - - ਤਾਜ਼ਾ ਖੋਲ੍ਹੇ ਗਏ + ਤਾਜ਼ਾ ਬੁੱਕਮਾਰਕ ਕੀਤੇ + + ਤਾਜ਼ਾ ਖੋਲ੍ਹੇ ਗਏ Pocket @@ -532,7 +533,7 @@ ਸਿੰਕ ਚਾਲੂ ਹੈ - ਡੈਸਕਟਾਪ ਫਾਇਰਫਾਕਸ ‘ਚ ਪੇਅਰ ਕਰਨ ਵਾਲਾ ਕੋਡ ਸਕੈਨ ਕਰੋ + ਡੈਸਕਟਾਪ ਫਾਇਰਫਾਕਸ ‘ਚ ਪੇਅਰ ਕਰਨ ਵਾਲਾ ਕੋਡ ਸਕੈਨ ਕਰੋ ਸਾਈਨ ਇਨ @@ -653,15 +654,15 @@ ਇੱਕ ਮਹੀਨੇ ਬਾਅਦ - + - ਮੁੱਖ ਸਕਰੀਨ ਤੋਂ ਸ਼ੁਰੂ ਕਰੋ + ਮੁੱਖ ਸਕਰੀਨ ਤੋਂ ਸ਼ੁਰੂ ਕਰੋ - ਕੁਝ ਘੰਟਿਆਂ ਬਾਦ + ਕੁਝ ਘੰਟਿਆਂ ਬਾਦ - ਹਮੇਸ਼ਾਂ + ਹਮੇਸ਼ਾਂ - ਕਦੇ ਨਹੀਂ + ਕਦੇ ਨਹੀਂ ਖੁਦ ਬੰਦ ਕਰੋ @@ -711,13 +712,13 @@ ਭੰਡਾਰ ਵਿੱਚ ਸੰਭਾਲੋ - ਚੁਣੋ + ਚੁਣੋ ਸਾਰੀਆਂ ਟੈਬਾਂ ਸਾਂਝੀਆਂ ਕਰੋ ਤਾਜ਼ਾ ਬੰਦ ਕੀਤੀਆਂ ਟੈਬਾਂ - ਤਾਜ਼ਾ ਬੰਦ ਕੀਤੀਆਂ + ਤਾਜ਼ਾ ਬੰਦ ਕੀਤੀਆਂ ਖਾਤਾ ਸੈਟਿੰਗਾਂ @@ -792,7 +793,7 @@ ਸੰਭਾਲੋ - ਹੋਰ + ਹੋਰ @@ -838,10 +839,6 @@ ਇ਼ੱਥੇ ਕੋਈ ਅਤੀਤ ਨਹੀਂ ਹੈ - - ਡਾਊਨਲੋਡ ਹਟਾਓ - - ਕੀ ਤੁਸੀਂ ਆਪਣੇ ਡਾਊਨਲੋਡਾਂ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ? ਡਾਊਨਲੋਡ ਹਟਾਏ ਗਏ @@ -882,7 +879,7 @@ ਬੁੱਕਮਾਰਕ ਮੇਨੂ - ਬੁੱਕਮਾਰਕ ਸੋਧੋ + ਬੁੱਕਮਾਰਕ ਸੋਧੋ ਫੋਲਡਰ ਚੁਣੋ @@ -1915,7 +1912,7 @@ ਠੀਕ ਹੈ, ਸਮਝ ਗਏ - ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹੀਆਂ ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ ਵੇਖੋ + ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹੀਆਂ ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ ਵੇਖੋ ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹੀਆਂ ਸਾਈਟਾਂ ਵੇਖੋ @@ -1928,15 +1925,15 @@ ਰੱਦ ਕਰੋ - - + + ਨਾ-ਸਰਗਰਮ ਟੈਬਾਂ - ਟੈਬਾਂ %s ਦਿਨਾਂ ਲਈ ਇੱਥੇ ਮੌਜੂਦ ਹੁੰਦੀਆਂ ਹਨ। ਉਸ ਸਮੇਂ ਬਾਅਦ, ਟੈਬਾਂ ਨੂੰ ਆਪਣੇ-ਆਪ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ। + ਟੈਬਾਂ %s ਦਿਨਾਂ ਲਈ ਇੱਥੇ ਮੌਜੂਦ ਹੁੰਦੀਆਂ ਹਨ। ਉਸ ਸਮੇਂ ਬਾਅਦ, ਟੈਬਾਂ ਨੂੰ ਆਪਣੇ-ਆਪ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ। - 30 ਦਿਨ + 30 ਦਿਨ - 1 ਹਫ਼ਤਾ + 1 ਹਫ਼ਤਾ ਵੈੱਬਸਾਈਟਾਂ, ਈਮੇਲਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਨੂੰ Firefox ਵਿੱਚ ਆਪਣੇ ਖੋਲ੍ਹਣ ਲਈ ਲਿੰਕ ਸੈੱਟ ਕਰੋ। @@ -1953,4 +1950,4 @@ ਬੰਦ ਕਰੋ - + From 1efd7d84c738f04a53fb2f7dcf12bc3976422519 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 464/517] Strings - app/src/main/res/values-pt-rBR/strings.xml --- app/src/main/res/values-pt-rBR/strings.xml | 196 +++++++++++++++++---- 1 file changed, 162 insertions(+), 34 deletions(-) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index a79bce04d2..b7532c676d 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -23,7 +23,7 @@ 1 aba aberta. Toque para alternar abas. - %1$s abas abertas. Toque para alternar abas. + %1$s abas abertas. Toque para alternar abas. %1$d selecionadas @@ -52,7 +52,9 @@ Salvos recentemente - Adicionado recentemente aos favoritos + Adicionado recentemente aos favoritos + + Favoritos recentes Favoritos salvos recentemente @@ -106,6 +108,20 @@ Descartar + + Abas que você não usa há duas semanas são movidas para cá. Você pode + + desativar nas configurações + + + Fechar automaticamente após um mês sem uso? + + O Firefox pode fechar abas que você não usou no mês anterior. + + Fechar + + Ativar fechamento automático + Nova aba @@ -113,14 +129,23 @@ Nova aba privativa - Sites preferidos + Sites preferidos - Retornar + Voltar direto para Mostrar tudo + + Botão de exibir todas as abas recentes + + Sua pesquisa de \"%1$s\" + + Sites: %1$s + @@ -128,11 +153,17 @@ - Visitado recentemente + Visitado recentemente + + Pesquisas recentes Remover + + Botão de exibir todas as explorações anteriores + Abas abertas @@ -145,7 +176,7 @@ Parar - Adicionar aos favoritos + Adicionar aos favoritos Editar favorito @@ -210,6 +241,8 @@ Personalizar tela inicial + + Personalizar tela inicial Tela inicial @@ -256,6 +289,29 @@ Pesquisar diretamente na barra de endereços + + + Novidades no Firefox + + Agora é mais fácil continuar de onde você parou. + + Tela inicial personalizada do Firefox + + Vá direto para suas abas abertas, favoritos e histórico de navegação. + + + Abas limpas e organizadas + + + Layout aprimorado e fechamento automático de abas para remover o excesso de abas. + + Pesquisas recentes + + Revisite suas pesquisas recentes feitas na tela inicial e em abas. + + + A página inicial personalizada do Firefox agora facilita continuar de onde você parou. Encontre suas recentes abas, favoritos e resultados de pesquisa. + Abrir uma nova aba no Firefox @@ -334,7 +390,9 @@ Tema - Tela inicial + Tela inicial + + Tela inicial Gestos @@ -404,13 +462,19 @@ - Retornar + Voltar direto para Salvo recentemente - Adicionado recentemente aos favoritos - - Visitado recentemente + Adicionado recentemente aos favoritos + + Favoritos recentes + + Visitado recentemente + + Pesquisas recentes Pocket @@ -523,7 +587,7 @@ Ativar a sincronização - Digitalizar código de pareamento no Firefox de computador + Digitalizar código de pareamento no Firefox de computador Entrar @@ -628,6 +692,10 @@ Lista Grade + + Grupos de pesquisa + + Agrupar sites relacionados Fechar abas @@ -639,15 +707,26 @@ Após um mês sem uso - + + Fechar abas abertas automaticamente + + - Iniciar na tela inicial + Iniciar na tela inicial + + Tela de abertura - Após quatro horas + Após quatro horas + + Página inicial - Sempre + Sempre + + Última aba - Nunca + Nunca + + Página inicial após quatro horas de inatividade Fechar manualmente @@ -657,6 +736,13 @@ Fechar após um mês sem uso + + + Mover abas antigas para inativas + + + Abas que você não usa há duas semanas são movidas para a seção de inativas. + Remover @@ -699,13 +785,13 @@ Salvar em coleção - Selecionar + Selecionar Compartilhar todas as abas Abas fechadas recentemente - Fechado recentemente + Fechado recentemente Configurações de contas @@ -779,7 +865,10 @@ Salvar - Outros + Outros + + + Outras abas @@ -824,10 +913,6 @@ Nenhum histórico aqui - - Excluir downloads - - Tem certeza que quer limpar seus downloads? Downloads removidos @@ -868,7 +953,7 @@ Menu de favoritos - Editar favorito + Editar favorito Selecionar pasta @@ -1399,7 +1484,7 @@ Navegue sem ser seguido - Mantenha seus dados com você. O %s te protege de muitos dos rastreadores mais comuns que tentam seguir o que você faz online. + Defenda seus dados. O %s te protege de muitos dos rastreadores mais comuns que tentam seguir o que você faz online. Saiba mais @@ -1563,11 +1648,11 @@ Preenchimento automático no %1$s - Preencha e salve nomes de usuário e senhas em sites ao usar o %1$s. + Preencher e salvar nomes de usuário e senhas em sites ao usar o %1$s. Preenchimento automático em outros aplicativos - Preencha nomes de usuário e senhas em outros aplicativos no seu dispositivo. + Preencher nomes de usuário e senhas em outros aplicativos no dispositivo. Adicionar conta @@ -1903,8 +1988,10 @@ OK, entendi + + Sites preferidos mais visitados - Mostrar sites preferidos mais visitados + Mostrar sites preferidos mais visitados Mostrar os sites mais visitados @@ -1917,15 +2004,41 @@ Cancelar - - + + Abas inativas + + Fechar todas as abas inativas - Abas ficam disponíveis aqui por %s. Após esse período, são fechadas automaticamente. + Abas ficam disponíveis aqui por %s. Após esse período, são fechadas automaticamente. - 30 dias + 30 dias - 1 semana + 1 semana + + + + Fechar automaticamente após um mês sem uso? + + O Firefox pode fechar abas que você não viu no mês anterior. + + ATIVAR FECHAMENTO AUTOMÁTICO + + + + Ajude-nos a melhorar + + Por que você desativou o recurso de abas inativas? + + Não tenho interesse no recurso + + O tempo para inativar é longo demais + + O tempo para inativar é curto demais + + Enviar + + Fechar Abra links, emails e mensagens automaticamente no Firefox. @@ -1942,4 +2055,19 @@ Fechar + + + Histórias que instigam o pensamento + + Histórias que instigam o pensamento + + Histórias por tópico + + Descubra mais + + Proporcionado pelo Pocket. + + Parte da família Firefox. %s + + Saiba mais From 1c209af3dbff0bf3e7c9725ad3161a314953b259 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 465/517] Strings - app/src/main/res/values-pt-rPT/strings.xml --- app/src/main/res/values-pt-rPT/strings.xml | 148 +++++++++++++++++++-- 1 file changed, 140 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 91745f0699..bd0bf513c7 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -54,7 +54,9 @@ Recentemente guardados - Marcadores recentes + Marcadores recentes + + Marcadores recentes Marcadores guardados recentemente @@ -107,6 +109,11 @@ Dispensar + + Os separadores que não acede há duas semanas são movidos para aqui. + + Desligar nas definições + Novo separador @@ -122,6 +129,15 @@ Mostrar todos + + Botão Mostrar todos os separadores recentes + + A sua pesquisa por \"%1$s\" + + Sites: %1$s + @@ -129,11 +145,17 @@ - Visualizados recentemente + Visualizados recentemente + + Pesquisas recentes Remover + + Botão Mostrar todas as explorações anteriores + Separadores abertos @@ -210,6 +232,8 @@ Personalizar página principal + + Personalizar página inicial Ecrã inicial @@ -256,6 +280,27 @@ Pesquisar diretamente a partir da barra de endereço + + + O que há de novo no Firefox + + Agora é mais simples continuar de onde parou. + + Página inicial personalizada do Firefox + + Aceda aos seus separadores abertos, marcadores e histórico de navegação. + + Separadores limpos e organizados + + Elimine a confusão de separadores com um esquema melhorado e encerramento automático de separadores. + + Pesquisas recentes + + Revisite as suas pesquisas mais recentes na sua página inicial e separadores. + + + A sua página inicial personalizada do Firefox faz com que agora seja mais simples continuar de onde parou. Encontre os seus separadores, marcadores e resultados de pesquisa recentes. + Abrir um novo separador Firefox @@ -408,9 +453,15 @@ Recentemente guardados - Marcadores recentes - - Visualizados recentemente + Marcadores recentes + + Marcadores recentes + + Visualizados recentemente + + Pesquisas recentes Pocket @@ -631,6 +682,10 @@ Lista Grelha + + Pesquisar grupos + + Agrupar sites relacionados Fechar separadores @@ -642,6 +697,9 @@ Depois de um mês + + Auto-encerrar separadores abertos + Começar no ecrã inicial @@ -660,6 +718,12 @@ Fechar depois um mês + + + Mover separadores antigos para inativos + + Os separadores que não acedeu há duas semanas são movidos para a secção inativos. + Remover @@ -780,7 +844,10 @@ Guardar - Outro + Outro + + + Outros separadores @@ -1636,6 +1703,8 @@ Copiar nome de utilizador Limpar nome de utilizador + + Limpar servidor Copiar site @@ -1802,6 +1871,10 @@ %1$s]]> + + A ligação é segura + + A ligação não é segura Ligação segura @@ -1845,8 +1918,14 @@ Descartar alterações Editar + + Adicionar nova credencial É necessária uma palavra-passe + + É necessário um nome de utilizador + + É necessário um nome de servidor Pesquisa por voz @@ -1855,6 +1934,16 @@ Já existe uma credencial com este nome. + + https://www.example.com + + + O endereço web deve conter "https://" ou "http://" + + O endereço web deve conter "https://" ou "http://" + + É necessário um nome de servidor válido + Associar outro dispositivo. @@ -1880,6 +1969,8 @@ OK, percebi + + Mostrar os principais sites mais visitados Mostrar os sites mais visitados @@ -1892,9 +1983,11 @@ Cancelar - - + + Separadores inativos + + Fechar todos os separadores inativos Os separadores estarão disponíveis aqui durante %s. Após este período, os separadores serão fechados automaticamente. @@ -1902,6 +1995,30 @@ 1 semana + + + Auto-encerrar após um mês? + + O Firefox pode encerrar separadores que não acedeu no último mês. + + ATIVAR O ENCERRAMENTO AUTOMÁTICO + + + + Ajude-nos a melhorar + + Porque desativou os separadores inativos? + + Não estou interessado na funcionalidade + + O tempo de inatividade é muito longo + + O tempo de inatividade é muito curto + + Enviar + + Fechar + Definir para que as ligações de sites, e-mails e mensagens sejam abertas automaticamente no Firefox. @@ -1917,4 +2034,19 @@ Fechar + + + Histórias que fazem pensar + + Histórias que fazem pensar + + Histórias por tópico + + Descobrir mais + + Com tecnologia do Pocket. + + Membro da família Firefox. %s + + Saber mais From d6b85b1dcf3d79c933fb2601cf3f261d46105dec Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 466/517] Strings - app/src/main/res/values-rm/strings.xml --- app/src/main/res/values-rm/strings.xml | 126 +++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml index 52d1e829e6..f7978bdf02 100644 --- a/app/src/main/res/values-rm/strings.xml +++ b/app/src/main/res/values-rm/strings.xml @@ -52,7 +52,9 @@ Memorisà dacurt - Tschernì dacurt sco segnapagina + Tschernì dacurt sco segnapagina + + Tschernì dacurt sco segnapagina Segnapaginas memorisads dacurt @@ -103,6 +105,11 @@ Serrar + + Ils tabs che ti n\'has betg consultà durant las ultimas duas emnas vegnan spustads nà qua. + + Deactivar en ils parameters + Nov tab @@ -118,6 +125,15 @@ Mussar tut + + Buttun per mussar tut ils tabs averts dacurt + + Tia tschertga da «%1$s» + + Websites: %1$s + @@ -125,11 +141,17 @@ - Visità dacurt + Visità dacurt + + Tschertgà dacurt Allontanar + + Buttun per mussar ils artitgels gia visitads + Tabs averts @@ -206,6 +228,8 @@ Persunalisar la pagina da partenza + + Persunalisar la pagina da partenza Visur da partenza @@ -251,6 +275,29 @@ Tschertgar direct en la trav d\'adressas + + + Las novaziuns da Firefox + + + Ussa èsi pli simpel da cuntinuar là nua che ti has chalà. + + Pagina da partenza da Firefox persunalisada + + Siglir tar tes tabs averts, tes segnapaginas e tia cronologia da navigaziun. + + Tabs organisads e survesaivels + + + Reducescha il caos cun in layout optimà e cun tabs che sa serran automaticamain. + + Tschertgà dacurt + + Turna als resultats da tias ultimas tschertgas – directamain da la pagina da partenza e dals tabs. + + + Cun tia pagina da partenza da Firefox persunalisada èsi ussa pli simpel da cuntinuar là nua che ti has chalà. Ti chattas tes ultims tabs, segnapaginas e resultats da tschertga. + Avrir in nov tab da Firefox @@ -403,9 +450,15 @@ Memorisà dacurt - Tschernì dacurt sco segnapagina - - Visità dacurt + Tschernì dacurt sco segnapagina + + Tschernì dacurt sco segnapagina + + Visità dacurt + + Tschertgà dacurt Pocket @@ -622,6 +675,10 @@ Glista Griglia + + Gruppas da tschertgar + + Gruppar websites che tutgan ensemen Serrar ils tabs @@ -633,6 +690,9 @@ Suenter in mais + + Serrar automaticamain tabs averts + Recumenzar cun il visur da partenza @@ -651,6 +711,12 @@ Serrar suenter in mais + + + Spustar tabs vegls en «Inactiv» + + Tabs betg consultads durant las ultimas duas emnas vegnan spustads en la secziun «Inactiv». + Allontanar @@ -770,7 +836,10 @@ Memorisar - Auters + Auters + + + Auters tabs @@ -1915,9 +1984,11 @@ Interrumper - - + + Tabs inactivs + + Serrar tut ils tabs inactivs Tabs stattan a disposiziun qua per %s. Suenter quest temp vegnan els serrads automaticamain. @@ -1925,6 +1996,30 @@ 1 emna + + + Serrar automaticamain suenter in mais? + + Firefox po serrar tabs che ti n\'has betg consultà durant l\'ultim mais. + + ACTIVAR LA SERRADA AUTOMATICA + + + + Grazia per ans gidar d\'ans meglierar + + Pertge has ti deactivà ils tabs inactivs? + + La funcziun na m\'interessa betg + + Il cuzza memia ditg fin l\'inactivitad + + I va memia svelt fin l\'inactivitad + + Trametter + + Serrar + Definescha che colliaziuns da websites, e-mails e messadis vegnan averts automaticamain en Firefox. @@ -1940,4 +2035,19 @@ Serrar + + + Istorgias che dattan da pensar + + Istorgias che dattan da pensar + + Istorgias tenor tema + + Scuvrir dapli + + Cun tecnologia da Pocket. + + Part da la paletta da products Firefox. %s + + Ulteriuras infurmaziuns From 9fd90e0592d04f433072c55ab1a467efd8760920 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 467/517] Strings - app/src/main/res/values-ru/strings.xml --- app/src/main/res/values-ru/strings.xml | 189 +++++++++++++++++++++---- 1 file changed, 158 insertions(+), 31 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index f5920f18f0..ab9f11bfde 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -26,7 +26,7 @@ 1 открытая вкладка. Нажмите, чтобы переключить вкладки. - Открытых вкладок: %1$s. Нажмите, чтобы переключить вкладки. + Открытых вкладок: %1$s. Нажмите, чтобы переключить вкладки. Выбрано: %1$d @@ -56,7 +56,9 @@ Недавно добавленные - Недавно добавленные + Недавно добавленные + + Недавние закладки Недавно добавленные закладки @@ -112,6 +114,20 @@ Пропустить + + Вкладки, к которым вы не обращались в течение двух недель, перемещаются сюда. + + Отключить в настройках + + Автоматически закрывать через месяц? + + Firefox может закрывать вкладки, к которым вы не обращались за последний месяц. + + Закрыть + + + Включить автоматическое закрытие + Новая вкладка @@ -119,7 +135,7 @@ Новая приватная вкладка - Топ сайтов + Топ сайтов @@ -127,6 +143,15 @@ Показать все + + Показать кнопку всех недавних вкладок + + Ваш поиск \"%1$s\" + + Сайты: %1$s + @@ -134,11 +159,17 @@ - Недавно посещённые + Недавно посещённые + + Недавние поиски Удалить + + Показать кнопку отображения всей истории + Открытые вкладки @@ -153,7 +184,7 @@ - Добавить в закладки + Добавить в закладки Изменить закладку @@ -171,7 +202,7 @@ Библиотека - Версия для ПК + Версия для компьютера На домашний экран @@ -218,7 +249,9 @@ Изменить - Настроить домашний + Настроить домашний экран + + Настроить домашнюю страницу Домашний экран @@ -265,6 +298,28 @@ Поиск прямо из адресной строки + + + Что нового в Firefox + + Теперь легче продолжить с того места, где вы остановились. + + Настраиваемая домашняя страница Firefox + + Переходите к открытым вкладкам, закладкам и истории просмотра. + + Лучшая организация вкладок + + + Вкладки стали более организованы с использованием улучшенного размещения и автоматического закрытия. + + Недавние поиски + + Вернитесь к последним поисковым запросам с домашней страницы и вкладок. + + + На настроенной вами домашней странице Firefox теперь легче продолжить с того места, где вы остановились. Найдите свои недавние вкладки, закладки и результаты поиска. + Открыть новую вкладку Firefox @@ -344,7 +399,9 @@ Тема - Домой + Домой + + Домашняя страница Жесты @@ -419,9 +476,15 @@ Недавно добавленные - Недавно добавленные - - Недавно посещённые + Недавно добавленные + + Недавние закладки + + Недавно посещённые + + Недавние поиски Pocket @@ -534,7 +597,7 @@ Включить синхронизацию - Отсканируйте код сопряжения в компьютерном Firefox + Отсканируйте код сопряжения в компьютерном Firefox Войти @@ -641,6 +704,10 @@ Списком Сеткой + + Поисковые группы + + Группируйте связанные сайты Закрывать вкладки @@ -652,15 +719,26 @@ Через месяц - + + Автозакрывать открытые вкладки + + - Запускать на домашнем экране + Запускать на домашнем экране + + Начальный экран - Через четыре часа + Через четыре часа + + Домашняя страница - Всегда + Всегда + + Последняя вкладка - Никогда + Никогда + + Домашняя страница после четырех часов бездействия Закрывать вручную @@ -670,6 +748,12 @@ Закрывать через месяц + + + Перемещать старые вкладки в неактивные + + Вкладки, к которым вы не обращались в течение двух недель, перемещаются в неактивные. + Удалить @@ -710,13 +794,13 @@ Сохранить в коллекцию - Выбрать + Выбрать Поделиться всеми вкладками Недавно закрытые вкладки - Недавно закрытые + Недавно закрытые Настройки Аккаунта @@ -791,7 +875,10 @@ Сохранить - Другие + Другие + + + Другие вкладки @@ -837,10 +924,6 @@ История отсутствует - - Удалить загрузки - - Вы уверены, что хотите удалить свои загрузки? Загрузки удалены @@ -882,7 +965,7 @@ Меню закладок - Изменить закладку + Изменить закладку Выбрать папку @@ -1926,8 +2009,10 @@ OK, понятно + + Самые посещаемые топ-сайты - Показывать самые посещаемые из топа сайтов + Показывать самые посещаемые из топа сайтов Показывать самые посещаемые сайты @@ -1940,15 +2025,42 @@ Отмена - - + + Неактивные вкладки + + Закрыть все неактивные вкладки - Вкладки доступны здесь в течение %s дней. По истечении этого времени вкладки будут автоматически закрыты. + Вкладки доступны здесь в течение %s дней. По истечении этого времени вкладки будут автоматически закрыты. - 30 дней + 30 дней - 1 неделя + 1 неделя + + + + Автоматически закрывать через месяц? + + + Firefox может закрывать вкладки, к которым вы не обращались за последний месяц. + + ВКЛЮЧИТЬ АВТОЗАКРЫТИЕ + + + + Пожалуйста, помогите нам это улучшить + + Почему вы отключили неактивные вкладки? + + Функция не интересует + + Слишком большое время неактивности + + Слишком малое время неактивности + + Отправить + + Закрыть Настройте автоматическое открытие ссылок с веб-сайтов, из электронных писем и сообщений в Firefox. @@ -1965,4 +2077,19 @@ Закрыть + + + Истории, наводящие на размышления + + Истории, наводящие на размышления + + Истории по темам + + Узнайте больше + + С использованием Pocket. + + Является частью семейства Firefox. %s + + Узнать больше From bf600a0feb4acde033eb3f2c36666a646668a245 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 468/517] Strings - app/src/main/res/values-sat/strings.xml --- app/src/main/res/values-sat/strings.xml | 98 +++++++++++++++++-------- 1 file changed, 69 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml index 997d238ac9..fe55fd0fe5 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -21,7 +21,7 @@ 1 ᱴᱮᱵᱽ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮ ᱾ ᱴᱮᱵᱽ ᱵᱚᱫᱚᱞ ᱞᱟᱹᱜᱤᱫ ᱴᱤᱯᱟᱹᱣ ᱢᱮ ᱾ - %1$s ᱴᱮᱵᱽ ᱠᱚ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮ ᱾ ᱴᱮᱵᱽ ᱵᱚᱫᱚᱞ ᱞᱟᱹᱜᱤᱫ ᱴᱤᱯᱟᱹᱣ ᱢᱮ ᱾ + %1$s ᱴᱮᱵᱽ ᱠᱚ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮ ᱾ ᱴᱮᱵᱽ ᱵᱚᱫᱚᱞ ᱞᱟᱹᱜᱤᱫ ᱴᱤᱯᱟᱹᱣ ᱢᱮ ᱾ %1$d ᱵᱟᱪᱷᱟᱣᱮᱱᱟ @@ -50,7 +50,9 @@ ᱱᱤᱛ ᱥᱟᱺᱪᱟᱣᱟᱜ - ᱱᱤᱛᱚᱜᱼᱟᱜ ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ + ᱱᱤᱛᱚᱜᱼᱟᱜ ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ + + ᱱᱤᱛᱚᱜᱼᱟᱜ ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ ᱱᱮᱛᱚᱜ ᱜᱮ ᱥᱟᱺᱪᱟᱣ ᱟᱠᱟᱱ ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ @@ -106,6 +108,16 @@ ᱵᱟᱹᱰ + + ᱟᱢᱟᱜ ᱴᱮᱵ ᱚᱠᱟ ᱫᱚ ᱵᱟᱨ ᱦᱟᱴ ᱫᱷᱟᱹᱵᱤᱡ ᱵᱟᱝ ᱧᱮᱞ ᱠᱟᱱᱟ ᱚᱱᱟ ᱫᱚ ᱱᱚᱰᱮ ᱩᱪᱟᱹᱲᱚᱜᱼᱟ ᱾ + + ᱥᱟᱡᱟᱣ ᱠᱚ ᱨᱮ ᱵᱚᱸᱫᱚᱭ ᱢᱮ + + + ᱢᱤᱫ ᱪᱟᱸᱫᱚ ᱛᱟᱭᱚᱢ ᱟᱡ ᱛᱮ ᱵᱚᱸᱫᱽ ᱟᱢ ᱥᱮ? + + ᱵᱚᱸᱫᱚᱭ ᱢᱮ + ᱱᱟᱶᱟ ᱴᱮᱵᱽ @@ -113,7 +125,7 @@ ᱱᱟᱶᱟ ᱱᱤᱡᱮᱨᱟᱜ ᱴᱮᱵᱽ - ᱪᱮᱛᱟᱱ ᱨᱤᱱ ᱥᱟᱭᱤᱴ ᱠᱚ + ᱪᱮᱛᱟᱱ ᱨᱤᱱ ᱥᱟᱭᱤᱴ ᱠᱚ @@ -121,6 +133,15 @@ ᱡᱷᱚᱛᱚ ᱫᱮᱠᱷᱟᱣ ᱢᱮ + + ᱱᱤᱛᱚᱜᱟᱜ ᱴᱮᱵᱥ ᱵᱩᱛᱟᱹᱢ ᱠᱚ ᱡᱷᱚᱛᱚ ᱫᱮᱠᱷᱟᱣ ᱢᱮ + + \"%1$s\" ᱞᱟᱹᱜᱤᱜ ᱟᱢᱟᱜ ᱥᱮᱸᱫᱽᱨᱟ + + ᱥᱟᱭᱤᱴᱮ: %1$s + @@ -128,11 +149,14 @@ - ᱱᱤᱛᱚᱜᱟᱜ ᱦᱤᱨᱤᱭᱟᱜ + ᱱᱤᱛᱚᱜᱟᱜ ᱦᱤᱨᱤᱭᱟᱜ ᱚᱪᱚᱜᱽ ᱢᱮ + + ᱢᱟᱨᱮ ᱥᱮᱸᱫᱽᱨᱟ ᱵᱩᱛᱟᱹᱢ ᱠᱚ ᱜᱟᱠᱷᱟᱣ ᱢᱮ + ᱡᱷᱤᱡᱽ ᱟᱠᱟᱱ ᱴᱮᱵᱽ ᱠᱚ @@ -145,7 +169,7 @@ ᱛᱤᱸᱜᱩ - ᱵᱩᱠᱢᱟᱨᱠ + ᱵᱩᱠᱢᱟᱨᱠ ᱵᱩᱠᱢᱟᱨᱠ ᱥᱟᱯᱲᱟᱣ ᱢᱮ @@ -257,6 +281,10 @@ ᱴᱷᱤᱠᱬᱟ ᱵᱟᱨ ᱠᱷᱚᱱ ᱥᱤᱫᱷᱟᱹ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ + + + %s ᱨᱮ ᱪᱮᱫ ᱱᱟᱶᱟ ᱢᱮᱱᱟᱜ-ᱟ + ᱱᱟᱶᱟ Firefox ᱴᱮᱵᱽ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮ @@ -334,7 +362,7 @@ ᱩᱭᱦᱟᱹᱨ - ᱚᱲᱟᱜ + ᱚᱲᱟᱜ ᱪᱤᱱᱦᱟᱹ ᱠᱚ @@ -407,9 +435,10 @@ ᱱᱤᱛᱜᱮ ᱥᱟᱺᱪᱟᱣ ᱟᱠᱟᱱᱟ - ᱱᱤᱛᱚᱜᱼᱟᱜ ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ - - ᱱᱤᱛᱚᱜᱟᱜ ᱦᱤᱨᱤᱭᱟᱜ + ᱱᱤᱛᱚᱜᱼᱟᱜ ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ + + ᱱᱤᱛᱚᱜᱟᱜ ᱦᱤᱨᱤᱭᱟᱜ ᱯᱚᱠᱮᱴ @@ -522,7 +551,7 @@ ᱥᱭᱸᱠ ᱪᱟᱹᱞᱩᱭ ᱢᱮ - ᱡᱚᱲᱟᱣ ᱠᱳᱰ ᱥᱠᱟᱱ ᱢᱮ ᱰᱮᱥᱠᱴᱚᱯ Firefox ᱨᱮ + ᱡᱚᱲᱟᱣ ᱠᱳᱰ ᱥᱠᱟᱱ ᱢᱮ ᱰᱮᱥᱠᱴᱚᱯ Firefox ᱨᱮ ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ @@ -642,15 +671,15 @@ ᱢᱤᱫ ᱪᱟᱸᱫᱚ ᱛᱟᱭᱚᱢ - + - ᱚᱲᱟᱜ ᱠᱷᱚᱱ ᱮᱦᱚᱵᱽ ᱢᱮ + ᱚᱲᱟᱜ ᱠᱷᱚᱱ ᱮᱦᱚᱵᱽ ᱢᱮ - ᱯᱩᱱ ᱴᱟᱲᱟᱝ ᱛᱟᱭᱚᱢ + ᱯᱩᱱ ᱴᱟᱲᱟᱝ ᱛᱟᱭᱚᱢ - ᱡᱟᱣᱜᱮ + ᱡᱟᱣᱜᱮ - ᱛᱤᱥ ᱦᱚᱸ ᱵᱟᱝ + ᱛᱤᱥ ᱦᱚᱸ ᱵᱟᱝ ᱱᱤᱡᱮ ᱛᱮ ᱵᱚᱸᱫᱽ ᱢᱮ @@ -703,13 +732,13 @@ ᱛᱩᱢᱟᱹᱞ ᱨᱮ ᱥᱟᱺᱪᱟᱣ ᱢᱮ - ᱵᱟᱪᱷᱟᱣ + ᱵᱟᱪᱷᱟᱣ ᱡᱷᱚᱛᱚ ᱴᱮᱵᱽ ᱠᱚ ᱦᱟᱹᱴᱧ ᱢᱮ ᱱᱤᱛᱚᱜᱽᱼᱟᱜ ᱵᱚᱸᱫᱚᱼᱟᱜ ᱴᱮᱵᱽ ᱠᱚ - ᱱᱤᱛᱚᱜ ᱵᱚᱸᱫᱚᱼᱟᱜ + ᱱᱤᱛᱚᱜ ᱵᱚᱸᱫᱚᱼᱟᱜ ᱠᱷᱟᱛᱟ ᱨᱮᱭᱟᱜ ᱥᱟᱡᱟᱣ ᱠᱚ @@ -780,7 +809,10 @@ ᱥᱟᱺᱪᱟᱣ - ᱮᱴᱟᱜᱽ-ᱟᱜ + ᱮᱴᱟᱜᱽ-ᱟᱜ + + + ᱵᱷᱮᱜᱟᱨ ᱴᱮᱵᱽ ᱠᱚ @@ -825,10 +857,6 @@ ᱱᱚᱰᱮ ᱱᱟᱜᱟᱢ ᱵᱚᱱᱩᱜ-ᱟ - - ᱰᱟᱩᱱᱞᱚᱰ ᱠᱚ ᱢᱮᱴᱟᱣ ᱢᱮ - - ᱟᱢ ᱡᱷᱚᱛᱚ ᱞᱮᱠᱷᱟ ᱛᱮ ᱟᱢᱟᱜ ᱰᱟᱩᱱᱞᱚᱰ ᱠᱚ ᱢᱮᱴᱟᱣ ᱥᱟᱱᱟᱢ ᱠᱟᱱᱟ ᱥᱮ? ᱰᱟᱩᱱᱞᱚᱰᱠᱚ ᱚᱪᱚᱜᱮᱱᱟ @@ -871,7 +899,7 @@ ᱵᱩᱠᱢᱟᱨᱠ ᱢᱮᱱᱩ - ᱵᱩᱠᱢᱟᱨᱠ ᱥᱟᱯᱲᱣ ᱢᱮ + ᱵᱩᱠᱢᱟᱨᱠ ᱥᱟᱯᱲᱣ ᱢᱮ ᱯᱚᱴᱚᱢ ᱵᱟᱪᱷᱟᱣ ᱢᱮ @@ -1900,7 +1928,7 @@ ᱴᱷᱤᱠ, ᱵᱟᱰᱟᱭ ᱠᱮᱜᱼᱟᱹᱧ - ᱡᱟᱹᱥᱛᱤ ᱦᱤᱨᱤ ᱟᱠᱟᱱ ᱥᱟᱭᱤᱴ ᱫᱮᱠᱷᱟᱣᱢᱮ + ᱡᱟᱹᱥᱛᱤ ᱦᱤᱨᱤ ᱟᱠᱟᱱ ᱥᱟᱭᱤᱴ ᱫᱮᱠᱷᱟᱣᱢᱮ ᱡᱟᱹᱥᱛᱤ ᱦᱤᱨᱤ ᱟᱠᱟᱱ ᱥᱟᱭᱤᱴ ᱫᱮᱠᱷᱟᱣᱢᱮ @@ -1913,15 +1941,25 @@ ᱵᱟᱹᱰᱨᱟᱹ - - + + ᱵᱟᱝ ᱩᱥᱨᱟᱹᱣ ᱴᱮᱵᱽᱥ + + ᱵᱟᱝ ᱮᱠᱴᱤᱵᱷ ᱴᱮᱵᱽ ᱠᱚ ᱵᱚᱸᱫᱚᱭ ᱢᱮ - %s ᱞᱟᱹᱜᱤᱫ ᱱᱚᱰᱮ ᱴᱮᱵᱽ ᱢᱮᱱᱟᱜᱼᱟ ᱾ ᱚᱱᱟ ᱚᱠᱛᱚ ᱛᱟᱭᱚᱢ, ᱴᱮᱵᱽ ᱠᱚ ᱟᱡ ᱛᱮᱜᱮ ᱵᱚᱸᱫᱚᱜᱼᱟ ᱾ + %s ᱞᱟᱹᱜᱤᱫ ᱱᱚᱰᱮ ᱴᱮᱵᱽ ᱢᱮᱱᱟᱜᱼᱟ ᱾ ᱚᱱᱟ ᱚᱠᱛᱚ ᱛᱟᱭᱚᱢ, ᱴᱮᱵᱽ ᱠᱚ ᱟᱡ ᱛᱮᱜᱮ ᱵᱚᱸᱫᱚᱜᱼᱟ ᱾ - 30 ᱫᱤᱱ + 30 ᱫᱤᱱ - 1 ᱦᱟᱴ + 1 ᱦᱟᱴ + + + ᱟᱡ ᱛᱮ ᱵᱚᱸᱫ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ + + + ᱠᱩᱞ ᱢᱮ + + ᱵᱚᱸᱫᱚᱭ ᱢᱮ Firefox ᱟᱡ ᱛᱮ ᱠᱷᱩᱞᱟᱹ ᱪᱷᱚ ᱞᱟᱹᱜᱤᱫ ᱣᱮᱵᱥᱟᱭᱤᱴ, ᱤᱢᱮᱞ, ᱟᱨ ᱢᱮᱥᱮᱡᱽ ᱨᱮᱭᱟᱜ ᱞᱤᱝᱠ ᱥᱮᱴ ᱢᱮ ᱾ @@ -1938,4 +1976,6 @@ ᱵᱚᱸᱫ + + ᱰᱷᱮᱨ ᱥᱮᱬᱟᱭ ᱢᱮ From 630ee7e9d7d7a5699c265c9916db1df67f4fa7d9 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:52 +0000 Subject: [PATCH 469/517] Strings - app/src/main/res/values-sk/strings.xml --- app/src/main/res/values-sk/strings.xml | 183 +++++++++++++++++++++---- 1 file changed, 154 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index af66863efa..d1b7ad2c73 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -25,7 +25,7 @@ 1 otvorená karta. Ťuknutím prepnete karty. - 1 otvorených kariet. Ťuknutím prepnete karty. + 1 otvorených kariet. Ťuknutím prepnete karty. Počet vybraných položiek: %1$d @@ -54,7 +54,9 @@ Nedávno uložené - Nedávno pridané medzi záložky + Nedávno pridané medzi záložky + + Nedávno pridané medzi záložky Nedávno uložené záložky @@ -107,6 +109,20 @@ Zavrieť + + Tu sa presunú karty, ktoré ste dva týždne nevideli. + + Vypnúť v nastaveniach + + + Automaticky zavrieť po jednom mesiaci? + + Firefox môže zavrieť karty, ktoré ste za posledný mesiac nevideli. + + Zavrieť + + Zapnúť automatické zatváranie + Nová karta @@ -114,7 +130,7 @@ Nová súkromná karta - Top stránky + Top stránky @@ -122,6 +138,15 @@ Zobraziť všetko + + Tlačidlo Zobraziť všetky posledné karty + + Vaše hľadanie výrazu \"%1$s\" + + Stránky: %1$s + @@ -129,11 +154,17 @@ - Nedávno navštívené + Nedávno navštívené + + Nedávne vyhľadávania Odstrániť + + Tlačidlo Zobraziť všetky minulé prieskumy + Otvorené karty @@ -146,7 +177,7 @@ Zastaviť - Pridať medzi záložky + Pridať medzi záložky Upraviť záložku @@ -213,6 +244,8 @@ Prispôsobiť domovskú stránku + + Prispôsobiť domovskú stránku Úvodná obrazovka @@ -259,6 +292,27 @@ Vyhľadať na webe priamo z panela s adresou + + + Čo je nové vo Firefoxe + + Teraz je jednoduchšie pokračovať tam, kde ste prestali. + + Prispôsobená domovská stránka Firefoxu + + Prejdite na otvorené karty, záložky a históriu prehliadania. + + Čisté a usporiadané karty + + Odstráňte neporiadok zo záložiek vďaka vylepšenému rozloženiu a automatickému zatváraniu kariet. + + Nedávne vyhľadávania + + Zopakujte svoje najnovšie vyhľadávania z domovskej stránky a kariet. + + + Vaša prispôsobená domovská stránka Firefoxu teraz uľahčuje pokračovať tam, kde ste prestali. Nájdite svoje najnovšie karty, záložky a výsledky vyhľadávania. + Otvoriť novú kartu vo Firefoxe @@ -339,7 +393,9 @@ Téma vzhľadu - Domov + Domov + + Domovská stránka Gestá @@ -415,10 +471,16 @@ Nedávno uložené - Nedávno pridané medzi záložky + Nedávno pridané medzi záložky - - Nedávno navštívené + + Nedávno pridané medzi záložky + + Nedávno navštívené + + Nedávne vyhľadávania Pocket @@ -531,7 +593,7 @@ Zapnúť synchronizáciu - Naskenujte párovací kód zobrazený vo Firefoxe na počítači + Naskenujte párovací kód zobrazený vo Firefoxe na počítači Prihlásiť sa @@ -637,6 +699,10 @@ Zoznam Mriežka + + Hľadať v skupinách + + Zoskupovať súvisiace stránky Zavrieť karty @@ -648,15 +714,26 @@ Po mesiaci - + + Automatické zatváranie otvorených kariet + + - Pri spustení zobraziť úvodnú obrazovku + Pri spustení zobraziť úvodnú obrazovku + + Otváracia obrazovka - Po štyroch hodinách + Po štyroch hodinách + + Domovská stránka - Vždy + Vždy + + Posledná karta - Nikdy + Nikdy + + Domovská stránka po štyroch hodinách nečinnosti Zavrieť ručne @@ -666,6 +743,12 @@ Zavrieť po jednom mesiaci + + + Presúvať staré karty medzi neaktívne + + Karty, ktoré ste dva týždne nevideli, sa presunú do neaktívnej sekcie. + Odstrániť @@ -706,13 +789,13 @@ Uložiť do kolekcie - Vybrať + Vybrať Zobraziť všetky karty Nedávno zatvorené karty - Nedávno zatvorené + Nedávno zatvorené Nastavenia účtu @@ -787,7 +870,10 @@ Uložiť - Iné + Iné + + + Ostatné karty @@ -833,10 +919,6 @@ Nemáte žiadnu históriu prehliadania - - Vymazať zoznam stiahnutých súborov - - Naozaj chcete vymazať svoju históriu sťahovania? Stiahnuté súbory boli odstránené @@ -877,7 +959,7 @@ Ponuka záložiek - Úprava záložky + Úprava záložky Výber priečinku @@ -1904,8 +1986,10 @@ Ok, rozumiem + + Najnavštevovanejšie stránky - Zobraziť najnavštevovanejšie stránky + Zobraziť najnavštevovanejšie stránky Zobraziť najnavštevovanejšie stránky @@ -1918,15 +2002,41 @@ Zrušiť - - + + Neaktívne karty + + Zavrieť všetky neaktívne karty - Karty sú tu k dispozícii %s. Po uplynutí tejto doby sa karty automaticky zatvoria. + Karty sú tu k dispozícii %s. Po uplynutí tejto doby sa karty automaticky zatvoria. - 30 dní + 30 dní - 1 týždeň + 1 týždeň + + + + Automaticky zavrieť po jednom mesiaci? + + Firefox môže zavrieť karty, ktoré ste za posledný mesiac nevideli. + + ZAPNÚŤ AUTOMATICKÉ ZATVÁRANIE + + + + Pomôžte nám zlepšiť sa + + Prečo ste deaktivovali neaktívne karty? + + Táto funkcia ma nezaujíma + + Čas neaktivity je príliš dlhý + + Čas do neaktivity je príliš krátky + + Odoslať + + Zavrieť Nastavte si automatické otváranie webových stránok, e-mailov a správ vo Firefoxe. @@ -1943,4 +2053,19 @@ Zavrieť + + + Príbehy na zamyslenie + + Príbehy na zamyslenie + + Príbehy podľa témy + + Objavte ďalšie + + Službu poskytuje Pocket + + Súčasť rodiny Firefoxu. %s + + Ďalšie informácie From 53f7695ef2da9c3f26d3d84fd434999c1b4f5951 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:53 +0000 Subject: [PATCH 470/517] Strings - app/src/main/res/values-sl/strings.xml --- app/src/main/res/values-sl/strings.xml | 161 ++++++++++++++++++++----- 1 file changed, 132 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 847f4631d6..c4e47924a0 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -24,7 +24,7 @@ 1 odprt zavihek. Tapnite za preklop zavihkov. - Odprtih zavihkov: %1$s. Tapnite za preklop zavihkov. + Odprtih zavihkov: %1$s. Tapnite za preklop zavihkov. %1$d izbranih @@ -54,7 +54,9 @@ Nedavno shranjeno - Nedavno dodano med zaznamke + Nedavno dodano med zaznamke + + Nedavni zaznamki Nedavno shranjeni zaznamki @@ -108,6 +110,20 @@ Zapri + + Zavihki, ki jih dva tedna niste odprli, se premaknejo sem. + + Izklopi v nastavitvah + + + Samodejno zapri po enem mesecu? + + Firefox lahko zapre zavihke, ki si jih niste ogledali v zadnjem mesecu. + + Zapri + + Vklopi samodejno zapiranje + Nov zavihek @@ -115,7 +131,7 @@ Nov zasebni zavihek - Glavne strani + Glavne strani @@ -123,6 +139,14 @@ Prikaži vse + + Vaše iskanje \"%1$s\" + + + Strani: %1$s + @@ -130,7 +154,10 @@ - Nedavno obiskano + Nedavno obiskano + + Nedavna iskanja Odstrani @@ -147,7 +174,7 @@ Ustavi - Dodaj med zaznamke + Dodaj med zaznamke Uredi zaznamek @@ -211,6 +238,8 @@ Prilagodi začetno stran + + Prilagodi domačo stran Domači zaslon @@ -256,6 +285,15 @@ Iščite neposredno iz naslovne vrstice + + + Kaj je novega v Firefoxu + + + Čisti, urejeni zavihki + + Nedavna iskanja + Odpri nov zavihek v Firefoxu @@ -335,7 +373,9 @@ Tema - Domača stran + Domača stran + + Domača stran Poteze @@ -411,9 +451,15 @@ Nedavno shranjeno - Nedavno dodano med zaznamke - - Nedavno obiskano + Nedavno dodano med zaznamke + + Nedavni zaznamki + + Nedavno obiskano + + Nedavna iskanja Pocket @@ -528,7 +574,7 @@ Vklopi Sync - Skenirajte kodo za seznanjanje v Firefoxu za namizja + Skenirajte kodo za seznanjanje v Firefoxu za namizja Prijava @@ -637,6 +683,10 @@ Seznam Mreža + + Išči skupine + + Združi sorodna spletna mesta v skupine Zapri zavihke @@ -648,15 +698,24 @@ Po enem mesecu - + + Samodejno zapiraj odprte zavihke + + - Začni na domačem zaslonu + Začni na domačem zaslonu - po štirih urah + po štirih urah + + Domača stran - vedno + vedno + + Zadnji zavihek - nikoli + nikoli + + Domača stran po štirih urah nedejavnosti Zapri ročno @@ -666,6 +725,13 @@ Zapri po enem mesecu + + + Premakni stare zavihke med nedejavne + + + Zavihki, ki jih dva tedna niste odprli, se premaknejo v razdelek nedejavnih. + Odstrani @@ -708,13 +774,13 @@ Shrani v zbirko - Izberi + Izberi Deli vse zavihke Nedavno zaprti zavihki - Nedavno zaprto + Nedavno zaprto Nastavitve računa @@ -789,7 +855,10 @@ Shrani - Drugo + Drugo + + + Ostali zavihki @@ -835,10 +904,6 @@ Ni zgodovine - - Izbriši prenose - - Ste prepričani, da želite počistiti vaše prenose? Prenosi odstranjeni @@ -879,7 +944,7 @@ Meni zaznamkov - Uredi zaznamek + Uredi zaznamek Izberi mapo @@ -1921,7 +1986,7 @@ V redu, razumem - Prikaži najbolj obiskana spletna mesta + Prikaži najbolj obiskana spletna mesta Prikaži najbolj obiskana spletna mesta @@ -1934,15 +1999,43 @@ Prekliči - - + + Nedejavni zavihki + + Zapri vse nedejavne zavihke - Zavihki so tukaj na voljo %s. Po tem času se samodejno zaprejo. + Zavihki so tukaj na voljo %s. Po tem času se samodejno zaprejo. - 30 dni + 30 dni - 1 teden + 1 teden + + + + Samodejno zapri po enem mesecu? + + + Firefox lahko zapre zavihke, ki si jih niste ogledali v zadnjem mesecu. + + VKLOPI SAMODEJNO ZAPIRANJE + + + + Pomagajte nam, da postanemo boljši + + Zakaj ste onemogočili nedejavne zavihke? + + Možnost me ne zanima + + Čas do neaktivnosti je predolg + + Čas do neaktivnosti je prekratek + + Pošlji + + + Zapre se Nastavite, naj se povezave s spletnih strani, e-pošte in sporočil samodejno odpirajo v Firefoxu. @@ -1959,4 +2052,14 @@ Zapri + + Zgodbe po temi + + Odkrijte več + + Omogoča Pocket + + Del družine Firefox. %s + + Več o tem From 20da04bf96c4130af2917a15a9b87631b477a2a6 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:53 +0000 Subject: [PATCH 471/517] Strings - app/src/main/res/values-su/strings.xml --- app/src/main/res/values-su/strings.xml | 222 ++++++++++++++++++++----- 1 file changed, 181 insertions(+), 41 deletions(-) diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml index ed4dbad6f3..d22b018237 100644 --- a/app/src/main/res/values-su/strings.xml +++ b/app/src/main/res/values-su/strings.xml @@ -23,7 +23,7 @@ 1 tab muka. Toél pikeun ngagilir tab. - %1$s tab muka. Toél pikeun ngagilir tab. + %1$s tab muka. Toél pikeun ngagilir tab. %1$d dipilih @@ -52,7 +52,9 @@ Anyar diteundeun - Anyar dimarkahan + Anyar dimarkahan + + Markah anyar Anyar dicirian @@ -103,13 +105,27 @@ Tutup + + Tab anu geus dua minggu teu dibuka dipindahkeun ka dieu. + + Pareuman dina setélan + + + Tutup langsung sanggeus sabulan? + + Firefox bisa nutup tab anu geus teu dibuka leuwih ti sabulan. + + Tutup + + Hurungkeun otomatis nutup + Tab anyar Tab nyamuni anyar - Loka kawentar + Loka kawentar @@ -117,10 +133,33 @@ Témbongkeun sadayana + + Témbongkeun sakabéh tombol tab anyar + + + Sungsian anjeun ngeunaan \"%1$s\" + + Loka: %1$s + - Éksplorasi tiheula + Éksplorasi tiheula + + + Anyar dianjangan + + Anyar nyungsi + + Piceun + + + Témbongkeun sadaya tombol langlangan nu geus kaliwat @@ -135,7 +174,7 @@ Eureun - Markah + Markah Édit markah @@ -201,6 +240,8 @@ Kustomkeun tepas + + Kustomkeun tepas Layar tepas @@ -247,6 +288,27 @@ Paluruh langsung tina bilah alamat + + + Anu anyar di Firefox + + Ayeuna leuwih babari nyokot cadangan nalika anjeun ninggalkeun. + + Tepas Firefox pribadi + + Luncat ka tab muka, markah, jeung jujutan ngalanglang. + + Tab beresih tur rapih + + Singkahan tabrakan tab ku tata perenah anu leuwih hadé tur tab oto-nutup. + + Anyar nyungsi + + Anjangan deui sungsian panungtung ti tepas tab. + + + Tepas Firefox pribadi kiwari mantuan anjeun nyokot ti anu ditinggalkeun. Néangan tab anu can lila, markah, jeung hasil nyungsi. + Buka tab Firefox anyar @@ -325,7 +387,9 @@ Téma - Tepas + Tepas + + Tepas Réngkak @@ -402,9 +466,15 @@ Anyar diteundeun - Anyar dimarkahan - - Anyar dianjangan + Anyar dimarkahan + + Markah anyar + + Anyar dianjangan + + Anyar nyungsi Saku @@ -518,7 +588,7 @@ Hurungkeun Sync - Pinday sandi papasangan dina Firefox déstop + Pinday sandi papasangan dina Firefox déstop Asup @@ -598,6 +668,13 @@ Tutup + + %d loka + + %d loka + Tab anu anyar ditutup @@ -620,6 +697,10 @@ Béréndélan Grid + + Sungsi grup + + Gorombolkeun loka anu patali Tutup tab @@ -631,15 +712,26 @@ Sanggeus sabulan - + + Oto-nutup tab anu muka + + - Mimitian di tepas + Mimitian di tepas + + Layar pamuka - Sanggeus opat jam + Sanggeus opat jam + + Tepas - Matuh + Matuh + + Tab panungtung - Ulah + Ulah + + Tepas sanggeus opat jam teu aktip Tutup manual @@ -649,6 +741,12 @@ Tutup sanggeus sabulan + + + Pindahkeun tab heubeul ka nganggur + + Tab anu teu dibuka salila dua minggu dipindahkeun ka bagian nganggur. + Piceun @@ -689,13 +787,13 @@ Simpen kana koléksi - Pilih + Pilih Bagikeun sadaya tab Tab nu anyar ditutup - Anyar ditutup + Anyar ditutup Setélan akun @@ -767,7 +865,10 @@ Teundeun - Lianna + Lianna + + + Tab lianna @@ -781,15 +882,15 @@ Beresihan - Tiron + Tiron - Bagikeun + Bagikeun - Buka dina tab anyar + Buka dina tab anyar - Buka dina tab nyamuni + Buka dina tab nyamuni - Pupus + Pupus %1$d dipilih @@ -814,10 +915,6 @@ Teu aya jujutan di dieu - - Hapus undeuran - - Yakin rék meresihan undeuran anjeun? Undeuran Disingkahkeun @@ -858,7 +955,7 @@ Menu markah - Édit markah + Édit markah Pilih map @@ -1269,12 +1366,12 @@ Geus boga akun? - Tingali naon nu anyar + Tingali naon nu anyar - Boga patalekan ngeunaan rarancang anyar %s? Hoyong uninga naon anu robah? + Boga patalekan ngeunaan rarancang anyar %s? Hoyong uninga naon anu robah? - Kéngingkeun waleran di dieu + Kéngingkeun waleran di dieu Singkronkeun Firefox sakur parabot @@ -1315,14 +1412,14 @@ Perenahkeun tulbar sangkan babari kahontal. Teundeun di handap, atawa pindahkeun ka punclut. - Nyungsi nyamuni + Nyungsi nyamuni - Buka tab nyamuni sakali: Toél ikon %s. + Buka tab nyamuni sakali: Toél ikon %s. - Unggal buka tab nyamuni: Anyarkeun sétélan langlangan nyamuni anjeun. + Unggal buka tab nyamuni: Anyarkeun sétélan langlangan nyamuni anjeun. - Buka setélan + Buka setélan Salindungan anjeun Okéh, Ngarti + + Loka punclut anu pangmindengna dianjangan - Témbongkeun loka anu pangmindengna dianjangan + Témbongkeun loka anu pangmindengna dianjangan Témbongkeun loka anu pangmindengna dianjangan @@ -1911,15 +2010,41 @@ Bolay - - + + Tab teu aktip + + Tutup sakabéh tab nganggur - Tab sayaga di dieu salila %s. Sanggeusna, tab bakal otomatis ditutup. + Tab sayaga di dieu salila %s. Sanggeusna, tab bakal otomatis ditutup. - 30 poé + 30 poé - 1 minggu + 1 minggu + + + + Oto-tutup sanggeus sabulan? + + Firefox bisa nutup tab anu ku anjeun teu dibuka leuwih ti sabulan. + + HURUNGKEUN OTO-TUTUP + + + + Bantuan ronjatkeun + + Naha anjeun mareuman tab nganggur? + + Henteu kabita ku piturna + + Mangsa jadi nganggurna lila teuing + + Mangsa jadi nganggurna téréh teuing + + Kirim + + Tutup Setél tutumbu ti raramatloka, surélék, jeung surat pikeun muka otomatis dina Firefox. @@ -1936,4 +2061,19 @@ Tutup + + + Carita pikiraneun + + Carita pikiraneun + + Carita dumasar jejer + + Panggihan nu lianna + + Ditanagaan ku Pocket. + + Bagéan ti kulawarga Firefox. %s + + Lenyepan From b1fa97aaaca9b74d4187e907f8b9de8a31de9baa Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:53 +0000 Subject: [PATCH 472/517] Strings - app/src/main/res/values-sv-rSE/strings.xml --- app/src/main/res/values-sv-rSE/strings.xml | 187 +++++++++++++++++---- 1 file changed, 157 insertions(+), 30 deletions(-) diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index 658770f98a..7c2da00bb8 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -24,7 +24,7 @@ 1 öppen flik. Tryck för att växla mellan flikar. - %1$s öppna flikar. Tryck för att växla mellan flikar. + %1$s öppna flikar. Tryck för att växla mellan flikar. %1$d markerade @@ -53,7 +53,9 @@ Nyligen sparade - Nyligen bokmärkt + Nyligen bokmärkt + + Senaste bokmärken Nyligen sparade bokmärken @@ -108,6 +110,20 @@ Ignorera + + Flikar som du inte har besökt på två veckor flyttas hit. + + Stäng av i inställningarna + + + Stäng automatiskt efter en månad? + + Firefox kan stänga flikar som du inte har besökt under den senaste månaden. + + Stäng + + Aktivera automatisk stängning + Ny flik @@ -115,7 +131,7 @@ Ny privat flik - Mest besökta + Mest besökta @@ -123,6 +139,15 @@ Visa alla + + Visa knappen alla senaste flikar + + Du sökte efter \"%1$s\" + + Webbplatser: %1$s + @@ -130,11 +155,17 @@ - Nyligen besökta + Nyligen besökta + + Senaste sökningar Ta bort + + Visa knappen alla tidigare utforskningar + Öppna flikar @@ -147,7 +178,7 @@ Stopp - Bokmärk + Bokmärk Redigera bokmärke @@ -213,7 +244,9 @@ Redigera - Anpassa hem + Anpassa startsida + + Anpassa startsidan Startsidan @@ -260,6 +293,29 @@ Sök direkt från adressfältet + + + Vad är nytt i Firefox + + + Det är nu enklare att fortsätta där du slutade. + + Personlig Firefox-startsida + + Hoppa till dina öppna flikar, bokmärken och surfhistorik. + + Rena, organiserade flikar + + Rensa bort röran med förbättrad layout och flikar som stängs automatiskt. + + Senaste sökningar + + + Återbesök dina senaste sökningar från din startsida och flikar. + + + Din personliga Firefox-startsida gör det nu lättare att fortsätta där du slutade. Hitta dina senaste flikar, bokmärken och sökresultat. + Öppna en ny Firefox-flik @@ -338,7 +394,9 @@ Tema - Hem + Hem + + Startsida Gester @@ -414,9 +472,15 @@ Nyligen sparade - Nyligen bokmärkt - - Nyligen besökta + Nyligen bokmärkt + + Senaste bokmärken + + Nyligen besökta + + Senaste sökningar Pocket @@ -531,7 +595,7 @@ Aktivera Sync - Skanna parningskod i Firefox för datorer + Skanna parningskod i Firefox för datorer Logga in @@ -638,6 +702,10 @@ Lista Rutnät + + Sökgrupper + + Gruppera relaterade webbplatser tillsammans Stäng flikar @@ -649,15 +717,26 @@ Efter en månad - + + Stäng öppna flikar automatiskt + + - Börja på startsidan + Börja på startsidan + + Öppningsskärm - Efter fyra timmar + Efter fyra timmar + + Startsida - Alltid + Alltid + + Senaste flik - Aldrig + Aldrig + + Startsida efter fyra timmars inaktivitet Stäng manuellt @@ -667,6 +746,12 @@ Stäng efter en månad + + + Flytta gamla flikar till inaktiva + + Flikar som du inte har besökt på två veckor flyttas till inaktiva. + Ta bort @@ -708,13 +793,13 @@ Spara i samling - Välj + Välj Dela alla flikar Nyligen stängda flikar - Nyligen stängda + Nyligen stängda Kontoinställningar @@ -789,7 +874,10 @@ Spara - Annat + Annat + + + Andra flikar @@ -835,10 +923,6 @@ Ingen historik här - - Ta bort nedladdningar - - Är du säker på att du vill rensa nedladdningarna? Nedladdningar har tagits bort @@ -879,7 +963,7 @@ Bokmärkesmeny - Redigera bokmärke + Redigera bokmärke Välj mapp @@ -1914,8 +1998,10 @@ Ok, jag förstår + + Mest besökta webbplatser - Visa de mest besökta webbplatserna + Visa de mest besökta webbplatserna Visa mest besökta webbplatser @@ -1928,16 +2014,42 @@ Avbryt - - + + Inaktiva flikar + + Stäng alla inaktiva flikar - Flikar finns här i %s. Efter den tiden stängs flikarna automatiskt. + Flikar finns här i %s. Efter den tiden stängs flikarna automatiskt. - 30 dagar + 30 dagar - 1 vecka + 1 vecka + + + + Stäng de automatiskt efter en månad? + + Firefox kan stänga flikar som du inte har besökt under den senaste månaden. + + SLÅ PÅ AUTOMATISK STÄNGNING + + + + Hjälp oss att bli bättre + + Varför inaktiverade du inaktiva flikar? + + Inte intresserad av funktionen + + Tiden till inaktiv är för lång + + Tiden till inaktiv är för kort + + Skicka + + Stäng Ställ in länkar från webbplatser, e-post och meddelanden så att de öppnas automatiskt i Firefox. @@ -1954,4 +2066,19 @@ Stäng + + + Tankeväckande historier + + Tankeväckande historier + + Historier efter ämne + + Upptäck mer + + Tillhandahålls av Pocket + + Del av Firefox-familjen. %s + + Läs mer From a6283ebcb696bff0df5d5aef4fefc9c0a199efab Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:53 +0000 Subject: [PATCH 473/517] Strings - app/src/main/res/values-tg/strings.xml --- app/src/main/res/values-tg/strings.xml | 186 +++++++++++++++++++++---- 1 file changed, 157 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-tg/strings.xml b/app/src/main/res/values-tg/strings.xml index eb27813775..04f026a774 100644 --- a/app/src/main/res/values-tg/strings.xml +++ b/app/src/main/res/values-tg/strings.xml @@ -22,7 +22,7 @@ 1 варақаи кушодашуда. Барои гузариш байни варақаҳо зарба занед. - %1$s варақаи кушодашуда. Барои гузариш байни варақаҳо зарба занед. + %1$s варақаи кушодашуда. Барои гузариш байни варақаҳо зарба занед. %1$d интихоб шуд @@ -52,7 +52,9 @@ Иловашудаи охирин - Хатбаракҳои иловашудаи охирин + Хатбаракҳои иловашудаи охирин + + Хатбаракҳои охирин Хатбаракҳои нигоҳдошташудаи охирин @@ -107,13 +109,27 @@ Нодида гузарондан + + Варақаҳое, ки шумо ду ҳафта такроран надидаед, ба ин ҷо интиқол дода мешаванд. + + Дар танзимот хомӯш кунед + + + Пас аз як моҳ ба таври худкор пӯшида шавад? + + Firefox метавонад варақаҳоеро, ки шумо дар давоми як моҳи охир надидаед, пӯшонад. + + Пӯшидан + + Фаъол кардани «Пӯшидани худкор» + Варақаи нав Варақаи махфии нав - Сомонаҳои беҳтарин + Сомонаҳои беҳтарин @@ -121,6 +137,15 @@ Ҳамаро намоиш додан + + Нишон додани тугмаи ҳамаи хатбаракҳои охирин + + Ҷустуҷӯи шумо барои \"%1$s\" + + Сомонаҳо: %1$s + @@ -128,11 +153,17 @@ - Дидашудаи охирин + Дидашудаи охирин + + Ҷустуҷӯҳои охирин Тоза кардан + + Нишон додани тугмаи ҳамаи тадқиқотҳои охирин + Варақаҳои кушодашуда @@ -145,7 +176,7 @@ Истодан - Хатбарак + Хатбарак Таҳрир кардани хатбарак @@ -210,6 +241,8 @@ Танзимоти асосӣ + + Танзимоти саҳифаи асосӣ Экрани асосӣ @@ -255,6 +288,29 @@ Ҷустуҷӯи бевосита аз навори нишонӣ + + + Дар Firefox чӣ нав аст + + + Акнун ба он сомонае, ки шумо ба қарибӣ тамошо кардаед, баргардонидан осонтар аст. + + Шахсисозии саҳифаи асосии Firefox + + Ба варақаҳои кушода, хатбаракҳо ва таърихи тамошокунӣ гузаред. + + Варакаҳои ботартиб ва соф + + Ба тартиб даровардани варақаҳо тавассути тарҳбандии беҳтаршуда ва пӯшидани варақаҳо ба таври худкор. + + Ҷустуҷӯҳои охирин + + + Ҷустуҷӯҳои охирини худро тавассути саҳифаи асосӣ ва варақаҳо боздид намоед. + + + Акнун саҳифаи асосии шахсишудаи шумо дар браузери Firefox бозгашти шуморо ба сомонаҳои охирин осон мекунад. Варақаҳо, хатбаракҳо ва натиҷаҳои ҷустуҷӯи охиринро ба даст оред. + Кушодани варақаи нави Firefox @@ -333,7 +389,9 @@ Мавзӯъ - Асосӣ + Асосӣ + + Саҳифаи асосӣ Ишораҳо @@ -405,9 +463,15 @@ Иловашудаи охирин - Хатбаракҳои иловашудаи охирин - - Дидашудаи охирин + Хатбаракҳои иловашудаи охирин + + Хатбаракҳои охирин + + Дидашудаи охирин + + Ҷустуҷӯҳои охирин Pocket @@ -519,7 +583,7 @@ Фаъол кардани ҳамоҳангсозӣ - Рамзи ҷуфтро дар версияи Firefox-и мизи корӣ скан кунед + Рамзи ҷуфтро дар версияи Firefox-и мизи корӣ скан кунед Ворид шудан @@ -626,6 +690,10 @@ Рӯйхат Тӯр + + Гурӯҳҳои ҷустуҷӯ + + Сомонаҳои марбутро гурӯҳбандӣ кунед Пӯшидани варақаҳо @@ -637,15 +705,26 @@ Пас аз як моҳ - + + Пӯшидани варақаҳои кушода ба таври худкор + + - Оғоз дар экрани асосӣ + Оғоз дар экрани асосӣ + + Экрани ибтидоӣ - Пас аз чор соат + Пас аз чор соат + + Саҳифаи асосӣ - Ҳамеша + Ҳамеша + + Варақаи охирин - Ҳеҷ гоҳ + Ҳеҷ гоҳ + + Саҳифаи асосӣ пас аз чор соати беамалӣ Ба таври дастӣ пӯшидан @@ -655,6 +734,12 @@ Пас аз як моҳ пӯшидан + + + Варақаҳои куҳнаро ба ғайрифаъол интиқол диҳед + + Варақаҳое, ки шумо ду ҳафта такроран надидаед, ба қисмати ғайрифаъол интиқол дода мешаванд. + Тоза кардан @@ -698,13 +783,13 @@ Нигоҳ доштан дар маҷмӯа - Интихоб кардан + Интихоб кардан Мубодила кардани ҳамаи варақаҳо Варақаҳои ба наздикӣ пӯшидашуда - Пӯшидашудаи охирин + Пӯшидашудаи охирин Танзимоти ҳисоб @@ -775,7 +860,10 @@ Нигоҳ доштан - Дигар + Дигар + + + Варақаҳои дигар @@ -820,10 +908,6 @@ Ягон таърих нест - - Нест кардани богириҳо - - Шумо мутмаин ҳастед, ки мехоҳед боргириҳои худро нест намоед? Боргириҳо тоза шуданд @@ -866,7 +950,7 @@ Менюи хатбаракҳо - Таҳрир кардани хатбарак + Таҳрир кардани хатбарак Интихоб кардани ҷузвадон @@ -1899,8 +1983,10 @@ Хуб, фаҳмидам + + Сомонаҳои дидашудаи роиҷ - Намоиш додани сомонаҳои дидашудаи роиҷ + Намоиш додани сомонаҳои дидашудаи роиҷ Намоиш додани сомонаҳои роиҷ @@ -1913,15 +1999,42 @@ Бекор кардан - - + + Варақаҳои ғайрифаъол + + Пӯшидани ҳамаи варақаҳои ғайрифаъол - Варақаҳо дар ин ҷо ба муддати %s дастрас мешаванд. Баъд аз ин вақт, варақаҳо ба таври худкор пӯшида мешаванд. + Варақаҳо дар ин ҷо ба муддати %s дастрас мешаванд. Баъд аз ин вақт, варақаҳо ба таври худкор пӯшида мешаванд. - 30 рӯз + 30 рӯз - 1 ҳафта + 1 ҳафта + + + + Пас аз як моҳ ба таври худкор пӯшида шавад? + + + Firefox метавонад варақаҳоеро, ки шумо дар давоми як моҳи охир надидаед, пӯшонад. + + ФАЪОЛ КАРДАНИ ПӮШИШИ ХУДКОР + + + + Лутфан, ба мо барои такмилсозии браузер кумак кунед + + Чаро шумо варақаҳои ғайрифаъолро хомӯш кардед? + + Ба ин хусусият таваҷҷуҳ надорам + + Вақти ғайрифаъолсозӣ хеле дароз аст + + Вақти ғайрифаъолсозӣ хеле кутоҳ аст + + Фиристодан + + Пӯшидааст Пайванҳоеро, танзим кунед, ки онҳо аз сомонаҳо, почтаи электронӣ ва паёмҳо дар браузери Firefox ба таври худкор кушода шаванд. @@ -1938,4 +2051,19 @@ Пӯшидан + + + Ҳикояҳои андешаангез + + Ҳикояҳои андешаангез + + Ҳикояҳо аз рӯи мавзӯъ + + Бештар омӯзед + + Дар асоси Pocket кор мекунад. + + Қисми оилаи Firefox.%s + + Маълумоти бештар From 21fc908d17ddfbf7685600eb8d095a45a33f1d9c Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:53 +0000 Subject: [PATCH 474/517] Strings - app/src/main/res/values-tr/strings.xml --- app/src/main/res/values-tr/strings.xml | 125 +++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 427d62d296..52b1855b76 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -53,7 +53,9 @@ Son kaydedilenler - Son yer imleri + Son yer imleri + + Son yer imleri Son kaydedilen yer imleri @@ -106,6 +108,11 @@ Kapat + + İki haftadır bakmadığınız sekmeler buraya taşınır. + + Ayarlardan kapat + Yeni sekme @@ -121,6 +128,15 @@ Tümünü göster + + Son sekmeleri göster düğmesi + + \"%1$s\" aramanız + + Site: %1$s + @@ -128,11 +144,17 @@ - Son bakılanlar + Son bakılanlar + + Son aramalar Kaldır + + Geçmiş keşifleri göster düğmesi + Açık sekmeler @@ -210,6 +232,8 @@ Giriş sayfasını özelleştir + + Giriş sayfasını özelleştir Ana ekran @@ -256,6 +280,28 @@ Doğrudan adres çubuğundan arama yapın + + + Firefox’taki yenilikler + + Kaldığınız yerden devam etmek artık daha kolay. + + Size özel Firefox giriş sayfası + + Açık sekmelerinizi, yer imlerinizi ve gezinti geçmişinizi görün. + + Düzenli sekmeler + + Yeni sekme düzeni ve kendiliğinden kapanan sekmelerle karmaşayı ortadan kaldırın. + + Son aramalar + + + Son aramalarınıza giriş sayfasından ve sekmelerden tekrar ulaşın. + + + Kişisel Firefox giriş sayfanız, kaldığınız yerden devam etmeyi kolaylaştırıyor. Son sekmeleriniz, yer imleriniz ve arama sonuçlarınız artık giriş sayfanızda. + Yeni Firefox sekmesi aç @@ -409,9 +455,15 @@ Son kaydedilenler - Son yer imleri - - Son bakılanlar + Son yer imleri + + Son yer imleri + + Son bakılanlar + + Son aramalar Pocket @@ -629,6 +681,10 @@ Liste Izgara + + Arama grupları + + İlgili siteleri gruplandırın Sekmeleri kapat @@ -640,6 +696,9 @@ Bir ay sonra + + Açık sekmeleri otomatik kapat + Ana ekrandan başla @@ -658,6 +717,12 @@ Bir ay sonra kapat + + + Eski sekmeleri pasife taşı + + İki haftadır bakmadığınız sekmeler pasif bölümüne taşınır. + Kaldır @@ -781,7 +846,10 @@ Kaydet - Diğer + Diğer + + + Diğer sekmeler @@ -1911,9 +1979,11 @@ İptal - - + + Pasif sekmeler + + Tüm pasif sekmeleri kapat Sekmeler burada %s kalacaktır. Bu sürenin sonunda sekmeler otomatik olarak kapatılacaktır. @@ -1921,6 +1991,30 @@ 1 hafta + + + Bir ay sonra kendiliğinden kapatılsın mı? + + Firefox, bir aydır bakmadığınız sekmeleri kapatabilir. + + OTOMATİK KAPATMAYI AÇ + + + + Fikirlerinizi paylaşır mısınız? + + Pasif sekmeleri neden devre dışı bıraktınız? + + Bu özellik ilgimi çekmiyor + + Pasife alma süresi çok uzun + + Pasife alma süresi çok kısa + + Gönder + + Kapat + Web siteleri, e-postalar ve mesajlardaki bağlantılar otomatik olarak Firefox’ta açılsın. @@ -1936,4 +2030,19 @@ Kapat + + + Merak uyandıran makaleler + + Merak uyandıran makaleler + + Konuya göre makaleler + + Daha fazlasını keşfedin + + Pocket desteğiyle. + + Firefox ailesinden. %s + + Daha fazla bilgi al From 0fd6e9af32ffb780b22c45ed78e11fbc3116d609 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:53 +0000 Subject: [PATCH 475/517] Strings - app/src/main/res/values-uk/strings.xml --- app/src/main/res/values-uk/strings.xml | 186 +++++++++++++++++++++---- 1 file changed, 156 insertions(+), 30 deletions(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 11ff5208b8..6399b5b31f 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -23,7 +23,7 @@ 1 відкрита вкладка. Торкніться, щоб перемкнути вкладки. - %1$s відкритих вкладок. Торкніться, щоб перемкнути вкладки. + %1$s відкритих вкладок. Торкніться, щоб перемкнути вкладки. Вибрано %1$d @@ -52,7 +52,9 @@ Недавно збережені - Останні закладки + Останні закладки + + Останні закладки Недавно збережені закладки @@ -106,6 +108,20 @@ Відхилити + + Сюди переміщуються вкладки, які ви не переглядали впродовж двох тижнів. + + Вимкнути у налаштуваннях + + + Закрити через місяць автоматично? + + Firefox може закривати вкладки, які ви не переглядали протягом останнього місяця. + + Закрити + + Увімкнути автозакриття + Нова вкладка @@ -113,7 +129,7 @@ Приватна вкладка - Популярні сайти + Популярні сайти @@ -121,6 +137,15 @@ Показати всі + + Кнопка показу всіх останніх вкладок + + Результати пошуку \"%1$s\" + + Сайти: %1$s + @@ -128,11 +153,17 @@ - Недавно відвідані + Недавно відвідані + + Недавні пошуки Вилучити + + Кнопка показу всіх попередніх пошуків + Відкриті вкладки @@ -145,7 +176,7 @@ Зупинити - Додати закладку + Додати закладку Змінити закладку @@ -212,6 +243,8 @@ Налаштувати домівку + + Налаштувати домівку Домашній екран @@ -258,6 +291,27 @@ Пошук безпосередньо з панелі адреси + + + Що нового у Firefox + + Тепер легше повернутися туди, де ви зупинилися. + + Персоналізована сторінка домівки Firefox + + Переходьте до відкритих вкладок, закладок та історії перегляду. + + Чисті, упорядковані вкладки + + Приберіть безлад у вкладках із вдосконаленим виглядом та автозакриттям вкладок. + + Недавні пошуки + + Перегляньте останні пошукові запити зі своєї сторінки домівки та вкладок. + + + Відтепер ваша персоналізована сторінка домівки Firefox полегшує продовження роботи з місця, де ви зупинилися. Знайдіть останні вкладки, закладки та результати пошуку. + Відкрити нову вкладку Firefox @@ -338,7 +392,9 @@ Тема - Домівка + Домівка + + Домівка Жести @@ -414,9 +470,15 @@ Недавно збережені - Останні закладки - - Недавно відвідані + Останні закладки + + Останні закладки + + Недавно відвідані + + Недавні пошуки Pocket @@ -530,7 +592,7 @@ Увімкнути синхронізацію - Скануйте код у Firefox на комп’ютері + Скануйте код у Firefox на комп’ютері Увійти @@ -636,6 +698,10 @@ Списком Сіткою + + Пошук груп + + Групуйте пов’язані сайти разом Закривати вкладки @@ -647,15 +713,26 @@ Через місяць - + + Автозакриття відкритих вкладок + + - Відкривати домівку після запуску + Відкривати домівку після запуску + + Початковий екран - Через чотири години + Через чотири години + + Домівка - Завжди + Завжди + + Остання вкладка - Ніколи + Ніколи + + Домівка через чотири години бездіяльності Закривати власноруч @@ -665,6 +742,12 @@ Закрити через місяць + + + Переміщувати старі вкладки в неактивні + + Вкладки, які ви не переглядали впродовж двох тижнів, переміщуватимуться у розділ неактивних. + Вилучити @@ -707,13 +790,13 @@ Зберегти до збірки - Вибрати + Вибрати Поділитися всіма вкладками Недавно закриті вкладки - Недавно закриті + Недавно закриті Обліковий запис @@ -787,7 +870,10 @@ Зберегти - Інші + Інші + + + Інші вкладки @@ -833,10 +919,6 @@ Історія відсутня - - Видалити завантаження - - Ви впевнені, що хочете очистити завантаження? Завантаження вилучено @@ -877,7 +959,7 @@ Меню закладок - Змінити закладку + Змінити закладку Обрати теку @@ -1075,7 +1157,7 @@ Надіслати на пристрій - Всі дії + Усі дії Недавно використані @@ -1913,8 +1995,10 @@ Гаразд, зрозуміло + + Найвідвідуваніші сайти - Показати найвідвідуваніші сайти + Показати найвідвідуваніші сайти Показати найвідвідуваніші сайти @@ -1927,15 +2011,42 @@ Скасувати - - + + Неактивні вкладки + + Закрити всі неактивні вкладки - Вкладки доступні тут впродовж %s. Після цього вони будуть автоматично закриті. + Вкладки доступні тут впродовж %s. Після цього вони будуть автоматично закриті. - 30 днів + 30 днів - 1 тижня + 1 тижня + + + + Закрити через місяць автоматично? + + Firefox може закривати вкладки, які ви не переглядали впродовж останнього місяця. + + УВІМКНУТИ АВТОЗАКРИТТЯ + + + + Допоможіть нам вдосконалитися + + Чому ви вимкнули неактивні вкладки? + + Ця функція мені не цікава + + Надто тривалий час неактивності + + Надто короткий час неактивності + + Надіслати + + + Закрити Автоматично відкривати посилання з вебсайтів, електронних листів та повідомлень у Firefox. @@ -1952,4 +2063,19 @@ Закрити + + + Розповіді, що спонукають замислитися + + Розповіді, що спонукають замислитися + + Розповіді за темами + + Знайти більше + + Надано Pocket. + + Частина родини Firefox. %s + + Докладніше From a1d053d464415f42bfa175939b88ec88af72a2fb Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:53 +0000 Subject: [PATCH 476/517] Strings - app/src/main/res/values-vi/strings.xml --- app/src/main/res/values-vi/strings.xml | 183 +++++++++++++++++++++---- 1 file changed, 154 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index b0cb42977d..e52b58feb3 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -24,7 +24,7 @@ 1 thẻ đang mở. Chạm để chuyển thẻ. - %1$s thẻ đang mở. Chạm để chuyển thẻ. + %1$s thẻ đang mở. Chạm để chuyển thẻ. %1$d đã chọn @@ -53,7 +53,9 @@ Đã lưu gần đây - Dấu trang gần đây + Dấu trang gần đây + + Dấu trang gần đây Dấu trang đã lưu gần đây @@ -106,6 +108,20 @@ Bỏ qua + + Các thẻ bạn đã không xem trong hai tuần sẽ được chuyển đến đây. + + Tắt trong cài đặt + + + Tự động đóng sau một tháng? + + Firefox có thể đóng các thẻ bạn không xem trong tháng qua. + + Đóng + + Bật tự động đóng + Thẻ mới @@ -113,7 +129,7 @@ Thẻ riêng tư mới - Trang web hàng đầu + Trang web hàng đầu @@ -121,6 +137,15 @@ Hiển thị tất cả + + Nút hiển thị tất cả các thẻ gần đây + + Tìm kiếm của bạn cho \"%1$s\" + + Trang web: %1$s + @@ -128,11 +153,17 @@ - Đã xem gần đây + Đã xem gần đây + + Tìm kiếm gần đây Xóa + + Nút hiển thị tất cả các khám phá trước đây + Các thẻ đang mở @@ -146,7 +177,7 @@ Dừng - Đánh dấu + Đánh dấu Chỉnh sửa dấu trang @@ -210,6 +241,8 @@ Tùy chỉnh trang chủ + + Tùy chỉnh trang chủ Màn hình chính @@ -255,6 +288,27 @@ Tìm kiếm trực tiếp từ thanh địa chỉ + + + Có gì mới trong Firefox + + Giờ đây, việc tiếp tục nơi bạn đã dừng lại sẽ trở nên dễ dàng hơn. + + Trang chủ Firefox được cá nhân hóa + + Chuyển đến các thẻ đang mở, dấu trang và lịch sử duyệt web của bạn. + + Các thẻ gọn gàng, có tổ chức + + Xóa các thẻ lộn xộn với bố cục được cải thiện và tự động đóng các thẻ. + + Tìm kiếm gần đây + + Xem lại các tìm kiếm mới nhất của bạn từ trang chủ và các thẻ của bạn. + + + Trang chủ Firefox được cá nhân hóa của bạn giờ đây giúp bạn tiếp tục lại nơi bạn đã dừng lại dễ dàng hơn. Tìm các thẻ, dấu trang và kết quả tìm kiếm gần đây của bạn. + Mở một thẻ Firefox mới @@ -333,7 +387,9 @@ Chủ đề - Trang chủ + Trang chủ + + Trang chủ Cử chỉ @@ -407,9 +463,15 @@ Đã lưu gần đây - Dấu trang gần đây - - Đã xem gần đây + Dấu trang gần đây + + Dấu trang gần đây + + Đã xem gần đây + + Tìm kiếm gần đây Pocket @@ -521,7 +583,7 @@ Bật đồng bộ hóa - Quét mã ghép nối trong máy tính để bàn Firefox + Quét mã ghép nối trong máy tính để bàn Firefox Đăng nhập @@ -626,6 +688,10 @@ Danh sách Lưới + + Tìm kiếm nhóm + + Nhóm các trang web liên quan lại với nhau Đóng thẻ @@ -637,15 +703,26 @@ Sau một tháng - + + Tự động đóng các thẻ đang mở + + - Bắt đầu tại màn hình chính + Bắt đầu tại màn hình chính + + Khi mở ứng dụng, hãy vào - Sau bốn giờ + Sau bốn giờ + + Trang chủ - Luôn luôn + Luôn luôn + + Thẻ cuối cùng - Không bao giờ + Không bao giờ + + Trang chủ sau bốn giờ không hoạt động Đóng thủ công @@ -655,6 +732,12 @@ Đóng sau một tháng + + + Chuyển các thẻ cũ sang không hoạt động + + Các thẻ bạn đã không xem trong hai tuần sẽ được chuyển sang phần không hoạt động. + Xóa @@ -696,13 +779,13 @@ Lưu vào bộ sưu tập - Chọn + Chọn Chia sẻ tất cả các thẻ Các thẻ đã đóng gần đây - Đã đóng gần đây + Đã đóng gần đây Cài đặt tài khoản @@ -776,7 +859,10 @@ Lưu - Khác + Khác + + + Các thẻ khác @@ -821,10 +907,6 @@ Không có lịch sử ở đây - - Xóa nội dung tải xuống - - Bạn có chắc chắn muốn xóa nội dung tải xuống của mình không? Đã xóa tải xuống @@ -865,7 +947,7 @@ Menu dấu trang - Chỉnh sửa dấu trang + Chỉnh sửa dấu trang Chọn thư mục @@ -1886,8 +1968,10 @@ OK, đã hiểu + + Trang web hàng đầu được truy cập nhiều nhất - Hiển thị các trang web hàng đầu được xem nhiều nhất + Hiển thị các trang web hàng đầu được xem nhiều nhất Hiển thị các trang web được truy cập nhiều nhất @@ -1900,15 +1984,41 @@ Huỷ bỏ - - + + Thẻ không hoạt động + + Đóng tất cả các thẻ không hoạt động - Các thẻ có sẵn ở đây trong %s. Sau thời gian đó, các thẻ sẽ tự động bị đóng. + Các thẻ có sẵn ở đây trong %s. Sau thời gian đó, các thẻ sẽ tự động bị đóng. - 30 ngày + 30 ngày - 1 tuần + 1 tuần + + + + Tự động đóng sau một tháng? + + Firefox có thể đóng các thẻ bạn không xem trong tháng qua. + + BẬT TỰ ĐỘNG ĐÓNG + + + + Hãy giúp chúng tôi cải thiện + + Tại sao bạn tắt tính năng các thẻ không hoạt động? + + Không quan tâm đến tính năng + + Thời gian đóng thẻ không hoạt động quá lâu + + Thời gian đóng thẻ không hoạt động quá ngắn + + Gửi + + Đóng Đặt các liên kết từ trang web, email và tin nhắn để tự động mở trong Firefox. @@ -1925,4 +2035,19 @@ Đóng + + + Những câu chuyện kích động tư tưởng + + Những câu chuyện kích động tư tưởng + + Các câu chuyện theo chủ đề + + Khám phá thêm nữa + + Được cung cấp bởi Pocket. + + Một phần của gia đình Firefox. %s + + Tìm hiểu thêm From 580e37a3a20560f2d140851601827f5697281d10 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:53 +0000 Subject: [PATCH 477/517] Strings - app/src/main/res/values-zh-rCN/strings.xml --- app/src/main/res/values-zh-rCN/strings.xml | 197 +++++++++++++++++---- 1 file changed, 163 insertions(+), 34 deletions(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 9e6f69638f..975577fa7c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -27,7 +27,7 @@ 打开了 1 个标签页,点击即可切换。 - 打开了 %1$s 个标签页,点击即可切换。 + 打开了 %1$s 个标签页,点击即可切换。 已选择 %1$d 个标签页 @@ -58,7 +58,9 @@ 最近保存 - 最近的书签 + 最近的书签 + + 最近的书签 最近保存的书签 @@ -110,6 +112,21 @@ 知道了 + + 两周内未查看的标签页将移至此处。 + + 在设置中关闭 + + + 要一个月后自动关闭吗? + + Firefox 可自动关闭您一个月内未查看的标签页。 + + 关闭 + + + 开启“自动关闭”功能 + 新建标签页 @@ -117,7 +134,7 @@ 新建隐私标签页 - 常用网站 + 常用网站 @@ -125,6 +142,15 @@ 显示全部 + + 显示所有近期标签页按钮 + + “%1$s”的搜索结果 + + %1$s 个网站 + @@ -132,11 +158,17 @@ - 最近访问 + 最近访问 + + 最近的搜索 移除 + + 显示所有过去探索按钮 + 打开的标签页 @@ -149,7 +181,7 @@ 停止 - 书签 + 书签 编辑书签 @@ -217,6 +249,8 @@ 定制主页 + + 定制主页 主屏幕 @@ -265,6 +299,29 @@ 直接从地址栏搜索 + + + Firefox 的新变化 + + 现在可以更轻松地从上次中断的地方继续浏览。 + + 个性化的 Firefox 主页 + + + 跳转到您打开的标签页、书签和浏览历史。 + + + 简洁有序的标签页 + + 改进的布局和自动关闭标签页,消除标签页混乱。 + + 最近的搜索 + + 从主页和标签页快速访问您的上次搜索。 + + + 个性化的 Firefox 主页,让您可以更轻松地从上次中断的地方继续浏览。快速找到您最近打开的标签页、书签和搜索结果。 + 新建 Firefox 标签页 @@ -344,7 +401,9 @@ 主题 - 主页 + 主页 + + 主页 手势 @@ -419,9 +478,15 @@ 最近保存 - 最近的书签 - - 最近访问 + 最近的书签 + + 最近的书签 + + 最近访问 + + 最近的搜索 Pocket @@ -537,7 +602,7 @@ 开启同步 - 扫描桌面 Firefox 中的配对二维码 + 扫描桌面 Firefox 中的配对二维码 登录 @@ -646,6 +711,10 @@ 列表 网格 + + 搜索分组 + + 将有关联的网站分组归并 关闭标签页 @@ -657,15 +726,26 @@ 1 个月后 - + + 自动关闭打开的标签页 + + - 启动后进入主屏幕 + 启动后进入主屏幕 + + 启动页 - 4 小时后 + 4 小时后 + + 主页 - 总是 + 总是 + + 最后打开的标签页 - 永不 + 永不 + + 四小时未使用即回到主页 手动关闭 @@ -675,6 +755,13 @@ 1 个月后关闭 + + + 将旧标签页切换至休眠状态 + + + 两周内未查看的标签页将进入休眠状态。 + 移除 @@ -717,13 +804,13 @@ 保存到收藏集 - 选择 + 选择 分享所有标签页 最近关闭的标签页 - 最近关闭 + 最近关闭 账户设置 @@ -798,7 +885,10 @@ 保存 - 其他 + 其他 + + + 其他标签页 @@ -828,9 +918,9 @@ is a digit showing the number of items you have selected --> 删除 %1$d 个项目 - 本日 + 今天 - 昨日 + 昨天 过去 24 小时 @@ -844,10 +934,6 @@ 无历史记录 - - 清空下载记录 - - 您确定要清空下载记录吗? 下载记录已清除 @@ -889,7 +975,7 @@ 书签菜单 - 编辑书签 + 编辑书签 选择文件夹 @@ -950,7 +1036,7 @@ 此处无书签 - 已删除 %1$s 条书签 + 已删除书签 %1$s 书签已删除 @@ -1387,7 +1473,7 @@ - 选择您的主题 + 选择主题 启用深色模式,既省电又护眼。 @@ -1942,8 +2028,10 @@ 我知道了 + + 最常访问的网站 - 显示最常访问的网站 + 显示最常访问的网站 显示最常访问的网站 @@ -1956,15 +2044,41 @@ 取消 - - - 非活动标签页 + + + 休眠标签页 + + 关闭所有休眠标签页 - 标签页将在此处保留 %s,之后将自动关闭。 + 标签页将在此处保留 %s,之后将自动关闭。 - 30 天 + 30 天 - 1 周 + 1 周 + + + + 要一个月后自动关闭吗? + + Firefox 可自动关闭您一个月内未查看的标签页。 + + 启用自动关闭标签页功能 + + + + 请帮助我们改进 + + 您为何想要关闭“休眠标签页”功能? + + 对该功能不感兴趣 + + 进入休眠状态时间过长 + + 进入休眠状态时间过短 + + 发送 + + 关闭 将网站、电子邮件及聊天工具中的链接设为在 Firefox 中自动打开。 @@ -1981,4 +2095,19 @@ 关闭 + + + 精选文章 + + 精选文章 + + 热门主题 + + 探索更多 + + 由 Pocket 提供 + + Firefox 系列产品。%s + + 详细了解 From 13ebb1fa6b9e4c182b3989bba90ed19d12172dac Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 21 Oct 2021 19:34:53 +0000 Subject: [PATCH 478/517] Strings - app/src/main/res/values-zh-rTW/strings.xml --- app/src/main/res/values-zh-rTW/strings.xml | 184 +++++++++++++++++---- 1 file changed, 155 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 0a94aefe61..a6e861e416 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -27,7 +27,7 @@ 開啟了 1 個分頁,點擊即可切換分頁。 - 開啟了 %1$s 個分頁,點擊即可切換分頁。 + 開啟了 %1$s 個分頁,點擊即可切換分頁。 已選擇 %1$d 個分頁 @@ -56,7 +56,9 @@ 最近儲存 - 最近加入的書籤 + 最近加入的書籤 + + 最近加入的書籤 最近加入的書籤 @@ -107,6 +109,21 @@ 知道了! + + 超過兩週沒有檢視過的分頁,將移動至此處。 + + 到設定中關閉 + + + 等一個月後再自動關閉? + + Firefox 可自動關閉您超過一個月未檢視的分頁。 + + 關閉 + + + 開啟「分頁自動關閉」功能 + 開新分頁 @@ -114,7 +131,7 @@ 開新隱私分頁 - 熱門網站 + 熱門網站 @@ -122,6 +139,15 @@ 顯示全部 + + 顯示所有近期分頁按鈕 + + 「%1$s」的搜尋結果 + + %1$s 個網站 + @@ -129,11 +155,17 @@ - 最近造訪 + 最近造訪 + + 最近搜尋內容 移除 + + 顯示所有過去的探索按鈕 + 開啟分頁 @@ -146,7 +178,7 @@ 停止 - 書籤 + 書籤 編輯書籤 @@ -213,6 +245,8 @@ 自訂首頁 + + 自訂首頁 主畫面 @@ -259,6 +293,27 @@ 從網址列直接搜尋 + + + Firefox 有什麼新鮮事 + + 更簡單就能回到上次中斷的地方繼續上網。 + + 個人化的 Firefox 首頁 + + 跳到您開啟的分頁、書籤、上網紀錄等。 + + 精簡有條理的分頁標籤 + + 透過改善過的版面設計與自動關閉閒置分頁功能,解決分頁雜亂的問題。 + + 最近搜尋內容 + + 從首頁與分頁快速重新造訪先前的搜尋結果。 + + + 現在起,有您的風格的 Firefox 首頁,可讓您更簡單就從上次結束瀏覽的地方繼續上網。快速找到您最近開啟的分頁、書籤、搜尋結果等分頁。 + 開新 Firefox 分頁 @@ -338,7 +393,9 @@ 佈景主題 - 主畫面 + 主畫面 + + 首頁 手勢 @@ -414,9 +471,15 @@ 最近儲存 - 最近加入的書籤 - - 最近造訪 + 最近加入的書籤 + + 最近加入的書籤 + + 最近造訪 + + 最近搜尋內容 Pocket @@ -530,7 +593,7 @@ 開啟 Sync - 使用桌面版 Firefox 掃描配對碼 + 使用桌面版 Firefox 掃描配對碼 登入 @@ -637,6 +700,10 @@ 清單 格線 + + 搜尋分頁群組 + + 將相關的網站放在一起 自動關閉分頁 @@ -648,15 +715,26 @@ 1 個月後 - + + 自動關閉分頁 + + - 啟動時,直接進入主畫面 + 啟動時,直接進入主畫面 + + 開啟畫面 - 4 小時後 + 4 小時後 + + 首頁 - 總是 + 總是 + + 最後開啟的分頁 - 永不 + 永不 + + 超過四個小時未使用就回到首頁 手動關閉 @@ -666,6 +744,12 @@ 1 個月後關閉 + + + 將舊分頁移動到閒置分頁 + + 超過兩週沒有檢視過的分頁,將移動至「閒置分頁」區塊。 + 移除 @@ -708,13 +792,13 @@ 儲存至收藏集 - 選擇 + 選擇 分享所有分頁 最近關閉的分頁 - 最近關閉的分頁 + 最近關閉的分頁 帳號設定 @@ -789,7 +873,10 @@ 儲存 - 其他 + 其他 + + + 其他分頁 @@ -835,10 +922,6 @@ 沒有紀錄 - - 清除下載紀錄 - - 您確定要清除下載紀錄? 已移除下載紀錄 @@ -880,7 +963,7 @@ 書籤選單 - 編輯書籤 + 編輯書籤 選擇資料夾 @@ -1926,8 +2009,10 @@ 好,知道了! + + 最常造訪的熱門網站 - 顯示最常造訪的網站 + 顯示最常造訪的網站 顯示最常造訪的網站 @@ -1940,15 +2025,41 @@ 取消 - - + + 閒置分頁 + + 關閉所有閒置分頁 - 分頁將於此處停留 %s,超過之後將自動關閉。 + 分頁將於此處停留 %s,超過之後將自動關閉。 - 30 天 + 30 天 - 1 週 + 1 週 + + + + 等一個月後再自動關閉? + + Firefox 可自動關閉您超過一個月未檢視的分頁。 + + 開啟自動關閉分頁功能 + + + + 請幫助我們改進 + + 您為什麼會想關閉「閒置分頁」功能? + + 對這個功能沒興趣 + + 判定為「閒置」的時間太長 + + 判定為「閒置」的時間太短 + + 傳送 + + 關閉 設定使用 Firefox 自動開啟網站、郵件、簡訊當中的鏈結。 @@ -1965,4 +2076,19 @@ 關閉 + + + 發人深省的文章 + + 發人深省的文章 + + 熱門主題 + + 探索更多 + + Powered by Pocket + + Firefox 系列產品。%s + + 了解更多 From f997b7b40f9f86065bca174bcca37a0b073a6399 Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Thu, 21 Oct 2021 17:11:34 -0400 Subject: [PATCH 479/517] Address UnusedResources warnings for strings --- app/lint-baseline.xml | 419 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+) diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index 34621d0f31..a48530ec56 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -7576,6 +7576,425 @@ column="13"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Thu, 21 Oct 2021 10:22:49 -0400 Subject: [PATCH 480/517] No issue: Only report telemetry when tabs tray is first opened --- .../org/mozilla/fenix/tabstray/browser/TabSorter.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt index 02d770a981..850fa69b5a 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/browser/TabSorter.kt @@ -32,6 +32,8 @@ class TabSorter( private val concatAdapter: ConcatAdapter, private val store: BrowserStore ) : TabsTray, Observable by ObserverRegistry() { + private var shouldReportMetrics: Boolean = true + override fun updateTabs(tabs: Tabs) { val inactiveTabs = tabs.list.getInactiveTabs(context) val searchTermTabs = tabs.list.getSearchGroupTabs(context) @@ -41,9 +43,6 @@ class TabSorter( // Inactive tabs val selectedInactiveIndex = inactiveTabs.findSelectedIndex(selectedTabId) concatAdapter.inactiveTabsAdapter.updateTabs((Tabs(inactiveTabs, selectedInactiveIndex))) - if (settings.inactiveTabsAreEnabled) { - metrics.track(Event.TabsTrayHasInactiveTabs(inactiveTabs.size)) - } // Tab groups // We don't need to provide a selectedId, because the [TabGroupAdapter] has that built-in with support from @@ -55,6 +54,14 @@ class TabSorter( val totalNormalTabs = (normalTabs + remainderTabs) val selectedTabIndex = totalNormalTabs.findSelectedIndex(selectedTabId) concatAdapter.browserAdapter.updateTabs(Tabs(totalNormalTabs, selectedTabIndex)) + + if (shouldReportMetrics) { + shouldReportMetrics = false + + if (settings.inactiveTabsAreEnabled) { + metrics.track(Event.TabsTrayHasInactiveTabs(inactiveTabs.size)) + } + } } override fun isTabSelected(tabs: Tabs, position: Int): Boolean = false From b234ca8dd46cc8d21276549390b32a928f2116e3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Oct 2021 20:03:49 -0400 Subject: [PATCH 481/517] Update to Android-Components 94.0.10. (#22091) Co-authored-by: MickeyMoz --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index bf0b8b56f2..a294240f76 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.9" + const val VERSION = "94.0.10" } From 36e59ba71ba984edac5fb6a7996be30e4126db4d Mon Sep 17 00:00:00 2001 From: Mozilla Releng Treescript Date: Fri, 22 Oct 2021 01:21:28 +0000 Subject: [PATCH 482/517] Automatic version bump CLOSED TREE NO BUG a=release --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 03b23e9b39..db30176a8d 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -94.0.0-beta.5 +94.0.0-beta.6 From 836e33cf732119695c68172bd71091e31a6a76a8 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Fri, 22 Oct 2021 13:58:02 +0300 Subject: [PATCH 483/517] For #22070 - Prevent crash for when a selected topic does not exist anymore The list of selected topics overwrites old data whenever user selects or deselects another so the old selections will not leak for long. (cherry picked from commit acdde511d3cd299d9befa96ce83411a428bb1a69) --- .../org/mozilla/fenix/ext/HomeFragmentState.kt | 4 ++-- .../mozilla/fenix/ext/HomeFragmentStateTest.kt | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt b/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt index 488b3e0095..49212f58b6 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/HomeFragmentState.kt @@ -32,8 +32,8 @@ fun HomeFragmentState.getFilteredStories( val oldestSortedCategories = pocketStoriesCategoriesSelections .sortedByDescending { it.selectionTimestamp } - .map { selectedCategory -> - pocketStoriesCategories.first { + .mapNotNull { selectedCategory -> + pocketStoriesCategories.find { it.name == selectedCategory.name } } diff --git a/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt b/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt index ec20715a04..540feff544 100644 --- a/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt +++ b/app/src/test/java/org/mozilla/fenix/ext/HomeFragmentStateTest.kt @@ -258,6 +258,22 @@ class HomeFragmentStateTest { assertSame(firstCategory.stories[0], result[4]) assertSame(firstCategory.stories[2], result[5]) } + + @Test + fun `GIVEN old selections of categories which do not exist anymore WHEN getFilteredStories is called THEN ignore not found selections`() { + val homeState = HomeFragmentState( + pocketStoriesCategories = listOf(otherStoriesCategory, anotherStoriesCategory, defaultStoriesCategory), + pocketStoriesCategoriesSelections = listOf( + PocketRecommendedStoriesSelectedCategory("unexistent"), + PocketRecommendedStoriesSelectedCategory(anotherStoriesCategory.name) + ) + ) + + val result = homeState.getFilteredStories(6) + + assertEquals(3, result.size) + assertNull(result.firstOrNull { it.category != anotherStoriesCategory.name }) + } } private fun getFakePocketStories( From 7f7d19855a172cfab3df494474bae346c588c508 Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:47 +0000 Subject: [PATCH 484/517] Strings - app/src/main/res/values-ar/strings.xml --- app/src/main/res/values-ar/strings.xml | 280 +++++++++++++++++++++---- 1 file changed, 242 insertions(+), 38 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 76e83b1a16..3896ee98ba 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1,5 +1,5 @@ - + متصفّح %s خاص @@ -24,7 +24,7 @@ لسان واحد مفتوح. انقر لتبديل الألسنة. - %1$s من الألسنة مفتوح. انقر لتبديل الألسنة. + %1$s من الألسنة مفتوح. انقر لتبديل الألسنة. حدّدتَ %1$d @@ -53,7 +53,11 @@ - المحفوظة حديثًا + المحفوظة حديثًا + + أحدث العلامات + + أحدث العلامات العلامات المحفوظة حديثًا @@ -105,6 +109,17 @@ أهمِل + + ستُنقل الألسنة التي لم تعرضها منذ أسبوعين إلى هنا. + + أتريد إغلاقها تلقائيًا بعد شهر؟ + + سيُغلق Firefox الألسنة التي لم تعرضها منذ شهر تلقائيًا. + + أغلِق + + شغّل الإغلاق التلقائي + لسان جديد @@ -112,11 +127,32 @@ لسان خاص جديد - المواقع الأكثر زيارة + المواقع الأكثر زيارة أظهر الكل + + نتائج البحث عن ”%1$s“ + + المواقع: %1$s + + + + الاكتشافات السابقة + + أحدث ما زرت + + عمليات البحث الأخيرة + + أزِل + الألسنة المفتوحة @@ -129,7 +165,7 @@ أوقف - علّم + علّم حرّر العلامة @@ -191,6 +227,10 @@ حرّر + + خصّص البداية + + خصّص صفحة البداية الشاشة الرئيسية @@ -238,6 +278,23 @@ ابحث مباشرة من شريط العنوان + + + ما جديد Firefox + + صفحة بداية Firefox مخصّصة + + انتقل إلى الألسنة المفتوحة والعلامات وتأريخ التصفح. + + ألسنة مرتّبة وواضحة + + رتّب فوضى الألسنة باستعمال قوّة التخطيط المحسّن وميزة الإغلاق التلقائي للألسنة. + + عمليات البحث الأخيرة + + + تُسهّل الآن صفحة بداية Firefox مواصلة ما كنتَ تركته سابقًا. ستجد فيها أحدث الألسنة والعلامات ونتائج البحث. + افتح في لسان Firefox جديد @@ -316,7 +373,9 @@ السمة - صفحة البداية + صفحة البداية + + صفحة البداية الإيماءات @@ -383,6 +442,21 @@ تعدّلت تجميعة الإضافات. يُنهي التطبيق لإجراء التغييرات… + + المحفوظة حديثًا + + أحدث العلامات + + أحدث العلامات + + أحدث ما زرت + + عمليات البحث الأخيرة + + Pocket + الإضافة غير مدعومة @@ -491,7 +565,7 @@ فعّل «تزامُن» - امسح رمز الاقتران رقميًا في Firefox على سطح المكتب + امسح رمز الاقتران رقميًا في Firefox على سطح المكتب لِج @@ -569,6 +643,13 @@ أغلِق + + عدد المواقع: %d + + عدد المواقع: %d + الألسنة المُغلقة حديثًا @@ -591,6 +672,8 @@ قائمة شبكة + + مجموعات البحث إغلاق الألسنة @@ -602,15 +685,26 @@ بعد شهر واحد - + + الإغلاق التلقائي للألسنة المفتوحة + + - ابدأ من الرئيسية + ابدأ من الرئيسية + + شاشة الفتح - بعد أربع ساعات + بعد أربع ساعات + + صفحة البداية - دائمًا + دائمًا + + اللسان الأخير - أبدًا + أبدًا + + صفحة البداية بعد أربع ساعات من عدم النشاط أغلِق يدويًا @@ -621,6 +715,30 @@ أغلِق بعد شهر واحد + + + انقل الألسنة القديمة إلى صفحة عدم النشاط + + ستُنقل الألسنة التي لم تعرضها منذ أسبوعين إلى قسم عدم النشاط. + + + + أزِل + + نشطة + + قد يثبّت Firefox بعض الدراسات ويشغّلها من وقت لآخر. + + اطّلع على المزيد + + سيُغلق التطبيق لتطبيق التغييرات + + حسنا + + ألغِ + + يُغلِق التطبيق الآن لتطبيق التغييرات… + الألسنة المفتوحة @@ -643,11 +761,13 @@ احفظ في التجميعة - اختر + اختر شارِك كل الألسنة الألسنة المُغلقة حديثًا + + المُغلقة حديثًا إعدادات الحساب @@ -718,6 +838,9 @@ احفظ + + الألسنة الأخرى + احذف التأريخ @@ -730,15 +853,15 @@ امسح - انسخ + انسخ - شارِك + شارِك - افتح في لسان جديد + افتح في لسان جديد - افتح في لسان خاص + افتح في لسان خاص - احذف + احذف حدّدتَ %1$d @@ -762,10 +885,6 @@ ما من تأريخ هنا - - احذف التنزيلات - - أمتاكّد من مسح كل التنزيلات؟ أُزيلت التنزيلات @@ -806,7 +925,7 @@ قائمة العلامات - حرّر العلامة + حرّر العلامة اختر مجلدًا @@ -943,6 +1062,11 @@ معطّل + + مفعّل + + معطّل + التجميعات @@ -1044,6 +1168,15 @@ تدعمه + + التسويق + + ‏%1$s سريع وخاص + + اجعل %1$s متصفّحك المبدئي + حُذفت التجميعة @@ -1095,7 +1228,7 @@ احذف - ألغِ + ألغِ تدخل وضع ملء الشاشة @@ -1197,12 +1330,12 @@ ألديك حساب؟ - اعرف ما الجديد + اعرف ما الجديد - ألديك أسئلة عن متصفّح %s الذي أعدنا تصميمه؟ أتريد معرفة ما تغيّر؟ + ألديك أسئلة عن متصفّح %s الذي أعدنا تصميمه؟ أتريد معرفة ما تغيّر؟ - ستجد هنا إجابات أسئلتك + ستجد هنا إجابات أسئلتك زامِن Firefox بين الأجهزة @@ -1242,14 +1375,14 @@ ضَع شريط الأدوات في متناول اليد. أبقِه في الأسفل، أو انقله إلى الأعلى. - تصفّح بخصوصية + تصفّح بخصوصية - افتح اللسان الخاص الآن فقط: انقر أيقونة %s. + افتح اللسان الخاص الآن فقط: انقر أيقونة %s. - افتح الألسنة الخاصة في كل مرة: حدّث إعدادات التصفّح الخاص. + افتح الألسنة الخاصة في كل مرة: حدّث إعدادات التصفّح الخاص. - افتح الإعدادات + افتح الإعدادات خصوصيتك اقرأ تنويه الخصوصية - أغلِق + أغلِق ابدأ التصفح @@ -1363,6 +1496,9 @@ المُعدّنات المعمّاة مسجّلات البصمات + + التفاصيل + حُجبت مسموح بها @@ -1387,7 +1523,7 @@ يُوقف تحميل الإعلانات والڤِديوهات وغيرها من محتويات خارجية تحتوي على برمجيات تعقّب. قد يؤثّر هذا على سلوك بعض المواقع. - متى ما رأيت الدرع بنفسجيًا اعرف أنّ %s حجب المتعقّبات في أحد المواقع. اضغط لتفاصيل أكثر. + متى ما رأيت الدرع بنفسجيًا اعرف أنّ %s حجب المتعقّبات في أحد المواقع. اضغط لتفاصيل أكثر. فُعّلت الحماية في هذا الموقع @@ -1413,6 +1549,9 @@ يمسح الكعكات التي ضبطتها التحويلات إلى مواقع تعقّب معروفة. + + اطّلع على المزيد + الدعم @@ -1469,7 +1608,10 @@ لا تحفظ أبدًا - الملء التلقائي + الملء التلقائي + + أضِف ولوجًا + زامِن جلسات الولوج @@ -1533,6 +1675,8 @@ اسنخ اسم المستخدم امسح اسم المستخدم + + امسح اسم المضيف انسخ الموقع @@ -1619,6 +1763,8 @@ ألغِ القفل لعرض بطاقاتك المحفوظة أمّن بطاقات ائتمانك + + اضبط نمطًا أو رمزًا أو كلمة سر لقفل الجهاز ذلك لحماية بطاقات الائتمان المحفوظة ضد الوصول إليها ضد من يستعمل جهازك. اضبط ذلك الآن @@ -1699,9 +1845,13 @@ %1$s على ”مفعّل/مفعّلة“]]> - اتصال آمن + الاتصال آمن + + الاتصال غير آمن + + اتصال آمن - اتصال غير آمن + اتصال غير آمن أمتأكد من مسح كل الصلاحيات لكل المواقع؟ @@ -1741,8 +1891,14 @@ أهمِل التعديلات حرّر - + + أضِف ولوجا جديدا + كلمة السر مطلوبة + + اسم المستخدم مطلوب + + اسم المضيف مطلوب البحث الصوتي @@ -1750,6 +1906,15 @@ يوجد بالفعل جلسة ولوج باسم المستخدم هذا + + https://www.example.com + + يجب أن يحتوي عنوان الوِب على ”https://‎“ أو ”http://‎“ + + يجب أن يحتوي عنوان الوِب على ”https://‎“ أو ”http://‎“ + + مطلوب اسم مضيف صالح + صِلْ جهازا آخر. @@ -1775,8 +1940,10 @@ حسنًا، فهمت + + اعرض المواقع الأكثر زيارة - اعرض المواقع الأكثر زيارة + اعرض المواقع الأكثر زيارة الاسم @@ -1787,6 +1954,37 @@ ألغِ + + + الألسنة غير النشطة + + أغلِق كل الألسنة غير النشطة + + 30 يوما + + أسبوع واحد + + + + أتريد إغلاقها تلقائيًا بعد شهر؟ + + سيُغلق Firefox الألسنة التي لم تعرضها منذ شهر تلقائيًا. + + شغّل الإغلاق التلقائي + + + لماذا اخترت تعطيل ميزة الألسنة غير النشطة؟ + + غير مهتم بالميزة + + وقت عدم النشاط طويل جدًا + + وقت عدم النشاط قصير جدًا + + أرسِل + + أغلِق + اضبط روابط المواقع والبريد الإلكتروني والرسائل لتفتح تلقائيًا في Firefox. @@ -1799,4 +1997,10 @@ أغلِق + + اكتشف المزيد + + جزء من عائلة Firefox. ‏%s + + اطّلع على المزيد From 1fba0d11c0fa409260cef05cd9a930127281a9a2 Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:47 +0000 Subject: [PATCH 485/517] Strings - app/src/main/res/values-es-rMX/strings.xml --- app/src/main/res/values-es-rMX/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index 317f92ff89..93befdf3b5 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -224,6 +224,8 @@ Editar + + Personalizar inicio Pantalla de inicio @@ -356,6 +358,8 @@ Tema Inicio + + Página de inicio Gestos @@ -428,6 +432,8 @@ Saltar hacia atrás Guardados recientemente + + Marcadores recientes Visitados recientemente @@ -669,6 +675,8 @@ Comenzar en la página de inicio Después de cuatro horas + + Página de inicio Siempre From f670e3adc897bec23de8a3e7e09282b159419598 Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:48 +0000 Subject: [PATCH 486/517] Strings - app/src/main/res/values-fr/strings.xml --- app/src/main/res/values-fr/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f816205056..e4c09fd7d2 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -2087,6 +2087,8 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n Articles par sujet En découvrir davantage + + Membre de la famille Firefox. %s En savoir plus From 44e3fe23ee46ac854aa3d885b61d7eb193b5ca7e Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:48 +0000 Subject: [PATCH 487/517] Strings - app/src/main/res/values-hr/strings.xml --- app/src/main/res/values-hr/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 8872672239..bcbad444a3 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -667,6 +667,10 @@ %d is a placeholder for the number of sites in the group. --> %d stranica + + %d stranica + Nedavno zatvorene kartice From 13e38220d62c011e5da10dedc4e735072ffd0725 Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:48 +0000 Subject: [PATCH 488/517] Strings - app/src/main/res/values-ja/strings.xml --- app/src/main/res/values-ja/strings.xml | 63 ++++++++++++++++---------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 16fdcef221..08f6e46830 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -26,7 +26,7 @@ 開いているタブ 1 個。タップしてタブを切り替えます。 - 開いているタブ %1$s 個。タップしてタブを切り替えます。 + 開いているタブ %1$s 個。タップしてタブを切り替えます。 %1$d 個選択 @@ -113,9 +113,18 @@ 閉じる - 2 週間以上表示していないタブをここに移動します。 + 2 週間以上表示していないタブをここに移動します。 - 設定でオフにする + 設定でオフにする + + + 1 か月後に自動的に閉じますか? + + Firefox は 1 か月以上表示していないタブを閉じることができます。 + + 閉じる + + タブを自動的に閉じる @@ -124,7 +133,7 @@ 新しいプライベートタブ - よく見るサイト + よく見るサイト @@ -171,7 +180,7 @@ 中止 - ブックマーク + ブックマーク ブックマークを編集 @@ -387,7 +396,9 @@ テーマ - ホーム画面 + ホーム画面 + + ホームページ ジェスチャー @@ -584,7 +595,7 @@ Sync を有効化 - デスクトップ版 Firefox のペアリングコードをスキャン + デスクトップ版 Firefox のペアリングコードをスキャン ログイン @@ -708,15 +719,23 @@ 開いたタブを自動的に閉じる - + - ホーム画面で開始 + ホーム画面で開始 + + 最初の画面 - 4 時間後 + 4 時間後 + + ホームページ - 常に使用する + 常に使用する + + 最後のタブ - 使用しない + 使用しない + + 操作せずに 4 時間経つとホームページへ戻ります 手動で閉じる @@ -773,13 +792,13 @@ コレクションに保存 - 選択 + 選択 すべてのタブを共有 最近閉じたタブ - 最近閉じたタブ + 最近閉じたタブ アカウント設定 @@ -904,10 +923,6 @@ 履歴はありません - - ダウンロード履歴を消去 - - 本当にダウンロード履歴を消去しますか? ダウンロード履歴を削除しました @@ -948,7 +963,7 @@ ブックマークメニュー - ブックマークを編集 + ブックマークを編集 フォルダー選択 @@ -1989,8 +2004,10 @@ OK + + よく訪れるサイト - よく訪れるサイトを表示 + よく訪れるサイトを表示 よく訪れるサイトを表示 @@ -2009,11 +2026,11 @@ 休止中のタブをすべて閉じます - これらのタブは %sを過ぎると自動的に閉じられます。 + これらのタブは %sを過ぎると自動的に閉じられます。 - 30 日 + 30 日 - 1 週間 + 1 週間 From 19fc0f027e283846dcf7b5a372fd79a201daa4ad Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:48 +0000 Subject: [PATCH 489/517] Strings - app/src/main/res/values-kk/strings.xml --- app/src/main/res/values-kk/strings.xml | 85 +++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 1f3b708fe5..533732219d 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -159,6 +159,9 @@ in the Recently visited section --> Өшіру + + Барлық тарихты көрсету батырмасын көрсету + Ашық беттер @@ -286,6 +289,24 @@ Firefox ішінде не жаңалық + + Енді тоқтаған жерден жалғастыру оңайырақ. + + Жеке Firefox басты беті + + Ашық беттер, бетбелгілер және шолу тарихына өтіңіз. + + Таза, реттелген беттер + + Беттер енді жақсартылған орналастыру және автожабуды қолдану салдарынан көбірек реттелген болды. + + Жуырдағы іздеулер + + Үй беті және беттерден соңғы іздеулеріңізді қайта қараңыз. + + + Сіздің жекелендірілген Firefox үй беті енді тоқтаған жерден жалғастыруды жеңілдетеді. Соңғы беттерді, бетбелгілерді және іздеу нәтижелерін табыңыз. + Жаңа Firefox бетін ашу @@ -663,6 +684,10 @@ Тізім Тор + + Іздеу топтары + + Байланысқан сайттарды біріктіру Беттерді жабу @@ -674,15 +699,26 @@ Бір айдан кейін + + Ашық беттерді автожабу + Бастапқы экраннан бастау + + Бастапқы экраны Төрт сағаттан кейін + + Үй парағы Әрқашан + + Соңғы бет Ешқашан + + Төрт сағат әрекетсіздіктен кейін үй парағы Қолмен жабу @@ -692,6 +728,12 @@ Бір айдан кейін жабу + + + Ескі беттерді белсенді емес күйге жылжыту + + Сіз екі апта бойы қарамаған беттер белсенді емес топқа жылжытылады. + Өшіру @@ -815,6 +857,9 @@ Басқа + + Басқа беттер + Тарихты өшіру @@ -1930,6 +1975,8 @@ Жақсы, түсіндім + + Ең көп қаралған үздік сайттар Ең көп қаралған үздік сайттарды көрсету @@ -1947,6 +1994,8 @@ Белсенді емес беттер + + Барлық белсенді емес беттерді жабу Беттер осында %s ішінде қолжетімді. Осы уақыттан кейін беттер автоматты түрде жабылады. @@ -1954,6 +2003,25 @@ 1 апта + + + Бір айдан кейін автожабу керек пе? + + Firefox соңғы айда қаралмаған беттерді жаба алады. + + АВТОЖАБУДЫ ІСКЕ ҚОСУ + + + + Бізге осыны жақсартуға көмектесіңіз + + Белсенді емес беттер мүмкіндігін неліктен сөндірдіңіз? + + Бұл мүмкіндік мені қызықтырмайды + + Белсенді емес күйін күту уақыты тым ұзақ + + Белсенді емес күйін күту уақыты тым қысқа Жіберу @@ -1974,4 +2042,19 @@ Жабу - + + + Ойландыратын әңгімелер + + Ойландыратын әңгімелер + + Тақырып бойынша әңгімелер + + Көбірек шолу + + Pocket негізінде. + + Firefox отбасының бөлігі. %s + + Көбірек білу + From bcf7b5ef8bfd439dd66649fa826570168fca359a Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:48 +0000 Subject: [PATCH 490/517] Strings - app/src/main/res/values-oc/strings.xml --- app/src/main/res/values-oc/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml index b922df0bb5..b0842a9f8e 100644 --- a/app/src/main/res/values-oc/strings.xml +++ b/app/src/main/res/values-oc/strings.xml @@ -719,6 +719,8 @@ Començar per l’ecran d’acuèlh + + Ecran a la dubertura Aprèp 4 oras From 5f64e6487319f9e087b4e5a03e139e64528be384 Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:48 +0000 Subject: [PATCH 491/517] Strings - app/src/main/res/values-pa-rIN/strings.xml --- app/src/main/res/values-pa-rIN/strings.xml | 129 ++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-pa-rIN/strings.xml b/app/src/main/res/values-pa-rIN/strings.xml index fcc1ffb47b..e06771c8b8 100644 --- a/app/src/main/res/values-pa-rIN/strings.xml +++ b/app/src/main/res/values-pa-rIN/strings.xml @@ -55,6 +55,8 @@ ਤਾਜ਼ਾ ਸੰਭਾਲੇ ਤਾਜ਼ਾ ਬੁੱਕਮਾਰਕ ਕੀਤੇ + + ਸੱਜਰੇ ਬੁੱਕਮਾਰਕ ਤਾਜ਼ਾ ਸੰਭਾਲੇ ਬੁੱਕਮਾਰਕ @@ -111,6 +113,19 @@ ਖ਼ਾਰਜ ਕਰੋ + + ਪਿਛਲੇ ਦੋ ਹਫ਼ਤਿਆਂ ਵਿੱਚ ਤੁਹਾਡੇ ਵਲੋਂ ਨਾ ਵੇਖੀਆਂ ਟੈਬਾਂ ਨੂੰ ਇੱਥੇ ਭੇਜਿਆ ਜਾਂਦਾ ਹੈ। + + ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਬੰਦ ਕਰੋ + + ਇੱਕ ਮਹੀਨੇ ਬਾਅਦ ਆਪੇ ਬੰਦ ਕਰਨਾ ਹੈ? + + Firefox ਉਹਨਾਂ ਟੈਬਾਂ ਨੂੰ ਬੰਦ ਕਰ ਸਕਦਾ ਹੈ, ਜਿਨਾਂ ਨੂੰ ਤੁਸੀਂ ਪਿਛਲੇ ਮਹੀਨੇ ਭਰ ਤੋਂ ਨਹੀਂ ਵੇਖਿਆ ਹੈ। + + ਬੰਦ ਕਰੋ + + ਆਪੇ ਬੰਦ ਕਰਨਾ ਚਾਲੂ ਕਰੋ + ਨਵੀਂ ਟੈਬ @@ -126,6 +141,15 @@ ਸਭ ਵੇਖੋ + + ਸਾਰੀਆਂ ਸੱਜਰੀਆਂ ਟੈਬਾਂ ਬਟਨ ਨੂੰ ਵੇਖਾਓ + + \"%1$s\" ਲਈ ਤੁਹਾਡੀ ਖੋਜ + + ਸਾਈਟਾਂ: %1$s + @@ -133,10 +157,16 @@ ਹਾਲ ਦੇ ਖੋਲ੍ਹੀਆਂ ਗਈਆਂ + + ਸੱਜਰੀਆਂ ਖੋਜਾਂ ਹਟਾਓ + + ਸਾਰੀਆਂ ਪਿਛਲੀਆਂ ਖੋਜਾਂ ਦੇ ਬਟਨ ਨੂੰ ਵੇਖਾਓ + ਟੈਬਾਂ ਖੋਲ੍ਹੋ @@ -215,6 +245,8 @@ ਘਰ ਨੂੰ ਕਸਟਮਾਈਜ਼ ਕਰੋ + + ਮੁੱਖ-ਸਫ਼ੇ ਨੂੰ ਕਸਟਮਾਈਜ਼ ਕਰੋ ਮੁੱਖ ਸਕਰੀਨ @@ -261,6 +293,27 @@ ਸਿਰਨਾਵਾਂ ਪੱਟੀ ਵਿੱਚੋਂ ਸਿ਼ੱਧਾ ਖੋਜੋ + + + Firefox ਵਿੱਚ ਨਵਾਂ ਕੀ ਹੈ + + ਜਿੱਥੇ ਤੁਸੀਂ ਛੱਡ ਕੇ ਗਈ ਸੀ, ਉਥੋਂ ਹੀ ਮੁੜ ਸ਼ੁਰੂ ਕਰਨਾ ਸੌਖਾ ਹੈ। + + ਆਪਣੇ ਮੁਤਾਬਕ ਢਾਲਿਆ Firefox ਮੁੱਖ-ਸਫ਼ਾ + + ਆਪਣੀਆਂ ਖੋਲ੍ਹੀਆ ਟੈਬਾਂ, ਬੁੱਕਮਾਰਕਾਂ ਅਤੇ ਬਰਾਊਜ਼ ਕਰਨ ਦੇ ਅਤੀਤ ਉੱਤੇ ਜਾਓ। + + ਸਾਫ਼, ਸਜਾਈਆਂ ਟੈਬਾਂ + + ਸੁਧਾਰੇ ਖਾਕੇ ਤੇ ਆਪਣੇ-ਬੰਦ ਹੋਣ ਵਾਲੀਆਂ ਟੈਬਾਂ ਨਾਲ ਟੈਬਾਂ ਦੇ ਖਿਲਾਰੇ ਨੂੰ ਸਾਫ਼ ਕਰੋ। + + ਸੱਜਰੀਆਂ ਖੋਜਾਂ + + ਆਪਣੇ ਮੁੱਖ-ਸਫ਼ੇ ਅਤੇ ਟੈਬਾਂ ਤੋਂ ਆਪਣੀਆਂ ਸੱਜਰੀਆਂ ਖੋਜਾਂ ਨੂੰ ਮੁੜ-ਖੋਲ੍ਹੋ। + + + ਤੁਹਾਡੇ ਆਪਣੇ ਬਣਾਏ Firefox ਮੁੱਖ-ਸਫ਼ੇ ਨੇ ਹੁਣ ਜਿੱਥੇ ਤੁਸੀਂ ਛੱਡ ਕੇ ਗਏ ਸੀ, ਉਥੋਂ ਹੀ ਸ਼ੁਰੂ ਕਰਨਾ ਸੌਖਾ ਬਣਾ ਦਿੱਤਾ ਹੈ। ਆਪਣੀਆਂ ਸੱਜਰੀਆਂ ਟੈਬਾਂ, ਬੁੱਕਮਾਰਕ ਅਤੇ ਖੋਜ ਨਤੀਜੇ ਲੱਭੋ। + ਨਵੀਂ Firefox ਟੈਬ ਖੋਲ੍ਹੋ @@ -340,6 +393,8 @@ ਥੀਮ ਮੁੱਖ ਸਫ਼ਾ + + ਮੁੱਖ ਸਫ਼ਾ ਇਸ਼ਾਰੇ @@ -417,9 +472,14 @@ ਤਾਜ਼ਾ ਸੰਭਾਲੇ ਤਾਜ਼ਾ ਬੁੱਕਮਾਰਕ ਕੀਤੇ + + ਸੱਜਰੇ ਬੁੱਕਮਾਰਕ ਤਾਜ਼ਾ ਖੋਲ੍ਹੇ ਗਏ + + ਸੱਜਰੀਆਂ ਖੋਜਾਂ Pocket @@ -643,6 +703,10 @@ ਸੂਚੀ ਗਰਿੱਡ + + ਗਰੁੱਪ ਲੱਭੋ + + ਮਿਲਦੀਆਂ ਸਾਈਟਾਂ ਨੂੰ ਇਕੱਠੀਆਂ ਕਰਕੇ ਗਰੁੱਪ ਬਣਾਓ ਟੈਬਾਂ ਨੂੰ ਬੰਦ ਕਰੋ @@ -654,15 +718,26 @@ ਇੱਕ ਮਹੀਨੇ ਬਾਅਦ + + ਖੁੱਲ੍ਹੀਆਂ ਟੈਬਾਂ ਲਈ ਆਪੇ-ਬੰਦ ਕਰੋ + ਮੁੱਖ ਸਕਰੀਨ ਤੋਂ ਸ਼ੁਰੂ ਕਰੋ + + ਸਕਰੀਨ ਖੋਲ੍ਹਣੀ ਕੁਝ ਘੰਟਿਆਂ ਬਾਦ + + ਮੁੱਖ ਸਫ਼ਾ ਹਮੇਸ਼ਾਂ + + ਪਿਛਲੀ ਟੈਬ ਕਦੇ ਨਹੀਂ + + ਚਾਰ ਘੰਟਿਆਂ ਦੀ ਨਾ-ਸਰਗਰਮੀ ਦੇ ਬਾਅਦ ਮੁੱਖ-ਸਫ਼ਾ ਖੁਦ ਬੰਦ ਕਰੋ @@ -672,6 +747,12 @@ ਇੱਕ ਮਹੀਨੇ ਬਾਅਦ ਬੰਦ ਕਰੋ + + + ਪੁਰਾਣੀਆਂ ਟੈਬਾਂ ਨੂੰ ਨਾ-ਸਰਗਰਮ ਵਿੱਚ ਭੇਜੋ + + ਪਿਛਲੇ ਦੋ ਹਫ਼ਤਿਆਂ ਵਿੱਚ ਤੁਹਾਡੇ ਵਲੋਂ ਨਾ ਵੇਖੀਆਂ ਟੈਬਾਂ ਨੂੰ ਨਾ-ਸਰਗਰਮ ਭਾਗ ਵਿੱਚ ਭੇਜਿਆ ਜਾਂਦਾ ਹੈ। + ਹਟਾਓ @@ -795,6 +876,9 @@ ਹੋਰ + + ਹੋਰ ਟੈਬਾਂ + ਅਤੀਤ ਹਟਾਓ @@ -1911,6 +1995,8 @@ ਠੀਕ ਹੈ, ਸਮਝ ਗਏ + + ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹੀਆਂ ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ ਸਭ ਤੋਂ ਵੱਧ ਖੋਲ੍ਹੀਆਂ ਸਿਖਰਲੀਆਂ ਸਾਈਟਾਂ ਵੇਖੋ @@ -1928,6 +2014,8 @@ ਨਾ-ਸਰਗਰਮ ਟੈਬਾਂ + + ਸਾਰੀਆਂ ਨਾ-ਸਰਗਰਮ ਟੈਬਾਂ ਬੰਦ ਕਰੋ ਟੈਬਾਂ %s ਦਿਨਾਂ ਲਈ ਇੱਥੇ ਮੌਜੂਦ ਹੁੰਦੀਆਂ ਹਨ। ਉਸ ਸਮੇਂ ਬਾਅਦ, ਟੈਬਾਂ ਨੂੰ ਆਪਣੇ-ਆਪ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ। @@ -1935,6 +2023,30 @@ 1 ਹਫ਼ਤਾ + + + ਇੱਕ ਮਹੀਨੇ ਬਾਅਦ ਆਪੇ ਬੰਦ ਕਰਨਾ ਹੈ? + + Firefox ਉਹਨਾਂ ਟੈਬਾਂ ਨੂੰ ਬੰਦ ਕਰ ਸਕਦਾ ਹੈ, ਜਿਨਾਂ ਨੂੰ ਤੁਸੀਂ ਪਿਛਲੇ ਮਹੀਨੇ ਭਰ ਤੋਂ ਨਹੀਂ ਵੇਖਿਆ ਹੈ। + + ਆਪੇ-ਬੰਦ ਕਰਨ ਨੂੰ ਚਾਲੂ ਕਰੋ + + + + ਸੁਧਾਰ ਕਰਨ ਲਈ ਸਾਡੀ ਮਦਦ ਕਰੋ + + ਤੁਸੀਂ ਨਾ-ਸਰਗਰਮ ਟੈਬਾਂ ਨੂੰ ਅਸਮਰੱਥ ਕਿਉਂ ਕੀਤਾ ਸੀ? + + ਫ਼ੀਚਰ ਵਿੱਚ ਦਿਲਚਸਪੀ ਨਹੀਂ ਹੈ + + ਨਾ-ਸਰਗਰਮੀ ਲਈ ਸਮਾਂ ਬਹੁਤ ਜ਼ਿਆਦਾ ਹੈ + + ਨਾ-ਸਰਗਰਮੀ ਲਈ ਸਮਾਂ ਬਹੁਤ ਘੱਟ ਹੈ + + ਭੇਜੋ + + ਬੰਦ ਕਰੋ + ਵੈੱਬਸਾਈਟਾਂ, ਈਮੇਲਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਨੂੰ Firefox ਵਿੱਚ ਆਪਣੇ ਖੋਲ੍ਹਣ ਲਈ ਲਿੰਕ ਸੈੱਟ ਕਰੋ। @@ -1950,4 +2062,19 @@ ਬੰਦ ਕਰੋ - + + + ਸੋਚਣ ਲਈ ਮਜ਼ਬੂਰ ਕਰਨ ਵਾਲੇ ਲੇਖ + + ਸੋਚਣ ਲਈ ਮਜ਼ਬੂਰ ਕਰਨ ਵਾਲੇ ਲੇਖ + + ਵਿਸ਼ੇ ਮੁਤਾਬਕ ਲੇਖ + + ਹੋਰ ਲੱਭੋ + + Pocket ਵਲੋਂ ਇਖ਼ਤਿਆਰ + + Firefox ਪਰਿਵਾਰ ਦਾ ਹਿੱਸਾ। %s + + ਹੋਰ ਜਾਣੋ + From 0706e665772c93ff8e82d97582349c77900d08ee Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:48 +0000 Subject: [PATCH 492/517] Strings - app/src/main/res/values-rm/strings.xml --- app/src/main/res/values-rm/strings.xml | 63 ++++++++++++++++---------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml index f7978bdf02..4f70d355d8 100644 --- a/app/src/main/res/values-rm/strings.xml +++ b/app/src/main/res/values-rm/strings.xml @@ -23,7 +23,7 @@ 1 tab avert. Tutgar per midar tab. - %1$s tabs averts. Tutgar per midar tab. + %1$s tabs averts. Tutgar per midar tab. %1$d tschernids @@ -106,9 +106,18 @@ Serrar - Ils tabs che ti n\'has betg consultà durant las ultimas duas emnas vegnan spustads nà qua. + Ils tabs che ti n\'has betg consultà durant las ultimas duas emnas vegnan spustads nà qua. - Deactivar en ils parameters + Deactivar en ils parameters + + + Serrar automaticamain suenter in mais? + + Firefox po serrar tabs che ti n\'has betg consultà durant l\'ultim mais. + + Serrar + + Activar il serrar automatic @@ -117,7 +126,7 @@ Nov tab privat - Paginas preferidas + Paginas preferidas @@ -164,7 +173,7 @@ Fermar - Segnapagina + Segnapagina Modifitgar il segnapagina @@ -376,7 +385,9 @@ Design - Pagina iniziala + Pagina iniziala + + Pagina da partenza Gests @@ -570,7 +581,7 @@ Activar Sync - Scannescha il code d\'associaziun en Firefox sin il computer + Scannescha il code d\'associaziun en Firefox sin il computer S\'annunziar @@ -693,15 +704,23 @@ Serrar automaticamain tabs averts - + - Recumenzar cun il visur da partenza + Recumenzar cun il visur da partenza + + Visur da partenza - Suenter quatter uras + Suenter quatter uras + + Pagina da partenza - Adina + Adina + + Ultim tab - Mai + Mai + + Pagina da partenza suenter quatter uras dad inactivitad Serrar a maun @@ -757,13 +776,13 @@ Memorisar en ina collecziun - Tscherner + Tscherner Cundivider tut ils tabs Tabs serrads dacurt - Serrà dacurt + Serrà dacurt Parameters dal conto @@ -884,10 +903,6 @@ Nagina cronologia - - Stizzar la glista da telechargiadas - - Vuls ti propi stizzar tia glista da telechargiadas? Allontanà las telechargiadas @@ -929,7 +944,7 @@ Menu dals segnapaginas - Modifitgar il segnapagina + Modifitgar il segnapagina Tscherner in ordinatur @@ -1970,8 +1985,10 @@ OK, chapì + + Las websites principalas visitadas il pli savens - Mussar las websites principalas visitadas il pli savens + Mussar las websites principalas visitadas il pli savens Mussar las websites visitadas il pli savens @@ -1990,11 +2007,11 @@ Serrar tut ils tabs inactivs - Tabs stattan a disposiziun qua per %s. Suenter quest temp vegnan els serrads automaticamain. + Tabs stattan a disposiziun qua per %s. Suenter quest temp vegnan els serrads automaticamain. - 30 dis + 30 dis - 1 emna + 1 emna From 46dd4be040fdb563ecd402f49c68c46069b92538 Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:48 +0000 Subject: [PATCH 493/517] Strings - app/src/main/res/values-th/strings.xml --- app/src/main/res/values-th/strings.xml | 74 +++++++++++++++----------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 364275ddf5..089d40fd7e 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -23,7 +23,7 @@ 1 แท็บที่เปิด แตะเพื่อสลับไปยังแท็บ - %1$s แท็บที่เปิด แตะเพื่อสลับไปยังแท็บ + %1$s แท็บที่เปิด แตะเพื่อสลับไปยังแท็บ เลือกอยู่ %1$d @@ -52,7 +52,9 @@ บันทึกไว้ล่าสุด - ที่คั่นหน้าที่เพิ่มล่าสุด + ที่คั่นหน้าที่เพิ่มล่าสุด + + ที่คั่นหน้าล่าสุด ที่คั่นหน้าที่บันทึกไว้ล่าสุด @@ -105,6 +107,11 @@ ยกเลิก + + ปิดอัตโนมัติหลังจากหนึ่งเดือน? + + ปิด + แท็บใหม่ @@ -112,7 +119,7 @@ แท็บส่วนตัวใหม่ - ไซต์เด่น + ไซต์เด่น @@ -127,7 +134,10 @@ - เยี่ยมชมล่าสุด + เยี่ยมชมล่าสุด + + ผลค้นหาล่าสุด ลบ @@ -145,7 +155,7 @@ หยุด - ที่คั่นหน้า + ที่คั่นหน้า แก้ไขที่คั่นหน้า @@ -209,6 +219,8 @@ ปรับแต่งหน้าแรก + + ปรับแต่งหน้าแรก หน้าแรก @@ -255,6 +267,10 @@ ค้นหาโดยตรงจากแถบที่อยู่ + + + มีอะไรใหม่ใน Firefox + เปิดแท็บ Firefox ใหม่ @@ -334,7 +350,7 @@ ชุดตกแต่ง - หน้าแรก + หน้าแรก ท่าทาง @@ -408,9 +424,10 @@ บันทึกไว้ล่าสุด - ที่คั่นหน้าที่เพิ่มล่าสุด - - เยี่ยมชมล่าสุด + ที่คั่นหน้าที่เพิ่มล่าสุด + + เยี่ยมชมล่าสุด Pocket @@ -522,7 +539,7 @@ เปิด Sync - สแกนรหัสการจับคู่ใน Firefox เดสก์ท็อป + สแกนรหัสการจับคู่ใน Firefox เดสก์ท็อป ลงชื่อเข้า @@ -638,15 +655,15 @@ หลังจากผ่านไปหนึ่งเดือน - + - เริ่มที่หน้าแรก + เริ่มที่หน้าแรก - หลังจาก 4 ชั่วโมง + หลังจาก 4 ชั่วโมง - ทุกครั้ง + ทุกครั้ง - ไม่เลย + ไม่เลย ปิดด้วยตนเอง @@ -697,13 +714,13 @@ บันทึกไปยังชุดสะสม - เลือก + เลือก แบ่งปันแท็บทั้งหมด แท็บที่ปิดล่าสุด - เพิ่งปิดล่าสุด + เพิ่งปิดล่าสุด การตั้งค่าบัญชี @@ -778,7 +795,7 @@ บันทึก - อื่น ๆ + อื่น ๆ @@ -823,11 +840,6 @@ ไม่มีประวัติที่นี่ - - ลบการดาวน์โหลด - - คุณแน่ใจหรือไม่ว่าต้องการล้างการดาวน์โหลดของคุณ? - การดาวน์โหลดถูกลบ @@ -868,7 +880,7 @@ เมนูที่คั่นหน้า - แก้ไขที่คั่นหน้า + แก้ไขที่คั่นหน้า เลือกโฟลเดอร์ @@ -1895,7 +1907,7 @@ ตกลง เข้าใจแล้ว - แสดงไซต์เด่นที่เยี่ยมชมบ่อยที่สุด + แสดงไซต์เด่นที่เยี่ยมชมบ่อยที่สุด แสดงไซต์ที่เยี่ยมชมบ่อยที่สุด @@ -1908,15 +1920,15 @@ ยกเลิก - - + + แท็บที่ไม่ได้ใช้งาน - แท็บจะปรากฏที่นี่เป็นเวลา %s หลังจากนั้น แท็บต่าง ๆ จะถูกปิดโดยอัตโนมัติ + แท็บจะปรากฏที่นี่เป็นเวลา %s หลังจากนั้น แท็บต่าง ๆ จะถูกปิดโดยอัตโนมัติ - 30 วัน + 30 วัน - 1 สัปดาห์ + 1 สัปดาห์ ตั้งลิงก์จากเว็บไซต์ อีเมล และข้อความให้เปิดโดยอัตโนมัติใน Firefox @@ -1933,4 +1945,4 @@ ปิด - + From 6fb35cd71c557208e270cd4b75b3920a7f3fc173 Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 25 Oct 2021 02:16:48 +0000 Subject: [PATCH 494/517] Strings - app/src/main/res/values-tr/strings.xml --- app/src/main/res/values-tr/strings.xml | 63 ++++++++++++++++---------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 52b1855b76..fd5fba5cb6 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -24,7 +24,7 @@ 1 açık sekme. Sekme değiştirmek için dokunun. - %1$s açık sekme. Sekme değiştirmek için dokunun. + %1$s açık sekme. Sekme değiştirmek için dokunun. %1$d sekme seçildi @@ -109,9 +109,18 @@ Kapat - İki haftadır bakmadığınız sekmeler buraya taşınır. + İki haftadır bakmadığınız sekmeler buraya taşınır. - Ayarlardan kapat + Ayarlardan kapat + + + Bir ay sonra kendiliğinden kapatılsın mı? + + Firefox, bir aydır bakmadığınız sekmeleri kapatabilir. + + Kapat + + Otomatik kapatmayı aç @@ -120,7 +129,7 @@ Yeni gizli sekme - Sık kullanılanlar + Sık kullanılanlar @@ -167,7 +176,7 @@ Durdur - Yer imlerine ekle + Yer imlerine ekle Yer imini düzenle @@ -380,7 +389,9 @@ Tema - Giriş sayfası + Giriş sayfası + + Giriş sayfası Parmak hareketleri @@ -575,7 +586,7 @@ Sync’i etkinleştir - Masaüstü Firefox’taki eşleştirme kodunu tarayın + Masaüstü Firefox’taki eşleştirme kodunu tarayın Oturum aç @@ -699,15 +710,23 @@ Açık sekmeleri otomatik kapat - + - Ana ekrandan başla + Ana ekrandan başla + + Açılış ekranı - Dört saat sonra + Dört saat sonra + + Giriş sayfası - Her zaman + Her zaman + + Son sekme - Asla + Asla + + Dört saat hareketsizlikten sonra giriş sayfası Elle kapat @@ -765,13 +784,13 @@ Koleksiyona kaydet - Seç + Seç Tüm sekmeleri paylaş Son kapatılan sekmeler - Son kapatılanlar + Son kapatılanlar Hesap ayarları @@ -894,10 +913,6 @@ Geçmiş yok - - İndirmeleri sil - - İndirmelerinizi temizlemek istediğinizden emin misiniz? İndirmeler kaldırıldı @@ -938,7 +953,7 @@ Yer imi menüsü - Yer imini düzenle + Yer imini düzenle Klasör seç @@ -1965,8 +1980,10 @@ Tamam + + En sık ziyaret edilen siteler - Sık ziyaret edilen siteleri göster + Sık ziyaret edilen siteleri göster Sık ziyaret edilen siteleri göster @@ -1985,11 +2002,11 @@ Tüm pasif sekmeleri kapat - Sekmeler burada %s kalacaktır. Bu sürenin sonunda sekmeler otomatik olarak kapatılacaktır. + Sekmeler burada %s kalacaktır. Bu sürenin sonunda sekmeler otomatik olarak kapatılacaktır. - 30 gün + 30 gün - 1 hafta + 1 hafta From 4b946fb2a42879667bf6e7d01e0e939c23640683 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Tue, 12 Oct 2021 13:53:10 +0300 Subject: [PATCH 495/517] For #21861 - Ignore the UnusedResources lint check for localized strings --- app/lint.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/lint.xml b/app/lint.xml index 33a1a4d1bd..c490f2dbe3 100644 --- a/app/lint.xml +++ b/app/lint.xml @@ -50,7 +50,11 @@ - + + + + From 2e1a15a58c422272c2182834e2128c1cbacc1773 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 25 Oct 2021 12:32:24 -0400 Subject: [PATCH 496/517] No issue: Add external load flag (#22136) (cherry picked from commit 367c5f42d874b15b5ce40632146ccdcb5169c00e) Co-authored-by: Roger Yang --- .../java/org/mozilla/fenix/search/SearchDialogFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchDialogFragment.kt index bd0a8e1ded..c11c49078a 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchDialogFragment.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import mozilla.components.browser.toolbar.BrowserToolbar +import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.storage.HistoryStorage import mozilla.components.feature.qr.QrFeature import mozilla.components.lib.state.ext.consumeFlow @@ -484,7 +485,8 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler { (activity as? HomeActivity)?.openToBrowserAndLoad( searchTermOrURL = result, newTab = store.state.tabId == null, - from = BrowserDirection.FromSearchDialog + from = BrowserDirection.FromSearchDialog, + flags = EngineSession.LoadUrlFlags.external() ) dialog.dismiss() } From 78b4e75acd48d49badd5160bd3731f14bbb43457 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Tue, 26 Oct 2021 08:40:07 -0400 Subject: [PATCH 497/517] Bump version to 94.1.0 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index db30176a8d..2591d80296 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -94.0.0-beta.6 +94.1.0 From badfa48167287cd2b6291dcbcd07b2b5692f8a01 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 Oct 2021 10:00:53 -0400 Subject: [PATCH 498/517] For #21623 - Pocket recommended stories telemetry (#22156) (cherry picked from commit 507801e5d5e3d7697f81d951d2cdff268ceff044) Co-authored-by: Mugurell --- app/metrics.yaml | 85 +++++++++++ .../mozilla/fenix/components/metrics/Event.kt | 30 ++++ .../components/metrics/GleanMetricsService.kt | 17 +++ .../org/mozilla/fenix/home/HomeFragment.kt | 3 +- .../SessionControlInteractor.kt | 18 ++- .../pocket/PocketStoriesComposables.kt | 34 +++-- .../pocket/PocketStoriesController.kt | 67 +++++++-- .../pocket/PocketStoriesInteractor.kt | 31 +++- .../pocket/PocketStoriesViewHolder.kt | 19 ++- .../home/SessionControlInteractorTest.kt | 37 ++++- .../DefaultPocketStoriesControllerTest.kt | 142 +++++++++++++++--- 11 files changed, 414 insertions(+), 69 deletions(-) diff --git a/app/metrics.yaml b/app/metrics.yaml index 1a0a1b3a3f..e49f819795 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -3775,6 +3775,91 @@ pocket: notification_emails: - android-probes@mozilla.com expires: "2022-02-01" + home_recs_shown: + type: event + description: | + The Pocket recommended stories are shown on the home screen. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21593 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21625#issuecomment-936745506 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-10-01" + home_recs_story_clicked: + type: event + description: | + User tapped a Pocket recommended story to be opened. + extra_keys: + times_shown: + description: | + How many times was this story shown, including current. + position: + description: | + Position of the clicked story in the list shown. + Uses the [row x column] matrix notation. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21593 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21625#issuecomment-936745506 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-10-01" + home_recs_category_clicked: + type: event + description: | + User tapped a Pocket stories category to filter stories. + extra_keys: + category_name: + description: | + Pocket set topic name representing the just clicked category. + selected_total: + description: | + How many categories were selected before this being tapped. + new_state: + description: | + Category's new state after being tapped. + Possible values: [selected], [deselected]. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21593 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21625#issuecomment-936745506 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-10-01" + home_recs_discover_clicked: + type: event + description: | + User tapped the "Discover more" tile to open a new tab + for more Pocket stories. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21593 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21625#issuecomment-936745506 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-10-01" + home_recs_learn_more_clicked: + type: event + description: | + User tapped "Learn more" to open a new tab for Pocket. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21593 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21625#issuecomment-936745506 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-10-01" first_session: campaign: diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt index dd02deed94..f01bf18c81 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt @@ -19,6 +19,7 @@ import org.mozilla.fenix.GleanMetrics.ErrorPage import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.GleanMetrics.Logins import org.mozilla.fenix.GleanMetrics.Onboarding +import org.mozilla.fenix.GleanMetrics.Pocket import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp import org.mozilla.fenix.GleanMetrics.SearchShortcuts import org.mozilla.fenix.GleanMetrics.TabsTray @@ -127,6 +128,35 @@ sealed class Event { object WhatsNewTapped : Event() object PocketTopSiteClicked : Event() object PocketTopSiteRemoved : Event() + object PocketHomeRecsShown : Event() + object PocketHomeRecsDiscoverMoreClicked : Event() + object PocketHomeRecsLearnMoreClicked : Event() + data class PocketHomeRecsStoryClicked( + val timesShown: Long, + val storyPosition: Pair, + ) : Event() { + override val extras: Map + get() = mapOf( + Pocket.homeRecsStoryClickedKeys.timesShown to timesShown.toString(), + Pocket.homeRecsStoryClickedKeys.position to "${storyPosition.first}x${storyPosition.second}" + ) + } + + data class PocketHomeRecsCategoryClicked( + val categoryname: String, + val previousSelectedCategoriesTotal: Int, + val isSelectedNextState: Boolean + ) : Event() { + override val extras: Map + get() = mapOf( + Pocket.homeRecsCategoryClickedKeys.categoryName to categoryname, + Pocket.homeRecsCategoryClickedKeys.selectedTotal to previousSelectedCategoriesTotal.toString(), + Pocket.homeRecsCategoryClickedKeys.newState to when (isSelectedNextState) { + true -> "selected" + false -> "deselected" + } + ) + } object FennecToFenixMigrated : Event() object AddonsOpenInSettings : Event() object VoiceSearchTapped : Event() diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt index 6b518bdc58..f28c97e4f6 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt @@ -493,6 +493,23 @@ private val Event.wrapper: EventWrapper<*>? is Event.PocketTopSiteRemoved -> EventWrapper( { Pocket.pocketTopSiteRemoved.record(it) } ) + is Event.PocketHomeRecsShown -> EventWrapper( + { Pocket.homeRecsShown.record(it) } + ) + is Event.PocketHomeRecsLearnMoreClicked -> EventWrapper( + { Pocket.homeRecsLearnMoreClicked.record(it) } + ) + is Event.PocketHomeRecsDiscoverMoreClicked -> EventWrapper( + { Pocket.homeRecsDiscoverClicked.record(it) } + ) + is Event.PocketHomeRecsStoryClicked -> EventWrapper( + { Pocket.homeRecsStoryClicked.record(it) }, + { Pocket.homeRecsStoryClickedKeys.valueOf(it) } + ) + is Event.PocketHomeRecsCategoryClicked -> EventWrapper( + { Pocket.homeRecsCategoryClicked.record(it) }, + { Pocket.homeRecsCategoryClickedKeys.valueOf(it) } + ) is Event.DarkThemeSelected -> EventWrapper( { AppTheme.darkThemeSelected.record(it) }, { AppTheme.darkThemeSelectedKeys.valueOf(it) } diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 3bea7b450b..9233864816 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -357,7 +357,8 @@ class HomeFragment : Fragment() { pocketStoriesController = DefaultPocketStoriesController( homeActivity = activity, homeStore = homeFragmentStore, - navController = findNavController() + navController = findNavController(), + metrics = requireComponents.analytics.metrics ) ) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt index 2963da5967..78aa0b037f 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt @@ -391,15 +391,23 @@ class SessionControlInteractor( controller.handleCustomizeHomeTapped() } - override fun onCategoryClick(categoryClicked: PocketRecommendedStoriesCategory) { + override fun onStoriesShown(storiesShown: List) { + pocketStoriesController.handleStoriesShown(storiesShown) + } + + override fun onCategoryClicked(categoryClicked: PocketRecommendedStoriesCategory) { pocketStoriesController.handleCategoryClick(categoryClicked) } - override fun onStoriesShown(storiesShown: List) { - pocketStoriesController.handleStoriesShown(storiesShown) + override fun onStoryClicked(storyClicked: PocketRecommendedStory, storyPosition: Pair) { + pocketStoriesController.handleStoryClicked(storyClicked, storyPosition) + } + + override fun onLearnMoreClicked(link: String) { + pocketStoriesController.handleLearnMoreClicked(link) } - override fun onExternalLinkClicked(link: String) { - pocketStoriesController.handleExternalLinkClick(link) + override fun onDiscoverMoreClicked(link: String) { + pocketStoriesController.handleDiscoverMoreClicked(link) } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt index 8860ffb9e5..b059f6c8be 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt @@ -19,7 +19,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.Icon import androidx.compose.material.Text @@ -107,14 +107,15 @@ fun PocketStory( * @param stories The list of [PocketRecommendedStory]ies to be displayed. Expect a list with 8 items. * @param contentPadding Dimension for padding the content after it has been clipped. * This space will be used for shadows and also content rendering when the list is scrolled. - * @param onExternalLinkClicked Callback for when the user taps an element which contains an - * external link for where user can go for more recommendations. + * @param onStoryClicked Callback for when the user taps on a recommended story. + * @param onDiscoverMoreClicked Callback for when the user taps an element which contains an */ @Composable fun PocketStories( @PreviewParameter(PocketStoryProvider::class) stories: List, contentPadding: Dp, - onExternalLinkClicked: (String) -> Unit + onStoryClicked: (PocketRecommendedStory, Pair) -> Unit, + onDiscoverMoreClicked: (String) -> Unit ) { // Show stories in at most 3 rows but on any number of columns depending on the data received. val maxRowsNo = 3 @@ -129,20 +130,20 @@ fun PocketStories( flingBehavior = flingBehavior, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - items(storiesToShow) { columnItems -> + itemsIndexed(storiesToShow) { columnIndex, columnItems -> Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - columnItems.forEach { story -> + columnItems.forEachIndexed { rowIndex, story -> if (story == placeholderStory) { ListItemTabLargePlaceholder(stringResource(R.string.pocket_stories_placeholder_text)) { - onExternalLinkClicked("https://getpocket.com/explore?$POCKET_FEATURE_UTM_KEY_VALUE") + onDiscoverMoreClicked("https://getpocket.com/explore?$POCKET_FEATURE_UTM_KEY_VALUE") } } else { - val uri = Uri.parse(story.url) - .buildUpon() - .appendQueryParameter(URI_PARAM_UTM_KEY, POCKET_STORIES_UTM_VALUE) - .build().toString() PocketStory(story) { - onExternalLinkClicked(uri) + val uri = Uri.parse(story.url) + .buildUpon() + .appendQueryParameter(URI_PARAM_UTM_KEY, POCKET_STORIES_UTM_VALUE) + .build().toString() + onStoryClicked(it.copy(url = uri), rowIndex to columnIndex) } } } @@ -184,13 +185,13 @@ fun PocketStoriesCategories( * Pocket feature section title. * Shows a default text about Pocket and offers a external link to learn more. * - * @param onExternalLinkClicked Callback invoked when the user clicks the "Learn more" link. + * @param onLearnMoreClicked Callback invoked when the user clicks the "Learn more" link. * Contains the full URL for where the user should be navigated to. * @param modifier [Modifier] to be applied to the layout. */ @Composable fun PoweredByPocketHeader( - onExternalLinkClicked: (String) -> Unit, + onLearnMoreClicked: (String) -> Unit, modifier: Modifier = Modifier ) { val color = when (isSystemInDarkTheme()) { @@ -231,7 +232,7 @@ fun PoweredByPocketHeader( ) ClickableSubstringLink(text, color, linkStartIndex, linkEndIndex) { - onExternalLinkClicked("https://www.mozilla.org/en-US/firefox/pocket/?$POCKET_FEATURE_UTM_KEY_VALUE") + onLearnMoreClicked("https://www.mozilla.org/en-US/firefox/pocket/?$POCKET_FEATURE_UTM_KEY_VALUE") } } } @@ -247,7 +248,8 @@ private fun PocketStoriesComposablesPreview() { PocketStories( stories = getFakePocketStories(8), contentPadding = 0.dp, - onExternalLinkClicked = { } + onStoryClicked = { _, _ -> }, + onDiscoverMoreClicked = { } ) Spacer(Modifier.height(10.dp)) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt index 627415e8ba..708f3136bc 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesController.kt @@ -13,11 +13,20 @@ import mozilla.components.service.pocket.PocketRecommendedStory import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController /** * Contract for how all user interactions with the Pocket recommended stories feature are to be handled. */ interface PocketStoriesController { + /** + * Callback to decide what should happen as an effect of a new list of stories being shown. + * + * @param storiesShown the new list of [PocketRecommendedStory]es shown to the user. + */ + fun handleStoriesShown(storiesShown: List) + /** * Callback allowing to handle a specific [PocketRecommendedStoriesCategory] being clicked by the user. * @@ -26,18 +35,26 @@ interface PocketStoriesController { fun handleCategoryClick(categoryClicked: PocketRecommendedStoriesCategory): Unit /** - * Callback to decide what should happen as an effect of a new list of stories being shown. + * Callback for when the user clicks on a specific story. * - * @param storiesShown the new list of [PocketRecommendedStory]es shown to the user. + * @param storyClicked The just clicked [PocketRecommendedStory] URL. + * @param storyPosition `row x column` matrix representing the grid position of the clicked story. */ - fun handleStoriesShown(storiesShown: List) + fun handleStoryClicked(storyClicked: PocketRecommendedStory, storyPosition: Pair) /** - * Callback for when the an external link is clicked. + * Callback for when the "Learn more" link is clicked. * * @param link URL clicked. */ - fun handleExternalLinkClick(link: String) + fun handleLearnMoreClicked(link: String) + + /** + * Callback for when the "Discover more" link is clicked. + * + * @param link URL clicked. + */ + fun handleDiscoverMoreClicked(link: String) } /** @@ -50,14 +67,27 @@ interface PocketStoriesController { internal class DefaultPocketStoriesController( private val homeActivity: HomeActivity, private val homeStore: HomeFragmentStore, - private val navController: NavController + private val navController: NavController, + private val metrics: MetricController ) : PocketStoriesController { + override fun handleStoriesShown(storiesShown: List) { + homeStore.dispatch(HomeFragmentAction.PocketStoriesShown(storiesShown)) + metrics.track(Event.PocketHomeRecsShown) + } + override fun handleCategoryClick(categoryClicked: PocketRecommendedStoriesCategory) { val initialCategoriesSelections = homeStore.state.pocketStoriesCategoriesSelections // First check whether the category is clicked to be deselected. if (initialCategoriesSelections.map { it.name }.contains(categoryClicked.name)) { homeStore.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(categoryClicked.name)) + metrics.track( + Event.PocketHomeRecsCategoryClicked( + categoryClicked.name, + initialCategoriesSelections.size, + false + ) + ) return } @@ -75,19 +105,36 @@ internal class DefaultPocketStoriesController( // Finally update the selection. homeStore.dispatch(HomeFragmentAction.SelectPocketStoriesCategory(categoryClicked.name)) + + metrics.track( + Event.PocketHomeRecsCategoryClicked( + categoryClicked.name, + initialCategoriesSelections.size, + true + ) + ) } - override fun handleStoriesShown(storiesShown: List) { - homeStore.dispatch(HomeFragmentAction.PocketStoriesShown(storiesShown)) + override fun handleStoryClicked(storyClicked: PocketRecommendedStory, storyPosition: Pair) { + dismissSearchDialogIfDisplayed() + homeActivity.openToBrowserAndLoad(storyClicked.url, true, BrowserDirection.FromHome) + metrics.track(Event.PocketHomeRecsStoryClicked(storyClicked.timesShown.inc(), storyPosition)) + } + + override fun handleLearnMoreClicked(link: String) { + dismissSearchDialogIfDisplayed() + homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) + metrics.track(Event.PocketHomeRecsLearnMoreClicked) } - override fun handleExternalLinkClick(link: String) { + override fun handleDiscoverMoreClicked(link: String) { dismissSearchDialogIfDisplayed() homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) + metrics.track(Event.PocketHomeRecsDiscoverMoreClicked) } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - fun dismissSearchDialogIfDisplayed() { + internal fun dismissSearchDialogIfDisplayed() { if (navController.currentDestination?.id == R.id.searchDialogFragment) { navController.navigateUp() } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt index 67dab51372..db814fc844 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesInteractor.kt @@ -11,23 +11,38 @@ import mozilla.components.service.pocket.PocketRecommendedStory */ interface PocketStoriesInteractor { /** - * Callback for when the user clicked a specific category. + * Callback for then new stories are shown to the user. * - * @param categoryClicked the just clicked [PocketRecommendedStoriesCategory]. + * @param storiesShown The new list of [PocketRecommendedStory]es shown to the user. */ - fun onCategoryClick(categoryClicked: PocketRecommendedStoriesCategory) + fun onStoriesShown(storiesShown: List) /** - * Callback for then new stories are shown to the user. + * Callback for when the user clicks a specific category. * - * @param storiesShown the new list of [PocketRecommendedStory]es shown to the user. + * @param categoryClicked The just clicked [PocketRecommendedStoriesCategory]. */ - fun onStoriesShown(storiesShown: List) + fun onCategoryClicked(categoryClicked: PocketRecommendedStoriesCategory) + + /** + * Callback for when the user clicks on a specific story. + * + * @param storyClicked The just clicked [PocketRecommendedStory] URL. + * @param storyPosition `row x column` matrix representing the grid position of the clicked story. + */ + fun onStoryClicked(storyClicked: PocketRecommendedStory, storyPosition: Pair) + + /** + * Callback for when the user clicks the "Learn more" link. + * + * @param link URL clicked. + */ + fun onLearnMoreClicked(link: String) /** - * Callback for when the user clicks an external link. + * Callback for when the user clicks the "Discover more" link. * * @param link URL clicked. */ - fun onExternalLinkClicked(link: String) + fun onDiscoverMoreClicked(link: String) } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt index 35d3262705..145f283767 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesViewHolder.kt @@ -54,8 +54,10 @@ class PocketStoriesViewHolder( PocketStories( store, interactor::onStoriesShown, - interactor::onCategoryClick, - interactor::onExternalLinkClicked, + interactor::onStoryClicked, + interactor::onCategoryClicked, + interactor::onDiscoverMoreClicked, + interactor::onLearnMoreClicked, with(composeView.resources) { getDimensionPixelSize(R.dimen.home_item_horizontal_margin) / displayMetrics.density } @@ -70,11 +72,14 @@ class PocketStoriesViewHolder( } @Composable +@Suppress("LongParameterList") fun PocketStories( store: HomeFragmentStore, onStoriesShown: (List) -> Unit, - onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit, - onExternalLinkClicked: (String) -> Unit, + onStoryClicked: (PocketRecommendedStory, Pair) -> Unit, + onCategoryClicked: (PocketRecommendedStoriesCategory) -> Unit, + onDiscoverMoreClicked: (String) -> Unit, + onLearnMoreClicked: (String) -> Unit, @Dimension horizontalPadding: Float = 0f ) { val stories = store @@ -105,7 +110,7 @@ fun PocketStories( Spacer(Modifier.height(17.dp)) - PocketStories(stories ?: emptyList(), horizontalPadding.dp, onExternalLinkClicked) + PocketStories(stories ?: emptyList(), horizontalPadding.dp, onStoryClicked, onDiscoverMoreClicked) Spacer(Modifier.height(24.dp)) @@ -122,7 +127,7 @@ fun PocketStories( PocketStoriesCategories( categories = categories ?: emptyList(), selections = categoriesSelections ?: emptyList(), - onCategoryClick = onCategoryClick, + onCategoryClick = onCategoryClicked, modifier = Modifier .fillMaxWidth() .padding(horizontal = horizontalPadding.dp) @@ -131,7 +136,7 @@ fun PocketStories( Spacer(Modifier.height(24.dp)) PoweredByPocketHeader( - onExternalLinkClicked, + onLearnMoreClicked, modifier = Modifier .fillMaxWidth() .padding(horizontal = horizontalPadding.dp) diff --git a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt index 7be805ce99..9bee29a0e5 100644 --- a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt @@ -10,6 +10,7 @@ import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNodeType import mozilla.components.feature.tab.collections.Tab import mozilla.components.feature.tab.collections.TabCollection +import mozilla.components.service.pocket.PocketRecommendedStory import org.junit.Before import org.junit.Test import org.mozilla.fenix.browser.browsingmode.BrowsingMode @@ -213,21 +214,49 @@ class SessionControlInteractorTest { verify { controller.handlePrivateModeButtonClicked(newMode, hasBeenOnboarded) } } + @Test + fun `GIVEN a PocketStoriesInteractor WHEN stories are shown THEN handle it in a PocketStoriesController`() { + val shownStories: List = mockk() + + interactor.onStoriesShown(shownStories) + + verify { pocketStoriesController.handleStoriesShown(shownStories) } + } + @Test fun `GIVEN a PocketStoriesInteractor WHEN a category is clicked THEN handle it in a PocketStoriesController`() { val clickedCategory: PocketRecommendedStoriesCategory = mockk() - interactor.onCategoryClick(clickedCategory) + interactor.onCategoryClicked(clickedCategory) verify { pocketStoriesController.handleCategoryClick(clickedCategory) } } @Test - fun `GIVEN a PocketStoriesInteractor WHEN an external link is clicked THEN handle it in a PocketStoriesController`() { + fun `GIVEN a PocketStoriesInteractor WHEN a story is clicked THEN handle it in a PocketStoriesController`() { + val clickedStory: PocketRecommendedStory = mockk() + val storyGridLocation = 1 to 2 + + interactor.onStoryClicked(clickedStory, storyGridLocation) + + verify { pocketStoriesController.handleStoryClicked(clickedStory, storyGridLocation) } + } + + @Test + fun `GIVEN a PocketStoriesInteractor WHEN discover more clicked THEN handle it in a PocketStoriesController`() { + val link = "http://getpocket.com/explore" + + interactor.onDiscoverMoreClicked(link) + + verify { pocketStoriesController.handleDiscoverMoreClicked(link) } + } + + @Test + fun `GIVEN a PocketStoriesInteractor WHEN learn more clicked THEN handle it in a PocketStoriesController`() { val link = "https://www.mozilla.org/en-US/firefox/pocket/" - interactor.onExternalLinkClicked(link) + interactor.onLearnMoreClicked(link) - verify { pocketStoriesController.handleExternalLinkClick(link) } + verify { pocketStoriesController.handleLearnMoreClicked(link) } } } diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt index ad4effd8f2..9185ac21d0 100644 --- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/DefaultPocketStoriesControllerTest.kt @@ -9,18 +9,23 @@ import io.mockk.every import io.mockk.mockk import io.mockk.spyk import io.mockk.verify +import io.mockk.verifyOrder import mozilla.components.service.pocket.PocketRecommendedStory import org.junit.Test import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.home.HomeFragmentAction import org.mozilla.fenix.home.HomeFragmentState import org.mozilla.fenix.home.HomeFragmentStore class DefaultPocketStoriesControllerTest { + val metrics: MetricController = mockk(relaxed = true) + @Test - fun `GIVEN a category is selected WHEN that same category is clicked THEN deselect it`() { + fun `GIVEN a category is selected WHEN that same category is clicked THEN deselect it and record telemetry`() { val category1 = PocketRecommendedStoriesCategory("cat1", emptyList()) val category2 = PocketRecommendedStoriesCategory("cat2", emptyList()) val selections = listOf(PocketRecommendedStoriesSelectedCategory(category2.name)) @@ -32,17 +37,21 @@ class DefaultPocketStoriesControllerTest { ) ) ) - val controller = DefaultPocketStoriesController(mockk(), store, mockk()) + val controller = DefaultPocketStoriesController(mockk(), store, mockk(), metrics) controller.handleCategoryClick(category1) verify(exactly = 0) { store.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(category1.name)) } + verify { store.dispatch(HomeFragmentAction.SelectPocketStoriesCategory(category1.name)) } + verify { metrics.track(Event.PocketHomeRecsCategoryClicked(category1.name, 1, true)) } controller.handleCategoryClick(category2) + verify(exactly = 0) { store.dispatch(HomeFragmentAction.SelectPocketStoriesCategory(category2.name)) } verify { store.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(category2.name)) } + verify { metrics.track(Event.PocketHomeRecsCategoryClicked(category2.name, 2, false)) } } @Test - fun `GIVEN 8 categories are selected WHEN when a new one is clicked THEN the oldest selected is deselected before selecting the new one`() { + fun `GIVEN 8 categories are selected WHEN when a new one is clicked THEN the oldest selected is deselected before selecting the new one and record telemetry`() { val category1 = PocketRecommendedStoriesSelectedCategory(name = "cat1", selectionTimestamp = 111) val category2 = PocketRecommendedStoriesSelectedCategory(name = "cat2", selectionTimestamp = 222) val category3 = PocketRecommendedStoriesSelectedCategory(name = "cat3", selectionTimestamp = 333) @@ -61,16 +70,17 @@ class DefaultPocketStoriesControllerTest { ) ) ) - val controller = DefaultPocketStoriesController(mockk(), store, mockk()) + val controller = DefaultPocketStoriesController(mockk(), store, mockk(), metrics) controller.handleCategoryClick(PocketRecommendedStoriesCategory(newSelectedCategory.name)) verify { store.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(oldestSelectedCategory.name)) } verify { store.dispatch(HomeFragmentAction.SelectPocketStoriesCategory(newSelectedCategory.name)) } + verify { metrics.track(Event.PocketHomeRecsCategoryClicked(newSelectedCategory.name, 8, true)) } } @Test - fun `GIVEN fewer than 8 categories are selected WHEN when a new one is clicked THEN don't deselect anything but select the newly clicked category`() { + fun `GIVEN fewer than 8 categories are selected WHEN when a new one is clicked THEN don't deselect anything but select the newly clicked category and record telemetry`() { val category1 = PocketRecommendedStoriesSelectedCategory(name = "cat1", selectionTimestamp = 111) val category2 = PocketRecommendedStoriesSelectedCategory(name = "cat2", selectionTimestamp = 222) val category3 = PocketRecommendedStoriesSelectedCategory(name = "cat3", selectionTimestamp = 333) @@ -87,50 +97,146 @@ class DefaultPocketStoriesControllerTest { ) ) ) - val controller = DefaultPocketStoriesController(mockk(), store, mockk()) + val newSelectedCategoryName = "newSelectedCategory" + val controller = DefaultPocketStoriesController(mockk(), store, mockk(), metrics) - controller.handleCategoryClick(PocketRecommendedStoriesCategory("newSelectedCategory")) + controller.handleCategoryClick(PocketRecommendedStoriesCategory(newSelectedCategoryName)) verify(exactly = 0) { store.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(oldestSelectedCategory.name)) } - verify { store.dispatch(HomeFragmentAction.SelectPocketStoriesCategory("newSelectedCategory")) } + verify { store.dispatch(HomeFragmentAction.SelectPocketStoriesCategory(newSelectedCategoryName)) } + verify { metrics.track(Event.PocketHomeRecsCategoryClicked(newSelectedCategoryName, 7, true)) } } @Test - fun `WHEN new stories are shown THEN update the State`() { + fun `WHEN new stories are shown THEN update the State and record telemetry`() { val store = spyk(HomeFragmentStore()) - val controller = DefaultPocketStoriesController(mockk(), store, mockk()) + val controller = DefaultPocketStoriesController(mockk(), store, mockk(), metrics) val storiesShown: List = mockk() controller.handleStoriesShown(storiesShown) verify { store.dispatch(HomeFragmentAction.PocketStoriesShown(storiesShown)) } + verify { metrics.track(Event.PocketHomeRecsShown) } } @Test - fun `WHEN an external link is clicked THEN link is opened`() { - val link = "https://www.mozilla.org/en-US/firefox/pocket/" + fun `WHEN a story is clicked then open that story's url using HomeActivity and record telemetry`() { + val story = PocketRecommendedStory( + title = "", + url = "testLink", + imageUrl = "", + publisher = "", + category = "", + timeToRead = 0, + timesShown = 123 + ) + val homeActivity: HomeActivity = mockk(relaxed = true) + val controller = DefaultPocketStoriesController(homeActivity, mockk(), mockk(relaxed = true), metrics) + + controller.handleStoryClicked(story, 1 to 2) + + verify { homeActivity.openToBrowserAndLoad(story.url, true, BrowserDirection.FromHome) } + metrics.track(Event.PocketHomeRecsStoryClicked(story.timesShown, 1 to 2)) + } + + @Test + fun `WHEN discover more is clicked then open that using HomeActivity and record telemetry`() { + val link = "http://getpocket.com/explore" val homeActivity: HomeActivity = mockk(relaxed = true) - val controller = DefaultPocketStoriesController(homeActivity, mockk(), mockk(relaxed = true)) + val controller = DefaultPocketStoriesController(homeActivity, mockk(), mockk(relaxed = true), metrics) - controller.handleExternalLinkClick(link) + controller.handleDiscoverMoreClicked(link) verify { homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) } + metrics.track(Event.PocketHomeRecsDiscoverMoreClicked) } @Test - fun `WHEN an external link is clicked THEN link is opened and search dismissed`() { + fun `WHEN learn more is clicked then open that using HomeActivity and record telemetry`() { val link = "https://www.mozilla.org/en-US/firefox/pocket/" val homeActivity: HomeActivity = mockk(relaxed = true) + val controller = DefaultPocketStoriesController(homeActivity, mockk(), mockk(relaxed = true), metrics) + + controller.handleLearnMoreClicked(link) + + verify { homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) } + metrics.track(Event.PocketHomeRecsLearnMoreClicked) + } + + @Test + fun `WHEN a story is clicked THEN search is dismissed and then its link opened`() { + val story = PocketRecommendedStory("", "url", "", "", "", 0, 0) + val homeActivity: HomeActivity = mockk(relaxed = true) val navController: NavController = mockk(relaxed = true) + every { navController.currentDestination } returns mockk { + every { id } returns R.id.searchDialogFragment + } + val controller = DefaultPocketStoriesController(homeActivity, mockk(), navController, metrics) + + controller.handleStoryClicked(story, 1 to 2) + verifyOrder { + navController.navigateUp() + homeActivity.openToBrowserAndLoad(story.url, true, BrowserDirection.FromHome) + } + } + + @Test + fun `WHEN discover more is clicked THEN search is dismissed and then its link opened`() { + val link = "https://discoverMore.link" + val homeActivity: HomeActivity = mockk(relaxed = true) + val navController: NavController = mockk(relaxed = true) every { navController.currentDestination } returns mockk { every { id } returns R.id.searchDialogFragment } + val controller = DefaultPocketStoriesController(homeActivity, mockk(), navController, metrics) - val controller = DefaultPocketStoriesController(homeActivity, mockk(), navController) - controller.handleExternalLinkClick(link) + controller.handleDiscoverMoreClicked(link) + + verifyOrder { + navController.navigateUp() + homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) + } + } + + @Test + fun `WHEN learn more link is clicked THEN search is dismissed and then that link is opened`() { + val link = "https://learnMore.link" + val homeActivity: HomeActivity = mockk(relaxed = true) + val navController: NavController = mockk(relaxed = true) + every { navController.currentDestination } returns mockk { + every { id } returns R.id.searchDialogFragment + } + val controller = DefaultPocketStoriesController(homeActivity, mockk(), navController, metrics) + + controller.handleLearnMoreClicked(link) + + verifyOrder { + navController.navigateUp() + homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) + } + } + + @Test + fun `GIVEN search dialog is currently focused WHEN dismissSearchDialogIfDisplayed is called THEN close the search dialog`() { + val navController: NavController = mockk(relaxed = true) + every { navController.currentDestination } returns mockk { + every { id } returns R.id.searchDialogFragment + } + val controller = DefaultPocketStoriesController(mockk(), mockk(), navController, mockk()) + + controller.dismissSearchDialogIfDisplayed() - verify { homeActivity.openToBrowserAndLoad(link, true, BrowserDirection.FromHome) } verify { navController.navigateUp() } } + + @Test + fun `GIVEN search dialog is not currently focused WHEN dismissSearchDialogIfDisplayed is called THEN do nothing`() { + val navController: NavController = mockk(relaxed = true) + val controller = DefaultPocketStoriesController(mockk(), mockk(), navController, mockk()) + + controller.dismissSearchDialogIfDisplayed() + + verify(exactly = 0) { navController.navigateUp() } + } } From 3b1a10009a5d291d9d5f8a0d500bbc953336b440 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 Oct 2021 10:15:00 -0400 Subject: [PATCH 499/517] Update to Android-Components 94.0.11. (#22159) Co-authored-by: MickeyMoz Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index a294240f76..2bfb287b6a 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "94.0.10" + const val VERSION = "94.0.11" } From a2fb5ce4a3b60fb320ff3986227f2c4482364714 Mon Sep 17 00:00:00 2001 From: "codrut.topliceanu" Date: Tue, 12 Oct 2021 13:44:30 +0300 Subject: [PATCH 500/517] For #21732 - Adds inactive tabs survey on disable + telemetry (cherry picked from commit bba787e87ee045d17347b69c20d58aa82c2bec13) --- app/metrics.yaml | 32 +++++ .../mozilla/fenix/components/metrics/Event.kt | 7 ++ .../components/metrics/GleanMetricsService.kt | 8 ++ .../fenix/settings/TabsSettingsFragment.kt | 93 +++++++++++++- .../java/org/mozilla/fenix/utils/Settings.kt | 8 ++ .../layout/survey_inactive_tabs_disable.xml | 114 ++++++++++++++++++ app/src/main/res/values/preference_keys.xml | 3 + app/src/main/res/values/strings.xml | 16 +-- 8 files changed, 272 insertions(+), 9 deletions(-) create mode 100644 app/src/main/res/layout/survey_inactive_tabs_disable.xml diff --git a/app/metrics.yaml b/app/metrics.yaml index e49f819795..2ec3444553 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -1667,6 +1667,38 @@ preferences: notification_emails: - android-probes@mozilla.com expires: "2022-11-01" + inactive_tabs_survey_opened: + type: event + description: > + A survey for asking the user why she intends to turn off the + inactive tabs feature is shown. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21732 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21862#issuecomment-949598042 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-02-01" + turn_off_inactive_tabs_survey: + type: event + description: > + The user has disabled inactive tabs feature and responded + to our request for feedback. + extra_keys: + feedback: + description: | + The user's feedback regarding inactive tabs feature. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/21732 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/21862#issuecomment-946977614 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-02-01" search.default_engine: code: diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt index f01bf18c81..87cac78f89 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt @@ -20,6 +20,7 @@ import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.GleanMetrics.Logins import org.mozilla.fenix.GleanMetrics.Onboarding import org.mozilla.fenix.GleanMetrics.Pocket +import org.mozilla.fenix.GleanMetrics.Preferences import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp import org.mozilla.fenix.GleanMetrics.SearchShortcuts import org.mozilla.fenix.GleanMetrics.TabsTray @@ -201,6 +202,12 @@ sealed class Event { data class TabsTrayCloseInactiveTab(val amountClosed: Int = 1) : Event() object TabsTrayOpenInactiveTab : Event() + object InactiveTabsSurveyOpened : Event() + data class InactiveTabsOffSurvey(val feedback: String) : Event() { + override val extras: Map + get() = mapOf(Preferences.turnOffInactiveTabsSurveyKeys.feedback to feedback.lowercase(Locale.ROOT)) + } + object ProgressiveWebAppOpenFromHomescreenTap : Event() object ProgressiveWebAppInstallAsShortcut : Event() diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt index f28c97e4f6..bddbc006bd 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt @@ -37,6 +37,7 @@ import org.mozilla.fenix.GleanMetrics.Metrics import org.mozilla.fenix.GleanMetrics.Onboarding import org.mozilla.fenix.GleanMetrics.Pings import org.mozilla.fenix.GleanMetrics.Pocket +import org.mozilla.fenix.GleanMetrics.Preferences import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp import org.mozilla.fenix.GleanMetrics.ReaderMode import org.mozilla.fenix.GleanMetrics.RecentBookmarks @@ -620,6 +621,13 @@ private val Event.wrapper: EventWrapper<*>? is Event.TabsTrayOpenInactiveTab -> EventWrapper( { TabsTray.openInactiveTab.add() } ) + is Event.InactiveTabsSurveyOpened -> EventWrapper( + { Preferences.inactiveTabsSurveyOpened.record(it) } + ) + is Event.InactiveTabsOffSurvey -> EventWrapper( + { Preferences.turnOffInactiveTabsSurvey.record(it) }, + { Preferences.turnOffInactiveTabsSurveyKeys.valueOf(it) } + ) is Event.AutoPlaySettingVisited -> EventWrapper( { Autoplay.visitedSetting.record(it) } ) diff --git a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt index 964cfc4096..0e184f18b3 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt @@ -4,8 +4,12 @@ package org.mozilla.fenix.settings +import android.content.res.Configuration import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup +import android.widget.RadioButton import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference @@ -14,14 +18,18 @@ import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event.TabViewSettingChanged import org.mozilla.fenix.components.metrics.Event.TabViewSettingChanged.Type +import org.mozilla.fenix.databinding.SurveyInactiveTabsDisableBinding import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.utils.view.addToRadioGroup +import java.util.Locale /** * Lets the user customize auto closing tabs. */ +@Suppress("TooManyFunctions") class TabsSettingsFragment : PreferenceFragmentCompat() { private lateinit var listRadioButton: RadioButtonPreference private lateinit var gridRadioButton: RadioButtonPreference @@ -35,6 +43,9 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { private lateinit var inactiveTabsCategory: PreferenceCategory private lateinit var inactiveTabs: SwitchPreference private lateinit var searchTermTabGroups: SwitchPreference + private val shouldShowInactiveTabsTurnOffSurvey + get() = requireContext().settings().isTelemetryEnabled && + requireContext().settings().shouldShowInactiveTabsTurnOffSurvey override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.tabs_preferences, rootKey) @@ -48,6 +59,7 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { override fun onResume() { super.onResume() showToolbar(getString(R.string.preferences_tabs)) + setupPreferences() } @@ -78,8 +90,25 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { FeatureFlags.showStartOnHomeSettings inactiveTabs = requirePreference(R.string.pref_key_inactive_tabs).also { - it.isChecked = it.context.settings().inactiveTabsAreEnabled - it.onPreferenceChangeListener = SharedPreferenceUpdater() + it.isChecked = requireContext().settings().inactiveTabsAreEnabled + it.setOnPreferenceChangeListener { preference, newValue -> + if (shouldShowInactiveTabsTurnOffSurvey && newValue == false) { + // The first time the user tries to disable the feature show a little survey for her motives. + val inactiveTabsSurveyBinding = SurveyInactiveTabsDisableBinding.inflate( + LayoutInflater.from(context), + view as ViewGroup, + true + ) + setupSurvey(inactiveTabsSurveyBinding) + requireContext().metrics.track(Event.InactiveTabsSurveyOpened) + + // Don't update the preference as a direct action of user tapping the switch. + // Only disable the feature after the user selects an option in the survey or expressly closes it. + false + } else { + SharedPreferenceUpdater().onPreferenceChange(preference, newValue) + } + } } inactiveTabsCategory = requirePreference(R.string.pref_key_inactive_tabs_category).also { @@ -98,6 +127,66 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { setupRadioGroups() } + private fun setupSurvey(inactiveTabsSurveyBinding: SurveyInactiveTabsDisableBinding) { + inactiveTabsSurveyBinding.closeSurvey.setOnClickListener { + finishInactiveTabsSurvey(inactiveTabsSurveyBinding) + + // Register that user closed this survey without picking any option. + requireContext().metrics.track( + Event.InactiveTabsOffSurvey("none") + ) + } + + // A map is needed to help retrieve the correct string on SEND. + // These values are also sent to Glean which will truncate anything over 100 UTF8 characters. + val radioButtonsMap: Map = mapOf( + R.id.rb_do_not_understand to R.string.inactive_tabs_survey_do_not_understand, + R.id.rb_do_it_myself to R.string.inactive_tabs_survey_do_it_myself, + R.id.rb_time_too_long to R.string.inactive_tabs_survey_time_too_long_option, + R.id.rb_time_too_short to R.string.inactive_tabs_survey_time_too_short_option, + ) + + // Sets the Radio buttons' text + radioButtonsMap.forEach { + inactiveTabsSurveyBinding.surveyGroup.findViewById(it.key)?.text = + requireContext().getText(it.value) + } + + inactiveTabsSurveyBinding.sendButton.setOnClickListener { + val checkedRadioButtonId = inactiveTabsSurveyBinding.surveyGroup.checkedRadioButtonId + // If no option has been selected the button does not need to do anything. + if (checkedRadioButtonId != -1) { + finishInactiveTabsSurvey(inactiveTabsSurveyBinding) + + // Using the stringId of the selected option an event is sent using English. + radioButtonsMap[checkedRadioButtonId]?.let { stringId -> + requireContext().metrics.track( + Event.InactiveTabsOffSurvey(getDefaultString(stringId)) + ) + } + } + } + } + + /** + * Set the inactive tabs survey completed and the feature disabled. + */ + private fun finishInactiveTabsSurvey(inactiveTabsSurveyBinding: SurveyInactiveTabsDisableBinding) { + inactiveTabsSurveyBinding.surveyContainer.visibility = View.GONE + requireContext().settings().shouldShowInactiveTabsTurnOffSurvey = false + requireContext().settings().inactiveTabsAreEnabled = false + requirePreference(R.string.pref_key_inactive_tabs).isChecked = false + } + + /** + * Get the "en-US" string value for the indicated [resourceId]. + */ + private fun getDefaultString(resourceId: Int): String { + val config = Configuration(requireContext().resources.configuration) + config.setLocale(Locale.ENGLISH) + return requireContext().createConfigurationContext(config).getText(resourceId).toString() + } + private fun setupRadioGroups() { addToRadioGroup( listRadioButton, diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index 7fbfc0e207..1029620179 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -866,6 +866,14 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = true ) + /** + * Should we display a feedback request to the user when he turns off the Inactive Tabs feature + */ + var shouldShowInactiveTabsTurnOffSurvey by booleanPreference( + appContext.getPreferenceKey(R.string.pref_key_should_show_inactive_tabs_turn_off_survey), + default = true + ) + fun getSitePermissionsPhoneFeatureAction( feature: PhoneFeature, default: Action = Action.ASK_TO_ALLOW diff --git a/app/src/main/res/layout/survey_inactive_tabs_disable.xml b/app/src/main/res/layout/survey_inactive_tabs_disable.xml new file mode 100644 index 0000000000..fe150c81df --- /dev/null +++ b/app/src/main/res/layout/survey_inactive_tabs_disable.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + +