Implemented BankingClientCallback to abstract away FinTsClientCallback

This commit is contained in:
dankl 2020-01-03 00:35:36 +01:00 committed by dankito
parent b7e294bcbe
commit 6e712316ab
38 changed files with 447 additions and 164 deletions

View File

@ -0,0 +1,19 @@
package net.dankito.banking.ui
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult
import net.dankito.banking.ui.model.tan.TanChallenge
import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
interface BankingClientCallback {
fun enterTan(account: Account, tanChallenge: TanChallenge): EnterTanResult
/**
* This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator.
*/
fun enterTanGeneratorAtc(tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult
}

View File

@ -1,5 +1,7 @@
package net.dankito.banking.ui.model
import net.dankito.banking.ui.model.tan.TanMedium
import net.dankito.banking.ui.model.tan.TanProcedure
import java.math.BigDecimal

View File

@ -0,0 +1,30 @@
package net.dankito.banking.ui.model.tan
open class EnterTanGeneratorAtcResult protected constructor(
val tan: String?,
val atc: Int?
) {
companion object {
fun userEnteredAtc(enteredTan: String, enteredAtc: Int): EnterTanGeneratorAtcResult {
return EnterTanGeneratorAtcResult(enteredTan, enteredAtc)
}
fun userDidNotEnterTan(): EnterTanGeneratorAtcResult {
return EnterTanGeneratorAtcResult(null, null)
}
}
val hasAtcBeenEntered: Boolean
get() = tan != null && atc != null
override fun toString(): String {
return "TAN: $tan, ATC: $atc"
}
}

View File

@ -0,0 +1,45 @@
package net.dankito.banking.ui.model.tan
import net.dankito.banking.ui.model.responses.BankingClientResponse
open class EnterTanResult protected constructor(
val enteredTan: String?,
val changeTanProcedureTo: TanProcedure? = null,
val changeTanMediumTo: TanMedium? = null,
val changeTanMediumResultCallback: ((BankingClientResponse) -> Unit)? = null
) {
companion object {
fun userEnteredTan(enteredTan: String): EnterTanResult {
return EnterTanResult(enteredTan)
}
fun userDidNotEnterTan(): EnterTanResult {
return EnterTanResult(null)
}
fun userAsksToChangeTanProcedure(changeTanProcedureTo: TanProcedure): EnterTanResult {
return EnterTanResult(null, changeTanProcedureTo)
}
fun userAsksToChangeTanMedium(changeTanMediumTo: TanMedium, changeTanMediumResultCallback: ((BankingClientResponse) -> Unit)?): EnterTanResult {
return EnterTanResult(null, null, changeTanMediumTo, changeTanMediumResultCallback)
}
}
override fun toString(): String {
if (changeTanProcedureTo != null) {
return "User asks to change TAN procedure to $changeTanProcedureTo"
}
if (changeTanMediumTo != null) {
return "User asks to change TAN medium to $changeTanMediumTo"
}
return "enteredTan = $enteredTan"
}
}

View File

@ -0,0 +1,22 @@
package net.dankito.banking.ui.model.tan
open class FlickerCode(
val challengeHHD_UC: String,
val parsedDataSet: String,
val decodingError: Exception? = null
) {
val decodingSuccessful: Boolean
get() = decodingError == null
override fun toString(): String {
if (decodingSuccessful == false) {
return "Decoding error: $decodingError"
}
return "Parsed $challengeHHD_UC to $parsedDataSet"
}
}

View File

@ -0,0 +1,15 @@
package net.dankito.banking.ui.model.tan
open class FlickerCodeTanChallenge(
val flickerCode: FlickerCode,
messageToShowToUser: String,
tanProcedure: TanProcedure
) : TanChallenge(messageToShowToUser, tanProcedure) {
override fun toString(): String {
return "$tanProcedure $flickerCode: $messageToShowToUser"
}
}

View File

@ -0,0 +1,15 @@
package net.dankito.banking.ui.model.tan
open class ImageTanChallenge(
val image: TanImage,
messageToShowToUser: String,
tanProcedure: TanProcedure
) : TanChallenge(messageToShowToUser, tanProcedure) {
override fun toString(): String {
return "$tanProcedure $image: $messageToShowToUser"
}
}

View File

@ -0,0 +1,13 @@
package net.dankito.banking.ui.model.tan
open class TanChallenge(
val messageToShowToUser: String,
val tanProcedure: TanProcedure
) {
override fun toString(): String {
return "$tanProcedure: $messageToShowToUser"
}
}

View File

@ -0,0 +1,15 @@
package net.dankito.banking.ui.model.tan
open class TanGeneratorTanMedium(
displayName: String,
status: TanMediumStatus,
val cardNumber: String
) : TanMedium(displayName, status) {
override fun toString(): String {
return "$displayName $status"
}
}

View File

@ -0,0 +1,22 @@
package net.dankito.banking.ui.model.tan
open class TanImage(
val mimeType: String,
val imageBytes: ByteArray,
val decodingError: Exception? = null
) {
val decodingSuccessful: Boolean
get() = decodingError == null
override fun toString(): String {
if (decodingSuccessful == false) {
return "Decoding error: $decodingError"
}
return "$mimeType ${imageBytes.size} bytes"
}
}

View File

@ -1,10 +1,9 @@
package net.dankito.banking.ui.model
package net.dankito.banking.ui.model.tan
open class TanMedium(
val displayName: String,
val status: TanMediumStatus,
val originalObject: Any
val status: TanMediumStatus
) {
override fun toString(): String {

View File

@ -1,4 +1,4 @@
package net.dankito.banking.ui.model
package net.dankito.banking.ui.model.tan
enum class TanMediumStatus {

View File

@ -1,4 +1,4 @@
package net.dankito.banking.ui.model
package net.dankito.banking.ui.model.tan
open class TanProcedure(

View File

@ -1,4 +1,4 @@
package net.dankito.banking.ui.model
package net.dankito.banking.ui.model.tan
enum class TanProcedureType {

View File

@ -8,17 +8,17 @@ import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
import androidx.navigation.findNavController
import net.dankito.banking.mapper.fints4javaModelMapper
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
import net.dankito.banking.fints4java.android.ui.dialogs.AddAccountDialog
import net.dankito.banking.fints4java.android.ui.dialogs.EnterAtcDialog
import net.dankito.banking.fints4java.android.ui.dialogs.EnterTanDialog
import net.dankito.fints.FinTsClientCallback
import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
import net.dankito.fints.model.CustomerData
import net.dankito.fints.model.EnterTanGeneratorAtcResult
import net.dankito.fints.model.EnterTanResult
import net.dankito.fints.model.TanChallenge
import net.dankito.banking.mapper.fints4javaModelMapper
import net.dankito.banking.ui.BankingClientCallback
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult
import net.dankito.banking.ui.model.tan.TanChallenge
import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicReference
@ -27,14 +27,14 @@ class MainActivity : AppCompatActivity() {
// private lateinit var appBarConfiguration: AppBarConfiguration
val presenter = MainWindowPresenter(Base64ServiceAndroid(), object : FinTsClientCallback {
val presenter = MainWindowPresenter(Base64ServiceAndroid(), object : BankingClientCallback {
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult {
return getTanFromUserOffUiThread(customer, tanChallenge)
override fun enterTan(account: Account, tanChallenge: TanChallenge): EnterTanResult {
return getTanFromUserOffUiThread(account, tanChallenge)
}
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
return getAtcFromUserOffUiThread(customer, tanMedium)
override fun enterTanGeneratorAtc(tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
return getAtcFromUserOffUiThread(tanMedium)
}
})
@ -86,12 +86,10 @@ class MainActivity : AppCompatActivity() {
// return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
// }
private fun getTanFromUserOffUiThread(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult {
private fun getTanFromUserOffUiThread(account: Account, tanChallenge: TanChallenge): EnterTanResult {
val enteredTan = AtomicReference<EnterTanResult>(null)
val tanEnteredLatch = CountDownLatch(1)
val account = presenter.getAccountForCustomer(customer)
runOnUiThread {
EnterTanDialog().show(account, tanChallenge, presenter, this@MainActivity, false) {
enteredTan.set(it)
@ -104,13 +102,13 @@ class MainActivity : AppCompatActivity() {
return enteredTan.get()
}
private fun getAtcFromUserOffUiThread(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
private fun getAtcFromUserOffUiThread(tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
val result = AtomicReference<EnterTanGeneratorAtcResult>(null)
val tanEnteredLatch = CountDownLatch(1)
runOnUiThread {
// TODO: don't create a fints4javaModelMapper instance here, let MainWindowPresenter do the job
EnterAtcDialog().show(net.dankito.banking.mapper.fints4javaModelMapper().mapTanMedium(tanMedium), this@MainActivity, false) { enteredResult ->
EnterAtcDialog().show(tanMedium, this@MainActivity, false) { enteredResult ->
result.set(enteredResult)
tanEnteredLatch.countDown()
}

View File

@ -1,6 +1,7 @@
package net.dankito.banking.fints4java.android.ui
import net.dankito.banking.ui.IBankingClient
import net.dankito.banking.ui.BankingClientCallback
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount
@ -8,11 +9,8 @@ import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.AddAccountResponse
import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
import net.dankito.fints.FinTsClientCallback
import net.dankito.fints.banks.BankFinder
import net.dankito.fints.model.BankInfo
import net.dankito.fints.model.CustomerData
import net.dankito.fints.response.client.FinTsClientResponse
import net.dankito.fints.util.IBase64Service
import net.dankito.utils.IThreadPool
import net.dankito.utils.ThreadPool
@ -23,7 +21,7 @@ import kotlin.collections.ArrayList
open class MainWindowPresenter(protected val base64Service: IBase64Service,
protected val callback: FinTsClientCallback
protected val callback: BankingClientCallback
) {
companion object {
@ -35,10 +33,8 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service,
protected val threadPool: IThreadPool = ThreadPool()
protected val fints4javaModelMapper = net.dankito.banking.mapper.fints4javaModelMapper()
protected val accounts = mutableMapOf<Account, IBankingClient>()
protected val clientsForAccounts = mutableMapOf<Account, IBankingClient>()
protected val accountAddedListeners = mutableListOf<(Account) -> Unit>()
@ -54,7 +50,7 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service,
val account = response.account
if (response.isSuccessful) {
accounts.put(account, newClient)
clientsForAccounts.put(account, newClient)
callAccountAddedListeners(account)
@ -98,7 +94,7 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service,
}
open fun updateAccountsTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) {
accounts.keys.forEach { account ->
clientsForAccounts.keys.forEach { account ->
account.bankAccounts.forEach { bankAccount ->
val today = Date() // TODO: still don't know where this bug is coming from that bank returns a transaction dated at end of year
val lastRetrievedTransactionDate = bankAccount.bookedTransactions.firstOrNull { it.bookingDate <= today }?.bookingDate
@ -179,7 +175,7 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service,
protected open fun getClientForAccount(account: Account): IBankingClient? {
accounts.get(account)?.let { client ->
clientsForAccounts.get(account)?.let { client ->
// TODO: is this code still needed after updating data model is implemented?
// account.selectedTanProcedure?.let { selectedTanProcedure ->
// client.customer.selectedTanProcedure = fints4javaModelMapper.mapTanProcedureBack(selectedTanProcedure)
@ -192,20 +188,14 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service,
}
open fun getErrorToShowToUser(response: FinTsClientResponse): String? {
return fints4javaModelMapper.mapErrorToShowToUser(response)
}
open val accounts: List<Account>
get() = clientsForAccounts.keys.toList()
open val allTransactions: List<AccountTransaction>
get() = accounts.keys.flatMap { it.transactions }.sortedByDescending { it.bookingDate } // TODO: someday add unbooked transactions
get() = clientsForAccounts.keys.flatMap { it.transactions }.sortedByDescending { it.bookingDate } // TODO: someday add unbooked transactions
open val balanceOfAllAccounts: BigDecimal
get() = accounts.keys.map { it.balance }.fold(BigDecimal.ZERO) { acc, e -> acc + e }
open fun getAccountForCustomer(customer: CustomerData): Account { // TODO: remove as presenter should not be aware of fints4java objects
return accounts.keys.first { it.customerId == customer.customerId }
}
get() = clientsForAccounts.keys.map { it.balance }.fold(BigDecimal.ZERO) { acc, e -> acc + e }
open fun addAccountAddedListener(listener: (Account) -> Unit) {

View File

@ -4,7 +4,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.ui.model.TanMedium
import net.dankito.banking.ui.model.tan.TanMedium
import net.dankito.utils.android.extensions.asActivity
import net.dankito.utils.android.ui.adapter.ListAdapter

View File

@ -4,7 +4,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.ui.model.TanProcedure
import net.dankito.banking.ui.model.tan.TanProcedure
import net.dankito.utils.android.extensions.asActivity
import net.dankito.utils.android.ui.adapter.ListAdapter

View File

@ -18,7 +18,6 @@ import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.fints.messages.segmente.implementierte.sepa.ISepaMessageCreator
import net.dankito.fints.messages.segmente.implementierte.sepa.SepaMessageCreator
import net.dankito.fints.model.BankTransferData
import net.dankito.utils.android.extensions.asActivity
import java.math.BigDecimal
@ -34,7 +33,7 @@ open class BankTransferDialog : DialogFragment() {
protected lateinit var bankAccount: BankAccount
protected var preselectedValues: BankTransferData? = null
protected var preselectedValues: TransferMoneyData? = null
protected val sepaMessageCreator: ISepaMessageCreator = SepaMessageCreator()
@ -43,7 +42,7 @@ open class BankTransferDialog : DialogFragment() {
show(activity, presenter, bankAccount, null, fullscreen)
}
open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, bankAccount: BankAccount, preselectedValues: BankTransferData?, fullscreen: Boolean = false) {
open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, bankAccount: BankAccount, preselectedValues: TransferMoneyData?, fullscreen: Boolean = false) {
this.presenter = presenter
this.bankAccount = bankAccount
this.preselectedValues = preselectedValues
@ -92,7 +91,7 @@ open class BankTransferDialog : DialogFragment() {
}
}
protected open fun focusEditTextAccordingToPreselectedValues(rootView: View, data: BankTransferData) {
protected open fun focusEditTextAccordingToPreselectedValues(rootView: View, data: TransferMoneyData) {
if (data.creditorName.trim().isNotEmpty()) {
if (data.creditorIban.trim().isNotEmpty()) {
if (data.creditorBic.trim().isNotEmpty()) {

View File

@ -10,8 +10,8 @@ import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.dialog_enter_atc.view.*
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.ui.model.TanMedium
import net.dankito.fints.model.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.TanMedium
open class EnterAtcDialog : DialogFragment() {

View File

@ -15,17 +15,13 @@ import android.widget.Spinner
import kotlinx.android.synthetic.main.dialog_enter_tan.view.*
import kotlinx.android.synthetic.main.view_tan_image.view.*
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.mapper.fints4javaModelMapper
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
import net.dankito.banking.fints4java.android.ui.adapter.TanMediumAdapter
import net.dankito.banking.fints4java.android.ui.adapter.TanProceduresAdapter
import net.dankito.banking.fints4java.android.ui.listener.ListItemSelectedListener
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.TanMedium
import net.dankito.banking.ui.model.TanMediumStatus
import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
import net.dankito.fints.model.*
import net.dankito.fints.response.client.FinTsClientResponse
import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.tan.*
open class EnterTanDialog : DialogFragment() {
@ -96,8 +92,7 @@ open class EnterTanDialog : DialogFragment() {
spinner.onItemSelectedListener = ListItemSelectedListener(adapter) { newSelectedTanProcedure ->
if (newSelectedTanProcedure != selectedTanProcedure) {
val mappedTanProcedure = net.dankito.banking.mapper.fints4javaModelMapper().mapTanProcedureBack(newSelectedTanProcedure) // TODO: move to MainWindowPresenter
tanEnteredCallback(EnterTanResult.userAsksToChangeTanProcedure(mappedTanProcedure))
tanEnteredCallback(EnterTanResult.userAsksToChangeTanProcedure(newSelectedTanProcedure))
// TODO: find a way to update account.selectedTanProcedure afterwards
dismiss()
@ -114,7 +109,7 @@ open class EnterTanDialog : DialogFragment() {
rootView.spnTanMedium.adapter = tanMediumAdapter
rootView.spnTanMedium.onItemSelectedListener = ListItemSelectedListener(tanMediumAdapter) { selectedTanMedium ->
if (selectedTanMedium.status != TanMediumStatus.Used) {
(selectedTanMedium.originalObject as? TanGeneratorTanMedium)?.let { tanGeneratorTanMedium ->
(selectedTanMedium as? TanGeneratorTanMedium)?.let { tanGeneratorTanMedium ->
tanEnteredCallback(EnterTanResult.userAsksToChangeTanMedium(tanGeneratorTanMedium) { response ->
handleChangeTanMediumResponse(selectedTanMedium, response)
})
@ -132,16 +127,16 @@ open class EnterTanDialog : DialogFragment() {
setupSelectTanMediumView(rootView)
}
if (tanChallenge is FlickercodeTanChallenge) {
if (tanChallenge is FlickerCodeTanChallenge) {
val flickerCodeView = rootView.flickerCodeView
flickerCodeView.visibility = View.VISIBLE
val flickercode = (tanChallenge as FlickercodeTanChallenge).flickercode
val flickercode = (tanChallenge as FlickerCodeTanChallenge).flickerCode
if (flickercode.decodingSuccessful) {
flickerCodeView.setCode(flickercode)
}
else {
showDecodingTanChallengeFailedErrorDelayed(flickercode.error)
showDecodingTanChallengeFailedErrorDelayed(flickercode.decodingError)
}
}
else if (tanChallenge is ImageTanChallenge) {
@ -153,7 +148,7 @@ open class EnterTanDialog : DialogFragment() {
rootView.imgTanImageView.setImageBitmap(bitmap)
}
else {
showDecodingTanChallengeFailedErrorDelayed(decodedImage.error)
showDecodingTanChallengeFailedErrorDelayed(decodedImage.decodingError)
}
}
@ -182,7 +177,7 @@ open class EnterTanDialog : DialogFragment() {
}
protected open fun handleChangeTanMediumResponse(newUsedTanMedium: TanMedium, response: FinTsClientResponse) {
protected open fun handleChangeTanMediumResponse(newUsedTanMedium: TanMedium, response: BankingClientResponse) {
activity?.let { activity ->
activity.runOnUiThread {
handleChangeTanMediumResponseOnUiThread(activity, newUsedTanMedium, response)
@ -190,7 +185,7 @@ open class EnterTanDialog : DialogFragment() {
}
}
protected open fun handleChangeTanMediumResponseOnUiThread(context: Context, newUsedTanMedium: TanMedium, response: FinTsClientResponse) {
protected open fun handleChangeTanMediumResponseOnUiThread(context: Context, newUsedTanMedium: TanMedium, response: BankingClientResponse) {
if (response.isSuccessful) {
AlertDialog.Builder(context)
.setMessage(context.getString(R.string.dialog_enter_tan_tan_medium_successfully_changed, newUsedTanMedium.displayName))
@ -202,7 +197,7 @@ open class EnterTanDialog : DialogFragment() {
}
else {
AlertDialog.Builder(context)
.setMessage(context.getString(R.string.dialog_enter_tan_error_changing_tan_medium, newUsedTanMedium.displayName, presenter.getErrorToShowToUser(response)))
.setMessage(context.getString(R.string.dialog_enter_tan_error_changing_tan_medium, newUsedTanMedium.displayName, response.errorToShowToUser))
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
.show()
}

View File

@ -19,8 +19,8 @@ import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
import net.dankito.banking.fints4java.android.ui.adapter.AccountTransactionAdapter
import net.dankito.banking.fints4java.android.ui.dialogs.BankTransferDialog
import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
import net.dankito.fints.model.BankTransferData
import net.dankito.utils.android.extensions.asActivity
import java.math.BigDecimal
@ -177,9 +177,9 @@ class HomeFragment : Fragment() {
}
}
private fun mapPreselectedValues(selectedTransaction: AccountTransaction?): BankTransferData? {
private fun mapPreselectedValues(selectedTransaction: AccountTransaction?): TransferMoneyData? {
selectedTransaction?.let {
return BankTransferData(
return TransferMoneyData(
selectedTransaction.otherPartyName ?: "",
selectedTransaction.otherPartyAccountId ?: "",
selectedTransaction.otherPartyBankCode ?: "",

View File

@ -8,9 +8,9 @@ import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_flicker_code.view.*
import kotlinx.android.synthetic.main.view_tan_image_size_controls.view.*
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.fints4java.android.util.FlickercodeAnimator
import net.dankito.banking.fints4java.android.util.FlickerCodeAnimator
import net.dankito.banking.ui.model.tan.FlickerCode
import net.dankito.fints.tan.Bit
import net.dankito.fints.tan.Flickercode
import net.dankito.utils.android.extensions.asActivity
@ -40,7 +40,7 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
protected lateinit var allStripes: List<ChipTanFlickerCodeStripeView>
protected val animator = FlickercodeAnimator()
protected val animator = FlickerCodeAnimator()
protected var stripesHeight = 360
@ -160,12 +160,12 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
}
open fun setCode(flickercode: Flickercode) {
open fun setCode(flickerCode: FlickerCode) {
animator.stop()
setFrequency(currentFrequency)
animator.animateFlickercode(flickercode) { step ->
animator.animateFlickerCode(flickerCode) { step ->
context.asActivity()?.runOnUiThread {
showStepOnUiThread(step)
}

View File

@ -1,20 +1,20 @@
package net.dankito.banking.fints4java.android.util
import net.dankito.banking.ui.model.tan.FlickerCode
import net.dankito.fints.tan.Bit
import net.dankito.fints.tan.FlickerCanvas
import net.dankito.fints.tan.Flickercode
import org.slf4j.LoggerFactory
import java.util.concurrent.TimeUnit
open class FlickercodeAnimator { // TODO: move to fints4javaLib
open class FlickerCodeAnimator { // TODO: move to fints4javaLib
companion object {
const val MinFrequency = 2
const val MaxFrequency = 40
const val DefaultFrequency = 20
private val log = LoggerFactory.getLogger(FlickercodeAnimator::class.java)
private val log = LoggerFactory.getLogger(FlickerCodeAnimator::class.java)
}
@ -27,14 +27,14 @@ open class FlickercodeAnimator { // TODO: move to fints4javaLib
@JvmOverloads
open fun animateFlickercode(flickercode: Flickercode, frequency: Int = DefaultFrequency, showStep: (Array<Bit>) -> Unit) {
open fun animateFlickerCode(flickerCode: FlickerCode, frequency: Int = DefaultFrequency, showStep: (Array<Bit>) -> Unit) {
currentFrequency = frequency
currentStepIndex = 0
val steps = FlickerCanvas(flickercode.parsedDataSet).steps
val steps = FlickerCanvas(flickerCode.parsedDataSet).steps
stop() // stop may still running previous animation
calculateAnimationThread = Thread({ calculateAnimation(steps, showStep) }, "CalculateFlickercodeAnimation")
calculateAnimationThread = Thread({ calculateAnimation(steps, showStep) }, "CalculateFlickerCodeAnimation")
calculateAnimationThread?.start()
}

View File

@ -1,6 +1,8 @@
package net.dankito.banking
import net.dankito.banking.ui.BankingClientCallback
import net.dankito.banking.ui.IBankingClient
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
import net.dankito.banking.ui.model.parameters.TransferMoneyData
@ -9,9 +11,8 @@ import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
import net.dankito.fints.FinTsClientCallback
import net.dankito.fints.FinTsClientForCustomer
import net.dankito.fints.model.BankInfo
import net.dankito.fints.model.BankTransferData
import net.dankito.fints.model.CustomerData
import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
import net.dankito.fints.model.*
import net.dankito.fints.model.mapper.BankDataMapper
import net.dankito.fints.util.IBase64Service
import net.dankito.utils.IThreadPool
@ -27,7 +28,7 @@ open class fints4javaBankingClient(
webClient: IWebClient = OkHttpWebClient(),
base64Service: IBase64Service,
threadPool: IThreadPool = ThreadPool(),
callback: FinTsClientCallback
callback: BankingClientCallback
) : IBankingClient {
@ -39,12 +40,28 @@ open class fints4javaBankingClient(
protected val customer = CustomerData(customerId, pin)
protected val client = FinTsClientForCustomer(bank, customer, webClient, base64Service, threadPool, callback)
protected lateinit var account: Account
protected val client = FinTsClientForCustomer(bank, customer, webClient, base64Service, threadPool, object : FinTsClientCallback {
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult {
val result = callback.enterTan(account, mapper.mapTanChallenge(tanChallenge))
return mapper.mapEnterTanResult(result, customer)
}
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
val result = callback.enterTanGeneratorAtc(mapper.mapTanMedium(tanMedium))
return mapper.mapEnterTanGeneratorAtcResult(result)
}
})
override fun addAccountAsync(callback: (AddAccountResponse) -> Unit) {
client.addAccountAsync { response ->
val account = mapper.mapAccount(customer, bank)
this.account = mapper.mapAccount(customer, bank)
val mappedResponse = mapper.mapResponse(account, response)
callback(mappedResponse)

View File

@ -4,8 +4,8 @@ import net.dankito.banking.ui.model.*
import net.dankito.banking.ui.model.responses.AddAccountResponse
import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
import net.dankito.banking.ui.model.tan.*
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
import net.dankito.fints.model.AccountData
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
@ -16,6 +16,40 @@ import java.math.BigDecimal
open class fints4javaModelMapper {
open fun mapResponse(response: FinTsClientResponse): BankingClientResponse {
return BankingClientResponse(response.isSuccessful, mapErrorToShowToUser(response))
}
open fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse {
var bookedTransactions = mapOf<BankAccount, List<AccountTransaction>>()
var balances = mapOf<BankAccount, BigDecimal>()
account.bankAccounts.firstOrNull()?.let { bankAccount -> // TODO: set bank account also on net.dankito.fints.response.client.GetTransactionsResponse
bookedTransactions = mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions))
response.balance?.let { balances = mapOf(bankAccount to it) }
}
return AddAccountResponse(response.isSuccessful, mapErrorToShowToUser(response),
account, response.supportsRetrievingTransactionsOfLast90DaysWithoutTan,
bookedTransactions,
mapOf(), // TODO: map unbooked transactions
balances)
}
open fun mapResponse(bankAccount: BankAccount, response: net.dankito.fints.response.client.GetTransactionsResponse): GetTransactionsResponse {
return GetTransactionsResponse(response.isSuccessful, mapErrorToShowToUser(response),
mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions)),
mapOf(), // TODO: map unbooked transactions
response.balance?.let { mapOf(bankAccount to it) } ?: mapOf())
}
open fun mapErrorToShowToUser(response: FinTsClientResponse): String? {
return response.exception?.localizedMessage ?: response.errorsToShowToUser.joinToString("\n")
}
open fun mapAccount(customer: CustomerData, bank: BankData): Account {
val mappedBank = mapBank(bank)
@ -73,6 +107,7 @@ open class fints4javaModelMapper {
)
}
open fun mapTanProcedures(tanProcedures: List<net.dankito.fints.model.TanProcedure>): List<TanProcedure> {
return tanProcedures.map { mapTanProcedure(it) }
}
@ -107,13 +142,22 @@ open class fints4javaModelMapper {
}
open fun mapTanMedium(tanMedium: net.dankito.fints.messages.datenelemente.implementierte.tan.TanMedium): TanMedium {
val status = if (tanMedium.status.name.contains("Aktiv")) TanMediumStatus.Used else TanMediumStatus.Available
return TanMedium(
getDisplayNameForTanMedium(tanMedium),
mapTanMediumStatus(tanMedium)
)
}
return TanMedium(getDisplayNameForTanMedium(tanMedium), status, tanMedium)
open fun mapTanMedium(tanMedium: net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium): TanGeneratorTanMedium {
return TanGeneratorTanMedium(
getDisplayNameForTanMedium(tanMedium),
mapTanMediumStatus(tanMedium),
tanMedium.cardNumber
)
}
protected open fun getDisplayNameForTanMedium(tanMedium: net.dankito.fints.messages.datenelemente.implementierte.tan.TanMedium): String {
if (tanMedium is TanGeneratorTanMedium) {
if (tanMedium is net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium) {
var cardNumber = tanMedium.cardNumber
tanMedium.cardSequenceNumber?.let {
cardNumber += " (Kartenfolgenummer $it)" // TODO: translate
@ -129,49 +173,37 @@ open class fints4javaModelMapper {
return tanMedium.mediumClass.name
}
open fun mapResponse(response: FinTsClientResponse): BankingClientResponse {
return BankingClientResponse(response.isSuccessful, mapErrorToShowToUser(response))
open fun mapTanMediumStatus(tanMedium: net.dankito.fints.messages.datenelemente.implementierte.tan.TanMedium): TanMediumStatus {
return if (tanMedium.status.name.contains("aktiv", true)) TanMediumStatus.Used else TanMediumStatus.Available
}
open fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse {
var bookedTransactions = mapOf<BankAccount, List<AccountTransaction>>()
var balances = mapOf<BankAccount, BigDecimal>()
account.bankAccounts.firstOrNull()?.let { bankAccount -> // TODO: set bank account also on net.dankito.fints.response.client.GetTransactionsResponse
bookedTransactions = mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions))
response.balance?.let { balances = mapOf(bankAccount to it) }
open fun mapTanMedium(tanMedium: TanMedium, customer: CustomerData): net.dankito.fints.messages.datenelemente.implementierte.tan.TanMedium {
if (tanMedium is TanGeneratorTanMedium) {
return mapTanMedium(tanMedium, customer)
}
return AddAccountResponse(response.isSuccessful, mapErrorToShowToUser(response),
account, response.supportsRetrievingTransactionsOfLast90DaysWithoutTan,
bookedTransactions,
mapOf(), // TODO: map unbooked transactions
balances)
val statusToHave = if (tanMedium.status == TanMediumStatus.Used) listOf(net.dankito.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.Aktiv, net.dankito.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.AktivFolgekarte)
else listOf(net.dankito.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.Verfuegbar, net.dankito.fints.messages.datenelemente.implementierte.tan.TanMediumStatus.VerfuegbarFolgekarte)
return customer.tanMedia.first { tanMedium.displayName == it.mediumClass.name && statusToHave.contains(it.status) }
}
open fun mapResponse(bankAccount: BankAccount, response: net.dankito.fints.response.client.GetTransactionsResponse): GetTransactionsResponse {
return GetTransactionsResponse(response.isSuccessful, mapErrorToShowToUser(response),
mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions)),
mapOf(), // TODO: map unbooked transactions
response.balance?.let { mapOf(bankAccount to it) } ?: mapOf())
}
open fun mapErrorToShowToUser(response: FinTsClientResponse): String? {
return response.exception?.localizedMessage ?: response.errorsToShowToUser.joinToString("\n")
open fun mapTanMedium(tanMedium: TanGeneratorTanMedium, customer: CustomerData): net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium {
return customer.tanMedia.mapNotNull { it as? net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium }
.first { it.cardNumber == tanMedium.cardNumber
&& (it.cardSequenceNumber == null || tanMedium.displayName.contains(it.cardSequenceNumber!!)) }
}
open fun mapTanProcedureBack(tanProcedure: TanProcedure): net.dankito.fints.model.TanProcedure {
open fun mapTanProcedure(tanProcedure: TanProcedure): net.dankito.fints.model.TanProcedure {
return net.dankito.fints.model.TanProcedure(
tanProcedure.displayName,
Sicherheitsfunktion.values().first { it.code == tanProcedure.bankInternalProcedureCode },
mapTanProcedureTypeBack(tanProcedure.type)
mapTanProcedureType(tanProcedure.type)
)
}
open fun mapTanProcedureTypeBack(type: TanProcedureType): net.dankito.fints.model.TanProcedureType {
open fun mapTanProcedureType(type: TanProcedureType): net.dankito.fints.model.TanProcedureType {
return when (type) {
TanProcedureType.EnterTan -> net.dankito.fints.model.TanProcedureType.EnterTan
TanProcedureType.ChipTanManuell -> net.dankito.fints.model.TanProcedureType.ChipTanManuell
@ -183,4 +215,64 @@ open class fints4javaModelMapper {
}
}
open fun mapEnterTanResult(result: EnterTanResult, customer: CustomerData): net.dankito.fints.model.EnterTanResult {
result.changeTanProcedureTo?.let { changeTanProcedureTo ->
return net.dankito.fints.model.EnterTanResult.userAsksToChangeTanProcedure(mapTanProcedure(changeTanProcedureTo))
}
result.changeTanMediumTo?.let { changeTanMediumTo ->
val callback: ((FinTsClientResponse) -> Unit)? = if (result.changeTanMediumResultCallback == null) null
else { response -> result.changeTanMediumResultCallback?.invoke(mapResponse(response)) }
return net.dankito.fints.model.EnterTanResult.userAsksToChangeTanMedium(mapTanMedium(changeTanMediumTo, customer), callback)
}
result.enteredTan?.let { enteredTan ->
return net.dankito.fints.model.EnterTanResult.userEnteredTan(enteredTan)
}
return net.dankito.fints.model.EnterTanResult.userDidNotEnterTan()
}
open fun mapEnterTanGeneratorAtcResult(result: EnterTanGeneratorAtcResult): net.dankito.fints.model.EnterTanGeneratorAtcResult {
if (result.hasAtcBeenEntered) {
return net.dankito.fints.model.EnterTanGeneratorAtcResult.userEnteredAtc(result.tan!!, result.atc!!)
}
return net.dankito.fints.model.EnterTanGeneratorAtcResult.userDidNotEnterTan()
}
open fun mapTanChallenge(tanChallenge: net.dankito.fints.model.TanChallenge): TanChallenge {
if (tanChallenge is net.dankito.fints.model.FlickerCodeTanChallenge) {
return mapTanChallenge(tanChallenge)
}
if (tanChallenge is net.dankito.fints.model.ImageTanChallenge) {
return mapTanChallenge(tanChallenge)
}
return TanChallenge(tanChallenge.messageToShowToUser,
mapTanProcedure(tanChallenge.tanProcedure)
)
}
open fun mapTanChallenge(tanChallenge: net.dankito.fints.model.FlickerCodeTanChallenge): FlickerCodeTanChallenge {
return FlickerCodeTanChallenge(mapFlickerCode(tanChallenge.flickerCode), tanChallenge.messageToShowToUser,
mapTanProcedure(tanChallenge.tanProcedure)
)
}
open fun mapFlickerCode(flickerCode: net.dankito.fints.tan.FlickerCode): FlickerCode {
return FlickerCode(flickerCode.challengeHHD_UC, flickerCode.parsedDataSet, flickerCode.decodingError)
}
open fun mapTanChallenge(tanChallenge: net.dankito.fints.model.ImageTanChallenge): ImageTanChallenge {
return ImageTanChallenge(mapTanImage(tanChallenge.image), tanChallenge.messageToShowToUser,
mapTanProcedure(tanChallenge.tanProcedure)
)
}
open fun mapTanImage(image: net.dankito.fints.tan.TanImage): TanImage {
return TanImage(image.mimeType, image.imageBytes, image.decodingError)
}
}

View File

@ -18,7 +18,7 @@ import net.dankito.fints.response.client.FinTsClientResponse
import net.dankito.fints.response.client.GetTanMediaListResponse
import net.dankito.fints.response.client.GetTransactionsResponse
import net.dankito.fints.response.segments.*
import net.dankito.fints.tan.FlickercodeDecoder
import net.dankito.fints.tan.FlickerCodeDecoder
import net.dankito.fints.tan.TanImageDecoder
import net.dankito.fints.transactions.IAccountTransactionsParser
import net.dankito.fints.transactions.Mt940AccountTransactionsParser
@ -648,7 +648,7 @@ open class FinTsClient @JvmOverloads constructor(
return when (tanProcedure.type) {
TanProcedureType.ChipTanOptisch, TanProcedureType.ChipTanManuell ->
FlickercodeTanChallenge(FlickercodeDecoder().decodeChallenge(challenge), messageToShowToUser, challenge, tanProcedure, tanResponse.tanMediaIdentifier)
FlickerCodeTanChallenge(FlickerCodeDecoder().decodeChallenge(challenge), messageToShowToUser, challenge, tanProcedure, tanResponse.tanMediaIdentifier)
TanProcedureType.ChipTanQrCode, TanProcedureType.PhotoTan ->
ImageTanChallenge(TanImageDecoder().decodeChallenge(challenge), messageToShowToUser, challenge, tanProcedure, tanResponse.tanMediaIdentifier)

View File

@ -3,7 +3,7 @@ package net.dankito.fints.messages.datenelemente.implementierte.tan
import java.util.*
class TanGeneratorTanMedium(
open class TanGeneratorTanMedium(
mediumClass: TanMediumKlasse,
status: TanMediumStatus,
val cardNumber: String,

View File

@ -25,7 +25,7 @@ open class EnterTanResult protected constructor(
return EnterTanResult(null, changeTanProcedureTo)
}
fun userAsksToChangeTanMedium(changeTanMediumTo: TanMedium, changeTanMediumResultCallback: (FinTsClientResponse) -> Unit): EnterTanResult {
fun userAsksToChangeTanMedium(changeTanMediumTo: TanMedium, changeTanMediumResultCallback: ((FinTsClientResponse) -> Unit)?): EnterTanResult {
return EnterTanResult(null, null, changeTanMediumTo, changeTanMediumResultCallback)
}

View File

@ -1,10 +1,10 @@
package net.dankito.fints.model
import net.dankito.fints.tan.Flickercode
import net.dankito.fints.tan.FlickerCode
open class FlickercodeTanChallenge(
val flickercode: Flickercode,
open class FlickerCodeTanChallenge(
val flickerCode: FlickerCode,
messageToShowToUser: String,
challenge: String,
tanProcedure: TanProcedure,
@ -12,7 +12,7 @@ open class FlickercodeTanChallenge(
) : TanChallenge(messageToShowToUser, challenge, tanProcedure, tanMediaIdentifier) {
override fun toString(): String {
return "$tanProcedure (medium: $tanMediaIdentifier) $flickercode: $messageToShowToUser"
return "$tanProcedure (medium: $tanMediaIdentifier) $flickerCode: $messageToShowToUser"
}
}

View File

@ -1,15 +1,8 @@
package net.dankito.fints.tan
import org.slf4j.LoggerFactory
open class FlickerCanvas(var code: String) {
companion object {
private val log = LoggerFactory.getLogger(FlickerCanvas::class.java)
}
var halfbyteid = 0
var clock = Bit.High
var bitarray = mutableListOf<MutableList<Bit>>()

View File

@ -1,19 +1,19 @@
package net.dankito.fints.tan
open class Flickercode(
open class FlickerCode(
val challengeHHD_UC: String,
val parsedDataSet: String,
val error: Exception? = null
val decodingError: Exception? = null
) {
val decodingSuccessful: Boolean
get() = error == null
get() = decodingError == null
override fun toString(): String {
if (decodingSuccessful == false) {
return "Decoding error: $error"
return "Decoding error: $decodingError"
}
return "Parsed $challengeHHD_UC to $parsedDataSet"

View File

@ -1,10 +1,10 @@
package net.dankito.fints.tan
open class FlickercodeDatenelement(
open class FlickerCodeDatenelement(
val lengthInByte: String,
val data: String,
val encoding: FlickercodeEncoding,
val encoding: FlickerCodeEncoding,
val endIndex: Int
) {

View File

@ -4,16 +4,16 @@ import org.slf4j.LoggerFactory
import java.util.regex.Pattern
open class FlickercodeDecoder {
open class FlickerCodeDecoder {
companion object {
val ContainsOtherSymbolsThanFiguresPattern: Pattern = Pattern.compile("\\D")
private val log = LoggerFactory.getLogger(FlickercodeDecoder::class.java)
private val log = LoggerFactory.getLogger(FlickerCodeDecoder::class.java)
}
open fun decodeChallenge(challengeHHD_UC: String): Flickercode {
open fun decodeChallenge(challengeHHD_UC: String): FlickerCode {
try {
val challengeLength = parseIntToHex(challengeHHD_UC.substring(0, 2))
@ -40,28 +40,28 @@ open class FlickercodeDecoder {
val parsedDataSet = dataWithoutChecksum + luhnChecksum + xorChecksumString
return Flickercode(challengeHHD_UC, parsedDataSet)
return FlickerCode(challengeHHD_UC, parsedDataSet)
} catch (e: Exception) {
log.error("Could not decode challenge $challengeHHD_UC")
return Flickercode(challengeHHD_UC, "", e)
return FlickerCode(challengeHHD_UC, "", e)
}
}
protected fun parseStartCode(challengeHHD_UC: String, startIndex: Int): FlickercodeDatenelement {
protected open fun parseStartCode(challengeHHD_UC: String, startIndex: Int): FlickerCodeDatenelement {
return parseDatenelement(challengeHHD_UC, startIndex) { lengthByteString -> parseIntToHex(lengthByteString) }
}
protected open fun parseDatenelement(code: String, startIndex: Int): FlickercodeDatenelement {
protected open fun parseDatenelement(code: String, startIndex: Int): FlickerCodeDatenelement {
return parseDatenelement(code, startIndex) { lengthByteString -> lengthByteString.toInt() }
}
protected open fun parseDatenelement(code: String, startIndex: Int, lengthParser: (lengthByteString: String) -> Int): FlickercodeDatenelement {
protected open fun parseDatenelement(code: String, startIndex: Int, lengthParser: (lengthByteString: String) -> Int): FlickerCodeDatenelement {
val lengthByteLength = 2
val dataElementAndRest = code.substring(startIndex)
if (dataElementAndRest.isEmpty() || dataElementAndRest.length < lengthByteLength) { // data element not set
return FlickercodeDatenelement("", "", FlickercodeEncoding.BCD, startIndex)
return FlickerCodeDatenelement("", "", FlickerCodeEncoding.BCD, startIndex)
}
val lengthByteString = dataElementAndRest.substring(0, lengthByteLength)
@ -76,14 +76,14 @@ open class FlickercodeDecoder {
// Sollte ein Datenelement eine Zahl mit Komma-Trennung oder Vorzeichen beinhalten (z. B. Betrag oder Anzahl),
// so muss als Format ASCII gewählt werden, da ggf. auch ein Sonderzeichen mit übertragen werden muss.
if (ContainsOtherSymbolsThanFiguresPattern.matcher(data).find()) {
encoding = FlickercodeEncoding.ASCII
encoding = FlickerCodeEncoding.ASCII
}
if (encoding == FlickercodeEncoding.ASCII) {
if (encoding == FlickerCodeEncoding.ASCII) {
data = data.map { toHex(it.toInt(), 2) }.joinToString("")
}
if (encoding == FlickercodeEncoding.BCD && data.length % 2 != 0) {
if (encoding == FlickerCodeEncoding.BCD && data.length % 2 != 0) {
data += "F" // Im Format BCD ggf. mit „F“ auf Bytegrenze ergänzt
}
@ -91,7 +91,7 @@ open class FlickercodeDecoder {
var lengthInByte = dataLength / 2
if (encoding == FlickercodeEncoding.ASCII) {
if (encoding == FlickerCodeEncoding.ASCII) {
if (lengthInByte < 16) {
lengthInByte += 16 // set left half byte to '1' for ASCII
}
@ -99,7 +99,7 @@ open class FlickercodeDecoder {
val lengthInByteString = toHex(lengthInByte, 2)
return FlickercodeDatenelement(
return FlickerCodeDatenelement(
lengthInByteString,
data,
encoding,
@ -107,8 +107,8 @@ open class FlickercodeDecoder {
)
}
protected open fun getEncodingFromLengthByte(engthByte: Int): FlickercodeEncoding {
return if (isBitSet(engthByte, 6)) FlickercodeEncoding.ASCII else FlickercodeEncoding.BCD
protected open fun getEncodingFromLengthByte(engthByte: Int): FlickerCodeEncoding {
return if (isBitSet(engthByte, 6)) FlickerCodeEncoding.ASCII else FlickerCodeEncoding.BCD
}
protected open fun getLengthFromLengthByte(lengthByte: Int): Int {
@ -116,8 +116,8 @@ open class FlickercodeDecoder {
}
protected open fun calculateLuhnChecksum(startCode: FlickercodeDatenelement, controlByte: String,
de1: FlickercodeDatenelement, de2: FlickercodeDatenelement, de3: FlickercodeDatenelement): Int {
protected open fun calculateLuhnChecksum(startCode: FlickerCodeDatenelement, controlByte: String,
de1: FlickerCodeDatenelement, de2: FlickerCodeDatenelement, de3: FlickerCodeDatenelement): Int {
val luhnData = controlByte + startCode.data + de1.data + de2.data + de3.data

View File

@ -1,7 +1,7 @@
package net.dankito.fints.tan
enum class FlickercodeEncoding {
enum class FlickerCodeEncoding {
BCD,

View File

@ -4,16 +4,16 @@ package net.dankito.fints.tan
open class TanImage(
val mimeType: String,
val imageBytes: ByteArray,
val error: Exception? = null
val decodingError: Exception? = null
) {
val decodingSuccessful: Boolean
get() = error == null
get() = decodingError == null
override fun toString(): String {
if (decodingSuccessful == false) {
return "Decoding error: $error"
return "Decoding error: $decodingError"
}
return "$mimeType ${imageBytes.size} bytes"

View File

@ -4,9 +4,9 @@ import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
class FlickercodeDecoderTest {
class FlickerCodeDecoderTest {
private val underTest = FlickercodeDecoder()
private val underTest = FlickerCodeDecoder()
@Test

View File

@ -6,4 +6,6 @@ include ':BankListCreator'
include ':BankingUiCommon'
include ':fints4javaBankingClient'
include ':fints4javaAndroidApp'