1
0
Fork 0

For #7087 - Add SearchView to Logins Fragment

master
ekager 2020-03-06 00:12:55 -08:00 committed by Emily Kager
parent d5f4444447
commit 709bf6f627
6 changed files with 73 additions and 8 deletions

View File

@ -6,9 +6,13 @@ package org.mozilla.fenix.settings.logins
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.EditorInfo
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -47,6 +51,11 @@ class SavedLoginsFragment : Fragment() {
showToolbar(getString(R.string.preferences_passwords_saved_logins)) showToolbar(getString(R.string.preferences_passwords_saved_logins))
} }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -56,7 +65,8 @@ class SavedLoginsFragment : Fragment() {
savedLoginsStore = StoreProvider.get(this) { savedLoginsStore = StoreProvider.get(this) {
SavedLoginsFragmentStore( SavedLoginsFragmentStore(
SavedLoginsFragmentState( SavedLoginsFragmentState(
items = listOf() items = listOf(),
filteredItems = listOf()
) )
) )
} }
@ -75,6 +85,25 @@ class SavedLoginsFragment : Fragment() {
} }
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.login_list, menu)
val searchItem = menu.findItem(R.id.search)
val searchView: SearchView = searchItem.actionView as SearchView
searchView.imeOptions = EditorInfo.IME_ACTION_DONE
searchView.queryHint = getString(R.string.preferences_passwords_saved_logins_search)
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
savedLoginsStore.dispatch(SavedLoginsFragmentAction.FilterLogins(newText))
return false
}
})
}
/** /**
* If we pause this fragment, we want to pop users back to reauth * If we pause this fragment, we want to pop users back to reauth
*/ */

View File

@ -38,14 +38,19 @@ class SavedLoginsFragmentStore(initialState: SavedLoginsFragmentState) :
* Actions to dispatch through the `SavedLoginsStore` to modify `SavedLoginsFragmentState` through the reducer. * Actions to dispatch through the `SavedLoginsStore` to modify `SavedLoginsFragmentState` through the reducer.
*/ */
sealed class SavedLoginsFragmentAction : Action { sealed class SavedLoginsFragmentAction : Action {
data class FilterLogins(val newText: String?) : SavedLoginsFragmentAction()
data class UpdateLogins(val list: List<SavedLoginsItem>) : SavedLoginsFragmentAction() data class UpdateLogins(val list: List<SavedLoginsItem>) : SavedLoginsFragmentAction()
} }
/** /**
* The state for the Saved Logins Screen * The state for the Saved Logins Screen
* @property items List of logins to display * @property items Source of truth of list of logins
* @property items Filtered (or not) list of logins to display
*/ */
data class SavedLoginsFragmentState(val items: List<SavedLoginsItem>) : State data class SavedLoginsFragmentState(
val items: List<SavedLoginsItem>,
val filteredItems: List<SavedLoginsItem>
) : State
/** /**
* The SavedLoginsState Reducer. * The SavedLoginsState Reducer.
@ -55,6 +60,16 @@ private fun savedLoginsStateReducer(
action: SavedLoginsFragmentAction action: SavedLoginsFragmentAction
): SavedLoginsFragmentState { ): SavedLoginsFragmentState {
return when (action) { return when (action) {
is SavedLoginsFragmentAction.UpdateLogins -> state.copy(items = action.list) is SavedLoginsFragmentAction.UpdateLogins -> state.copy(
items = action.list,
filteredItems = action.list
)
is SavedLoginsFragmentAction.FilterLogins -> {
if (action.newText.isNullOrBlank()) {
state.copy(filteredItems = state.items)
} else {
state.copy(filteredItems = state.items.filter { it.url.contains(action.newText) })
}
}
} }
} }

View File

@ -71,6 +71,6 @@ class SavedLoginsView(
fun update(state: SavedLoginsFragmentState) { fun update(state: SavedLoginsFragmentState) {
view.saved_logins_list.isVisible = state.items.isNotEmpty() view.saved_logins_list.isVisible = state.items.isNotEmpty()
view.saved_passwords_empty_view.isVisible = state.items.isEmpty() view.saved_passwords_empty_view.isVisible = state.items.isEmpty()
loginsAdapter.submitList(state.items) loginsAdapter.submitList(state.filteredItems)
} }
} }

View File

@ -9,7 +9,7 @@
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:minHeight="?android:attr/listPreferredItemHeightSmall"> android:minHeight="?android:attr/listPreferredItemHeight">
<ImageView <ImageView
android:id="@+id/favicon_image" android:id="@+id/favicon_image"
@ -28,7 +28,6 @@
android:id="@+id/domainView" android:id="@+id/domainView"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:layout_weight="1" android:layout_weight="1"
android:ellipsize="middle" android:ellipsize="middle"
@ -37,9 +36,11 @@
android:singleLine="true" android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem" android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?primaryText" android:textColor="?primaryText"
app:layout_constraintBottom_toTopOf="@id/userView"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/favicon_image" app:layout_constraintStart_toEndOf="@+id/favicon_image"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="mozilla.org" /> tools:text="mozilla.org" />
<TextView <TextView
@ -47,7 +48,6 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:layout_marginBottom="4dp"
android:layout_weight="1" android:layout_weight="1"
android:ellipsize="middle" android:ellipsize="middle"
android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingStart="?android:attr/listPreferredItemPaddingStart"
@ -58,5 +58,6 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/favicon_image" app:layout_constraintStart_toEndOf="@+id/favicon_image"
app:layout_constraintTop_toBottomOf="@id/domainView" app:layout_constraintTop_toBottomOf="@id/domainView"
app:layout_constraintVertical_chainStyle="packed"
tools:text="mozilla.org" /> tools:text="mozilla.org" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,14 @@
<?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/. -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/search"
android:icon="@drawable/ic_search"
android:title="@string/preferences_passwords_saved_logins_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:iconTint="?primaryText"
android:contentDescription="@string/preferences_passwords_saved_logins_search"
app:showAsAction="ifRoom|collapseActionView" />
</menu>

View File

@ -6,6 +6,7 @@
<style name="NormalThemeBase" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge"> <style name="NormalThemeBase" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
<!-- Android system styling --> <!-- Android system styling -->
<item name="searchViewStyle">@style/SearchViewStyle</item>
<item name="android:windowContentTransitions">true</item> <item name="android:windowContentTransitions">true</item>
<item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item> <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
<item name="android:progressBarStyleHorizontal">@style/progressBarStyleHorizontal</item> <item name="android:progressBarStyleHorizontal">@style/progressBarStyleHorizontal</item>
@ -101,6 +102,7 @@
<style name="PrivateThemeBase" parent="Theme.MaterialComponents.NoActionBar.Bridge"> <style name="PrivateThemeBase" parent="Theme.MaterialComponents.NoActionBar.Bridge">
<!-- Android system styling --> <!-- Android system styling -->
<item name="searchViewStyle">@style/SearchViewStyle</item>
<item name="android:windowContentTransitions">true</item> <item name="android:windowContentTransitions">true</item>
<item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item> <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
<item name="android:progressBarStyleHorizontal">@style/progressBarStyleHorizontal</item> <item name="android:progressBarStyleHorizontal">@style/progressBarStyleHorizontal</item>
@ -416,4 +418,8 @@
<item name="android:windowIsFloating">true</item> <item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item> <item name="android:backgroundDimEnabled">false</item>
</style> </style>
<style name="SearchViewStyle" parent="Widget.AppCompat.SearchView">
<item name="searchHintIcon">@null</item>
</style>
</resources> </resources>