mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Let user save credit card details after filling out new form
This commit is contained in:
@@ -191,16 +191,15 @@ class EntryEditActivity : LockingActivity(),
|
|||||||
val registerInfo = EntrySelectionHelper.retrieveRegisterInfoFromIntent(intent)
|
val registerInfo = EntrySelectionHelper.retrieveRegisterInfoFromIntent(intent)
|
||||||
val searchInfo: SearchInfo? = registerInfo?.searchInfo
|
val searchInfo: SearchInfo? = registerInfo?.searchInfo
|
||||||
?: EntrySelectionHelper.retrieveSearchInfoFromIntent(intent)
|
?: EntrySelectionHelper.retrieveSearchInfoFromIntent(intent)
|
||||||
registerInfo?.username?.let {
|
|
||||||
tempEntryInfo?.username = it
|
|
||||||
}
|
|
||||||
registerInfo?.password?.let {
|
|
||||||
tempEntryInfo?.password = it
|
|
||||||
}
|
|
||||||
searchInfo?.let { tempSearchInfo ->
|
searchInfo?.let { tempSearchInfo ->
|
||||||
tempEntryInfo?.saveSearchInfo(mDatabase, tempSearchInfo)
|
tempEntryInfo?.saveSearchInfo(mDatabase, tempSearchInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerInfo?.let { regInfo ->
|
||||||
|
tempEntryInfo?.saveRegisterInfo(mDatabase, regInfo)
|
||||||
|
}
|
||||||
|
|
||||||
// Build fragment to manage entry modification
|
// Build fragment to manage entry modification
|
||||||
entryEditFragment = supportFragmentManager.findFragmentByTag(ENTRY_EDIT_FRAGMENT_TAG) as? EntryEditFragment?
|
entryEditFragment = supportFragmentManager.findFragmentByTag(ENTRY_EDIT_FRAGMENT_TAG) as? EntryEditFragment?
|
||||||
if (entryEditFragment == null) {
|
if (entryEditFragment == null) {
|
||||||
@@ -408,7 +407,25 @@ class EntryEditActivity : LockingActivity(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addNewCreditCard() {
|
private fun addNewCreditCard() {
|
||||||
val cc = CreditCard(entryEditFragment?.getExtraFields())
|
var cardholder: String? = null
|
||||||
|
var number: String? = null
|
||||||
|
var expiration: String? = null
|
||||||
|
var cvv: String? = null
|
||||||
|
|
||||||
|
entryEditFragment?.getExtraFields()?.forEach() { field ->
|
||||||
|
when (field.name) {
|
||||||
|
CreditCardCustomFields.CC_CARDHOLDER_FIELD_NAME ->
|
||||||
|
cardholder = field.protectedValue.stringValue
|
||||||
|
CreditCardCustomFields.CC_NUMBER_FIELD_NAME ->
|
||||||
|
number = field.protectedValue.stringValue
|
||||||
|
CreditCardCustomFields.CC_EXP_FIELD_NAME ->
|
||||||
|
expiration = field.protectedValue.stringValue
|
||||||
|
CreditCardCustomFields.CC_CVV_FIELD_NAME ->
|
||||||
|
cvv = field.protectedValue.stringValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cc = CreditCard(cardholder, number, expiration, cvv)
|
||||||
CreditCardDetailsDialogFragment.build(cc).show(supportFragmentManager, "CreditCardDialog")
|
CreditCardDetailsDialogFragment.build(cc).show(supportFragmentManager, "CreditCardDialog")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package com.kunzisoft.keepass.activities.dialogs
|
|||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
|
||||||
import android.text.TextWatcher
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
@@ -13,9 +11,10 @@ import android.widget.Spinner
|
|||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.model.Field
|
|
||||||
import com.kunzisoft.keepass.model.CreditCardCustomFields.buildAllFields
|
|
||||||
import com.kunzisoft.keepass.model.CreditCard
|
import com.kunzisoft.keepass.model.CreditCard
|
||||||
|
import com.kunzisoft.keepass.model.CreditCardCustomFields.buildAllFields
|
||||||
|
import com.kunzisoft.keepass.model.Field
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class CreditCardDetailsDialogFragment : DialogFragment() {
|
class CreditCardDetailsDialogFragment : DialogFragment() {
|
||||||
private var mCreditCard: CreditCard? = null
|
private var mCreditCard: CreditCard? = null
|
||||||
@@ -28,9 +27,6 @@ class CreditCardDetailsDialogFragment : DialogFragment() {
|
|||||||
private var mCcExpirationMonthSpinner: Spinner? = null
|
private var mCcExpirationMonthSpinner: Spinner? = null
|
||||||
private var mCcExpirationYearSpinner: Spinner? = null
|
private var mCcExpirationYearSpinner: Spinner? = null
|
||||||
|
|
||||||
private var mCcCardNumberWellFormed: Boolean = false
|
|
||||||
private var mCcSecurityCodeWellFormed: Boolean = false
|
|
||||||
|
|
||||||
private var mPositiveButton: Button? = null
|
private var mPositiveButton: Button? = null
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
@@ -57,8 +53,6 @@ class CreditCardDetailsDialogFragment : DialogFragment() {
|
|||||||
if (d != null) {
|
if (d != null) {
|
||||||
mPositiveButton = d.getButton(Dialog.BUTTON_POSITIVE) as Button
|
mPositiveButton = d.getButton(Dialog.BUTTON_POSITIVE) as Button
|
||||||
mPositiveButton?.run {
|
mPositiveButton?.run {
|
||||||
isEnabled = mCcSecurityCodeWellFormed && mCcCardNumberWellFormed
|
|
||||||
attachListeners()
|
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
submitDialog()
|
submitDialog()
|
||||||
}
|
}
|
||||||
@@ -75,7 +69,7 @@ class CreditCardDetailsDialogFragment : DialogFragment() {
|
|||||||
val ccNumber = mCcCardNumber?.text?.toString() ?: ""
|
val ccNumber = mCcCardNumber?.text?.toString() ?: ""
|
||||||
|
|
||||||
val month = mCcExpirationMonthSpinner?.selectedItem?.toString() ?: ""
|
val month = mCcExpirationMonthSpinner?.selectedItem?.toString() ?: ""
|
||||||
val year = mCcExpirationYearSpinner?.selectedItem?.toString() ?: ""
|
val year = mCcExpirationYearSpinner?.selectedItem?.toString()?.substring(2,4) ?: ""
|
||||||
|
|
||||||
val cvv = mCcSecurityCode?.text?.toString() ?: ""
|
val cvv = mCcSecurityCode?.text?.toString() ?: ""
|
||||||
val ccName = mCcCardholderName?.text?.toString() ?: ""
|
val ccName = mCcCardholderName?.text?.toString() ?: ""
|
||||||
@@ -106,37 +100,43 @@ class CreditCardDetailsDialogFragment : DialogFragment() {
|
|||||||
activity?.let { activity ->
|
activity?.let { activity ->
|
||||||
val root = activity.layoutInflater.inflate(R.layout.entry_cc_details_dialog, null)
|
val root = activity.layoutInflater.inflate(R.layout.entry_cc_details_dialog, null)
|
||||||
|
|
||||||
mCcCardholderName = root?.findViewById(R.id.creditCardholderNameField)
|
root?.run {
|
||||||
|
mCcCardholderName = findViewById(R.id.creditCardholderNameField)
|
||||||
|
mCcCardNumber = findViewById(R.id.creditCardNumberField)
|
||||||
|
mCcSecurityCode = findViewById(R.id.creditCardSecurityCode)
|
||||||
|
mCcExpirationMonthSpinner = findViewById(R.id.expirationMonth)
|
||||||
|
mCcExpirationYearSpinner = findViewById(R.id.expirationYear)
|
||||||
|
|
||||||
mCcExpirationMonthSpinner = root?.findViewById(R.id.expirationMonth)
|
mCreditCard?.cardholder?.let {
|
||||||
mCcExpirationYearSpinner = root?.findViewById(R.id.expirationYear)
|
mCcCardholderName?.setText(it)
|
||||||
|
}
|
||||||
mCcCardNumber = root?.findViewById(R.id.creditCardNumberField)
|
mCreditCard?.number?.let {
|
||||||
mCcSecurityCode = root?.findViewById(R.id.creditCardSecurityCode)
|
mCcCardNumber?.setText(it)
|
||||||
|
}
|
||||||
mCreditCard?.let {
|
mCreditCard?.cvv?.let {
|
||||||
mCcCardholderName!!.setText(it.cardholder)
|
mCcSecurityCode?.setText(it)
|
||||||
mCcCardNumberWellFormed = true
|
}
|
||||||
mCcCardNumber!!.setText(it.number)
|
|
||||||
mCcSecurityCodeWellFormed = true
|
|
||||||
mCcSecurityCode!!.setText(it.cvv)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val monthAdapter = ArrayAdapter.createFromResource(requireContext(),
|
val months = arrayOf("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12")
|
||||||
R.array.month_array, android.R.layout.simple_spinner_item)
|
mCcExpirationMonthSpinner?.let { spinner ->
|
||||||
monthAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
spinner.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, months)
|
||||||
mCcExpirationMonthSpinner!!.adapter = monthAdapter
|
mCreditCard?.let { cc ->
|
||||||
|
spinner.setSelection(getIndex(spinner, cc.getExpirationMonth()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mCreditCard?.let { mCcExpirationMonthSpinner!!.setSelection(
|
val years = arrayOfNulls<String>(5)
|
||||||
getIndex(mCcExpirationMonthSpinner!!, it.getExpirationMonth()) ) }
|
val year = Calendar.getInstance()[Calendar.YEAR]
|
||||||
|
for (i in years.indices) {
|
||||||
val yearAdapter = ArrayAdapter.createFromResource(requireContext(),
|
years[i] = (year + i).toString()
|
||||||
R.array.year_array, android.R.layout.simple_spinner_item)
|
}
|
||||||
yearAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
mCcExpirationYearSpinner?.let { spinner ->
|
||||||
mCcExpirationYearSpinner!!.adapter = yearAdapter
|
spinner.adapter = ArrayAdapter<String>(requireContext(), android.R.layout.simple_spinner_item, years)
|
||||||
|
mCreditCard?.let { cc ->
|
||||||
mCreditCard?.let { mCcExpirationYearSpinner!!.setSelection(
|
spinner.setSelection(getIndex(spinner, "20" + cc.getExpirationYear()))
|
||||||
getIndex(mCcExpirationYearSpinner!!, it.getExpirationYear()) ) }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val builder = AlertDialog.Builder(activity)
|
val builder = AlertDialog.Builder(activity)
|
||||||
|
|
||||||
@@ -155,44 +155,14 @@ class CreditCardDetailsDialogFragment : DialogFragment() {
|
|||||||
|
|
||||||
private fun getIndex(spinner: Spinner, value: String?): Int {
|
private fun getIndex(spinner: Spinner, value: String?): Int {
|
||||||
for (i in 0 until spinner.count) {
|
for (i in 0 until spinner.count) {
|
||||||
if (spinner.getItemAtPosition(i).toString().equals(value, ignoreCase = true)) {
|
if (spinner.getItemAtPosition(i).toString() == value) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun attachListeners() {
|
|
||||||
mCcCardNumber?.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun afterTextChanged(s: Editable?) {
|
|
||||||
val userString = s?.toString()
|
|
||||||
mCcCardNumberWellFormed = userString?.length == 16
|
|
||||||
mPositiveButton?.run {
|
|
||||||
isEnabled = mCcSecurityCodeWellFormed && mCcCardNumberWellFormed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
|
||||||
})
|
|
||||||
|
|
||||||
mCcSecurityCode?.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun afterTextChanged(s: Editable?) {
|
|
||||||
val userString = s?.toString()
|
|
||||||
mCcSecurityCodeWellFormed = (userString?.length == 3 || userString?.length == 4)
|
|
||||||
mPositiveButton?.run {
|
|
||||||
isEnabled = mCcSecurityCodeWellFormed && mCcCardNumberWellFormed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val KEY_CREDIT_CARD = "KEY_CREDIT_CARD"
|
private const val KEY_CREDIT_CARD = "KEY_CREDIT_CARD"
|
||||||
|
|
||||||
fun build(creditCard: CreditCard? = null): CreditCardDetailsDialogFragment {
|
fun build(creditCard: CreditCard? = null): CreditCardDetailsDialogFragment {
|
||||||
|
|||||||
@@ -137,14 +137,15 @@ object AutofillHelper {
|
|||||||
// of length four in the format MMYY
|
// of length four in the format MMYY
|
||||||
if (field.protectedValue.stringValue.length != 4) continue
|
if (field.protectedValue.stringValue.length != 4) continue
|
||||||
|
|
||||||
// get month (month in database entry is stored as String in the range 01..12)
|
// get month (month in database entry is stored as String in the format MM)
|
||||||
val monthString = field.protectedValue.stringValue.substring(0, 2)
|
val monthString = field.protectedValue.stringValue.substring(0, 2)
|
||||||
if (monthString !in context.resources.getStringArray(R.array.month_array)) continue
|
val month = monthString.toIntOrNull() ?: 0
|
||||||
|
if (month < 1 || month > 12) continue
|
||||||
|
|
||||||
val month = monthString.toInt()
|
// get year (year in database entry is stored as String in the format YY)
|
||||||
// get year (year in database entry is stored as String in the range 20..29)
|
|
||||||
val yearString = field.protectedValue.stringValue.substring(2, 4)
|
val yearString = field.protectedValue.stringValue.substring(2, 4)
|
||||||
if (yearString !in context.resources.getStringArray(R.array.year_array)) continue
|
val year = "20$yearString".toIntOrNull() ?: 0
|
||||||
|
if (year == 0) continue
|
||||||
|
|
||||||
struct.ccExpDateId?.let {
|
struct.ccExpDateId?.let {
|
||||||
if (struct.isWebView) {
|
if (struct.isWebView) {
|
||||||
@@ -154,7 +155,6 @@ object AutofillHelper {
|
|||||||
} else {
|
} else {
|
||||||
val calendar = Calendar.getInstance()
|
val calendar = Calendar.getInstance()
|
||||||
calendar.clear()
|
calendar.clear()
|
||||||
val year = "20$yearString".toInt()
|
|
||||||
calendar[Calendar.YEAR] = year
|
calendar[Calendar.YEAR] = year
|
||||||
// Month value is 0-based. e.g., 0 for January
|
// Month value is 0-based. e.g., 0 for January
|
||||||
calendar[Calendar.MONTH] = month - 1
|
calendar[Calendar.MONTH] = month - 1
|
||||||
@@ -164,34 +164,33 @@ object AutofillHelper {
|
|||||||
}
|
}
|
||||||
struct.ccExpDateMonthId?.let {
|
struct.ccExpDateMonthId?.let {
|
||||||
if (struct.isWebView) {
|
if (struct.isWebView) {
|
||||||
builder.setValue(it, AutofillValue.forText(month.toString()))
|
builder.setValue(it, AutofillValue.forText(monthString))
|
||||||
} else {
|
} else {
|
||||||
if (struct.ccExpMonthOptions != null) {
|
if (struct.ccExpMonthOptions != null) {
|
||||||
// index starts at 0
|
// index starts at 0
|
||||||
builder.setValue(it, AutofillValue.forList(month - 1))
|
builder.setValue(it, AutofillValue.forList(month - 1))
|
||||||
} else {
|
} else {
|
||||||
builder.setValue(it, AutofillValue.forText(month.toString()))
|
builder.setValue(it, AutofillValue.forText(monthString))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
struct.ccExpDateYearId?.let {
|
struct.ccExpDateYearId?.let {
|
||||||
if (struct.isWebView) {
|
var autofillValue: AutofillValue? = null
|
||||||
builder.setValue(it, AutofillValue.forText(yearString))
|
|
||||||
} else {
|
|
||||||
if (struct.ccExpYearOptions != null) {
|
|
||||||
var yearIndex = struct.ccExpYearOptions!!.indexOf(yearString)
|
|
||||||
|
|
||||||
if (yearIndex == -1) {
|
struct.ccExpYearOptions?.let { options ->
|
||||||
yearIndex = struct.ccExpYearOptions!!.indexOf("20$yearString")
|
var yearIndex = options.indexOf(yearString)
|
||||||
}
|
|
||||||
if (yearIndex != -1) {
|
if (yearIndex == -1) {
|
||||||
builder.setValue(it, AutofillValue.forList(yearIndex))
|
yearIndex = options.indexOf("20$yearString")
|
||||||
} else {
|
|
||||||
builder.setValue(it, AutofillValue.forText(yearString))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
builder.setValue(it, AutofillValue.forText(yearString))
|
|
||||||
}
|
}
|
||||||
|
if (yearIndex != -1) {
|
||||||
|
autofillValue = AutofillValue.forList(yearIndex)
|
||||||
|
builder.setValue(it, autofillValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autofillValue == null) {
|
||||||
|
builder.setValue(it, AutofillValue.forText("20$yearString"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.activities.AutofillLauncherActivity
|
import com.kunzisoft.keepass.activities.AutofillLauncherActivity
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||||
|
import com.kunzisoft.keepass.model.CreditCard
|
||||||
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.settings.AutofillSettingsActivity
|
import com.kunzisoft.keepass.settings.AutofillSettingsActivity
|
||||||
@@ -101,10 +102,15 @@ class KeeAutofillService : AutofillService() {
|
|||||||
callback)
|
callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else {
|
// TODO does it make sense to disable autofill here? how long?
|
||||||
// TODO: Disable autofill for the app for API level >= 28
|
// else {
|
||||||
// public FillResponse.Builder disableAutofill (long duration)
|
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
// }
|
// val builder = FillResponse.Builder()
|
||||||
|
// // disable for a while (duration in ms)
|
||||||
|
// builder.disableAutofill(5*60*1000)
|
||||||
|
// callback.onSuccess(builder.build())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,23 +165,40 @@ class KeeAutofillService : AutofillService() {
|
|||||||
RemoteViews(packageName, R.layout.item_autofill_unlock)
|
RemoteViews(packageName, R.layout.item_autofill_unlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell to service the interest to save credentials
|
// Tell the autofill framework the interest to save credentials
|
||||||
if (askToSaveData) {
|
if (askToSaveData) {
|
||||||
var types: Int = SaveInfo.SAVE_DATA_TYPE_GENERIC
|
var types: Int = SaveInfo.SAVE_DATA_TYPE_GENERIC
|
||||||
val info = ArrayList<AutofillId>()
|
val requiredIds = ArrayList<AutofillId>()
|
||||||
|
val optionalIds = ArrayList<AutofillId>()
|
||||||
|
|
||||||
// Only if at least a password
|
// Only if at least a password
|
||||||
parseResult.passwordId?.let { passwordInfo ->
|
parseResult.passwordId?.let { passwordInfo ->
|
||||||
parseResult.usernameId?.let { usernameInfo ->
|
parseResult.usernameId?.let { usernameInfo ->
|
||||||
types = types or SaveInfo.SAVE_DATA_TYPE_USERNAME
|
types = types or SaveInfo.SAVE_DATA_TYPE_USERNAME
|
||||||
info.add(usernameInfo)
|
requiredIds.add(usernameInfo)
|
||||||
}
|
}
|
||||||
types = types or SaveInfo.SAVE_DATA_TYPE_PASSWORD
|
types = types or SaveInfo.SAVE_DATA_TYPE_PASSWORD
|
||||||
info.add(passwordInfo)
|
requiredIds.add(passwordInfo)
|
||||||
}
|
}
|
||||||
if (info.isNotEmpty()) {
|
// or a credit card form
|
||||||
responseBuilder.setSaveInfo(
|
if (requiredIds.isEmpty()) {
|
||||||
SaveInfo.Builder(types, info.toTypedArray()).build()
|
parseResult.ccnId?.let { numberId ->
|
||||||
)
|
types = types or SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD
|
||||||
|
requiredIds.add(numberId)
|
||||||
|
Log.d(TAG, "Asking to save credit card number")
|
||||||
|
}
|
||||||
|
parseResult.ccExpDateId?.let { id -> optionalIds.add(id) }
|
||||||
|
parseResult.ccExpDateYearId?.let { id -> optionalIds.add(id) }
|
||||||
|
parseResult.ccExpDateMonthId?.let { id -> optionalIds.add(id) }
|
||||||
|
parseResult.ccNameId?.let { id -> optionalIds.add(id) }
|
||||||
|
parseResult.cvvId?.let { id -> optionalIds.add(id) }
|
||||||
|
}
|
||||||
|
if (requiredIds.isNotEmpty()) {
|
||||||
|
val builder = SaveInfo.Builder(types, requiredIds.toTypedArray())
|
||||||
|
if (optionalIds.isNotEmpty()) {
|
||||||
|
builder.setOptionalIds(optionalIds.toTypedArray())
|
||||||
|
}
|
||||||
|
responseBuilder.setSaveInfo(builder.build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,14 +250,27 @@ class KeeAutofillService : AutofillService() {
|
|||||||
&& autofillAllowedFor(parseResult.webDomain, webDomainBlocklist)) {
|
&& autofillAllowedFor(parseResult.webDomain, webDomainBlocklist)) {
|
||||||
Log.d(TAG, "autofill onSaveRequest password")
|
Log.d(TAG, "autofill onSaveRequest password")
|
||||||
|
|
||||||
|
|
||||||
|
if (parseResult.ccExpirationValue == null) {
|
||||||
|
if (parseResult.ccExpDateMonthValue != 0 && parseResult.ccExpDateYearValue != 0) {
|
||||||
|
parseResult.ccExpirationValue = parseResult.ccExpDateMonthValue.toString().padStart(2, '0') + parseResult.ccExpDateYearValue.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val creditCard = CreditCard(parseResult.ccName, parseResult.ccNumber,
|
||||||
|
parseResult.ccExpirationValue, parseResult.cvv)
|
||||||
|
|
||||||
// Show UI to save data
|
// Show UI to save data
|
||||||
val registerInfo = RegisterInfo(SearchInfo().apply {
|
val registerInfo = RegisterInfo(
|
||||||
applicationId = parseResult.applicationId
|
SearchInfo().apply {
|
||||||
webDomain = parseResult.webDomain
|
applicationId = parseResult.applicationId
|
||||||
webScheme = parseResult.webScheme
|
webDomain = parseResult.webDomain
|
||||||
},
|
webScheme = parseResult.webScheme
|
||||||
|
},
|
||||||
parseResult.usernameValue?.textValue?.toString(),
|
parseResult.usernameValue?.textValue?.toString(),
|
||||||
parseResult.passwordValue?.textValue?.toString())
|
parseResult.passwordValue?.textValue?.toString(),
|
||||||
|
creditCard)
|
||||||
|
|
||||||
// TODO Callback in each activity #765
|
// TODO Callback in each activity #765
|
||||||
//if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
//if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
// callback.onSuccess(AutofillLauncherActivity.getAuthIntentSenderForRegistration(this,
|
// callback.onSuccess(AutofillLauncherActivity.getAuthIntentSenderForRegistration(this,
|
||||||
|
|||||||
@@ -64,7 +64,10 @@ class StructureParser(private val structure: AssistStructure) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return if (result?.passwordId != null || result?.ccnId != null)
|
||||||
|
result
|
||||||
|
else
|
||||||
|
null
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -140,62 +143,88 @@ class StructureParser(private val structure: AssistStructure) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
it == "cc-name" -> {
|
it == "cc-name" -> {
|
||||||
|
Log.d(TAG, "AUTOFILL cc-name hint")
|
||||||
result?.ccNameId = autofillId
|
result?.ccNameId = autofillId
|
||||||
result?.ccNameValue = node.autofillValue
|
result?.ccName = node.autofillValue?.textValue?.toString()
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
it == View.AUTOFILL_HINT_CREDIT_CARD_NUMBER || it == "cc-number" -> {
|
it == View.AUTOFILL_HINT_CREDIT_CARD_NUMBER || it == "cc-number" -> {
|
||||||
result?.ccnId = autofillId
|
|
||||||
result?.ccnValue = node.autofillValue
|
|
||||||
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_NUMBER hint")
|
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_NUMBER hint")
|
||||||
return true
|
result?.ccnId = autofillId
|
||||||
|
result?.ccNumber = node.autofillValue?.textValue?.toString()
|
||||||
}
|
}
|
||||||
it == View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE || it == "cc-exp" -> {
|
// expect date string as defined in https://html.spec.whatwg.org, e.g. 2014-12
|
||||||
|
it == "cc-exp" -> {
|
||||||
|
Log.d(TAG, "AUTOFILL cc-exp hint")
|
||||||
result?.ccExpDateId = autofillId
|
result?.ccExpDateId = autofillId
|
||||||
|
node.autofillValue?.let { value ->
|
||||||
|
if (value.isText && value.textValue.length == 7) {
|
||||||
|
value.textValue.let { date ->
|
||||||
|
result?.ccExpirationValue = date.substring(5, 7) + date.substring(2, 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it == View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE -> {
|
||||||
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE hint")
|
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE hint")
|
||||||
return true
|
result?.ccExpDateId = autofillId
|
||||||
|
node.autofillValue?.let { value ->
|
||||||
|
if (value.isDate) {
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
calendar.clear()
|
||||||
|
calendar.timeInMillis = value.dateValue
|
||||||
|
val year = calendar.get(Calendar.YEAR).toString().substring(2,4)
|
||||||
|
val month = calendar.get(Calendar.MONTH).inc().toString().padStart(2, '0')
|
||||||
|
result?.ccExpirationValue = month + year
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
it == View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR || it == "cc-exp-year" -> {
|
it == View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR || it == "cc-exp-year" -> {
|
||||||
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR hint")
|
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR hint")
|
||||||
result?.ccExpDateYearId = autofillId
|
result?.ccExpDateYearId = autofillId
|
||||||
if (node.autofillValue != null) {
|
|
||||||
if (node.autofillValue?.isText == true) {
|
|
||||||
try {
|
|
||||||
result?.ccExpDateYearValue =
|
|
||||||
node.autofillValue?.textValue.toString().toInt()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
result?.ccExpDateYearValue = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (node.autofillOptions != null) {
|
if (node.autofillOptions != null) {
|
||||||
result?.ccExpYearOptions = node.autofillOptions
|
result?.ccExpYearOptions = node.autofillOptions
|
||||||
}
|
}
|
||||||
return true
|
node.autofillValue?.let { value ->
|
||||||
|
var year = 0
|
||||||
|
try {
|
||||||
|
if (value.isText) {
|
||||||
|
year = value.textValue.toString().toInt()
|
||||||
|
}
|
||||||
|
if (value.isList) {
|
||||||
|
year = node.autofillOptions?.get(value.listValue).toString().toInt()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
year = 0
|
||||||
|
}
|
||||||
|
result?.ccExpDateYearValue = year % 100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
it == View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH || it == "cc-exp-month" -> {
|
it == View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH || it == "cc-exp-month" -> {
|
||||||
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH hint")
|
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH hint")
|
||||||
result?.ccExpDateMonthId = autofillId
|
result?.ccExpDateMonthId = autofillId
|
||||||
if (node.autofillValue != null) {
|
|
||||||
if (node.autofillValue?.isText == true) {
|
|
||||||
try {
|
|
||||||
result?.ccExpDateMonthValue =
|
|
||||||
node.autofillValue?.textValue.toString().toInt()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
result?.ccExpDateMonthValue = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (node.autofillOptions != null) {
|
if (node.autofillOptions != null) {
|
||||||
result?.ccExpMonthOptions = node.autofillOptions
|
result?.ccExpMonthOptions = node.autofillOptions
|
||||||
}
|
}
|
||||||
return true
|
node.autofillValue?.let { value ->
|
||||||
|
var month = 0
|
||||||
|
if (value.isText) {
|
||||||
|
try {
|
||||||
|
month = value.textValue.toString().toInt()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
month = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value.isList) {
|
||||||
|
// assume list starts with January (index 0)
|
||||||
|
month = value.listValue + 1
|
||||||
|
}
|
||||||
|
result?.ccExpDateMonthValue = month
|
||||||
|
}
|
||||||
}
|
}
|
||||||
it == View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE || it == "cc-csc" -> {
|
it == View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE || it == "cc-csc" -> {
|
||||||
result?.cvvId = autofillId
|
|
||||||
result?.cvvValue = node.autofillValue
|
|
||||||
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE hint")
|
Log.d(TAG, "AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE hint")
|
||||||
return true
|
result?.cvvId = autofillId
|
||||||
|
result?.cvv = node.autofillValue?.textValue?.toString()
|
||||||
}
|
}
|
||||||
// Ignore autocomplete="off"
|
// Ignore autocomplete="off"
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
|
// https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
|
||||||
@@ -409,15 +438,6 @@ class StructureParser(private val structure: AssistStructure) {
|
|||||||
ccnId?.let {
|
ccnId?.let {
|
||||||
all.add(it)
|
all.add(it)
|
||||||
}
|
}
|
||||||
ccExpDateId?.let {
|
|
||||||
all.add(it)
|
|
||||||
}
|
|
||||||
ccExpDateYearId?.let {
|
|
||||||
all.add(it)
|
|
||||||
}
|
|
||||||
ccExpDateMonthId?.let {
|
|
||||||
all.add(it)
|
|
||||||
}
|
|
||||||
cvvId?.let {
|
cvvId?.let {
|
||||||
all.add(it)
|
all.add(it)
|
||||||
}
|
}
|
||||||
@@ -439,28 +459,33 @@ class StructureParser(private val structure: AssistStructure) {
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// stores name of cardholder
|
var ccName: String? = null
|
||||||
var ccNameValue: AutofillValue? = null
|
|
||||||
set(value) {
|
set(value) {
|
||||||
if (allowSaveValues && field == null)
|
if (allowSaveValues)
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// stores credit card number
|
var ccNumber: String? = null
|
||||||
var ccnValue: AutofillValue? = null
|
|
||||||
set(value) {
|
set(value) {
|
||||||
if (allowSaveValues && field == null)
|
if (allowSaveValues)
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// for year of CC expiration date
|
// format MMYY
|
||||||
|
var ccExpirationValue: String? = null
|
||||||
|
set(value) {
|
||||||
|
if (allowSaveValues)
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// for year of CC expiration date: YY
|
||||||
var ccExpDateYearValue = 0
|
var ccExpDateYearValue = 0
|
||||||
set(value) {
|
set(value) {
|
||||||
if (allowSaveValues)
|
if (allowSaveValues)
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// for month of CC expiration date
|
// for month of CC expiration date: MM
|
||||||
var ccExpDateMonthValue = 0
|
var ccExpDateMonthValue = 0
|
||||||
set(value) {
|
set(value) {
|
||||||
if (allowSaveValues)
|
if (allowSaveValues)
|
||||||
@@ -468,9 +493,9 @@ class StructureParser(private val structure: AssistStructure) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the security code for the credit card (also called CVV)
|
// the security code for the credit card (also called CVV)
|
||||||
var cvvValue: AutofillValue? = null
|
var cvv: String? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
if (allowSaveValues && field == null)
|
if (allowSaveValues)
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,34 +3,14 @@ package com.kunzisoft.keepass.model
|
|||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
|
||||||
class CreditCard() : Parcelable {
|
data class CreditCard(val cardholder: String?, val number: String?,
|
||||||
var cardholder: String = "";
|
val expiration: String?, val cvv: String?) : Parcelable {
|
||||||
var number: String = "";
|
|
||||||
var expiration: String = "";
|
|
||||||
var cvv: String = "";
|
|
||||||
|
|
||||||
constructor(ccFields: List<Field>?) : this() {
|
constructor(parcel: Parcel) : this(
|
||||||
ccFields?.let {
|
parcel.readString(),
|
||||||
for (field in it) {
|
parcel.readString(),
|
||||||
when (field.name) {
|
parcel.readString(),
|
||||||
CreditCardCustomFields.CC_CARDHOLDER_FIELD_NAME ->
|
parcel.readString()) {
|
||||||
this.cardholder = field.protectedValue.stringValue
|
|
||||||
CreditCardCustomFields.CC_NUMBER_FIELD_NAME ->
|
|
||||||
this.number = field.protectedValue.stringValue
|
|
||||||
CreditCardCustomFields.CC_EXP_FIELD_NAME ->
|
|
||||||
this.expiration = field.protectedValue.stringValue
|
|
||||||
CreditCardCustomFields.CC_CVV_FIELD_NAME ->
|
|
||||||
this.cvv = field.protectedValue.stringValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this() {
|
|
||||||
cardholder = parcel.readString() ?: cardholder
|
|
||||||
number = parcel.readString() ?: number
|
|
||||||
expiration = parcel.readString() ?: expiration
|
|
||||||
cvv = parcel.readString() ?: cvv
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
@@ -41,7 +21,7 @@ class CreditCard() : Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getExpirationMonth(): String {
|
fun getExpirationMonth(): String {
|
||||||
return if (expiration.length == 4) {
|
return if (expiration?.length == 4) {
|
||||||
expiration.substring(0, 2)
|
expiration.substring(0, 2)
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
@@ -49,39 +29,17 @@ class CreditCard() : Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getExpirationYear(): String {
|
fun getExpirationYear(): String {
|
||||||
return if (expiration.length == 4) {
|
return if (expiration?.length == 4) {
|
||||||
expiration.substring(2, 4)
|
expiration.substring(2, 4)
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as CreditCard
|
|
||||||
|
|
||||||
if (cardholder != other.cardholder) return false
|
|
||||||
if (number != other.number) return false
|
|
||||||
if (expiration != other.expiration) return false
|
|
||||||
if (cvv != other.cvv) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun describeContents(): Int {
|
override fun describeContents(): Int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = cardholder.hashCode()
|
|
||||||
result = 31 * result + number.hashCode()
|
|
||||||
result = 31 * result + expiration.hashCode()
|
|
||||||
result = 31 * result + cvv.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object CREATOR : Parcelable.Creator<CreditCard> {
|
companion object CREATOR : Parcelable.Creator<CreditCard> {
|
||||||
override fun createFromParcel(parcel: Parcel): CreditCard {
|
override fun createFromParcel(parcel: Parcel): CreditCard {
|
||||||
return CreditCard(parcel)
|
return CreditCard(parcel)
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ class EntryInfo : NodeInfo {
|
|||||||
var attachments: List<Attachment> = ArrayList()
|
var attachments: List<Attachment> = ArrayList()
|
||||||
var otpModel: OtpModel? = null
|
var otpModel: OtpModel? = null
|
||||||
|
|
||||||
constructor(): super()
|
constructor() : super()
|
||||||
|
|
||||||
constructor(parcel: Parcel): super(parcel) {
|
constructor(parcel: Parcel) : super(parcel) {
|
||||||
id = parcel.readString() ?: id
|
id = parcel.readString() ?: id
|
||||||
username = parcel.readString() ?: username
|
username = parcel.readString() ?: username
|
||||||
password = parcel.readString() ?: password
|
password = parcel.readString() ?: password
|
||||||
@@ -133,8 +133,7 @@ class EntryInfo : NodeInfo {
|
|||||||
val webDomainToStore = "$webScheme://$webDomain"
|
val webDomainToStore = "$webScheme://$webDomain"
|
||||||
if (database?.allowEntryCustomFields() != true || url.isEmpty()) {
|
if (database?.allowEntryCustomFields() != true || url.isEmpty()) {
|
||||||
url = webDomainToStore
|
url = webDomainToStore
|
||||||
}
|
} else if (url != webDomainToStore) {
|
||||||
else if (url != webDomainToStore){
|
|
||||||
// Save web domain in custom field
|
// Save web domain in custom field
|
||||||
addUniqueField(Field(WEB_DOMAIN_FIELD_NAME,
|
addUniqueField(Field(WEB_DOMAIN_FIELD_NAME,
|
||||||
ProtectedString(false, webDomainToStore)),
|
ProtectedString(false, webDomainToStore)),
|
||||||
@@ -153,6 +152,38 @@ class EntryInfo : NodeInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveRegisterInfo(database: Database?, registerInfo: RegisterInfo) {
|
||||||
|
registerInfo.username?.let {
|
||||||
|
username = it
|
||||||
|
}
|
||||||
|
registerInfo.password?.let {
|
||||||
|
password = it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (database?.allowEntryCustomFields() == true) {
|
||||||
|
val creditCard: CreditCard? = registerInfo.cc
|
||||||
|
|
||||||
|
creditCard?.let { cc ->
|
||||||
|
cc.cardholder?.let {
|
||||||
|
val v = ProtectedString(false, it)
|
||||||
|
addUniqueField(Field(CreditCardCustomFields.CC_CARDHOLDER_FIELD_NAME, v))
|
||||||
|
}
|
||||||
|
cc.expiration?.let {
|
||||||
|
val v = ProtectedString(false, it)
|
||||||
|
addUniqueField(Field(CreditCardCustomFields.CC_EXP_FIELD_NAME, v))
|
||||||
|
}
|
||||||
|
cc.number?.let {
|
||||||
|
val v = ProtectedString(false, it)
|
||||||
|
addUniqueField(Field(CreditCardCustomFields.CC_NUMBER_FIELD_NAME, v))
|
||||||
|
}
|
||||||
|
cc.cvv?.let {
|
||||||
|
val v = ProtectedString(true, it)
|
||||||
|
addUniqueField(Field(CreditCardCustomFields.CC_CVV_FIELD_NAME, v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val WEB_DOMAIN_FIELD_NAME = "URL"
|
const val WEB_DOMAIN_FIELD_NAME = "URL"
|
||||||
|
|||||||
@@ -5,18 +5,21 @@ import android.os.Parcelable
|
|||||||
|
|
||||||
data class RegisterInfo(val searchInfo: SearchInfo,
|
data class RegisterInfo(val searchInfo: SearchInfo,
|
||||||
val username: String?,
|
val username: String?,
|
||||||
val password: String?): Parcelable {
|
val password: String?,
|
||||||
|
val cc: CreditCard?): Parcelable {
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this(
|
constructor(parcel: Parcel) : this(
|
||||||
parcel.readParcelable(SearchInfo::class.java.classLoader) ?: SearchInfo(),
|
parcel.readParcelable(SearchInfo::class.java.classLoader) ?: SearchInfo(),
|
||||||
parcel.readString() ?: "",
|
parcel.readString() ?: "",
|
||||||
parcel.readString() ?: "") {
|
parcel.readString() ?: "",
|
||||||
|
parcel.readParcelable(CreditCard::class.java.classLoader)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
parcel.writeParcelable(searchInfo, flags)
|
parcel.writeParcelable(searchInfo, flags)
|
||||||
parcel.writeString(username)
|
parcel.writeString(username)
|
||||||
parcel.writeString(password)
|
parcel.writeString(password)
|
||||||
|
parcel.writeParcelable(cc, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun describeContents(): Int {
|
override fun describeContents(): Int {
|
||||||
|
|||||||
@@ -357,7 +357,6 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
|
|||||||
activateCopyButton(allowCopy)
|
activateCopyButton(allowCopy)
|
||||||
assignCopyButtonClickListener(onCopyButtonClickListener)
|
assignCopyButtonClickListener(onCopyButtonClickListener)
|
||||||
applyFontVisibility(fontInVisibility)
|
applyFontVisibility(fontInVisibility)
|
||||||
checkCreditCardDetails(fieldName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entryCustomField?.let {
|
entryCustomField?.let {
|
||||||
|
|||||||
@@ -108,23 +108,6 @@ class EntryField @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setValueTextColor(color: Int) {
|
|
||||||
valueView.setTextColor(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkCreditCardDetails(fieldName: String) {
|
|
||||||
val value = valueView.text
|
|
||||||
|
|
||||||
when (fieldName) {
|
|
||||||
CreditCardCustomFields.CC_CVV_FIELD_NAME ->
|
|
||||||
if (value.length < 3 || value.length > 4) setValueTextColor(Color.RED)
|
|
||||||
CreditCardCustomFields.CC_EXP_FIELD_NAME ->
|
|
||||||
if (value.length != 4) setValueTextColor(Color.RED)
|
|
||||||
CreditCardCustomFields.CC_NUMBER_FIELD_NAME ->
|
|
||||||
if (value.length != 16) setValueTextColor(Color.RED)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setAutoLink() {
|
fun setAutoLink() {
|
||||||
if (!isProtected) linkify()
|
if (!isProtected) linkify()
|
||||||
changeProtectedValueParameters()
|
changeProtectedValueParameters()
|
||||||
|
|||||||
@@ -48,8 +48,6 @@
|
|||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:importantForAccessibility="no"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:labelFor="@+id/creditCardNumberField"
|
android:labelFor="@+id/creditCardNumberField"
|
||||||
android:text="@string/cc_cardholder"
|
android:text="@string/cc_cardholder"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
@@ -62,8 +60,6 @@
|
|||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:focusedByDefault="true"
|
android:focusedByDefault="true"
|
||||||
android:hint="@string/cc_cardholder"
|
android:hint="@string/cc_cardholder"
|
||||||
android:importantForAccessibility="no"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:inputType="textPersonName"
|
android:inputType="textPersonName"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/creditCardholderNameLabel" />
|
app:layout_constraintTop_toBottomOf="@+id/creditCardholderNameLabel" />
|
||||||
@@ -73,8 +69,6 @@
|
|||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:importantForAccessibility="no"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:labelFor="@+id/creditCardNumberField"
|
android:labelFor="@+id/creditCardNumberField"
|
||||||
android:text="@string/cc_number"
|
android:text="@string/cc_number"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
@@ -87,8 +81,6 @@
|
|||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:focusedByDefault="true"
|
android:focusedByDefault="true"
|
||||||
android:hint="@string/cc_number"
|
android:hint="@string/cc_number"
|
||||||
android:importantForAccessibility="no"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:maxLength="16"
|
android:maxLength="16"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
@@ -99,8 +91,6 @@
|
|||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:importantForAccessibility="no"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:text="@string/cc_expiration"
|
android:text="@string/cc_expiration"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/creditCardNumberField" />
|
app:layout_constraintTop_toBottomOf="@+id/creditCardNumberField" />
|
||||||
@@ -129,7 +119,6 @@
|
|||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:labelFor="@+id/creditCardSecurityCode"
|
android:labelFor="@+id/creditCardSecurityCode"
|
||||||
android:text="@string/cc_security_code"
|
android:text="@string/cc_security_code"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|||||||
@@ -530,32 +530,7 @@
|
|||||||
<string name="unit_kibibyte">KiB</string>
|
<string name="unit_kibibyte">KiB</string>
|
||||||
<string name="unit_mebibyte">MiB</string>
|
<string name="unit_mebibyte">MiB</string>
|
||||||
<string name="unit_gibibyte">GiB</string>
|
<string name="unit_gibibyte">GiB</string>
|
||||||
<string-array name="month_array">
|
|
||||||
<item>01</item>
|
|
||||||
<item>02</item>
|
|
||||||
<item>03</item>
|
|
||||||
<item>04</item>
|
|
||||||
<item>05</item>
|
|
||||||
<item>06</item>
|
|
||||||
<item>07</item>
|
|
||||||
<item>08</item>
|
|
||||||
<item>09</item>
|
|
||||||
<item>10</item>
|
|
||||||
<item>11</item>
|
|
||||||
<item>12</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="year_array">
|
|
||||||
<item>20</item>
|
|
||||||
<item>21</item>
|
|
||||||
<item>22</item>
|
|
||||||
<item>23</item>
|
|
||||||
<item>24</item>
|
|
||||||
<item>25</item>
|
|
||||||
<item>26</item>
|
|
||||||
<item>27</item>
|
|
||||||
<item>28</item>
|
|
||||||
<item>29</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="timeout_options">
|
<string-array name="timeout_options">
|
||||||
<item>5 seconds</item>
|
<item>5 seconds</item>
|
||||||
<item>10 seconds</item>
|
<item>10 seconds</item>
|
||||||
|
|||||||
Reference in New Issue
Block a user