Implemented passing allowed TAN format and max TAN input length to UI so that controls can be set accordingly

This commit is contained in:
dankito 2020-08-12 22:28:18 +02:00
parent fe5b2276c8
commit c8f29e2390
10 changed files with 65 additions and 13 deletions

View File

@ -1138,6 +1138,7 @@ open class FinTsClient(
return TanProcedure(procedureName, parameters.securityFunction, return TanProcedure(procedureName, parameters.securityFunction,
mapToTanProcedureType(parameters) ?: TanProcedureType.EnterTan, mapHhdVersion(parameters), mapToTanProcedureType(parameters) ?: TanProcedureType.EnterTan, mapHhdVersion(parameters),
parameters.maxTanInputLength, parameters.allowedTanFormat,
parameters.nameOfTanMediaRequired == BezeichnungDesTanMediumsErforderlich.BezeichnungDesTanMediumsMussAngegebenWerden) parameters.nameOfTanMediaRequired == BezeichnungDesTanMediumsErforderlich.BezeichnungDesTanMediumsMussAngegebenWerden)
} }

View File

@ -1,6 +1,7 @@
package net.dankito.banking.fints.model package net.dankito.banking.fints.model
import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat
open class TanProcedure( open class TanProcedure(
@ -8,6 +9,8 @@ open class TanProcedure(
val securityFunction: Sicherheitsfunktion, val securityFunction: Sicherheitsfunktion,
val type: TanProcedureType, val type: TanProcedureType,
val hhdVersion: HHDVersion? = null, val hhdVersion: HHDVersion? = null,
val maxTanInputLength: Int? = null,
val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric,
val nameOfTanMediaRequired: Boolean = false val nameOfTanMediaRequired: Boolean = false
) { ) {

View File

@ -3,6 +3,7 @@ package net.dankito.banking.ui.android.dialogs
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.text.InputFilter
import android.text.InputType import android.text.InputType
import android.view.KeyEvent import android.view.KeyEvent
import android.view.LayoutInflater import android.view.LayoutInflater
@ -14,11 +15,10 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import kotlinx.android.synthetic.main.dialog_enter_tan.* import kotlinx.android.synthetic.main.dialog_enter_tan.*
import kotlinx.android.synthetic.main.dialog_enter_tan.view.* import kotlinx.android.synthetic.main.dialog_enter_tan.view.*
import kotlinx.android.synthetic.main.dialog_enter_tan.view.flickerCodeView
import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.android.di.BankingComponent
import net.dankito.banking.ui.android.adapter.TanMediumAdapter import net.dankito.banking.ui.android.adapter.TanMediumAdapter
import net.dankito.banking.ui.android.adapter.TanProceduresAdapter import net.dankito.banking.ui.android.adapter.TanProceduresAdapter
import net.dankito.banking.ui.android.di.BankingComponent
import net.dankito.banking.ui.android.listener.ListItemSelectedListener import net.dankito.banking.ui.android.listener.ListItemSelectedListener
import net.dankito.banking.ui.model.Customer import net.dankito.banking.ui.model.Customer
import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.banking.ui.model.responses.BankingClientResponse
@ -151,7 +151,14 @@ open class EnterTanDialog : DialogFragment() {
setupImageTanView(rootView) setupImageTanView(rootView)
} }
rootView.edtxtEnteredTan.inputType = InputType.TYPE_CLASS_NUMBER // TODO: is this always true that TAN is a number?
if (tanChallenge.tanProcedure.isNumericTan) {
rootView.edtxtEnteredTan.inputType = InputType.TYPE_CLASS_NUMBER
}
tanChallenge.tanProcedure.maxTanInputLength?.let { maxInputLength ->
rootView.edtxtEnteredTan.filters = arrayOf<InputFilter>(InputFilter.LengthFilter(maxInputLength))
}
rootView.edtxtEnteredTan.setOnKeyListener { _, keyCode, _ -> rootView.edtxtEnteredTan.setOnKeyListener { _, keyCode, _ ->
if (keyCode == KeyEvent.KEYCODE_ENTER) { if (keyCode == KeyEvent.KEYCODE_ENTER) {

View File

@ -0,0 +1,10 @@
package net.dankito.banking.ui.model.tan
enum class AllowedTanFormat {
Numeric,
Alphanumeric
}

View File

@ -4,13 +4,18 @@ package net.dankito.banking.ui.model.tan
open class TanProcedure( open class TanProcedure(
val displayName: String, val displayName: String,
val type: TanProcedureType, val type: TanProcedureType,
val bankInternalProcedureCode: String val bankInternalProcedureCode: String,
val maxTanInputLength: Int? = null,
val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric
) { ) {
internal constructor() : this("", TanProcedureType.EnterTan, "") // for object deserializers internal constructor() : this("", TanProcedureType.EnterTan, "") // for object deserializers
val isNumericTan: Boolean = allowedTanFormat == AllowedTanFormat.Numeric
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is TanProcedure) return false if (other !is TanProcedure) return false

View File

@ -73,14 +73,16 @@
<relationship name="supportedTanProcedures" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PersistedTanProcedure"/> <relationship name="supportedTanProcedures" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PersistedTanProcedure"/>
</entity> </entity>
<entity name="PersistedTanProcedure" representedClassName="PersistedTanProcedure" syncable="YES" codeGenerationType="class"> <entity name="PersistedTanProcedure" representedClassName="PersistedTanProcedure" syncable="YES" codeGenerationType="class">
<attribute name="allowedTanFormat" attributeType="String" defaultValueString=""/>
<attribute name="bankInternalProcedureCode" attributeType="String"/> <attribute name="bankInternalProcedureCode" attributeType="String"/>
<attribute name="displayName" attributeType="String"/> <attribute name="displayName" attributeType="String"/>
<attribute name="maxTanInputLength" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="type" attributeType="String"/> <attribute name="type" attributeType="String"/>
</entity> </entity>
<elements> <elements>
<element name="PersistedAccountTransaction" positionX="-36" positionY="45" width="128" height="553"/> <element name="PersistedAccountTransaction" positionX="-36" positionY="45" width="128" height="553"/>
<element name="PersistedBankAccount" positionX="-54" positionY="63" width="128" height="343"/> <element name="PersistedBankAccount" positionX="-54" positionY="63" width="128" height="343"/>
<element name="PersistedCustomer" positionX="-63" positionY="-18" width="128" height="253"/> <element name="PersistedCustomer" positionX="-63" positionY="-18" width="128" height="253"/>
<element name="PersistedTanProcedure" positionX="-54" positionY="135" width="128" height="88"/> <element name="PersistedTanProcedure" positionX="-54" positionY="135" width="128" height="118"/>
</elements> </elements>
</model> </model>

View File

@ -216,7 +216,9 @@ class Mapper {
let mapped = TanProcedure( let mapped = TanProcedure(
displayName: map(tanProcedure.displayName), displayName: map(tanProcedure.displayName),
type: mapTanProcedureType(tanProcedure.type), type: mapTanProcedureType(tanProcedure.type),
bankInternalProcedureCode: map(tanProcedure.bankInternalProcedureCode) bankInternalProcedureCode: map(tanProcedure.bankInternalProcedureCode),
maxTanInputLength: map(tanProcedure.maxTanInputLength),
allowedTanFormat: tanProcedure.allowedTanFormat == "numeric" ? .numeric : .alphanumeric
) )
mappedTanProcedures[mapped] = tanProcedure mappedTanProcedures[mapped] = tanProcedure
@ -235,6 +237,9 @@ class Mapper {
mapped.type = tanProcedure.type.name mapped.type = tanProcedure.type.name
mapped.bankInternalProcedureCode = tanProcedure.bankInternalProcedureCode mapped.bankInternalProcedureCode = tanProcedure.bankInternalProcedureCode
mapped.maxTanInputLength = map(tanProcedure.maxTanInputLength) ?? -1
mapped.allowedTanFormat = tanProcedure.allowedTanFormat.name
mappedTanProcedures[tanProcedure] = mapped mappedTanProcedures[tanProcedure] = mapped
return mapped return mapped

View File

@ -105,7 +105,7 @@ struct EnterTanDialog: View {
.padding(.vertical, 2) .padding(.vertical, 2)
Section { Section {
UIKitTextField("Enter TAN:", text: $enteredTan, actionOnReturnKeyPress: { UIKitTextField("Enter TAN:", text: $enteredTan, keyboardType: tanChallenge.tanProcedure.isNumericTan ? .numberPad : .default, actionOnReturnKeyPress: {
if self.isRequiredDataEntered() { if self.isRequiredDataEntered() {
self.enteringTanDone() self.enteringTanDone()
return true return true
@ -214,9 +214,9 @@ struct EnterTanDialog_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
let customer = Customer(bankCode: "", customerId: "", password: "", finTsServerAddress: "") let customer = Customer(bankCode: "", customerId: "", password: "", finTsServerAddress: "")
customer.supportedTanProcedures = [ customer.supportedTanProcedures = [
TanProcedure(displayName: "chipTAN optisch", type: .chiptanflickercode, bankInternalProcedureCode: ""), TanProcedure(displayName: "chipTAN optisch", type: .chiptanflickercode, bankInternalProcedureCode: "", maxTanInputLength: 6, allowedTanFormat: .numeric),
TanProcedure(displayName: "chipTAN QR", type: .chiptanqrcode, bankInternalProcedureCode: ""), TanProcedure(displayName: "chipTAN QR", type: .chiptanqrcode, bankInternalProcedureCode: "", maxTanInputLength: 8, allowedTanFormat: .numeric),
TanProcedure(displayName: "Secure Super Duper Plus", type: .apptan, bankInternalProcedureCode: "") TanProcedure(displayName: "Secure Super Duper Plus", type: .apptan, bankInternalProcedureCode: "", maxTanInputLength: 6, allowedTanFormat: .alphanumeric)
] ]
customer.tanMedia = [ customer.tanMedia = [

View File

@ -72,7 +72,7 @@ struct ImageTanView: View {
struct ImageTanView_Previews: PreviewProvider { struct ImageTanView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
let tanChallenge = ImageTanChallenge(image: TanImage(mimeType: "image/png", imageBytes: KotlinByteArray(size: 0), decodingError: nil), messageToShowToUser: "", tanProcedure: TanProcedure(displayName: "", type: .phototan, bankInternalProcedureCode: "")) let tanChallenge = ImageTanChallenge(image: TanImage(mimeType: "image/png", imageBytes: KotlinByteArray(size: 0), decodingError: nil), messageToShowToUser: "", tanProcedure: TanProcedure(displayName: "", type: .phototan, bankInternalProcedureCode: "", maxTanInputLength: 6, allowedTanFormat: .numeric))
return ImageTanView(tanChallenge) return ImageTanView(tanChallenge)
} }

View File

@ -238,7 +238,9 @@ open class fints4kModelMapper {
return TanProcedure( return TanProcedure(
tanProcedure.displayName, tanProcedure.displayName,
mapTanProcedureType(tanProcedure.type), mapTanProcedureType(tanProcedure.type),
tanProcedure.securityFunction.code tanProcedure.securityFunction.code,
tanProcedure.maxTanInputLength,
mapAllowedTanFormat(tanProcedure.allowedTanFormat)
) )
} }
@ -257,6 +259,13 @@ open class fints4kModelMapper {
} }
} }
open fun mapAllowedTanFormat(allowedTanFormat: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat): AllowedTanFormat {
return when (allowedTanFormat) {
net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Alphanumeric -> AllowedTanFormat.Alphanumeric
net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Numeric -> AllowedTanFormat.Numeric
}
}
protected open fun findMappedTanProcedure(customer: Customer, tanProcedure: net.dankito.banking.fints.model.TanProcedure): TanProcedure? { protected open fun findMappedTanProcedure(customer: Customer, tanProcedure: net.dankito.banking.fints.model.TanProcedure): TanProcedure? {
return customer.supportedTanProcedures.firstOrNull { it.bankInternalProcedureCode == tanProcedure.securityFunction.code } return customer.supportedTanProcedures.firstOrNull { it.bankInternalProcedureCode == tanProcedure.securityFunction.code }
} }
@ -350,7 +359,10 @@ open class fints4kModelMapper {
return net.dankito.banking.fints.model.TanProcedure( return net.dankito.banking.fints.model.TanProcedure(
tanProcedure.displayName, tanProcedure.displayName,
Sicherheitsfunktion.values().first { it.code == tanProcedure.bankInternalProcedureCode }, Sicherheitsfunktion.values().first { it.code == tanProcedure.bankInternalProcedureCode },
mapTanProcedureType(tanProcedure.type) mapTanProcedureType(tanProcedure.type),
null, // TODO: where to get HDD Version from?
tanProcedure.maxTanInputLength,
mapAllowedTanFormat(tanProcedure.allowedTanFormat)
) )
} }
@ -369,6 +381,13 @@ open class fints4kModelMapper {
} }
} }
open fun mapAllowedTanFormat(allowedTanFormat: AllowedTanFormat): net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat {
return when (allowedTanFormat) {
AllowedTanFormat.Alphanumeric -> net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Alphanumeric
AllowedTanFormat.Numeric -> net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Numeric
}
}
open fun mapEnterTanResult(result: EnterTanResult, customer: CustomerData): net.dankito.banking.fints.model.EnterTanResult { open fun mapEnterTanResult(result: EnterTanResult, customer: CustomerData): net.dankito.banking.fints.model.EnterTanResult {
result.changeTanProcedureTo?.let { changeTanProcedureTo -> result.changeTanProcedureTo?.let { changeTanProcedureTo ->
return net.dankito.banking.fints.model.EnterTanResult.userAsksToChangeTanProcedure(mapTanProcedure(changeTanProcedureTo)) return net.dankito.banking.fints.model.EnterTanResult.userAsksToChangeTanProcedure(mapTanProcedure(changeTanProcedureTo))