Change credit card implementation

This commit is contained in:
J-Jamet
2021-05-16 23:19:45 +02:00
parent fac727cd3d
commit 80c0152d46
14 changed files with 117 additions and 266 deletions

View File

@@ -49,7 +49,7 @@ import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.node.NodeId import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.education.EntryActivityEducation import com.kunzisoft.keepass.education.EntryActivityEducation
import com.kunzisoft.keepass.magikeyboard.MagikIME import com.kunzisoft.keepass.magikeyboard.MagikIME
import com.kunzisoft.keepass.model.TemplatesCustomFields import com.kunzisoft.keepass.database.element.template.TemplatesFields
import com.kunzisoft.keepass.model.EntryAttachmentState import com.kunzisoft.keepass.model.EntryAttachmentState
import com.kunzisoft.keepass.model.StreamDirection import com.kunzisoft.keepass.model.StreamDirection
import com.kunzisoft.keepass.otp.OtpEntryFields import com.kunzisoft.keepass.otp.OtpEntryFields
@@ -334,7 +334,7 @@ class EntryActivity : LockingActivity() {
clipboardHelper?.timeoutCopyToClipboard( clipboardHelper?.timeoutCopyToClipboard(
value.toString(), value.toString(),
getString(R.string.copy_field, getString(R.string.copy_field,
TemplatesCustomFields.getLocalizedName(applicationContext, field.name)) TemplatesFields.getLocalizedName(applicationContext, field.name))
) )
} }
} else { } else {

View File

@@ -41,19 +41,16 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.DateInstant import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.database.element.security.ProtectedString import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.element.template.Template import com.kunzisoft.keepass.database.element.template.*
import com.kunzisoft.keepass.database.element.template.TemplateAttribute
import com.kunzisoft.keepass.database.element.template.TemplateAttributeAction
import com.kunzisoft.keepass.database.element.template.TemplateAttributeType
import com.kunzisoft.keepass.education.EntryEditActivityEducation import com.kunzisoft.keepass.education.EntryEditActivityEducation
import com.kunzisoft.keepass.icons.IconDrawableFactory import com.kunzisoft.keepass.icons.IconDrawableFactory
import com.kunzisoft.keepass.model.* import com.kunzisoft.keepass.model.*
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_EXPIRATION import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_EXPIRATION
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_NOTES import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_NOTES
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_PASSWORD import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_PASSWORD
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_TITLE import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_TITLE
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_URL import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_URL
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_USERNAME import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_USERNAME
import com.kunzisoft.keepass.otp.OtpEntryFields import com.kunzisoft.keepass.otp.OtpEntryFields
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.view.* import com.kunzisoft.keepass.view.*
@@ -352,7 +349,7 @@ class EntryEditFragment : StylishFragment() {
// Add an action icon if needed // Add an action icon if needed
return context?.let { return context?.let {
EntryEditFieldView(it).apply { EntryEditFieldView(it).apply {
label = TemplatesCustomFields.getLocalizedName(context, field.name) label = TemplatesFields.getLocalizedName(context, field.name)
setProtection(field.protectedValue.isProtected, mHideProtectedValue) setProtection(field.protectedValue.isProtected, mHideProtectedValue)
setValue(field.protectedValue.toString(), when (templateAttribute.type) { setValue(field.protectedValue.toString(), when (templateAttribute.type) {
TemplateAttributeType.MULTILINE -> EntryEditFieldView.TextType.MULTI_LINE TemplateAttributeType.MULTILINE -> EntryEditFieldView.TextType.MULTI_LINE
@@ -385,7 +382,7 @@ class EntryEditFragment : StylishFragment() {
field: Field): View? { field: Field): View? {
return context?.let { return context?.let {
DateTimeView(it).apply { DateTimeView(it).apply {
label = TemplatesCustomFields.getLocalizedName(context, field.name) label = TemplatesFields.getLocalizedName(context, field.name)
try { try {
val value = field.protectedValue.toString() val value = field.protectedValue.toString()
activation = value.trim().isNotEmpty() activation = value.trim().isNotEmpty()
@@ -525,7 +522,7 @@ class EntryEditFragment : StylishFragment() {
private data class FieldId(var viewId: Int, var protected: Boolean) private data class FieldId(var viewId: Int, var protected: Boolean)
private fun isStandardFieldName(name: String): Boolean { private fun isStandardFieldName(name: String): Boolean {
return TemplatesCustomFields.isStandardFieldName(name) return TemplatesFields.isStandardFieldName(name)
} }
private fun containsCustomFieldName(name: String): Boolean { private fun containsCustomFieldName(name: String): Boolean {

View File

@@ -48,7 +48,7 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.icon.IconImage
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.model.TemplatesCustomFields import com.kunzisoft.keepass.database.element.template.TemplatesFields
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 java.util.* import java.util.*
@@ -121,80 +121,71 @@ object AutofillHelper {
builder.setValue(passwordId, AutofillValue.forText(entryInfo.password)) builder.setValue(passwordId, AutofillValue.forText(entryInfo.password))
} }
if (entryInfo.expires) {
// get month (month in database entry is stored as String in the format MM)
val month = entryInfo.expiryTime.getMonthInt()
// get year (year in database entry is stored as String in the format YY)
val year = entryInfo.expiryTime.getYearInt()
struct.ccExpDateId?.let {
if (struct.isWebView) {
// set date string as defined in https://html.spec.whatwg.org
val dateString = "$year\u002D$month"
builder.setValue(it, AutofillValue.forText(dateString))
} else {
val calendar = Calendar.getInstance()
calendar.clear()
calendar[Calendar.YEAR] = year
// Month value is 0-based. e.g., 0 for January
calendar[Calendar.MONTH] = month - 1
val date = calendar.timeInMillis
builder.setValue(it, AutofillValue.forDate(date))
}
}
struct.ccExpDateMonthId?.let {
if (struct.isWebView) {
builder.setValue(it, AutofillValue.forText(month.toString()))
} else {
if (struct.ccExpMonthOptions != null) {
// index starts at 0
builder.setValue(it, AutofillValue.forList(month - 1))
} else {
builder.setValue(it, AutofillValue.forText(month.toString()))
}
}
}
struct.ccExpDateYearId?.let {
var autofillValue: AutofillValue? = null
struct.ccExpYearOptions?.let { options ->
var yearIndex = options.indexOf(year.toString().substring(0, 2))
if (yearIndex == -1) {
yearIndex = options.indexOf(year.toString())
}
if (yearIndex != -1) {
autofillValue = AutofillValue.forList(yearIndex)
builder.setValue(it, autofillValue)
}
}
if (autofillValue == null) {
builder.setValue(it, AutofillValue.forText(year.toString()))
}
}
}
for (field in entryInfo.customFields) { for (field in entryInfo.customFields) {
if (field.name == TemplatesCustomFields.CREDIT_CARD_CARDHOLDER) { if (field.name == TemplatesFields.CREDIT_CARD_CARDHOLDER) {
struct.ccNameId?.let { ccNameId -> struct.ccNameId?.let { ccNameId ->
builder.setValue(ccNameId, AutofillValue.forText(field.protectedValue.stringValue)) builder.setValue(ccNameId, AutofillValue.forText(field.protectedValue.stringValue))
} }
} }
if (field.name == TemplatesCustomFields.CREDIT_CARD_NUMBER) { if (field.name == TemplatesFields.CREDIT_CARD_NUMBER) {
struct.ccnId?.let { ccnId -> struct.ccnId?.let { ccnId ->
builder.setValue(ccnId, AutofillValue.forText(field.protectedValue.stringValue)) builder.setValue(ccnId, AutofillValue.forText(field.protectedValue.stringValue))
} }
} }
if (field.name == TemplatesCustomFields.CREDIT_CARD_EXPIRATION) { if (field.name == TemplatesFields.CREDIT_CARD_CVV) {
// the database stores the expiration month and year as a String
// of length four in the format MMYY
if (field.protectedValue.stringValue.length != 4) continue
// get month (month in database entry is stored as String in the format MM)
val monthString = field.protectedValue.stringValue.substring(0, 2)
val month = monthString.toIntOrNull() ?: 0
if (month < 1 || month > 12) continue
// get year (year in database entry is stored as String in the format YY)
val yearString = field.protectedValue.stringValue.substring(2, 4)
val year = "20$yearString".toIntOrNull() ?: 0
if (year == 0) continue
struct.ccExpDateId?.let {
if (struct.isWebView) {
// set date string as defined in https://html.spec.whatwg.org
val dateString = "20$yearString\u002D$monthString"
builder.setValue(it, AutofillValue.forText(dateString))
} else {
val calendar = Calendar.getInstance()
calendar.clear()
calendar[Calendar.YEAR] = year
// Month value is 0-based. e.g., 0 for January
calendar[Calendar.MONTH] = month - 1
val date = calendar.timeInMillis
builder.setValue(it, AutofillValue.forDate(date))
}
}
struct.ccExpDateMonthId?.let {
if (struct.isWebView) {
builder.setValue(it, AutofillValue.forText(monthString))
} else {
if (struct.ccExpMonthOptions != null) {
// index starts at 0
builder.setValue(it, AutofillValue.forList(month - 1))
} else {
builder.setValue(it, AutofillValue.forText(monthString))
}
}
}
struct.ccExpDateYearId?.let {
var autofillValue: AutofillValue? = null
struct.ccExpYearOptions?.let { options ->
var yearIndex = options.indexOf(yearString)
if (yearIndex == -1) {
yearIndex = options.indexOf("20$yearString")
}
if (yearIndex != -1) {
autofillValue = AutofillValue.forList(yearIndex)
builder.setValue(it, autofillValue)
}
}
if (autofillValue == null) {
builder.setValue(it, AutofillValue.forText("20$yearString"))
}
}
}
if (field.name == TemplatesCustomFields.CREDIT_CARD_CVV) {
struct.cvvId?.let { cvvId -> struct.cvvId?.let { cvvId ->
builder.setValue(cvvId, AutofillValue.forText(field.protectedValue.stringValue)) builder.setValue(cvvId, AutofillValue.forText(field.protectedValue.stringValue))
} }

View File

@@ -91,6 +91,14 @@ class DateInstant : Parcelable {
} }
} }
fun getMonthInt(): Int {
return Companion.getMonthInt(jDate)
}
fun getYearInt(): Int {
return Companion.getYearInt(jDate)
}
override fun toString(): String { override fun toString(): String {
return when (type) { return when (type) {
Type.DATE -> dateFormat.format(jDate) Type.DATE -> dateFormat.format(jDate)
@@ -173,11 +181,21 @@ class DateInstant : Parcelable {
} }
fun getMonthInt(date: Date): Int {
val dateFormat = SimpleDateFormat("MM", Locale.ENGLISH)
return dateFormat.format(date).toInt()
}
fun getYearInt(date: Date): Int {
val dateFormat = SimpleDateFormat("yyyy", Locale.ENGLISH)
return dateFormat.format(date).toInt()
}
fun getDateTimeString(resources: Resources, date: Date): String { fun getDateTimeString(resources: Resources, date: Date): String {
return java.text.DateFormat.getDateTimeInstance( return java.text.DateFormat.getDateTimeInstance(
java.text.DateFormat.MEDIUM, java.text.DateFormat.MEDIUM,
java.text.DateFormat.SHORT, java.text.DateFormat.SHORT,
ConfigurationCompat.getLocales(resources.configuration)[0]) ConfigurationCompat.getLocales(resources.configuration)[0])
.format(date) .format(date)
} }

View File

@@ -23,12 +23,11 @@ import android.os.ParcelUuid
import android.os.Parcelable import android.os.Parcelable
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_EXPIRATION import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_EXPIRATION
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_NOTES import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_NOTES
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_PASSWORD import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_PASSWORD
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_TITLE import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_URL
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_URL import com.kunzisoft.keepass.database.element.template.TemplatesFields.STANDARD_USERNAME
import com.kunzisoft.keepass.model.TemplatesCustomFields.STANDARD_USERNAME
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList

View File

@@ -73,7 +73,11 @@ class TemplateEngine(private val databaseKDBX: DatabaseKDBX) {
try { try {
val attributeName = key.substring(TEMPLATE_ATTRIBUTE_TITLE_PREFIX.length) val attributeName = key.substring(TEMPLATE_ATTRIBUTE_TITLE_PREFIX.length)
val attribute = getOrRetrieveAttributeFromName(attributes, attributeName) val attribute = getOrRetrieveAttributeFromName(attributes, attributeName)
attribute.attribute.label = value.stringValue var referenceLabel = value.stringValue
if (referenceLabel.equals(TEMPLATE_ATTRIBUTE_TITLE_EXPIRATION, true)) {
referenceLabel = TemplatesFields.STANDARD_EXPIRATION
}
attribute.attribute.label = referenceLabel
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Unable to retrieve template position", e) Log.e(TAG, "Unable to retrieve template position", e)
} }
@@ -154,6 +158,7 @@ class TemplateEngine(private val databaseKDBX: DatabaseKDBX) {
private const val TEMPLATE_ENTRY_UUID = "_etm_template_uuid" private const val TEMPLATE_ENTRY_UUID = "_etm_template_uuid"
private const val TEMPLATE_ATTRIBUTE_POSITION_PREFIX = "_etm_position" private const val TEMPLATE_ATTRIBUTE_POSITION_PREFIX = "_etm_position"
private const val TEMPLATE_ATTRIBUTE_TITLE_PREFIX = "_etm_title" private const val TEMPLATE_ATTRIBUTE_TITLE_PREFIX = "_etm_title"
private const val TEMPLATE_ATTRIBUTE_TITLE_EXPIRATION = "@exp_date"
private const val TEMPLATE_ATTRIBUTE_TYPE_PREFIX = "_etm_type" private const val TEMPLATE_ATTRIBUTE_TYPE_PREFIX = "_etm_type"
private const val TEMPLATE_ATTRIBUTE_TYPE_PROTECTED = "Protected" private const val TEMPLATE_ATTRIBUTE_TYPE_PROTECTED = "Protected"
private const val TEMPLATE_ATTRIBUTE_TYPE_INLINE = "Inline" private const val TEMPLATE_ATTRIBUTE_TYPE_INLINE = "Inline"

View File

@@ -1,9 +1,9 @@
package com.kunzisoft.keepass.model package com.kunzisoft.keepass.database.element.template
import android.content.Context import android.content.Context
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
object TemplatesCustomFields { object TemplatesFields {
const val STANDARD_TITLE = "title" const val STANDARD_TITLE = "title"
const val STANDARD_USERNAME = "username" const val STANDARD_USERNAME = "username"
@@ -14,7 +14,6 @@ object TemplatesCustomFields {
const val CREDIT_CARD_CARDHOLDER = "Card holder" const val CREDIT_CARD_CARDHOLDER = "Card holder"
const val CREDIT_CARD_NUMBER = "Number" const val CREDIT_CARD_NUMBER = "Number"
const val CREDIT_CARD_EXPIRATION = "@exp_date"
const val CREDIT_CARD_CVV = "CVV" const val CREDIT_CARD_CVV = "CVV"
private const val CREDIT_CARD_PIN = "PIN" private const val CREDIT_CARD_PIN = "PIN"
@@ -42,9 +41,10 @@ object TemplatesCustomFields {
CREDIT_CARD_CARDHOLDER -> context.getString(R.string.credit_card_cardholder) CREDIT_CARD_CARDHOLDER -> context.getString(R.string.credit_card_cardholder)
CREDIT_CARD_NUMBER -> context.getString(R.string.credit_card_number) CREDIT_CARD_NUMBER -> context.getString(R.string.credit_card_number)
CREDIT_CARD_EXPIRATION -> context.getString(R.string.credit_card_expiration)
CREDIT_CARD_CVV -> context.getString(R.string.credit_card_security_code) CREDIT_CARD_CVV -> context.getString(R.string.credit_card_security_code)
CREDIT_CARD_PIN -> context.getString(R.string.credit_card_pin) CREDIT_CARD_PIN -> context.getString(R.string.credit_card_pin)
// TODO Others translations
else -> fieldName else -> fieldName
} }
} }

View File

@@ -3,8 +3,10 @@ package com.kunzisoft.keepass.model
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
data class CreditCard(val cardholder: String?, val number: String?, data class CreditCard(val cardholder: String?,
val expiration: String?, val cvv: String?) : Parcelable { val number: String?,
val expiration: String?,
val cvv: String?) : Parcelable {
constructor(parcel: Parcel) : this( constructor(parcel: Parcel) : this(
parcel.readString(), parcel.readString(),
@@ -20,22 +22,6 @@ data class CreditCard(val cardholder: String?, val number: String?,
parcel.writeString(cvv) parcel.writeString(cvv)
} }
fun getExpirationMonth(): String {
return if (expiration?.length == 4) {
expiration.substring(0, 2)
} else {
""
}
}
fun getExpirationYear(): String {
return if (expiration?.length == 4) {
expiration.substring(2, 4)
} else {
""
}
}
override fun describeContents(): Int { override fun describeContents(): Int {
return 0 return 0
} }

View File

@@ -24,6 +24,7 @@ import android.os.Parcelable
import com.kunzisoft.keepass.database.element.Attachment import com.kunzisoft.keepass.database.element.Attachment
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.security.ProtectedString import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.element.template.TemplatesFields
import com.kunzisoft.keepass.otp.OtpElement import com.kunzisoft.keepass.otp.OtpElement
import com.kunzisoft.keepass.otp.OtpEntryFields import com.kunzisoft.keepass.otp.OtpEntryFields
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD
@@ -166,19 +167,19 @@ class EntryInfo : NodeInfo {
creditCard?.let { cc -> creditCard?.let { cc ->
cc.cardholder?.let { cc.cardholder?.let {
val v = ProtectedString(false, it) val v = ProtectedString(false, it)
addUniqueField(Field(TemplatesCustomFields.CREDIT_CARD_CARDHOLDER, v)) addUniqueField(Field(TemplatesFields.CREDIT_CARD_CARDHOLDER, v))
} }
cc.expiration?.let { cc.expiration?.let {
val v = ProtectedString(false, it) expires = true
addUniqueField(Field(TemplatesCustomFields.CREDIT_CARD_EXPIRATION, v)) // TODO Expiration expiryTime = it
} }
cc.number?.let { cc.number?.let {
val v = ProtectedString(false, it) val v = ProtectedString(false, it)
addUniqueField(Field(TemplatesCustomFields.CREDIT_CARD_NUMBER, v)) addUniqueField(Field(TemplatesFields.CREDIT_CARD_NUMBER, v))
} }
cc.cvv?.let { cc.cvv?.let {
val v = ProtectedString(true, it) val v = ProtectedString(true, it)
addUniqueField(Field(TemplatesCustomFields.CREDIT_CARD_CVV, v)) addUniqueField(Field(TemplatesFields.CREDIT_CARD_CVV, v))
} }
} }
} }

View File

@@ -42,7 +42,7 @@ import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.utils.UuidUtil import com.kunzisoft.keepass.utils.UuidUtil
import com.kunzisoft.keepass.model.EntryAttachmentState import com.kunzisoft.keepass.model.EntryAttachmentState
import com.kunzisoft.keepass.model.StreamDirection import com.kunzisoft.keepass.model.StreamDirection
import com.kunzisoft.keepass.model.TemplatesCustomFields import com.kunzisoft.keepass.database.element.template.TemplatesFields
import com.kunzisoft.keepass.otp.OtpElement import com.kunzisoft.keepass.otp.OtpElement
import com.kunzisoft.keepass.otp.OtpType import com.kunzisoft.keepass.otp.OtpType
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
@@ -313,7 +313,7 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
onCopyButtonClickListener: OnClickListener?) { onCopyButtonClickListener: OnClickListener?) {
extraFieldsListView.addView(EntryFieldView(context).apply { extraFieldsListView.addView(EntryFieldView(context).apply {
setLabel(TemplatesCustomFields.getLocalizedName(context, title)) setLabel(TemplatesFields.getLocalizedName(context, title))
setValue(value.toString(), value.isProtected) setValue(value.toString(), value.isProtected)
setAutoLink() setAutoLink()
activateCopyButton(allowCopy) activateCopyButton(allowCopy)

View File

@@ -1,143 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2019 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 <http://www.gnu.org/licenses/>.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:importantForAutofill="noExcludeDescendants"
android:orientation="vertical"
android:padding="@dimen/default_margin"
tools:targetApi="o">
<androidx.cardview.widget.CardView
android:id="@+id/card_view_cc_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="4dp"
app:cardCornerRadius="4dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="UnusedAttribute">
<TextView
android:id="@+id/creditCardholderNameLabel"
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/creditCardNumberField"
android:text="@string/credit_card_cardholder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/creditCardholderNameField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:focusedByDefault="true"
android:hint="@string/credit_card_cardholder"
android:inputType="textPersonName"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/creditCardholderNameLabel" />
<TextView
android:id="@+id/creditCardNumberLabel"
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/creditCardNumberField"
android:text="@string/credit_card_number"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/creditCardholderNameField" />
<EditText
android:id="@+id/creditCardNumberField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:focusedByDefault="true"
android:hint="@string/credit_card_number"
android:inputType="number"
android:maxLength="16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/creditCardNumberLabel" />
<TextView
android:id="@+id/creditCardExpirationLabel"
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/credit_card_expiration"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/creditCardNumberField" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/expirationMonth"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="48dp"
app:layout_constraintEnd_toStartOf="@+id/expirationYear"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/creditCardExpirationLabel" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/expirationYear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:minHeight="48dp"
app:layout_constraintStart_toEndOf="@+id/expirationMonth"
app:layout_constraintTop_toBottomOf="@+id/creditCardExpirationLabel" />
<TextView
android:id="@+id/creditCardSecurityCodeLabel"
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/creditCardSecurityCode"
android:text="@string/credit_card_security_code"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/expirationYear" />
<!-- American Express has four digits? -->
<EditText
android:id="@+id/creditCardSecurityCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/creditCardSecurityCodeLabel"
android:ems="6"
android:hint="@string/credit_card_security_code"
android:inputType="number"
android:maxLength="4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/creditCardSecurityCodeLabel" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>

View File

@@ -371,7 +371,6 @@
<string name="otp_algorithm">Algorithmus</string> <string name="otp_algorithm">Algorithmus</string>
<string name="credit_card_cardholder">Karteninhaber</string> <string name="credit_card_cardholder">Karteninhaber</string>
<string name="credit_card_number">Nummer</string> <string name="credit_card_number">Nummer</string>
<string name="credit_card_expiration">Gültig bis</string>
<string name="credit_card_security_code">Prüfnummer</string> <string name="credit_card_security_code">Prüfnummer</string>
<string name="entry_otp">OTP</string> <string name="entry_otp">OTP</string>
<string name="error_invalid_OTP">Ungültiges OTP-Geheimnis.</string> <string name="error_invalid_OTP">Ungültiges OTP-Geheimnis.</string>

View File

@@ -371,7 +371,6 @@
<string name="otp_algorithm">Algorithme</string> <string name="otp_algorithm">Algorithme</string>
<string name="credit_card_cardholder">Titulaire de la carte</string> <string name="credit_card_cardholder">Titulaire de la carte</string>
<string name="credit_card_number">Numéros</string> <string name="credit_card_number">Numéros</string>
<string name="credit_card_expiration">Expiration</string>
<string name="credit_card_security_code">CVV</string> <string name="credit_card_security_code">CVV</string>
<string name="credit_card_pin">PIN</string> <string name="credit_card_pin">PIN</string>
<string name="entry_otp">OTP</string> <string name="entry_otp">OTP</string>

View File

@@ -101,7 +101,6 @@
<string name="otp_algorithm">Algorithm</string> <string name="otp_algorithm">Algorithm</string>
<string name="credit_card_cardholder">Cardholder</string> <string name="credit_card_cardholder">Cardholder</string>
<string name="credit_card_number">Number</string> <string name="credit_card_number">Number</string>
<string name="credit_card_expiration">Expiration</string>
<string name="credit_card_security_code">CVV</string> <string name="credit_card_security_code">CVV</string>
<string name="credit_card_pin">PIN</string> <string name="credit_card_pin">PIN</string>
<string name="entry_otp">OTP</string> <string name="entry_otp">OTP</string>