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.master
parent
2e45244b6c
commit
24ba9f2fc8
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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<Toolbar>(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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<SavedLoginsItem>) : 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<SavedLoginsItem>,
|
||||
val filteredItems: List<SavedLoginsItem>
|
||||
val filteredItems: List<SavedLoginsItem>,
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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<SavedLoginsItem>): List<SavedLoginsItem>
|
||||
abstract val appContext: Context
|
||||
|
||||
data class Alphabetically(override val appContext: Context) : SortingStrategy() {
|
||||
override fun invoke(logins: List<SavedLoginsItem>): List<SavedLoginsItem> {
|
||||
return logins.sortedBy { it.url.urlToTrimmedHost(appContext) }
|
||||
}
|
||||
}
|
||||
|
||||
data class LastUsed(override val appContext: Context) : SortingStrategy() {
|
||||
override fun invoke(logins: List<SavedLoginsItem>): List<SavedLoginsItem> {
|
||||
return logins.sortedByDescending { it.timeLastUsed }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,11 @@
|
|||
app:titleTextAppearance="@style/ToolbarTitleTextStyle"
|
||||
android:background="?foundation"
|
||||
android:elevation="8dp">
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/toolbar_child_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/sort_logins_menu_root"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/toolbar_title"
|
||||
style="@style/ToolbarTitleTextStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preferences_passwords_saved_logins"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/toolbar_chevron_icon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/drop_down_menu_anchor_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_below="@id/toolbar_title"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar_title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/toolbar_chevron_icon"
|
||||
android:layout_width="@dimen/saved_logins_sort_menu_dropdown_chevron_icon_size"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="@dimen/saved_logins_sort_menu_dropdown_chevron_icon_margin_start"
|
||||
android:layout_toEndOf="@+id/toolbar_title"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@string/saved_logins_menu_dropdown_chevron_icon_content_description"
|
||||
app:srcCompat="@drawable/ic_chevron"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/toolbar_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/toolbar_title"
|
||||
app:layout_constraintTop_toTopOf="@+id/toolbar_title" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -147,4 +147,8 @@
|
|||
<dimen name="tab_tray_divider_margin_start">124dp</dimen>
|
||||
<dimen name="tab_tray_divider_margin_end">0dp</dimen>
|
||||
|
||||
<!-- Saved Logins Fragment -->
|
||||
<dimen name="saved_logins_sort_menu_dropdown_chevron_icon_margin_start">10dp</dimen>
|
||||
<dimen name="saved_logins_sort_menu_dropdown_chevron_icon_size">12dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
<string name="pref_key_fxa_signed_in" translatable="false">pref_key_fxa_signed_in</string>
|
||||
<string name="pref_key_fxa_has_synced_items" translatable="false">pref_key_fxa_has_synced_items</string>
|
||||
<string name="pref_key_search_widget_installed" translatable="false">pref_key_search_widget_installed</string>
|
||||
<string name="pref_key_saved_logins_sorting_strategy" translatable="false">pref_key_saved_logins_sorting_strategy</string>
|
||||
|
||||
<!-- Search Settings -->
|
||||
<string name="pref_key_search_engine_list" translatable="false">pref_key_search_engine_list</string>
|
||||
|
|
|
@ -1239,6 +1239,12 @@
|
|||
<string name="preference_accessibility_force_enable_zoom">Zoom on all websites</string>
|
||||
<!-- Summary for Accessibility Force Enable Zoom Preference -->
|
||||
<string name="preference_accessibility_force_enable_zoom_summary">Enable to allow pinch and zoom, even on websites that prevent this gesture.</string>
|
||||
<!-- Saved logins sorting strategy menu item -by name- (if selected, it will sort saved logins alphabetically) -->
|
||||
<string name="saved_logins_sort_strategy_alphabetically">Name (A-Z)</string>
|
||||
<!-- Saved logins sorting strategy menu item -by last used- (if selected, it will sort saved logins by last used) -->
|
||||
<string name="saved_logins_sort_strategy_last_used">Last used</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Sort saved logins dropdown menu chevron icon -->
|
||||
<string name="saved_logins_menu_dropdown_chevron_icon_content_description">Sort logins menu</string>
|
||||
|
||||
<!-- Title of the Add search engine screen -->
|
||||
<string name="search_engine_add_custom_search_engine_title">Add search engine</string>
|
||||
|
|
Loading…
Reference in New Issue