mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
@@ -819,6 +819,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
searchFiltersView?.setNumbers(group.numberOfChildEntries)
|
||||
searchFiltersView?.setCurrentGroupText(mMainGroup?.title ?: "")
|
||||
searchFiltersView?.availableOther(mDatabase?.allowEntryCustomFields() ?: false)
|
||||
searchFiltersView?.availableApplicationIds(mDatabase?.allowEntryCustomFields() ?: false)
|
||||
searchFiltersView?.availableTags(mDatabase?.allowTags() ?: false)
|
||||
searchFiltersView?.enableTags(mDatabase?.tagPool?.isNotEmpty() ?: false)
|
||||
searchFiltersView?.availableSearchableGroup(mDatabase?.allowCustomSearchableGroup() ?: false)
|
||||
@@ -1273,7 +1274,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
searchView = it.actionView as SearchView?
|
||||
searchView?.apply {
|
||||
setOnQueryTextFocusChangeListener(mOnSearchTextFocusChangeListener)
|
||||
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager?
|
||||
val searchManager = getSystemService(SEARCH_SERVICE) as SearchManager?
|
||||
(searchManager?.getSearchableInfo(
|
||||
ComponentName(this@GroupActivity, GroupActivity::class.java)
|
||||
))?.let { searchableInfo ->
|
||||
|
||||
@@ -45,7 +45,7 @@ import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseExcept
|
||||
import com.kunzisoft.keepass.database.helper.SearchHelper
|
||||
import com.kunzisoft.keepass.model.RegisterInfo
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.utils.AppUtil
|
||||
import com.kunzisoft.keepass.utils.AppUtil.getConcreteWebDomain
|
||||
import com.kunzisoft.keepass.utils.getParcelableCompat
|
||||
import com.kunzisoft.keepass.utils.getParcelableExtraCompat
|
||||
import com.kunzisoft.keepass.view.toastError
|
||||
@@ -81,10 +81,7 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
||||
}
|
||||
// Build search param
|
||||
bundle.getParcelableCompat<SearchInfo>(KEY_SEARCH_INFO)?.let { searchInfo ->
|
||||
AppUtil.getConcreteWebDomain(
|
||||
this,
|
||||
searchInfo.webDomain
|
||||
) { concreteWebDomain ->
|
||||
searchInfo.getConcreteWebDomain(this) { concreteWebDomain ->
|
||||
// Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE)
|
||||
val assistStructure = AutofillHelper
|
||||
.retrieveAutofillComponent(intent)
|
||||
@@ -111,7 +108,7 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
||||
KEY_REGISTER_INFO
|
||||
)
|
||||
val searchInfo = SearchInfo(registerInfo?.searchInfo)
|
||||
AppUtil.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
||||
searchInfo.getConcreteWebDomain(this) { concreteWebDomain ->
|
||||
searchInfo.webDomain = concreteWebDomain
|
||||
launchRegistration(database, searchInfo, registerInfo)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseExcept
|
||||
import com.kunzisoft.keepass.database.helper.SearchHelper
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.otp.OtpEntryFields
|
||||
import com.kunzisoft.keepass.utils.AppUtil
|
||||
import com.kunzisoft.keepass.utils.AppUtil.getConcreteWebDomain
|
||||
import com.kunzisoft.keepass.utils.KeyboardUtil.isKeyboardActivatedInSettings
|
||||
import com.kunzisoft.keepass.utils.getParcelableCompat
|
||||
import com.kunzisoft.keepass.view.toastError
|
||||
@@ -109,8 +109,7 @@ class EntrySelectionLauncherActivity : DatabaseModeActivity() {
|
||||
this.webDomain = sharedWebDomain
|
||||
this.otpString = otpString
|
||||
}
|
||||
|
||||
AppUtil.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
||||
searchInfo.getConcreteWebDomain(this) { concreteWebDomain ->
|
||||
searchInfo.webDomain = concreteWebDomain
|
||||
launch(database, searchInfo)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ import com.kunzisoft.keepass.model.RegisterInfo
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.settings.AutofillSettingsActivity
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.utils.AppUtil
|
||||
import com.kunzisoft.keepass.utils.AppUtil.getConcreteWebDomain
|
||||
import org.joda.time.DateTime
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ class KeeAutofillService : AutofillService() {
|
||||
webDomain = parseResult.webDomain
|
||||
webScheme = parseResult.webScheme
|
||||
}
|
||||
AppUtil.getConcreteWebDomain(this, searchInfo.webDomain) { webDomainWithoutSubDomain ->
|
||||
searchInfo.getConcreteWebDomain(this) { webDomainWithoutSubDomain ->
|
||||
searchInfo.webDomain = webDomainWithoutSubDomain
|
||||
val inlineSuggestionsRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
&& autofillInlineSuggestionsEnabled) {
|
||||
|
||||
@@ -93,8 +93,6 @@ class PasskeyProviderService : CredentialProviderService() {
|
||||
private fun buildPasskeySearchInfo(relyingParty: String): SearchInfo {
|
||||
return SearchInfo().apply {
|
||||
this.relyingParty = relyingParty
|
||||
this.isAPasskeySearch = true
|
||||
this.query = relyingParty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,10 @@ package com.kunzisoft.keepass.database.helper
|
||||
|
||||
import android.content.Context
|
||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||
import com.kunzisoft.keepass.database.search.SearchParameters
|
||||
import com.kunzisoft.keepass.model.EntryInfo
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil.searchSubDomains
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
|
||||
object SearchHelper {
|
||||
@@ -61,8 +63,28 @@ object SearchHelper {
|
||||
&& !searchInfo.containsOnlyNullValues()) {
|
||||
// If search provide results
|
||||
database.createVirtualGroupFromSearchInfo(
|
||||
searchInfo,
|
||||
MAX_SEARCH_ENTRY
|
||||
searchParameters = SearchParameters().apply {
|
||||
searchQuery = searchInfo.toString()
|
||||
allowEmptyQuery = false
|
||||
searchInTitles = false
|
||||
searchInUsernames = false
|
||||
searchInPasswords = false
|
||||
searchInAppIds = searchInfo.isAppIdSearch
|
||||
searchInUrls = searchInfo.isDomainSearch
|
||||
searchByDomain = true
|
||||
searchBySubDomain = searchSubDomains(context)
|
||||
searchInRelyingParty = searchInfo.isPasskeySearch
|
||||
searchInNotes = false
|
||||
searchInOTP = searchInfo.isOTPSearch
|
||||
searchInOther = false
|
||||
searchInUUIDs = false
|
||||
searchInTags = searchInfo.isTagSearch
|
||||
searchInCurrentGroup = false
|
||||
searchInSearchableGroup = true
|
||||
searchInRecycleBin = false
|
||||
searchInTemplates = false
|
||||
},
|
||||
max = MAX_SEARCH_ENTRY
|
||||
)?.let { searchGroup ->
|
||||
if (searchGroup.numberOfChildEntries > 0) {
|
||||
searchWithoutUI = true
|
||||
|
||||
@@ -108,7 +108,7 @@ object PreferencesUtil {
|
||||
context.resources.getBoolean(R.bool.auto_focus_search_default))
|
||||
}
|
||||
|
||||
fun searchSubdomains(context: Context): Boolean {
|
||||
fun searchSubDomains(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.subdomain_search_key),
|
||||
context.resources.getBoolean(R.bool.subdomain_search_default))
|
||||
@@ -352,6 +352,8 @@ object PreferencesUtil {
|
||||
context.resources.getBoolean(R.bool.search_option_username_default))
|
||||
searchInPasswords = prefs.getBoolean(context.getString(R.string.search_option_password_key),
|
||||
context.resources.getBoolean(R.bool.search_option_password_default))
|
||||
searchInAppIds = prefs.getBoolean(context.getString(R.string.search_option_application_id_key),
|
||||
context.resources.getBoolean(R.bool.search_option_application_id_default))
|
||||
searchInUrls = prefs.getBoolean(context.getString(R.string.search_option_url_key),
|
||||
context.resources.getBoolean(R.bool.search_option_url_default))
|
||||
searchInExpired = prefs.getBoolean(context.getString(R.string.search_option_expired_key),
|
||||
@@ -389,6 +391,8 @@ object PreferencesUtil {
|
||||
searchParameters.searchInUsernames)
|
||||
putBoolean(context.getString(R.string.search_option_password_key),
|
||||
searchParameters.searchInPasswords)
|
||||
putBoolean(context.getString(R.string.search_option_application_id_key),
|
||||
searchParameters.searchInAppIds)
|
||||
putBoolean(context.getString(R.string.search_option_url_key),
|
||||
searchParameters.searchInUrls)
|
||||
putBoolean(context.getString(R.string.search_option_expired_key),
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
|
||||
|
||||
object AppUtil {
|
||||
@@ -82,22 +83,31 @@ object AppUtil {
|
||||
/**
|
||||
* Get the concrete web domain AKA without sub domain if needed
|
||||
*/
|
||||
fun getConcreteWebDomain(context: Context,
|
||||
webDomain: String?,
|
||||
concreteWebDomain: (String?) -> Unit) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
if (webDomain != null) {
|
||||
fun SearchInfo.getConcreteWebDomain(
|
||||
context: Context,
|
||||
concreteWebDomain: (String?) -> Unit
|
||||
) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val domain = webDomain
|
||||
if (domain != null) {
|
||||
// Warning, web domain can contains IP, don't crop in this case
|
||||
if (PreferencesUtil.searchSubdomains(context)
|
||||
|| Regex(SearchInfo.WEB_IP_REGEX).matches(webDomain)) {
|
||||
concreteWebDomain.invoke(webDomain)
|
||||
if (PreferencesUtil.searchSubDomains(context)
|
||||
|| Regex(SearchInfo.WEB_IP_REGEX).matches(domain)) {
|
||||
withContext(Dispatchers.Main) {
|
||||
concreteWebDomain.invoke(webDomain)
|
||||
}
|
||||
} else {
|
||||
val publicSuffixList = PublicSuffixList(context)
|
||||
concreteWebDomain.invoke(publicSuffixList
|
||||
.getPublicSuffixPlusOne(webDomain).await())
|
||||
val publicSuffix = publicSuffixList
|
||||
.getPublicSuffixPlusOne(domain).await()
|
||||
withContext(Dispatchers.Main) {
|
||||
concreteWebDomain.invoke(publicSuffix)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
concreteWebDomain.invoke(null)
|
||||
withContext(Dispatchers.Main) {
|
||||
concreteWebDomain.invoke(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.kunzisoft.keepass.view
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.ImageView
|
||||
@@ -30,8 +29,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
private var searchTitle: CompoundButton
|
||||
private var searchUsername: CompoundButton
|
||||
private var searchPassword: CompoundButton
|
||||
private var searchApplicationId: CompoundButton
|
||||
private var searchURL: CompoundButton
|
||||
private var searchByURLDomain: Boolean = false
|
||||
private var searchByURLSubDomain: Boolean = false
|
||||
private var searchExpired: CompoundButton
|
||||
private var searchNotes: CompoundButton
|
||||
private var searchOther: CompoundButton
|
||||
@@ -50,8 +51,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
this.searchInTitles = searchTitle.isChecked
|
||||
this.searchInUsernames = searchUsername.isChecked
|
||||
this.searchInPasswords = searchPassword.isChecked
|
||||
this.searchInAppIds = searchApplicationId.isChecked
|
||||
this.searchInUrls = searchURL.isChecked
|
||||
this.searchByDomain = searchByURLDomain
|
||||
this.searchBySubDomain = searchByURLSubDomain
|
||||
this.searchInExpired = searchExpired.isChecked
|
||||
this.searchInNotes = searchNotes.isChecked
|
||||
this.searchInOther = searchOther.isChecked
|
||||
@@ -71,8 +74,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
searchTitle.isChecked = value.searchInTitles
|
||||
searchUsername.isChecked = value.searchInUsernames
|
||||
searchPassword.isChecked = value.searchInPasswords
|
||||
searchApplicationId.isChecked = value.searchInAppIds
|
||||
searchURL.isChecked = value.searchInUrls
|
||||
searchByURLDomain = value.searchByDomain
|
||||
searchByURLSubDomain = value.searchBySubDomain
|
||||
searchExpired.isChecked = value.searchInExpired
|
||||
searchNotes.isChecked = value.searchInNotes
|
||||
searchOther.isChecked = value.searchInOther
|
||||
@@ -87,7 +92,7 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
var onParametersChangeListener: ((searchParameters: SearchParameters) -> Unit)? = null
|
||||
private var mOnParametersChangeListener: ((searchParameters: SearchParameters) -> Unit)? = {
|
||||
// To recalculate height
|
||||
if (searchAdvanceFiltersContainer?.visibility == View.VISIBLE) {
|
||||
if (searchAdvanceFiltersContainer?.visibility == VISIBLE) {
|
||||
searchAdvanceFiltersContainer?.expand(
|
||||
false,
|
||||
searchAdvanceFiltersContainer?.getFullHeight()
|
||||
@@ -110,6 +115,7 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
searchTitle = findViewById(R.id.search_chip_title)
|
||||
searchUsername = findViewById(R.id.search_chip_username)
|
||||
searchPassword = findViewById(R.id.search_chip_password)
|
||||
searchApplicationId = findViewById(R.id.search_chip_application_id)
|
||||
searchURL = findViewById(R.id.search_chip_url)
|
||||
searchExpired = findViewById(R.id.search_chip_expires)
|
||||
searchNotes = findViewById(R.id.search_chip_note)
|
||||
@@ -125,7 +131,7 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
|
||||
// Expand menu with button
|
||||
searchExpandButton.setOnClickListener {
|
||||
val isVisible = searchAdvanceFiltersContainer?.visibility == View.VISIBLE
|
||||
val isVisible = searchAdvanceFiltersContainer?.visibility == VISIBLE
|
||||
if (isVisible)
|
||||
closeAdvancedFilters()
|
||||
else
|
||||
@@ -156,6 +162,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
searchParameters.searchInPasswords = isChecked
|
||||
mOnParametersChangeListener?.invoke(searchParameters)
|
||||
}
|
||||
searchApplicationId.setOnCheckedChangeListener { _, isChecked ->
|
||||
searchParameters.searchInAppIds = isChecked
|
||||
mOnParametersChangeListener?.invoke(searchParameters)
|
||||
}
|
||||
searchURL.setOnCheckedChangeListener { _, isChecked ->
|
||||
searchParameters.searchInUrls = isChecked
|
||||
mOnParametersChangeListener?.invoke(searchParameters)
|
||||
@@ -213,6 +223,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
searchOther.isVisible = available
|
||||
}
|
||||
|
||||
fun availableApplicationIds(available: Boolean) {
|
||||
searchApplicationId.isVisible = available
|
||||
}
|
||||
|
||||
fun availableTags(available: Boolean) {
|
||||
searchTag.isVisible = available
|
||||
}
|
||||
@@ -245,14 +259,14 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
|
||||
override fun setVisibility(visibility: Int) {
|
||||
when (visibility) {
|
||||
View.VISIBLE -> {
|
||||
searchAdvanceFiltersContainer?.visibility = View.GONE
|
||||
VISIBLE -> {
|
||||
searchAdvanceFiltersContainer?.visibility = GONE
|
||||
searchContainer.showByFading()
|
||||
}
|
||||
else -> {
|
||||
searchContainer.hideByFading()
|
||||
if (searchAdvanceFiltersContainer?.visibility == View.VISIBLE) {
|
||||
searchAdvanceFiltersContainer?.visibility = View.INVISIBLE
|
||||
if (searchAdvanceFiltersContainer?.visibility == VISIBLE) {
|
||||
searchAdvanceFiltersContainer?.visibility = INVISIBLE
|
||||
searchAdvanceFiltersContainer?.collapse()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,13 @@
|
||||
android:checked="false"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:text="@string/entry_password"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/search_chip_application_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:text="@string/entry_application_id"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/search_chip_url"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -247,6 +247,8 @@
|
||||
<bool name="search_option_username_default" translatable="false">true</bool>
|
||||
<string name="search_option_password_key" translatable="false">search_option_password_key</string>
|
||||
<bool name="search_option_password_default" translatable="false">false</bool>
|
||||
<string name="search_option_application_id_key" translatable="false">search_option_application_id_key</string>
|
||||
<bool name="search_option_application_id_default" translatable="false">false</bool>
|
||||
<string name="search_option_url_key" translatable="false">search_option_url_key</string>
|
||||
<bool name="search_option_url_default" translatable="false">true</bool>
|
||||
<string name="search_option_expired_key" translatable="false">search_option_expired_key</string>
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
<string name="auto_type_sequence">Auto-Type sequence</string>
|
||||
<string name="entry_not_found">Could not find entry data.</string>
|
||||
<string name="entry_password">Password</string>
|
||||
<string name="entry_application_id">App Id</string>
|
||||
<string name="tags">Tags</string>
|
||||
<string name="custom_data">Custom data</string>
|
||||
<string name="save">Save</string>
|
||||
|
||||
Reference in New Issue
Block a user