mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
@@ -4,6 +4,7 @@ KeePassDX(4.2.0)
|
|||||||
* Dialog to manage missing signature #2152 #2155 #2161 #2160
|
* Dialog to manage missing signature #2152 #2155 #2161 #2160
|
||||||
* Capture error #2159
|
* Capture error #2159
|
||||||
* Change Passkey Backup Eligibility & Backup State #2135 #2150
|
* Change Passkey Backup Eligibility & Backup State #2135 #2150
|
||||||
|
* Search settings #2112 #2181
|
||||||
* Small fixes #2157 #2164 #2171
|
* Small fixes #2157 #2164 #2171
|
||||||
|
|
||||||
KeePassDX(4.1.8)
|
KeePassDX(4.1.8)
|
||||||
|
|||||||
@@ -819,6 +819,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
searchFiltersView?.setNumbers(group.numberOfChildEntries)
|
searchFiltersView?.setNumbers(group.numberOfChildEntries)
|
||||||
searchFiltersView?.setCurrentGroupText(mMainGroup?.title ?: "")
|
searchFiltersView?.setCurrentGroupText(mMainGroup?.title ?: "")
|
||||||
searchFiltersView?.availableOther(mDatabase?.allowEntryCustomFields() ?: false)
|
searchFiltersView?.availableOther(mDatabase?.allowEntryCustomFields() ?: false)
|
||||||
|
searchFiltersView?.availableApplicationIds(mDatabase?.allowEntryCustomFields() ?: false)
|
||||||
searchFiltersView?.availableTags(mDatabase?.allowTags() ?: false)
|
searchFiltersView?.availableTags(mDatabase?.allowTags() ?: false)
|
||||||
searchFiltersView?.enableTags(mDatabase?.tagPool?.isNotEmpty() ?: false)
|
searchFiltersView?.enableTags(mDatabase?.tagPool?.isNotEmpty() ?: false)
|
||||||
searchFiltersView?.availableSearchableGroup(mDatabase?.allowCustomSearchableGroup() ?: false)
|
searchFiltersView?.availableSearchableGroup(mDatabase?.allowCustomSearchableGroup() ?: false)
|
||||||
@@ -1273,7 +1274,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
searchView = it.actionView as SearchView?
|
searchView = it.actionView as SearchView?
|
||||||
searchView?.apply {
|
searchView?.apply {
|
||||||
setOnQueryTextFocusChangeListener(mOnSearchTextFocusChangeListener)
|
setOnQueryTextFocusChangeListener(mOnSearchTextFocusChangeListener)
|
||||||
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager?
|
val searchManager = getSystemService(SEARCH_SERVICE) as SearchManager?
|
||||||
(searchManager?.getSearchableInfo(
|
(searchManager?.getSearchableInfo(
|
||||||
ComponentName(this@GroupActivity, GroupActivity::class.java)
|
ComponentName(this@GroupActivity, GroupActivity::class.java)
|
||||||
))?.let { searchableInfo ->
|
))?.let { searchableInfo ->
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseExcept
|
|||||||
import com.kunzisoft.keepass.database.helper.SearchHelper
|
import com.kunzisoft.keepass.database.helper.SearchHelper
|
||||||
import com.kunzisoft.keepass.model.RegisterInfo
|
import com.kunzisoft.keepass.model.RegisterInfo
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
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.getParcelableCompat
|
||||||
import com.kunzisoft.keepass.utils.getParcelableExtraCompat
|
import com.kunzisoft.keepass.utils.getParcelableExtraCompat
|
||||||
import com.kunzisoft.keepass.view.toastError
|
import com.kunzisoft.keepass.view.toastError
|
||||||
@@ -81,10 +81,7 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
}
|
}
|
||||||
// Build search param
|
// Build search param
|
||||||
bundle.getParcelableCompat<SearchInfo>(KEY_SEARCH_INFO)?.let { searchInfo ->
|
bundle.getParcelableCompat<SearchInfo>(KEY_SEARCH_INFO)?.let { searchInfo ->
|
||||||
AppUtil.getConcreteWebDomain(
|
searchInfo.getConcreteWebDomain(this) { concreteWebDomain ->
|
||||||
this,
|
|
||||||
searchInfo.webDomain
|
|
||||||
) { concreteWebDomain ->
|
|
||||||
// Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE)
|
// Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE)
|
||||||
val assistStructure = AutofillHelper
|
val assistStructure = AutofillHelper
|
||||||
.retrieveAutofillComponent(intent)
|
.retrieveAutofillComponent(intent)
|
||||||
@@ -111,7 +108,7 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
KEY_REGISTER_INFO
|
KEY_REGISTER_INFO
|
||||||
)
|
)
|
||||||
val searchInfo = SearchInfo(registerInfo?.searchInfo)
|
val searchInfo = SearchInfo(registerInfo?.searchInfo)
|
||||||
AppUtil.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
searchInfo.getConcreteWebDomain(this) { concreteWebDomain ->
|
||||||
searchInfo.webDomain = concreteWebDomain
|
searchInfo.webDomain = concreteWebDomain
|
||||||
launchRegistration(database, searchInfo, registerInfo)
|
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.database.helper.SearchHelper
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields
|
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.KeyboardUtil.isKeyboardActivatedInSettings
|
||||||
import com.kunzisoft.keepass.utils.getParcelableCompat
|
import com.kunzisoft.keepass.utils.getParcelableCompat
|
||||||
import com.kunzisoft.keepass.view.toastError
|
import com.kunzisoft.keepass.view.toastError
|
||||||
@@ -109,8 +109,7 @@ class EntrySelectionLauncherActivity : DatabaseModeActivity() {
|
|||||||
this.webDomain = sharedWebDomain
|
this.webDomain = sharedWebDomain
|
||||||
this.otpString = otpString
|
this.otpString = otpString
|
||||||
}
|
}
|
||||||
|
searchInfo.getConcreteWebDomain(this) { concreteWebDomain ->
|
||||||
AppUtil.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
|
||||||
searchInfo.webDomain = concreteWebDomain
|
searchInfo.webDomain = concreteWebDomain
|
||||||
launch(database, searchInfo)
|
launch(database, searchInfo)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ import com.kunzisoft.keepass.model.RegisterInfo
|
|||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
import com.kunzisoft.keepass.settings.AutofillSettingsActivity
|
import com.kunzisoft.keepass.settings.AutofillSettingsActivity
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.utils.AppUtil
|
import com.kunzisoft.keepass.utils.AppUtil.getConcreteWebDomain
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
|
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ class KeeAutofillService : AutofillService() {
|
|||||||
webDomain = parseResult.webDomain
|
webDomain = parseResult.webDomain
|
||||||
webScheme = parseResult.webScheme
|
webScheme = parseResult.webScheme
|
||||||
}
|
}
|
||||||
AppUtil.getConcreteWebDomain(this, searchInfo.webDomain) { webDomainWithoutSubDomain ->
|
searchInfo.getConcreteWebDomain(this) { webDomainWithoutSubDomain ->
|
||||||
searchInfo.webDomain = webDomainWithoutSubDomain
|
searchInfo.webDomain = webDomainWithoutSubDomain
|
||||||
val inlineSuggestionsRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
val inlineSuggestionsRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||||
&& autofillInlineSuggestionsEnabled) {
|
&& autofillInlineSuggestionsEnabled) {
|
||||||
|
|||||||
@@ -93,8 +93,6 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
private fun buildPasskeySearchInfo(relyingParty: String): SearchInfo {
|
private fun buildPasskeySearchInfo(relyingParty: String): SearchInfo {
|
||||||
return SearchInfo().apply {
|
return SearchInfo().apply {
|
||||||
this.relyingParty = relyingParty
|
this.relyingParty = relyingParty
|
||||||
this.isAPasskeySearch = true
|
|
||||||
this.query = relyingParty
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ package com.kunzisoft.keepass.database.helper
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
|
import com.kunzisoft.keepass.database.search.SearchParameters
|
||||||
import com.kunzisoft.keepass.model.EntryInfo
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
|
import com.kunzisoft.keepass.settings.PreferencesUtil.searchSubDomains
|
||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
|
|
||||||
object SearchHelper {
|
object SearchHelper {
|
||||||
@@ -61,8 +63,28 @@ object SearchHelper {
|
|||||||
&& !searchInfo.containsOnlyNullValues()) {
|
&& !searchInfo.containsOnlyNullValues()) {
|
||||||
// If search provide results
|
// If search provide results
|
||||||
database.createVirtualGroupFromSearchInfo(
|
database.createVirtualGroupFromSearchInfo(
|
||||||
searchInfo,
|
searchParameters = SearchParameters().apply {
|
||||||
MAX_SEARCH_ENTRY
|
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 ->
|
)?.let { searchGroup ->
|
||||||
if (searchGroup.numberOfChildEntries > 0) {
|
if (searchGroup.numberOfChildEntries > 0) {
|
||||||
searchWithoutUI = true
|
searchWithoutUI = true
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ object PreferencesUtil {
|
|||||||
context.resources.getBoolean(R.bool.auto_focus_search_default))
|
context.resources.getBoolean(R.bool.auto_focus_search_default))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchSubdomains(context: Context): Boolean {
|
fun searchSubDomains(context: Context): Boolean {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
return prefs.getBoolean(context.getString(R.string.subdomain_search_key),
|
return prefs.getBoolean(context.getString(R.string.subdomain_search_key),
|
||||||
context.resources.getBoolean(R.bool.subdomain_search_default))
|
context.resources.getBoolean(R.bool.subdomain_search_default))
|
||||||
@@ -352,6 +352,8 @@ object PreferencesUtil {
|
|||||||
context.resources.getBoolean(R.bool.search_option_username_default))
|
context.resources.getBoolean(R.bool.search_option_username_default))
|
||||||
searchInPasswords = prefs.getBoolean(context.getString(R.string.search_option_password_key),
|
searchInPasswords = prefs.getBoolean(context.getString(R.string.search_option_password_key),
|
||||||
context.resources.getBoolean(R.bool.search_option_password_default))
|
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),
|
searchInUrls = prefs.getBoolean(context.getString(R.string.search_option_url_key),
|
||||||
context.resources.getBoolean(R.bool.search_option_url_default))
|
context.resources.getBoolean(R.bool.search_option_url_default))
|
||||||
searchInExpired = prefs.getBoolean(context.getString(R.string.search_option_expired_key),
|
searchInExpired = prefs.getBoolean(context.getString(R.string.search_option_expired_key),
|
||||||
@@ -389,6 +391,8 @@ object PreferencesUtil {
|
|||||||
searchParameters.searchInUsernames)
|
searchParameters.searchInUsernames)
|
||||||
putBoolean(context.getString(R.string.search_option_password_key),
|
putBoolean(context.getString(R.string.search_option_password_key),
|
||||||
searchParameters.searchInPasswords)
|
searchParameters.searchInPasswords)
|
||||||
|
putBoolean(context.getString(R.string.search_option_application_id_key),
|
||||||
|
searchParameters.searchInAppIds)
|
||||||
putBoolean(context.getString(R.string.search_option_url_key),
|
putBoolean(context.getString(R.string.search_option_url_key),
|
||||||
searchParameters.searchInUrls)
|
searchParameters.searchInUrls)
|
||||||
putBoolean(context.getString(R.string.search_option_expired_key),
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
|
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
|
||||||
|
|
||||||
object AppUtil {
|
object AppUtil {
|
||||||
@@ -82,22 +83,31 @@ object AppUtil {
|
|||||||
/**
|
/**
|
||||||
* Get the concrete web domain AKA without sub domain if needed
|
* Get the concrete web domain AKA without sub domain if needed
|
||||||
*/
|
*/
|
||||||
fun getConcreteWebDomain(context: Context,
|
fun SearchInfo.getConcreteWebDomain(
|
||||||
webDomain: String?,
|
context: Context,
|
||||||
concreteWebDomain: (String?) -> Unit) {
|
concreteWebDomain: (String?) -> Unit
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
) {
|
||||||
if (webDomain != null) {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val domain = webDomain
|
||||||
|
if (domain != null) {
|
||||||
// Warning, web domain can contains IP, don't crop in this case
|
// Warning, web domain can contains IP, don't crop in this case
|
||||||
if (PreferencesUtil.searchSubdomains(context)
|
if (PreferencesUtil.searchSubDomains(context)
|
||||||
|| Regex(SearchInfo.WEB_IP_REGEX).matches(webDomain)) {
|
|| Regex(SearchInfo.WEB_IP_REGEX).matches(domain)) {
|
||||||
concreteWebDomain.invoke(webDomain)
|
withContext(Dispatchers.Main) {
|
||||||
|
concreteWebDomain.invoke(webDomain)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val publicSuffixList = PublicSuffixList(context)
|
val publicSuffixList = PublicSuffixList(context)
|
||||||
concreteWebDomain.invoke(publicSuffixList
|
val publicSuffix = publicSuffixList
|
||||||
.getPublicSuffixPlusOne(webDomain).await())
|
.getPublicSuffixPlusOne(domain).await()
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
concreteWebDomain.invoke(publicSuffix)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
@@ -30,8 +29,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
|||||||
private var searchTitle: CompoundButton
|
private var searchTitle: CompoundButton
|
||||||
private var searchUsername: CompoundButton
|
private var searchUsername: CompoundButton
|
||||||
private var searchPassword: CompoundButton
|
private var searchPassword: CompoundButton
|
||||||
|
private var searchApplicationId: CompoundButton
|
||||||
private var searchURL: CompoundButton
|
private var searchURL: CompoundButton
|
||||||
private var searchByURLDomain: Boolean = false
|
private var searchByURLDomain: Boolean = false
|
||||||
|
private var searchByURLSubDomain: Boolean = false
|
||||||
private var searchExpired: CompoundButton
|
private var searchExpired: CompoundButton
|
||||||
private var searchNotes: CompoundButton
|
private var searchNotes: CompoundButton
|
||||||
private var searchOther: CompoundButton
|
private var searchOther: CompoundButton
|
||||||
@@ -50,8 +51,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
|||||||
this.searchInTitles = searchTitle.isChecked
|
this.searchInTitles = searchTitle.isChecked
|
||||||
this.searchInUsernames = searchUsername.isChecked
|
this.searchInUsernames = searchUsername.isChecked
|
||||||
this.searchInPasswords = searchPassword.isChecked
|
this.searchInPasswords = searchPassword.isChecked
|
||||||
|
this.searchInAppIds = searchApplicationId.isChecked
|
||||||
this.searchInUrls = searchURL.isChecked
|
this.searchInUrls = searchURL.isChecked
|
||||||
this.searchByDomain = searchByURLDomain
|
this.searchByDomain = searchByURLDomain
|
||||||
|
this.searchBySubDomain = searchByURLSubDomain
|
||||||
this.searchInExpired = searchExpired.isChecked
|
this.searchInExpired = searchExpired.isChecked
|
||||||
this.searchInNotes = searchNotes.isChecked
|
this.searchInNotes = searchNotes.isChecked
|
||||||
this.searchInOther = searchOther.isChecked
|
this.searchInOther = searchOther.isChecked
|
||||||
@@ -71,8 +74,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
|||||||
searchTitle.isChecked = value.searchInTitles
|
searchTitle.isChecked = value.searchInTitles
|
||||||
searchUsername.isChecked = value.searchInUsernames
|
searchUsername.isChecked = value.searchInUsernames
|
||||||
searchPassword.isChecked = value.searchInPasswords
|
searchPassword.isChecked = value.searchInPasswords
|
||||||
|
searchApplicationId.isChecked = value.searchInAppIds
|
||||||
searchURL.isChecked = value.searchInUrls
|
searchURL.isChecked = value.searchInUrls
|
||||||
searchByURLDomain = value.searchByDomain
|
searchByURLDomain = value.searchByDomain
|
||||||
|
searchByURLSubDomain = value.searchBySubDomain
|
||||||
searchExpired.isChecked = value.searchInExpired
|
searchExpired.isChecked = value.searchInExpired
|
||||||
searchNotes.isChecked = value.searchInNotes
|
searchNotes.isChecked = value.searchInNotes
|
||||||
searchOther.isChecked = value.searchInOther
|
searchOther.isChecked = value.searchInOther
|
||||||
@@ -87,7 +92,7 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
|||||||
var onParametersChangeListener: ((searchParameters: SearchParameters) -> Unit)? = null
|
var onParametersChangeListener: ((searchParameters: SearchParameters) -> Unit)? = null
|
||||||
private var mOnParametersChangeListener: ((searchParameters: SearchParameters) -> Unit)? = {
|
private var mOnParametersChangeListener: ((searchParameters: SearchParameters) -> Unit)? = {
|
||||||
// To recalculate height
|
// To recalculate height
|
||||||
if (searchAdvanceFiltersContainer?.visibility == View.VISIBLE) {
|
if (searchAdvanceFiltersContainer?.visibility == VISIBLE) {
|
||||||
searchAdvanceFiltersContainer?.expand(
|
searchAdvanceFiltersContainer?.expand(
|
||||||
false,
|
false,
|
||||||
searchAdvanceFiltersContainer?.getFullHeight()
|
searchAdvanceFiltersContainer?.getFullHeight()
|
||||||
@@ -110,6 +115,7 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
|||||||
searchTitle = findViewById(R.id.search_chip_title)
|
searchTitle = findViewById(R.id.search_chip_title)
|
||||||
searchUsername = findViewById(R.id.search_chip_username)
|
searchUsername = findViewById(R.id.search_chip_username)
|
||||||
searchPassword = findViewById(R.id.search_chip_password)
|
searchPassword = findViewById(R.id.search_chip_password)
|
||||||
|
searchApplicationId = findViewById(R.id.search_chip_application_id)
|
||||||
searchURL = findViewById(R.id.search_chip_url)
|
searchURL = findViewById(R.id.search_chip_url)
|
||||||
searchExpired = findViewById(R.id.search_chip_expires)
|
searchExpired = findViewById(R.id.search_chip_expires)
|
||||||
searchNotes = findViewById(R.id.search_chip_note)
|
searchNotes = findViewById(R.id.search_chip_note)
|
||||||
@@ -125,7 +131,7 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
|||||||
|
|
||||||
// Expand menu with button
|
// Expand menu with button
|
||||||
searchExpandButton.setOnClickListener {
|
searchExpandButton.setOnClickListener {
|
||||||
val isVisible = searchAdvanceFiltersContainer?.visibility == View.VISIBLE
|
val isVisible = searchAdvanceFiltersContainer?.visibility == VISIBLE
|
||||||
if (isVisible)
|
if (isVisible)
|
||||||
closeAdvancedFilters()
|
closeAdvancedFilters()
|
||||||
else
|
else
|
||||||
@@ -156,6 +162,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
|||||||
searchParameters.searchInPasswords = isChecked
|
searchParameters.searchInPasswords = isChecked
|
||||||
mOnParametersChangeListener?.invoke(searchParameters)
|
mOnParametersChangeListener?.invoke(searchParameters)
|
||||||
}
|
}
|
||||||
|
searchApplicationId.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
searchParameters.searchInAppIds = isChecked
|
||||||
|
mOnParametersChangeListener?.invoke(searchParameters)
|
||||||
|
}
|
||||||
searchURL.setOnCheckedChangeListener { _, isChecked ->
|
searchURL.setOnCheckedChangeListener { _, isChecked ->
|
||||||
searchParameters.searchInUrls = isChecked
|
searchParameters.searchInUrls = isChecked
|
||||||
mOnParametersChangeListener?.invoke(searchParameters)
|
mOnParametersChangeListener?.invoke(searchParameters)
|
||||||
@@ -213,6 +223,10 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
|||||||
searchOther.isVisible = available
|
searchOther.isVisible = available
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun availableApplicationIds(available: Boolean) {
|
||||||
|
searchApplicationId.isVisible = available
|
||||||
|
}
|
||||||
|
|
||||||
fun availableTags(available: Boolean) {
|
fun availableTags(available: Boolean) {
|
||||||
searchTag.isVisible = available
|
searchTag.isVisible = available
|
||||||
}
|
}
|
||||||
@@ -245,14 +259,14 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
|||||||
|
|
||||||
override fun setVisibility(visibility: Int) {
|
override fun setVisibility(visibility: Int) {
|
||||||
when (visibility) {
|
when (visibility) {
|
||||||
View.VISIBLE -> {
|
VISIBLE -> {
|
||||||
searchAdvanceFiltersContainer?.visibility = View.GONE
|
searchAdvanceFiltersContainer?.visibility = GONE
|
||||||
searchContainer.showByFading()
|
searchContainer.showByFading()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
searchContainer.hideByFading()
|
searchContainer.hideByFading()
|
||||||
if (searchAdvanceFiltersContainer?.visibility == View.VISIBLE) {
|
if (searchAdvanceFiltersContainer?.visibility == VISIBLE) {
|
||||||
searchAdvanceFiltersContainer?.visibility = View.INVISIBLE
|
searchAdvanceFiltersContainer?.visibility = INVISIBLE
|
||||||
searchAdvanceFiltersContainer?.collapse()
|
searchAdvanceFiltersContainer?.collapse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,13 @@
|
|||||||
android:checked="false"
|
android:checked="false"
|
||||||
style="@style/KeepassDXStyle.Chip.Filter"
|
style="@style/KeepassDXStyle.Chip.Filter"
|
||||||
android:text="@string/entry_password"/>
|
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
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/search_chip_url"
|
android:id="@+id/search_chip_url"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -247,6 +247,8 @@
|
|||||||
<bool name="search_option_username_default" translatable="false">true</bool>
|
<bool name="search_option_username_default" translatable="false">true</bool>
|
||||||
<string name="search_option_password_key" translatable="false">search_option_password_key</string>
|
<string name="search_option_password_key" translatable="false">search_option_password_key</string>
|
||||||
<bool name="search_option_password_default" translatable="false">false</bool>
|
<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>
|
<string name="search_option_url_key" translatable="false">search_option_url_key</string>
|
||||||
<bool name="search_option_url_default" translatable="false">true</bool>
|
<bool name="search_option_url_default" translatable="false">true</bool>
|
||||||
<string name="search_option_expired_key" translatable="false">search_option_expired_key</string>
|
<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="auto_type_sequence">Auto-Type sequence</string>
|
||||||
<string name="entry_not_found">Could not find entry data.</string>
|
<string name="entry_not_found">Could not find entry data.</string>
|
||||||
<string name="entry_password">Password</string>
|
<string name="entry_password">Password</string>
|
||||||
|
<string name="entry_application_id">App Id</string>
|
||||||
<string name="tags">Tags</string>
|
<string name="tags">Tags</string>
|
||||||
<string name="custom_data">Custom data</string>
|
<string name="custom_data">Custom data</string>
|
||||||
<string name="save">Save</string>
|
<string name="save">Save</string>
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ import com.kunzisoft.keepass.database.merge.DatabaseKDBXMerger
|
|||||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||||
import com.kunzisoft.keepass.database.search.SearchParameters
|
import com.kunzisoft.keepass.database.search.SearchParameters
|
||||||
import com.kunzisoft.keepass.hardware.HardwareKey
|
import com.kunzisoft.keepass.hardware.HardwareKey
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
|
||||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||||
import com.kunzisoft.keepass.utils.SingletonHolder
|
import com.kunzisoft.keepass.utils.SingletonHolder
|
||||||
import com.kunzisoft.keepass.utils.StringUtil.toFormattedColorInt
|
import com.kunzisoft.keepass.utils.StringUtil.toFormattedColorInt
|
||||||
@@ -886,12 +885,12 @@ open class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createVirtualGroupFromSearchInfo(
|
fun createVirtualGroupFromSearchInfo(
|
||||||
searchInfo: SearchInfo,
|
searchParameters: SearchParameters,
|
||||||
max: Int = Integer.MAX_VALUE
|
max: Int = Integer.MAX_VALUE
|
||||||
): Group? {
|
): Group? {
|
||||||
return mSearchHelper.createVirtualGroupWithSearchResult(
|
return mSearchHelper.createVirtualGroupWithSearchResult(
|
||||||
database = this,
|
database = this,
|
||||||
searchParameters = searchInfo.buildSearchParameters(),
|
searchParameters = searchParameters,
|
||||||
fromGroup = null,
|
fromGroup = null,
|
||||||
max = max
|
max = max
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,10 +24,13 @@ import com.kunzisoft.keepass.database.element.Entry
|
|||||||
import com.kunzisoft.keepass.database.element.Group
|
import com.kunzisoft.keepass.database.element.Group
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeHandler
|
import com.kunzisoft.keepass.database.element.node.NodeHandler
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.FIELD_RELYING_PARTY
|
import com.kunzisoft.keepass.model.AppOriginEntryField.isAppId
|
||||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.isPasskeyExclusion
|
import com.kunzisoft.keepass.model.AppOriginEntryField.isAppIdSignature
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_FIELD
|
import com.kunzisoft.keepass.model.AppOriginEntryField.isWebDomain
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields.isOtpExclusion
|
import com.kunzisoft.keepass.model.PasskeyEntryFields.isPasskey
|
||||||
|
import com.kunzisoft.keepass.model.PasskeyEntryFields.isRelyingParty
|
||||||
|
import com.kunzisoft.keepass.otp.OtpEntryFields.isOTP
|
||||||
|
import com.kunzisoft.keepass.otp.OtpEntryFields.isOTPURIField
|
||||||
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
|
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
|
||||||
import com.kunzisoft.keepass.utils.inTheSameDomainAs
|
import com.kunzisoft.keepass.utils.inTheSameDomainAs
|
||||||
|
|
||||||
@@ -124,8 +127,10 @@ class SearchHelper {
|
|||||||
/**
|
/**
|
||||||
* Return true if the search query in search parameters is found in available parameters
|
* Return true if the search query in search parameters is found in available parameters
|
||||||
*/
|
*/
|
||||||
fun searchInEntry(entry: Entry,
|
fun searchInEntry(
|
||||||
searchParameters: SearchParameters): Boolean {
|
entry: Entry,
|
||||||
|
searchParameters: SearchParameters
|
||||||
|
): Boolean {
|
||||||
// Not found if the search string is empty
|
// Not found if the search string is empty
|
||||||
if (searchParameters.searchQuery.isEmpty())
|
if (searchParameters.searchQuery.isEmpty())
|
||||||
return searchParameters.allowEmptyQuery
|
return searchParameters.allowEmptyQuery
|
||||||
@@ -149,16 +154,32 @@ class SearchHelper {
|
|||||||
if (checkSearchQuery(entry.password, searchParameters))
|
if (checkSearchQuery(entry.password, searchParameters))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if (searchParameters.searchInAppIds) {
|
||||||
|
if (entry.getExtraFields().any { field ->
|
||||||
|
field.isAppId()
|
||||||
|
&& checkSearchQuery(field.protectedValue.stringValue, searchParameters)
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
if (searchParameters.searchInUrls) {
|
if (searchParameters.searchInUrls) {
|
||||||
if (checkSearchQuery(entry.url, searchParameters) { stringToCheck, word ->
|
if (checkSearchQuery(entry.url, searchParameters) { stringToCheck, word ->
|
||||||
if (searchParameters.searchByDomain) {
|
specialWebDomainComparison(searchParameters, stringToCheck, word)
|
||||||
try {
|
}) {
|
||||||
stringToCheck.inTheSameDomainAs(word, sameSubDomain = true)
|
return true
|
||||||
} catch (_: Exception) {
|
} else if (entry.getExtraFields().any { field ->
|
||||||
false
|
field.isWebDomain()
|
||||||
|
&& checkSearchQuery(field.protectedValue.stringValue, searchParameters) { stringToCheck, word ->
|
||||||
|
specialWebDomainComparison(searchParameters, stringToCheck, word)
|
||||||
}
|
}
|
||||||
} else null
|
}) {
|
||||||
})
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchParameters.searchInRelyingParty) {
|
||||||
|
if(entry.getExtraFields().any { field ->
|
||||||
|
field.isRelyingParty()
|
||||||
|
&& checkSearchQuery(field.protectedValue.stringValue, searchParameters)
|
||||||
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (searchParameters.searchInNotes) {
|
if (searchParameters.searchInNotes) {
|
||||||
@@ -171,24 +192,20 @@ class SearchHelper {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (searchParameters.searchInOTP) {
|
if (searchParameters.searchInOTP) {
|
||||||
if(entry.getExtraFields().any { field ->
|
if (entry.getExtraFields().any { field ->
|
||||||
field.name == OTP_FIELD
|
field.isOTPURIField()
|
||||||
&& checkSearchQuery(field.protectedValue.stringValue, searchParameters)
|
&& checkSearchQuery(field.protectedValue.stringValue, searchParameters)
|
||||||
})
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (searchParameters.searchInRelyingParty) {
|
|
||||||
if(entry.getExtraFields().any { field ->
|
|
||||||
field.name == FIELD_RELYING_PARTY
|
|
||||||
&& checkSearchQuery(field.protectedValue.stringValue, searchParameters)
|
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (searchParameters.searchInOther) {
|
if (searchParameters.searchInOther) {
|
||||||
if(entry.getExtraFields().any { field ->
|
if (entry.getExtraFields().any { field ->
|
||||||
field.isOtpExclusion()
|
!field.isAppId()
|
||||||
&& field.isPasskeyExclusion()
|
&& !field.isAppIdSignature()
|
||||||
&& checkSearchQuery(field.protectedValue.toString(), searchParameters)
|
&& !field.isWebDomain()
|
||||||
|
&& !field.isOTP()
|
||||||
|
&& !field.isPasskey()
|
||||||
|
&& checkSearchQuery(field.protectedValue.toString(), searchParameters)
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -199,6 +216,23 @@ class SearchHelper {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun specialWebDomainComparison(
|
||||||
|
searchParameters: SearchParameters,
|
||||||
|
stringToCheck: String,
|
||||||
|
word: String
|
||||||
|
): Boolean? {
|
||||||
|
return if (searchParameters.searchByDomain) {
|
||||||
|
try {
|
||||||
|
stringToCheck.inTheSameDomainAs(
|
||||||
|
value = word,
|
||||||
|
sameSubDomain = searchParameters.searchBySubDomain
|
||||||
|
)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkSearchQuery(
|
private fun checkSearchQuery(
|
||||||
stringToCheck: String,
|
stringToCheck: String,
|
||||||
searchParameters: SearchParameters,
|
searchParameters: SearchParameters,
|
||||||
|
|||||||
@@ -34,11 +34,14 @@ class SearchParameters() : Parcelable{
|
|||||||
var searchInTitles = true
|
var searchInTitles = true
|
||||||
var searchInUsernames = true
|
var searchInUsernames = true
|
||||||
var searchInPasswords = false
|
var searchInPasswords = false
|
||||||
|
var searchInAppIds = true
|
||||||
var searchInUrls = true
|
var searchInUrls = true
|
||||||
|
var searchByDomain = false
|
||||||
|
var searchBySubDomain = false
|
||||||
|
var searchInRelyingParty = false
|
||||||
var searchInExpired = false
|
var searchInExpired = false
|
||||||
var searchInNotes = true
|
var searchInNotes = true
|
||||||
var searchInOTP = false
|
var searchInOTP = false
|
||||||
var searchInRelyingParty = false
|
|
||||||
var searchInOther = true
|
var searchInOther = true
|
||||||
var searchInUUIDs = false
|
var searchInUUIDs = false
|
||||||
var searchInTags = false
|
var searchInTags = false
|
||||||
@@ -47,7 +50,6 @@ class SearchParameters() : Parcelable{
|
|||||||
var searchInSearchableGroup = true
|
var searchInSearchableGroup = true
|
||||||
var searchInRecycleBin = false
|
var searchInRecycleBin = false
|
||||||
var searchInTemplates = false
|
var searchInTemplates = false
|
||||||
var searchByDomain = false
|
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this() {
|
constructor(parcel: Parcel) : this() {
|
||||||
searchQuery = parcel.readString() ?: searchQuery
|
searchQuery = parcel.readString() ?: searchQuery
|
||||||
@@ -57,11 +59,14 @@ class SearchParameters() : Parcelable{
|
|||||||
searchInTitles = parcel.readByte() != 0.toByte()
|
searchInTitles = parcel.readByte() != 0.toByte()
|
||||||
searchInUsernames = parcel.readByte() != 0.toByte()
|
searchInUsernames = parcel.readByte() != 0.toByte()
|
||||||
searchInPasswords = parcel.readByte() != 0.toByte()
|
searchInPasswords = parcel.readByte() != 0.toByte()
|
||||||
|
searchInAppIds = parcel.readByte() != 0.toByte()
|
||||||
searchInUrls = parcel.readByte() != 0.toByte()
|
searchInUrls = parcel.readByte() != 0.toByte()
|
||||||
|
searchByDomain = parcel.readByte() != 0.toByte()
|
||||||
|
searchBySubDomain = parcel.readByte() != 0.toByte()
|
||||||
|
searchInRelyingParty = parcel.readByte() != 0.toByte()
|
||||||
searchInExpired = parcel.readByte() != 0.toByte()
|
searchInExpired = parcel.readByte() != 0.toByte()
|
||||||
searchInNotes = parcel.readByte() != 0.toByte()
|
searchInNotes = parcel.readByte() != 0.toByte()
|
||||||
searchInOTP = parcel.readByte() != 0.toByte()
|
searchInOTP = parcel.readByte() != 0.toByte()
|
||||||
searchInRelyingParty = parcel.readByte() != 0.toByte()
|
|
||||||
searchInOther = parcel.readByte() != 0.toByte()
|
searchInOther = parcel.readByte() != 0.toByte()
|
||||||
searchInUUIDs = parcel.readByte() != 0.toByte()
|
searchInUUIDs = parcel.readByte() != 0.toByte()
|
||||||
searchInTags = parcel.readByte() != 0.toByte()
|
searchInTags = parcel.readByte() != 0.toByte()
|
||||||
@@ -79,11 +84,14 @@ class SearchParameters() : Parcelable{
|
|||||||
parcel.writeByte(if (searchInTitles) 1 else 0)
|
parcel.writeByte(if (searchInTitles) 1 else 0)
|
||||||
parcel.writeByte(if (searchInUsernames) 1 else 0)
|
parcel.writeByte(if (searchInUsernames) 1 else 0)
|
||||||
parcel.writeByte(if (searchInPasswords) 1 else 0)
|
parcel.writeByte(if (searchInPasswords) 1 else 0)
|
||||||
|
parcel.writeByte(if (searchInAppIds) 1 else 0)
|
||||||
parcel.writeByte(if (searchInUrls) 1 else 0)
|
parcel.writeByte(if (searchInUrls) 1 else 0)
|
||||||
|
parcel.writeByte(if (searchByDomain) 1 else 0)
|
||||||
|
parcel.writeByte(if (searchBySubDomain) 1 else 0)
|
||||||
|
parcel.writeByte(if (searchInRelyingParty) 1 else 0)
|
||||||
parcel.writeByte(if (searchInExpired) 1 else 0)
|
parcel.writeByte(if (searchInExpired) 1 else 0)
|
||||||
parcel.writeByte(if (searchInNotes) 1 else 0)
|
parcel.writeByte(if (searchInNotes) 1 else 0)
|
||||||
parcel.writeByte(if (searchInOTP) 1 else 0)
|
parcel.writeByte(if (searchInOTP) 1 else 0)
|
||||||
parcel.writeByte(if (searchInRelyingParty) 1 else 0)
|
|
||||||
parcel.writeByte(if (searchInOther) 1 else 0)
|
parcel.writeByte(if (searchInOther) 1 else 0)
|
||||||
parcel.writeByte(if (searchInUUIDs) 1 else 0)
|
parcel.writeByte(if (searchInUUIDs) 1 else 0)
|
||||||
parcel.writeByte(if (searchInTags) 1 else 0)
|
parcel.writeByte(if (searchInTags) 1 else 0)
|
||||||
|
|||||||
@@ -139,4 +139,25 @@ object AppOriginEntryField {
|
|||||||
setWebDomain(webOrigin.origin, null, customFieldsAllowed)
|
setWebDomain(webOrigin.origin, null, customFieldsAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the current field is an application id
|
||||||
|
*/
|
||||||
|
fun Field.isAppId(): Boolean {
|
||||||
|
return this.name.startsWith(APPLICATION_ID_FIELD_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the current field is an application id signature
|
||||||
|
*/
|
||||||
|
fun Field.isAppIdSignature(): Boolean {
|
||||||
|
return this.name.startsWith(APPLICATION_SIGNATURE_FIELD_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the current field is a web domain
|
||||||
|
*/
|
||||||
|
fun Field.isWebDomain(): Boolean {
|
||||||
|
return this.name.startsWith(WEB_DOMAIN_FIELD_NAME)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -31,11 +31,11 @@ import com.kunzisoft.keepass.model.AppOriginEntryField.setAppOrigin
|
|||||||
import com.kunzisoft.keepass.model.AppOriginEntryField.setApplicationId
|
import com.kunzisoft.keepass.model.AppOriginEntryField.setApplicationId
|
||||||
import com.kunzisoft.keepass.model.AppOriginEntryField.setWebDomain
|
import com.kunzisoft.keepass.model.AppOriginEntryField.setWebDomain
|
||||||
import com.kunzisoft.keepass.model.CreditCardEntryFields.setCreditCard
|
import com.kunzisoft.keepass.model.CreditCardEntryFields.setCreditCard
|
||||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.isPasskeyExclusion
|
import com.kunzisoft.keepass.model.PasskeyEntryFields.isPasskey
|
||||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.setPasskey
|
import com.kunzisoft.keepass.model.PasskeyEntryFields.setPasskey
|
||||||
import com.kunzisoft.keepass.otp.OtpElement
|
import com.kunzisoft.keepass.otp.OtpElement
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD
|
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields.isOtpExclusion
|
import com.kunzisoft.keepass.otp.OtpEntryFields.isOTP
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields.setOtp
|
import com.kunzisoft.keepass.otp.OtpEntryFields.setOtp
|
||||||
import com.kunzisoft.keepass.utils.readBooleanCompat
|
import com.kunzisoft.keepass.utils.readBooleanCompat
|
||||||
import com.kunzisoft.keepass.utils.readListCompat
|
import com.kunzisoft.keepass.utils.readListCompat
|
||||||
@@ -109,8 +109,7 @@ class EntryInfo : NodeInfo {
|
|||||||
|
|
||||||
fun getCustomFieldsForFilling(): List<Field> {
|
fun getCustomFieldsForFilling(): List<Field> {
|
||||||
return customFields.filter {
|
return customFields.filter {
|
||||||
!it.isOtpExclusion()
|
!it.isOTP() && !it.isPasskey()
|
||||||
&& !it.isPasskeyExclusion()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ object PasskeyEntryFields {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field ignored for a search or a form filling
|
* Detect if the current field is a Passkey
|
||||||
*/
|
*/
|
||||||
fun Field.isPasskeyExclusion(): Boolean {
|
fun Field.isPasskey(): Boolean {
|
||||||
return when(name) {
|
return when(name) {
|
||||||
PASSKEY_FIELD -> true
|
PASSKEY_FIELD -> true
|
||||||
FIELD_USERNAME -> true
|
FIELD_USERNAME -> true
|
||||||
@@ -125,4 +125,11 @@ object PasskeyEntryFields {
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the current field is a Passkey relying party
|
||||||
|
*/
|
||||||
|
fun Field.isRelyingParty(): Boolean {
|
||||||
|
return name == FIELD_RELYING_PARTY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@ import android.content.res.Resources
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.kunzisoft.keepass.database.search.SearchParameters
|
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields
|
import com.kunzisoft.keepass.otp.OtpEntryFields
|
||||||
import com.kunzisoft.keepass.utils.ObjectNameResource
|
import com.kunzisoft.keepass.utils.ObjectNameResource
|
||||||
import com.kunzisoft.keepass.utils.readBooleanCompat
|
import com.kunzisoft.keepass.utils.readBooleanCompat
|
||||||
@@ -97,36 +96,26 @@ class SearchInfo : ObjectNameResource, Parcelable {
|
|||||||
&& relyingParty == null
|
&& relyingParty == null
|
||||||
&& otpString == null
|
&& otpString == null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isADomainSearch(): Boolean {
|
|
||||||
return toString() == webDomain && webDomain != null
|
|
||||||
}
|
|
||||||
|
|
||||||
var isAPasskeySearch: Boolean = false
|
var isTagSearch: Boolean = false
|
||||||
|
get() = tag != null
|
||||||
|
private set
|
||||||
|
|
||||||
var query: String? = null
|
var isAppIdSearch: Boolean = false
|
||||||
|
get() = applicationId != null
|
||||||
|
private set
|
||||||
|
|
||||||
fun buildSearchParameters(): SearchParameters {
|
var isDomainSearch: Boolean = false
|
||||||
return SearchParameters().apply {
|
get() = webDomain != null
|
||||||
searchQuery = query ?: this@SearchInfo.toString()
|
private set
|
||||||
allowEmptyQuery = false
|
|
||||||
searchInTitles = !isAPasskeySearch
|
var isPasskeySearch: Boolean = false
|
||||||
searchInUsernames = false
|
get() = relyingParty != null
|
||||||
searchInPasswords = false
|
private set
|
||||||
searchInUrls = !isAPasskeySearch
|
|
||||||
searchByDomain = isADomainSearch()
|
var isOTPSearch: Boolean = false
|
||||||
searchInNotes = false
|
get() = otpString != null
|
||||||
searchInOTP = false
|
private set
|
||||||
searchInOther = true
|
|
||||||
searchInUUIDs = false
|
|
||||||
searchInTags = false
|
|
||||||
searchInRelyingParty = isAPasskeySearch
|
|
||||||
searchInCurrentGroup = false
|
|
||||||
searchInSearchableGroup = true
|
|
||||||
searchInRecycleBin = false
|
|
||||||
searchInTemplates = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ object OtpEntryFields {
|
|||||||
private val TAG = OtpEntryFields::class.java.name
|
private val TAG = OtpEntryFields::class.java.name
|
||||||
|
|
||||||
// Field from KeePassXC
|
// Field from KeePassXC
|
||||||
const val OTP_FIELD = "otp"
|
private const val OTP_FIELD = "otp"
|
||||||
|
|
||||||
// URL parameters (https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
|
// URL parameters (https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
|
||||||
private const val OTP_SCHEME = "otpauth"
|
private const val OTP_SCHEME = "otpauth"
|
||||||
@@ -508,9 +508,9 @@ object OtpEntryFields {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field ignored for a search or a form filling
|
* Detect if the current field is an OTP
|
||||||
*/
|
*/
|
||||||
fun Field.isOtpExclusion(): Boolean {
|
fun Field.isOTP(): Boolean {
|
||||||
return when(name) {
|
return when(name) {
|
||||||
OTP_FIELD -> true
|
OTP_FIELD -> true
|
||||||
TOTP_SEED_FIELD -> true
|
TOTP_SEED_FIELD -> true
|
||||||
@@ -530,4 +530,11 @@ object OtpEntryFields {
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the current field is an OTP URI
|
||||||
|
*/
|
||||||
|
fun Field.isOTPURIField(): Boolean {
|
||||||
|
return name == OTP_FIELD
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
* Passkeys management #1421 (Thx @cali-95)
|
* Passkeys management #1421 (Thx @cali-95)
|
||||||
|
* Search settings #2112 #2181
|
||||||
* Small fixes #2171 #2150 #2159
|
* Small fixes #2171 #2150 #2159
|
||||||
3
fastlane/metadata/android/fr-FR/changelogs/143.txt
Normal file
3
fastlane/metadata/android/fr-FR/changelogs/143.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
* Gestion de Passkeys #1421 (Thx @cali-95)
|
||||||
|
* Paramètres de recherche #2112 #2181
|
||||||
|
* Petites corrections #2171 #2150 #2159
|
||||||
Reference in New Issue
Block a user