From 24ba9f2fc8691ebde8b6a9e178b172bc83434c87 Mon Sep 17 00:00:00 2001 From: ValentinTimisica Date: Wed, 8 Apr 2020 15:21:33 +0300 Subject: [PATCH] For #9505: Adds possibility to sort saved logins Currently we support sorting by name and by last used. Also, the selected option is saved in shared preferences so that the last option chosen by the user is properly displayed even after the app was restarted. --- .../settings/logins/SavedLoginsController.kt | 22 ++++ .../settings/logins/SavedLoginsFragment.kt | 104 ++++++++++++++++-- .../logins/SavedLoginsFragmentStore.kt | 95 +++++++++++++--- .../settings/logins/SavedLoginsInteractor.kt | 5 + .../logins/SavedLoginsSortingStrategyMenu.kt | 55 +++++++++ .../fenix/settings/logins/SavedLoginsView.kt | 3 + .../fenix/settings/logins/SortingStrategy.kt | 25 +++++ .../java/org/mozilla/fenix/utils/Settings.kt | 36 ++++++ .../main/res/layout/navigation_toolbar.xml | 9 +- .../saved_logins_sort_items_toolbar_child.xml | 49 +++++++++ app/src/main/res/values/dimens.xml | 4 + app/src/main/res/values/preference_keys.xml | 1 + app/src/main/res/values/strings.xml | 6 + 13 files changed, 389 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsController.kt create mode 100644 app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsSortingStrategyMenu.kt create mode 100644 app/src/main/java/org/mozilla/fenix/settings/logins/SortingStrategy.kt create mode 100644 app/src/main/res/layout/saved_logins_sort_items_toolbar_child.xml diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsController.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsController.kt new file mode 100644 index 000000000..224ff6c45 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsController.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.settings.logins + +import org.mozilla.fenix.utils.Settings + +interface SavedLoginsController { + fun handleSort(sortingStrategy: SortingStrategy) +} + +class DefaultSavedLoginsController( + val store: SavedLoginsFragmentStore, + val settings: Settings +) : SavedLoginsController { + + override fun handleSort(sortingStrategy: SortingStrategy) { + store.dispatch(SavedLoginsFragmentAction.SortLogins(sortingStrategy)) + settings.savedLoginsSortingStrategy = sortingStrategy + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsFragment.kt index b121ddbb3..1a9d7e769 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsFragment.kt @@ -6,26 +6,31 @@ package org.mozilla.fenix.settings.logins import android.os.Bundle import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.EditorInfo +import android.view.Menu +import android.view.MenuInflater +import android.widget.FrameLayout +import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.SearchView +import androidx.appcompat.widget.Toolbar +import androidx.constraintlayout.widget.ConstraintLayout import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import kotlinx.android.synthetic.main.fragment_saved_logins.view.* -import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.ObsoleteCoroutinesApi import kotlinx.coroutines.async import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.ObsoleteCoroutinesApi +import mozilla.components.browser.menu.BrowserMenu import mozilla.components.concept.storage.Login import mozilla.components.lib.state.ext.consumeFrom import org.mozilla.fenix.BrowserDirection @@ -39,10 +44,16 @@ import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.settings.SupportUtils +@SuppressWarnings("TooManyFunctions") class SavedLoginsFragment : Fragment() { private lateinit var savedLoginsStore: SavedLoginsFragmentStore private lateinit var savedLoginsView: SavedLoginsView private lateinit var savedLoginsInteractor: SavedLoginsInteractor + private lateinit var dropDownMenuAnchorView: View + private lateinit var sortingStrategyMenu: SavedLoginsSortingStrategyMenu + private lateinit var sortingStrategyPopupMenu: BrowserMenu + private lateinit var toolbarChildContainer: FrameLayout + private lateinit var sortLoginsMenuRoot: ConstraintLayout override fun onResume() { super.onResume() @@ -50,7 +61,7 @@ class SavedLoginsFragment : Fragment() { WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE ) - showToolbar(getString(R.string.preferences_passwords_saved_logins)) + initToolbar() } override fun onCreate(savedInstanceState: Bundle?) { @@ -69,11 +80,17 @@ class SavedLoginsFragment : Fragment() { SavedLoginsFragmentState( isLoading = true, items = listOf(), - filteredItems = listOf() + filteredItems = listOf(), + searchedForText = null, + sortingStrategy = requireContext().settings().savedLoginsSortingStrategy, + highlightedItem = requireContext().settings().savedLoginsMenuHighlightedItem ) ) } - savedLoginsInteractor = SavedLoginsInteractor(::itemClicked, ::openLearnMore) + val savedLoginsController: SavedLoginsController = + DefaultSavedLoginsController(savedLoginsStore, requireContext().settings()) + savedLoginsInteractor = + SavedLoginsInteractor(savedLoginsController, ::itemClicked, ::openLearnMore) savedLoginsView = SavedLoginsView(view.savedLoginsLayout, savedLoginsInteractor) loadAndMapLogins() return view @@ -84,6 +101,7 @@ class SavedLoginsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) consumeFrom(savedLoginsStore) { + sortingStrategyMenu.updateMenu(savedLoginsStore.state.highlightedItem) savedLoginsView.update(it) } } @@ -111,6 +129,11 @@ class SavedLoginsFragment : Fragment() { * If we pause this fragment, we want to pop users back to reauth */ override fun onPause() { + toolbarChildContainer.removeAllViews() + toolbarChildContainer.visibility = View.GONE + (activity as HomeActivity).getSupportActionBarAndInflateIfNecessary().setDisplayShowTitleEnabled(true) + sortingStrategyPopupMenu.dismiss() + if (findNavController().currentDestination?.id != R.id.savedLoginSiteInfoFragment) { activity?.let { it.checkAndUpdateScreenshotPermission(it.settings()) } findNavController().popBackStack(R.id.loginsFragment, false) @@ -144,7 +167,7 @@ class SavedLoginsFragment : Fragment() { logins?.let { withContext(Main) { savedLoginsStore.dispatch(SavedLoginsFragmentAction.UpdateLogins(logins.map { item -> - SavedLoginsItem(item.origin, item.username, item.password, item.guid!!) + SavedLoginsItem(item.origin, item.username, item.password, item.guid!!, item.timeLastUsed) })) } } @@ -155,4 +178,67 @@ class SavedLoginsFragment : Fragment() { } } } + + private fun initToolbar() { + showToolbar(getString(R.string.preferences_passwords_saved_logins)) + (activity as HomeActivity).getSupportActionBarAndInflateIfNecessary() + .setDisplayShowTitleEnabled(false) + toolbarChildContainer = initChildContainerFromToolbar() + sortLoginsMenuRoot = inflateSortLoginsMenuRoot() + dropDownMenuAnchorView = sortLoginsMenuRoot.findViewById(R.id.drop_down_menu_anchor_view) + when (requireContext().settings().savedLoginsSortingStrategy) { + is SortingStrategy.Alphabetically -> setupMenu(SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort) + is SortingStrategy.LastUsed -> setupMenu(SavedLoginsSortingStrategyMenu.Item.LastUsedSort) + } + } + + private fun initChildContainerFromToolbar(): FrameLayout { + val activity = activity as? AppCompatActivity + val toolbar = (activity as HomeActivity).findViewById(R.id.navigationToolbar) + + return (toolbar.findViewById(R.id.toolbar_child_container) as FrameLayout).apply { + visibility = View.VISIBLE + } + } + + private fun inflateSortLoginsMenuRoot(): ConstraintLayout { + return LayoutInflater.from(context) + .inflate(R.layout.saved_logins_sort_items_toolbar_child, toolbarChildContainer, true) + .findViewById(R.id.sort_logins_menu_root) + } + + private fun attachMenu() { + sortingStrategyPopupMenu = sortingStrategyMenu.menuBuilder.build(requireContext()) + + sortLoginsMenuRoot.setOnClickListener { + sortLoginsMenuRoot.isActivated = true + sortingStrategyPopupMenu.show( + anchor = dropDownMenuAnchorView, + orientation = BrowserMenu.Orientation.DOWN + ) { + sortLoginsMenuRoot.isActivated = false + } + } + } + + private fun setupMenu(itemToHighlight: SavedLoginsSortingStrategyMenu.Item) { + sortingStrategyMenu = SavedLoginsSortingStrategyMenu(requireContext(), itemToHighlight) { + when (it) { + SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort -> { + savedLoginsInteractor.sort(SortingStrategy.Alphabetically(requireContext().applicationContext)) + } + + SavedLoginsSortingStrategyMenu.Item.LastUsedSort -> { + savedLoginsInteractor.sort(SortingStrategy.LastUsed(requireContext().applicationContext)) + } + } + } + + attachMenu() + } + + companion object { + const val SORTING_STRATEGY_ALPHABETICALLY = "ALPHABETICALLY" + const val SORTING_STRATEGY_LAST_USED = "LAST_USED" + } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsFragmentStore.kt index 68898bf39..864c66257 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsFragmentStore.kt @@ -15,13 +15,16 @@ import mozilla.components.lib.state.Store * @property url Site of the saved login * @property userName Username that's saved for this site * @property password Password that's saved for this site + * @property id The unique identifier for this login entry + * @property timeLastUsed Time of last use in milliseconds from the unix epoch. */ @Parcelize data class SavedLoginsItem( val url: String, val userName: String?, val password: String?, - val id: String + val id: String, + val timeLastUsed: Long ) : Parcelable @@ -40,17 +43,25 @@ class SavedLoginsFragmentStore(initialState: SavedLoginsFragmentState) : sealed class SavedLoginsFragmentAction : Action { data class FilterLogins(val newText: String?) : SavedLoginsFragmentAction() data class UpdateLogins(val list: List) : SavedLoginsFragmentAction() + data class SortLogins(val sortingStrategy: SortingStrategy) : SavedLoginsFragmentAction() } /** * The state for the Saved Logins Screen + * @property isLoading State to know when to show loading * @property items Source of truth of list of logins - * @property items Filtered (or not) list of logins to display + * @property filteredItems Filtered (or not) list of logins to display + * @property searchedForText String used by the user to filter logins + * @property sortingStrategy sorting strategy selected by the user (Currently we support + * sorting alphabetically and by last used) */ data class SavedLoginsFragmentState( val isLoading: Boolean = false, val items: List, - val filteredItems: List + val filteredItems: List, + val searchedForText: String?, + val sortingStrategy: SortingStrategy, + val highlightedItem: SavedLoginsSortingStrategyMenu.Item ) : State /** @@ -61,21 +72,75 @@ private fun savedLoginsStateReducer( action: SavedLoginsFragmentAction ): SavedLoginsFragmentState { return when (action) { - is SavedLoginsFragmentAction.UpdateLogins -> state.copy( + is SavedLoginsFragmentAction.UpdateLogins -> { + filterItems( + state.searchedForText, state.sortingStrategy, state.copy( + isLoading = false, + items = action.list, + filteredItems = emptyList() + ) + ) + } + is SavedLoginsFragmentAction.FilterLogins -> + filterItems( + action.newText, + state.sortingStrategy, + state + ) + is SavedLoginsFragmentAction.SortLogins -> + filterItems( + state.searchedForText, + action.sortingStrategy, + state + ) + } +} + +/** + * @return [SavedLoginsFragmentState] containing a new [SavedLoginsFragmentState.filteredItems] + * with filtered [SavedLoginsFragmentState.items] + * + * @param searchedForText based on which [SavedLoginsFragmentState.items] will be filtered. + * @param sortingStrategy based on which [SavedLoginsFragmentState.items] will be sorted. + * @param state previous [SavedLoginsFragmentState] containing all the other properties + * with which a new state will be created + */ +private fun filterItems( + searchedForText: String?, + sortingStrategy: SortingStrategy, + state: SavedLoginsFragmentState +): SavedLoginsFragmentState { + return if (searchedForText.isNullOrBlank()) { + state.copy( isLoading = false, - items = action.list, - filteredItems = action.list + sortingStrategy = sortingStrategy, + highlightedItem = sortingStrategyToMenuItem(sortingStrategy), + searchedForText = searchedForText, + filteredItems = sortingStrategy(state.items) ) - is SavedLoginsFragmentAction.FilterLogins -> { - if (action.newText.isNullOrBlank()) { - state.copy( - isLoading = false, - filteredItems = state.items) - } else { - state.copy( - isLoading = false, - filteredItems = state.items.filter { it.url.contains(action.newText) }) + } else { + state.copy( + isLoading = false, + sortingStrategy = sortingStrategy, + highlightedItem = sortingStrategyToMenuItem(sortingStrategy), + searchedForText = searchedForText, + filteredItems = sortingStrategy(state.items).filter { + it.url.contains( + searchedForText + ) } + ) + } +} + +private fun sortingStrategyToMenuItem(sortingStrategy: SortingStrategy): SavedLoginsSortingStrategyMenu.Item { + return when (sortingStrategy) { + is SortingStrategy.Alphabetically -> { + SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort + } + + is SortingStrategy.LastUsed -> { + SavedLoginsSortingStrategyMenu.Item.LastUsedSort } } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsInteractor.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsInteractor.kt index 5aa7ae1c5..962cd692b 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsInteractor.kt @@ -9,6 +9,7 @@ package org.mozilla.fenix.settings.logins * Provides implementations for the SavedLoginsViewInteractor */ class SavedLoginsInteractor( + private val savedLoginsController: SavedLoginsController, private val itemClicked: (SavedLoginsItem) -> Unit, private val learnMore: () -> Unit ) : SavedLoginsViewInteractor { @@ -18,4 +19,8 @@ class SavedLoginsInteractor( override fun onLearnMore() { learnMore.invoke() } + + override fun sort(sortingStrategy: SortingStrategy) { + savedLoginsController.handleSort(sortingStrategy) + } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsSortingStrategyMenu.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsSortingStrategyMenu.kt new file mode 100644 index 000000000..8ac54dc81 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsSortingStrategyMenu.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.settings.logins + +import android.content.Context +import mozilla.components.browser.menu.BrowserMenuBuilder +import mozilla.components.browser.menu.item.SimpleBrowserMenuHighlightableItem +import mozilla.components.support.ktx.android.content.getColorFromAttr +import org.mozilla.fenix.R +import org.mozilla.fenix.theme.ThemeManager + +class SavedLoginsSortingStrategyMenu( + private val context: Context, + private val itemToHighlight: Item, + private val onItemTapped: (Item) -> Unit = {} +) { + sealed class Item { + object AlphabeticallySort : Item() + object LastUsedSort : Item() + } + + val menuBuilder by lazy { BrowserMenuBuilder(menuItems) } + + private val menuItems by lazy { + listOfNotNull( + SimpleBrowserMenuHighlightableItem( + label = context.getString(R.string.saved_logins_sort_strategy_alphabetically), + textColorResource = ThemeManager.resolveAttribute(R.attr.primaryText, context), + itemType = Item.AlphabeticallySort, + backgroundTint = context.getColorFromAttr(R.attr.colorControlHighlight), + isHighlighted = { itemToHighlight == Item.AlphabeticallySort } + ) { + onItemTapped.invoke(Item.AlphabeticallySort) + }, + + SimpleBrowserMenuHighlightableItem( + label = context.getString(R.string.saved_logins_sort_strategy_last_used), + textColorResource = ThemeManager.resolveAttribute(R.attr.primaryText, context), + itemType = Item.LastUsedSort, + backgroundTint = context.getColorFromAttr(R.attr.colorControlHighlight), + isHighlighted = { itemToHighlight == Item.LastUsedSort } + ) { + onItemTapped.invoke(Item.LastUsedSort) + } + ) + } + + internal fun updateMenu(itemToHighlight: Item) { + menuItems.forEach { + it.isHighlighted = { itemToHighlight == it.itemType } + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsView.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsView.kt index 42170ae5d..e083168be 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsView.kt @@ -28,6 +28,8 @@ interface SavedLoginsViewInteractor { fun itemClicked(item: SavedLoginsItem) fun onLearnMore() + + fun sort(sortingStrategy: SortingStrategy) } /** @@ -48,6 +50,7 @@ class SavedLoginsView( view.saved_logins_list.apply { adapter = loginsAdapter layoutManager = LinearLayoutManager(containerView.context) + itemAnimator = null } val learnMoreText = view.saved_passwords_empty_learn_more.text.toString() diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SortingStrategy.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SortingStrategy.kt new file mode 100644 index 000000000..2b50c4c89 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SortingStrategy.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.settings.logins + +import android.content.Context +import org.mozilla.fenix.ext.urlToTrimmedHost + +sealed class SortingStrategy { + abstract operator fun invoke(logins: List): List + abstract val appContext: Context + + data class Alphabetically(override val appContext: Context) : SortingStrategy() { + override fun invoke(logins: List): List { + return logins.sortedBy { it.url.urlToTrimmedHost(appContext) } + } + } + + data class LastUsed(override val appContext: Context) : SortingStrategy() { + override fun invoke(logins: List): List { + return logins.sortedByDescending { it.timeLastUsed } + } + } +} 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 6e26340e5..d2558ec46 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -31,6 +31,9 @@ import org.mozilla.fenix.components.metrics.MozillaProductDetector import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitType +import org.mozilla.fenix.settings.logins.SavedLoginsFragment +import org.mozilla.fenix.settings.logins.SavedLoginsSortingStrategyMenu +import org.mozilla.fenix.settings.logins.SortingStrategy import org.mozilla.fenix.settings.registerOnSharedPreferenceChangeListener import java.security.InvalidParameterException @@ -647,4 +650,37 @@ class Settings private constructor( .putBoolean(appContext.getPreferenceKey(R.string.pref_key_enable_new_tab_tray), value) .apply() } + + private var savedLoginsSortingStrategyString by stringPreference( + appContext.getPreferenceKey(R.string.pref_key_saved_logins_sorting_strategy), + default = SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY + ) + + val savedLoginsMenuHighlightedItem: SavedLoginsSortingStrategyMenu.Item + get() { + return when (savedLoginsSortingStrategyString) { + SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY -> { + SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort + } + SavedLoginsFragment.SORTING_STRATEGY_LAST_USED -> { + SavedLoginsSortingStrategyMenu.Item.LastUsedSort + } + else -> SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort + } + } + + var savedLoginsSortingStrategy: SortingStrategy + get() { + return when (savedLoginsSortingStrategyString) { + SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY -> SortingStrategy.Alphabetically(appContext) + SavedLoginsFragment.SORTING_STRATEGY_LAST_USED -> SortingStrategy.LastUsed(appContext) + else -> SortingStrategy.Alphabetically(appContext) + } + } + set(value) { + savedLoginsSortingStrategyString = when (value) { + is SortingStrategy.Alphabetically -> SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY + is SortingStrategy.LastUsed -> SavedLoginsFragment.SORTING_STRATEGY_LAST_USED + } + } } diff --git a/app/src/main/res/layout/navigation_toolbar.xml b/app/src/main/res/layout/navigation_toolbar.xml index 7bf884739..6ec00ed81 100644 --- a/app/src/main/res/layout/navigation_toolbar.xml +++ b/app/src/main/res/layout/navigation_toolbar.xml @@ -13,4 +13,11 @@ app:titleTextAppearance="@style/ToolbarTitleTextStyle" android:background="?foundation" android:elevation="8dp"> - \ No newline at end of file + + + + diff --git a/app/src/main/res/layout/saved_logins_sort_items_toolbar_child.xml b/app/src/main/res/layout/saved_logins_sort_items_toolbar_child.xml new file mode 100644 index 000000000..95ac73643 --- /dev/null +++ b/app/src/main/res/layout/saved_logins_sort_items_toolbar_child.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index b9b063c89..d9de93160 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -147,4 +147,8 @@ 124dp 0dp + + 10dp + 12dp + diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index 0388b54b9..378287639 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -76,6 +76,7 @@ pref_key_fxa_signed_in pref_key_fxa_has_synced_items pref_key_search_widget_installed + pref_key_saved_logins_sorting_strategy pref_key_search_engine_list diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d0ab524a0..0d4d37c76 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1239,6 +1239,12 @@ Zoom on all websites Enable to allow pinch and zoom, even on websites that prevent this gesture. + + Name (A-Z) + + Last used + + Sort logins menu Add search engine