diff --git a/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextEditFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextEditFieldView.kt
index faafa6709..0b6b26ae3 100644
--- a/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextEditFieldView.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextEditFieldView.kt
@@ -1,3 +1,22 @@
+/*
+ * Copyright 2024 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePassDX.
+ *
+ * KeePassDX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeePassDX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeePassDX. If not, see .
+ *
+ */
package com.kunzisoft.keepass.view
import android.content.Context
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextFieldView.kt
new file mode 100644
index 000000000..c56454d10
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextFieldView.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2024 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePassDX.
+ *
+ * KeePassDX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeePassDX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeePassDX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.view
+
+import android.content.Context
+import android.graphics.Color
+import android.text.SpannableString
+import android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
+import android.text.style.ImageSpan
+import android.util.AttributeSet
+import androidx.annotation.StringRes
+import androidx.core.content.ContextCompat
+import androidx.core.graphics.drawable.DrawableCompat
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.password.PasswordEntropy
+import com.kunzisoft.keepass.password.PasswordGenerator
+import com.kunzisoft.keepass.settings.PreferencesUtil
+
+
+class PasswordTextFieldView @JvmOverloads constructor(context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = 0)
+ : TextFieldView(context, attrs, defStyle) {
+
+ private var mPasswordEntropyCalculator: PasswordEntropy = PasswordEntropy {
+ valueView.text?.toString()?.let { firstPassword ->
+ getEntropyStrength(firstPassword)
+ }
+ }
+
+ private var indicatorDrawable = ContextCompat.getDrawable(
+ context,
+ R.drawable.ic_shield_white_24dp
+ )?.apply {
+ val lineHeight = labelView.lineHeight
+ setBounds(0,0,lineHeight, lineHeight)
+ DrawableCompat.setTint(this, Color.TRANSPARENT)
+ }
+
+ override var label: String
+ get() {
+ return labelView.text.toString().removeSuffix(ICON_STRING_SPACES)
+ }
+ set(value) {
+ indicatorDrawable?.let { drawable ->
+ val spannableString = SpannableString("$value$ICON_STRING_SPACES")
+ val startPosition = spannableString.split(ICON_STRING)[0].length
+ val endPosition = startPosition + ICON_STRING.length
+ spannableString
+ .setSpan(
+ ImageSpan(drawable),
+ startPosition,
+ endPosition,
+ SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ labelView.text = spannableString
+ } ?: kotlin.run {
+ labelView.text = value
+ }
+ }
+
+ override fun setLabel(@StringRes labelId: Int) {
+ label = resources.getString(labelId)
+ }
+
+ override var value: String
+ get() {
+ return valueView.text.toString()
+ }
+ set(value) {
+ val spannableString =
+ if (PreferencesUtil.colorizePassword(context))
+ PasswordGenerator.getColorizedPassword(value)
+ else
+ SpannableString(value)
+ valueView.text = spannableString
+ changeProtectedValueParameters()
+ }
+
+ override fun setValue(@StringRes valueId: Int) {
+ value = resources.getString(valueId)
+ }
+
+ private fun getEntropyStrength(passwordText: String) {
+ mPasswordEntropyCalculator.getEntropyStrength(passwordText) { entropyStrength ->
+ labelView.apply {
+ post {
+ val strengthColor = entropyStrength.strength.color
+ indicatorDrawable?.let { drawable ->
+ DrawableCompat.setTint(drawable, strengthColor)
+ }
+ invalidate()
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val ICON_STRING = "[icon]"
+ private const val ICON_STRING_SPACES = " $ICON_STRING"
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt
index 34ef53dc3..d21f850b0 100644
--- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt
@@ -10,6 +10,7 @@ import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.element.template.TemplateAttribute
import com.kunzisoft.keepass.database.element.template.TemplateField
import com.kunzisoft.keepass.database.helper.getLocalizedName
+import com.kunzisoft.keepass.database.helper.isStandardPasswordName
import com.kunzisoft.keepass.model.OtpModel
import com.kunzisoft.keepass.otp.OtpElement
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD
@@ -48,7 +49,9 @@ class TemplateView @JvmOverloads constructor(context: Context,
field: Field): TextFieldView? {
// Add an action icon if needed
return context?.let {
- TextFieldView(it).apply {
+ (if (TemplateField.isStandardPasswordName(context, templateAttribute.label))
+ PasswordTextFieldView(it)
+ else TextFieldView(it)).apply {
applyFontVisibility(mFontInVisibility)
setProtection(field.protectedValue.isProtected, mHideProtectedValue)
label = templateAttribute.alias
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt
index fa22cb6f9..6077aa7e5 100644
--- a/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt
@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.view
import android.content.Context
import android.os.Build
import android.text.InputFilter
-import android.text.SpannableString
import android.text.util.Linkify
import android.util.AttributeSet
import android.util.TypedValue
@@ -38,15 +37,11 @@ import androidx.core.text.util.LinkifyCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import com.kunzisoft.keepass.R
-import com.kunzisoft.keepass.database.element.template.TemplateField
-import com.kunzisoft.keepass.database.helper.isStandardPasswordName
import com.kunzisoft.keepass.model.EntryInfo.Companion.APPLICATION_ID_FIELD_NAME
-import com.kunzisoft.keepass.password.PasswordGenerator
-import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.UriUtil.openExternalApp
-class TextFieldView @JvmOverloads constructor(context: Context,
+open class TextFieldView @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0)
: RelativeLayout(context, attrs, defStyle), GenericTextFieldView {
@@ -56,7 +51,7 @@ class TextFieldView @JvmOverloads constructor(context: Context,
private var showButtonId = ViewCompat.generateViewId()
private var copyButtonId = ViewCompat.generateViewId()
- private val labelView = AppCompatTextView(context).apply {
+ protected val labelView = AppCompatTextView(context).apply {
setTextAppearance(context,
R.style.KeepassDXStyle_TextAppearance_LabelTextStyle)
layoutParams = LayoutParams(
@@ -77,7 +72,7 @@ class TextFieldView @JvmOverloads constructor(context: Context,
}
}
}
- private val valueView = AppCompatTextView(context).apply {
+ protected val valueView = AppCompatTextView(context).apply {
setTextAppearance(context,
R.style.KeepassDXStyle_TextAppearance_TextNode)
layoutParams = LayoutParams(
@@ -131,46 +126,46 @@ class TextFieldView @JvmOverloads constructor(context: Context,
private fun buildViews() {
copyButton.apply {
id = copyButtonId
- layoutParams = (layoutParams as LayoutParams?).also {
- it?.addRule(ALIGN_PARENT_RIGHT)
+ layoutParams = (layoutParams as LayoutParams?)?.also {
+ it.addRule(ALIGN_PARENT_RIGHT)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- it?.addRule(ALIGN_PARENT_END)
+ it.addRule(ALIGN_PARENT_END)
}
}
}
showButton.apply {
id = showButtonId
- layoutParams = (layoutParams as LayoutParams?).also {
+ layoutParams = (layoutParams as LayoutParams?)?.also {
if (copyButton.isVisible) {
- it?.addRule(LEFT_OF, copyButtonId)
+ it.addRule(LEFT_OF, copyButtonId)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- it?.addRule(START_OF, copyButtonId)
+ it.addRule(START_OF, copyButtonId)
}
} else {
- it?.addRule(ALIGN_PARENT_RIGHT)
+ it.addRule(ALIGN_PARENT_RIGHT)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- it?.addRule(ALIGN_PARENT_END)
+ it.addRule(ALIGN_PARENT_END)
}
}
}
}
labelView.apply {
id = labelViewId
- layoutParams = (layoutParams as LayoutParams?).also {
- it?.addRule(LEFT_OF, showButtonId)
+ layoutParams = (layoutParams as LayoutParams?)?.also {
+ it.addRule(LEFT_OF, showButtonId)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- it?.addRule(START_OF, showButtonId)
+ it.addRule(START_OF, showButtonId)
}
}
}
valueView.apply {
id = valueViewId
- layoutParams = (layoutParams as LayoutParams?).also {
- it?.addRule(LEFT_OF, showButtonId)
+ layoutParams = (layoutParams as LayoutParams?)?.also {
+ it.addRule(LEFT_OF, showButtonId)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- it?.addRule(START_OF, showButtonId)
+ it.addRule(START_OF, showButtonId)
}
- it?.addRule(BELOW, labelViewId)
+ it.addRule(BELOW, labelViewId)
}
}
}
@@ -188,7 +183,7 @@ class TextFieldView @JvmOverloads constructor(context: Context,
labelView.text = value
}
- fun setLabel(@StringRes labelId: Int) {
+ open fun setLabel(@StringRes labelId: Int) {
labelView.setText(labelId)
}
@@ -197,17 +192,11 @@ class TextFieldView @JvmOverloads constructor(context: Context,
return valueView.text.toString()
}
set(value) {
- val spannableString =
- if (PreferencesUtil.colorizePassword(context)
- && TemplateField.isStandardPasswordName(context, label))
- PasswordGenerator.getColorizedPassword(value)
- else
- SpannableString(value)
- valueView.text = spannableString
+ valueView.text = value
changeProtectedValueParameters()
}
- fun setValue(@StringRes valueId: Int) {
+ open fun setValue(@StringRes valueId: Int) {
value = resources.getString(valueId)
changeProtectedValueParameters()
}
@@ -237,7 +226,7 @@ class TextFieldView @JvmOverloads constructor(context: Context,
invalidate()
}
- private fun changeProtectedValueParameters() {
+ protected fun changeProtectedValueParameters() {
valueView.apply {
if (showButton.isVisible) {
applyHiddenStyle(showButton.isSelected)
diff --git a/app/src/main/res/drawable/ic_shield_white_24dp.xml b/app/src/main/res/drawable/ic_shield_white_24dp.xml
new file mode 100644
index 000000000..97970286a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_shield_white_24dp.xml
@@ -0,0 +1,9 @@
+
+
+