mirror of
https://github.com/android-password-store/Android-Password-Store
synced 2025-09-02 15:25:39 +00:00
Add a switch between Fuzzy and StrictDomain mode to Autofill search view
This commit is contained in:
committed by
Harsh Shandilya
parent
441b4d3b68
commit
7cd6f1d1cf
@@ -111,78 +111,82 @@ class AutofillFilterView : AppCompatActivity() {
|
|||||||
|
|
||||||
supportActionBar?.hide()
|
supportActionBar?.hide()
|
||||||
bindUI()
|
bindUI()
|
||||||
|
updateSearch()
|
||||||
setResult(RESULT_CANCELED)
|
setResult(RESULT_CANCELED)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindUI() {
|
private fun bindUI() {
|
||||||
val recyclerAdapter = SearchableRepositoryAdapter(
|
with(binding) {
|
||||||
R.layout.oreo_autofill_filter_row,
|
rvPassword.apply {
|
||||||
::PasswordViewHolder) { item ->
|
adapter = SearchableRepositoryAdapter(
|
||||||
val file = item.file.relativeTo(item.rootDir)
|
R.layout.oreo_autofill_filter_row,
|
||||||
val pathToIdentifier = directoryStructure.getPathToIdentifierFor(file)
|
::PasswordViewHolder
|
||||||
val identifier = directoryStructure.getIdentifierFor(file)
|
) { item ->
|
||||||
val accountPart = directoryStructure.getAccountPartFor(file)
|
val file = item.file.relativeTo(item.rootDir)
|
||||||
check(identifier != null || accountPart != null) { "At least one of identifier and accountPart should always be non-null" }
|
val pathToIdentifier = directoryStructure.getPathToIdentifierFor(file)
|
||||||
title.text = if (identifier != null) {
|
val identifier = directoryStructure.getIdentifierFor(file)
|
||||||
buildSpannedString {
|
val accountPart = directoryStructure.getAccountPartFor(file)
|
||||||
if (pathToIdentifier != null)
|
check(identifier != null || accountPart != null) { "At least one of identifier and accountPart should always be non-null" }
|
||||||
append("$pathToIdentifier/")
|
title.text = if (identifier != null) {
|
||||||
bold { underline { append(identifier) } }
|
buildSpannedString {
|
||||||
|
if (pathToIdentifier != null)
|
||||||
|
append("$pathToIdentifier/")
|
||||||
|
bold { underline { append(identifier) } }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
accountPart
|
||||||
|
}
|
||||||
|
subtitle.apply {
|
||||||
|
if (identifier != null && accountPart != null) {
|
||||||
|
text = accountPart
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.onItemClicked { _, item ->
|
||||||
|
decryptAndFill(item)
|
||||||
}
|
}
|
||||||
} else {
|
layoutManager = LinearLayoutManager(context)
|
||||||
accountPart
|
|
||||||
}
|
}
|
||||||
subtitle.apply {
|
search.apply {
|
||||||
if (identifier != null && accountPart != null) {
|
val initialSearch =
|
||||||
text = accountPart
|
formOrigin.getPrettyIdentifier(applicationContext, untrusted = false)
|
||||||
visibility = View.VISIBLE
|
setText(initialSearch, TextView.BufferType.EDITABLE)
|
||||||
} else {
|
addTextChangedListener { updateSearch() }
|
||||||
visibility = View.GONE
|
}
|
||||||
|
strictDomainSearch.apply {
|
||||||
|
visibility = if (formOrigin is FormOrigin.Web) View.VISIBLE else View.GONE
|
||||||
|
isChecked = formOrigin is FormOrigin.Web
|
||||||
|
setOnCheckedChangeListener { _, _ -> updateSearch() }
|
||||||
|
}
|
||||||
|
shouldMatch.text = getString(
|
||||||
|
R.string.oreo_autofill_match_with,
|
||||||
|
formOrigin.getPrettyIdentifier(applicationContext)
|
||||||
|
)
|
||||||
|
model.searchResult.observe(this@AutofillFilterView) { result ->
|
||||||
|
val list = result.passwordItems
|
||||||
|
(rvPassword.adapter as SearchableRepositoryAdapter).submitList(list) {
|
||||||
|
rvPassword.scrollToPosition(0)
|
||||||
|
}
|
||||||
|
// Switch RecyclerView out for a "no results" message if the new list is empty and
|
||||||
|
// the message is not yet shown (and vice versa).
|
||||||
|
if ((list.isEmpty() && rvPasswordSwitcher.nextView.id == rvPasswordEmpty.id) ||
|
||||||
|
(list.isNotEmpty() && rvPasswordSwitcher.nextView.id == rvPassword.id)
|
||||||
|
) {
|
||||||
|
rvPasswordSwitcher.showNext()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onItemClicked { _, item ->
|
|
||||||
decryptAndFill(item)
|
|
||||||
}
|
|
||||||
binding.rvPassword.apply {
|
|
||||||
adapter = recyclerAdapter
|
|
||||||
layoutManager = LinearLayoutManager(context)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val initialFilter = formOrigin.getPrettyIdentifier(applicationContext, untrusted = false)
|
private fun updateSearch() {
|
||||||
binding.search.setText(initialFilter, TextView.BufferType.EDITABLE)
|
|
||||||
val filterMode =
|
|
||||||
if (formOrigin is FormOrigin.Web) FilterMode.StrictDomain else FilterMode.Fuzzy
|
|
||||||
model.search(
|
model.search(
|
||||||
initialFilter,
|
binding.search.text.toString().trim(),
|
||||||
filterMode = filterMode,
|
filterMode = if (binding.strictDomainSearch.isChecked) FilterMode.StrictDomain else FilterMode.Fuzzy,
|
||||||
searchMode = SearchMode.RecursivelyInSubdirectories,
|
searchMode = SearchMode.RecursivelyInSubdirectories,
|
||||||
listMode = ListMode.FilesOnly
|
listMode = ListMode.FilesOnly
|
||||||
)
|
)
|
||||||
binding.search.addTextChangedListener {
|
|
||||||
model.search(
|
|
||||||
it.toString().trim(),
|
|
||||||
filterMode = FilterMode.Fuzzy,
|
|
||||||
searchMode = SearchMode.RecursivelyInSubdirectories,
|
|
||||||
listMode = ListMode.FilesOnly
|
|
||||||
)
|
|
||||||
}
|
|
||||||
model.searchResult.observe(this) { result ->
|
|
||||||
val list = result.passwordItems
|
|
||||||
recyclerAdapter.submitList(list) {
|
|
||||||
binding.rvPassword.scrollToPosition(0)
|
|
||||||
}
|
|
||||||
// Switch RecyclerView out for a "no results" message if the new list is empty and
|
|
||||||
// the message is not yet shown (and vice versa).
|
|
||||||
if ((list.isEmpty() && binding.rvPasswordSwitcher.nextView.id == binding.rvPasswordEmpty.id) ||
|
|
||||||
(list.isNotEmpty() && binding.rvPasswordSwitcher.nextView.id == binding.rvPassword.id)
|
|
||||||
)
|
|
||||||
binding.rvPasswordSwitcher.showNext()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.shouldMatch.text = getString(
|
|
||||||
R.string.oreo_autofill_match_with,
|
|
||||||
formOrigin.getPrettyIdentifier(applicationContext)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decryptAndFill(item: PasswordItem) {
|
private fun decryptAndFill(item: PasswordItem) {
|
||||||
|
@@ -46,7 +46,7 @@
|
|||||||
android:id="@+id/rvPasswordSwitcher"
|
android:id="@+id/rvPasswordSwitcher"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_marginTop="@dimen/activity_vertical_margin"
|
android:layout_marginTop="@dimen/activity_vertical_margin"
|
||||||
app:layout_constraintBottom_toTopOf="@id/shouldMatch"
|
app:layout_constraintBottom_toTopOf="@id/strictDomainSearch"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/searchLayout"
|
app:layout_constraintTop_toBottomOf="@id/searchLayout"
|
||||||
@@ -71,6 +71,21 @@
|
|||||||
|
|
||||||
</ViewSwitcher>
|
</ViewSwitcher>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/strictDomainSearch"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||||
|
android:layout_marginTop="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_marginEnd="@dimen/activity_horizontal_margin"
|
||||||
|
android:text="@string/oreo_autofill_strict_domain_search"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/shouldMatch"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/rvPasswordSwitcher"
|
||||||
|
app:layout_constraintVertical_bias="1.0"
|
||||||
|
tools:text="Phishing-resistant search"/>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
android:id="@+id/shouldMatch"
|
android:id="@+id/shouldMatch"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@@ -82,7 +97,7 @@
|
|||||||
app:layout_constraintBottom_toTopOf="@id/shouldClear"
|
app:layout_constraintBottom_toTopOf="@id/shouldClear"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/rvPasswordSwitcher"
|
app:layout_constraintTop_toBottomOf="@id/strictDomainSearch"
|
||||||
app:layout_constraintVertical_bias="1.0"
|
app:layout_constraintVertical_bias="1.0"
|
||||||
tools:text="Match with example.org" />
|
tools:text="Match with example.org" />
|
||||||
|
|
||||||
|
@@ -250,6 +250,7 @@
|
|||||||
<string name="folder_icon_hint">Folder icon</string>
|
<string name="folder_icon_hint">Folder icon</string>
|
||||||
|
|
||||||
<!-- Oreo Autofill -->
|
<!-- Oreo Autofill -->
|
||||||
|
<string name="oreo_autofill_strict_domain_search">Phishing-resistant search</string>
|
||||||
<string name="oreo_autofill_match_with">Match with %1$s</string>
|
<string name="oreo_autofill_match_with">Match with %1$s</string>
|
||||||
<string name="oreo_autofill_matches_clear_existing">Clear existing matches</string>
|
<string name="oreo_autofill_matches_clear_existing">Clear existing matches</string>
|
||||||
<string name="oreo_autofill_filter_no_results">No results.</string>
|
<string name="oreo_autofill_filter_no_results">No results.</string>
|
||||||
|
Reference in New Issue
Block a user