Added callbacks to FinTsClientCallback methods so that they can be responded asynchronously

This commit is contained in:
dankito 2020-07-21 10:59:33 +02:00
parent 2889d1b0ce
commit 7424688b33
12 changed files with 85 additions and 100 deletions

View File

@ -424,22 +424,27 @@ open class FinTsClient(
open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData, callback: (FinTsClientResponse) -> Unit) { open fun changeTanMedium(newActiveTanMedium: TanGeneratorTanMedium, bank: BankData, customer: CustomerData, callback: (FinTsClientResponse) -> Unit) {
var enteredAtc: EnterTanGeneratorAtcResult? = null
if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) { if (bank.changeTanMediumParameters?.enteringAtcAndTanRequired == true) {
enteredAtc = this.callback.enterTanGeneratorAtc(customer, newActiveTanMedium) this.callback.enterTanGeneratorAtc(customer, newActiveTanMedium) { enteredAtc ->
if (enteredAtc.hasAtcBeenEntered == false) {
if (enteredAtc.hasAtcBeenEntered == false) { val message = "Bank requires to enter ATC and TAN in order to change TAN medium." // TODO: translate
val message = "Bank requires to enter ATC and TAN in order to change TAN medium." // TODO: translate callback(FinTsClientResponse(Response(false, exception = Exception(message))))
callback(FinTsClientResponse(Response(false, exception = Exception(message)))) }
return else {
sendChangeTanMediumMessage(bank, customer, newActiveTanMedium, enteredAtc, callback)
}
} }
} }
else {
sendChangeTanMediumMessage(bank, customer, newActiveTanMedium, null, callback)
}
}
protected open fun sendChangeTanMediumMessage(bank: BankData, customer: CustomerData, newActiveTanMedium: TanGeneratorTanMedium,
enteredAtc: EnterTanGeneratorAtcResult?, callback: (FinTsClientResponse) -> Unit) {
sendMessageAndHandleResponse(bank, customer, false, { dialogContext -> sendMessageAndHandleResponse(bank, customer, false, { dialogContext ->
messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, dialogContext, messageBuilder.createChangeTanMediumMessage(newActiveTanMedium, dialogContext, enteredAtc?.tan, enteredAtc?.atc)
enteredAtc?.tan, enteredAtc?.atc)
}) { response -> }) { response ->
callback(FinTsClientResponse(response)) callback(FinTsClientResponse(response))
} }
@ -577,8 +582,10 @@ open class FinTsClient(
} }
else { else {
// we know user's supported tan procedures, now ask user which one to select // we know user's supported tan procedures, now ask user which one to select
callback.askUserForTanProcedure(customer.supportedTanProcedures, selectSuggestedTanProcedure(customer))?.let { callback.askUserForTanProcedure(customer.supportedTanProcedures, selectSuggestedTanProcedure(customer)) { selectedTanProcedure ->
customer.selectedTanProcedure = it selectedTanProcedure?.let {
customer.selectedTanProcedure = selectedTanProcedure
}
} }
} }
} }
@ -775,9 +782,9 @@ open class FinTsClient(
val customer = dialogContext.customer // TODO: copy required data to TanChallenge val customer = dialogContext.customer // TODO: copy required data to TanChallenge
val tanChallenge = createTanChallenge(tanResponse, customer) val tanChallenge = createTanChallenge(tanResponse, customer)
val enteredTanResult = this.callback.enterTan(customer, tanChallenge) // TODO: add callback to be more flexible in regard to thread handling this.callback.enterTan(customer, tanChallenge) { enteredTanResult ->
handleEnterTanResult(enteredTanResult, tanResponse, response, dialogContext, callback)
handleEnterTanResult(enteredTanResult, tanResponse, response, dialogContext, callback) }
} }
protected open fun createTanChallenge(tanResponse: TanResponse, customer: CustomerData): TanChallenge { protected open fun createTanChallenge(tanResponse: TanResponse, customer: CustomerData): TanChallenge {

View File

@ -16,15 +16,15 @@ interface FinTsClientCallback {
* If you do not support an enter tan dialog or if your enter tan dialog supports selecting a TAN procedure, it's * If you do not support an enter tan dialog or if your enter tan dialog supports selecting a TAN procedure, it's
* best returning [suggestedTanProcedure] and to not show an extra select TAN procedure dialog. * best returning [suggestedTanProcedure] and to not show an extra select TAN procedure dialog.
*/ */
fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?): TanProcedure? fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?, callback: (TanProcedure?) -> Unit)
fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit)
/** /**
* This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator. * This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator.
* *
* If you do not support entering TAN generator ATC, return [EnterTanGeneratorAtcResult.userDidNotEnterAtc] * If you do not support entering TAN generator ATC, return [EnterTanGeneratorAtcResult.userDidNotEnterAtc]
*/ */
fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit)
} }

View File

@ -7,17 +7,17 @@ import net.dankito.banking.fints.model.*
open class NoOpFinTsClientCallback : FinTsClientCallback { open class NoOpFinTsClientCallback : FinTsClientCallback {
override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>,
suggestedTanProcedure: TanProcedure?): TanProcedure? { suggestedTanProcedure: TanProcedure?, callback: (TanProcedure?) -> Unit) {
return suggestedTanProcedure callback(suggestedTanProcedure)
} }
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult { override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
return EnterTanResult.userDidNotEnterTan() callback(EnterTanResult.userDidNotEnterTan())
} }
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult { override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
return EnterTanGeneratorAtcResult.userDidNotEnterAtc() callback(EnterTanGeneratorAtcResult.userDidNotEnterAtc())
} }
} }

View File

@ -11,17 +11,17 @@ open class SimpleFinTsClientCallback(
) : FinTsClientCallback { ) : FinTsClientCallback {
override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>,
suggestedTanProcedure: TanProcedure?): TanProcedure? { suggestedTanProcedure: TanProcedure?, callback: (TanProcedure?) -> Unit) {
return askUserForTanProcedure?.invoke(supportedTanProcedures, suggestedTanProcedure) ?: suggestedTanProcedure callback(askUserForTanProcedure?.invoke(supportedTanProcedures, suggestedTanProcedure) ?: suggestedTanProcedure)
} }
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult { override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
return enterTan?.invoke(customer, tanChallenge) ?: EnterTanResult.userDidNotEnterTan() callback(enterTan?.invoke(customer, tanChallenge) ?: EnterTanResult.userDidNotEnterTan())
} }
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult { override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
return enterTanGeneratorAtc?.invoke(customer, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc() callback(enterTanGeneratorAtc?.invoke(customer, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc())
} }
} }

View File

@ -46,18 +46,18 @@ open class FinTsClientTestBase {
private val callback = object : FinTsClientCallback { private val callback = object : FinTsClientCallback {
override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?): TanProcedure? { override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?, callback: (TanProcedure?) -> Unit) {
didAskUserForTanProcedure = true didAskUserForTanProcedure = true
return suggestedTanProcedure // simply return suggestedTanProcedure as in most cases it's the best fitting one callback(suggestedTanProcedure) // simply return suggestedTanProcedure as in most cases it's the best fitting one
} }
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult { override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
didAskUserToEnterTan = true didAskUserToEnterTan = true
return EnterTanResult.userDidNotEnterTan() callback(EnterTanResult.userDidNotEnterTan())
} }
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult { override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
fail("Bank asks you to synchronize your TAN generator for card ${tanMedium.cardNumber} " + fail("Bank asks you to synchronize your TAN generator for card ${tanMedium.cardNumber} " +
"(card sequence number ${tanMedium.cardSequenceNumber}). Please do this via your online banking portal or Banking UI.") "(card sequence number ${tanMedium.cardSequenceNumber}). Please do this via your online banking portal or Banking UI.")
} }

View File

@ -23,40 +23,24 @@ open class RouterAndroid(protected val activityTracker: CurrentActivityTracker)
} }
} }
override fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter): EnterTanResult { override fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) {
val enteredTan = AtomicReference<EnterTanResult>(null) activityTracker.currentOrNextActivity { activity ->
val tanEnteredLatch = CountDownLatch(1)
activityTracker.currentOrNextActivity { activity ->
activity.runOnUiThread { activity.runOnUiThread {
EnterTanDialog().show(customer, tanChallenge, activity, false) { EnterTanDialog().show(customer, tanChallenge, activity, false) { result ->
enteredTan.set(it) callback(result)
tanEnteredLatch.countDown()
} }
} }
} }
try { tanEnteredLatch.await() } catch (ignored: Exception) { }
return enteredTan.get()
} }
override fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult { override fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
val result = AtomicReference<EnterTanGeneratorAtcResult>(null)
val tanEnteredLatch = CountDownLatch(1)
activityTracker.currentOrNextActivity { activity -> activityTracker.currentOrNextActivity { activity ->
activity.runOnUiThread { activity.runOnUiThread {
EnterAtcDialog().show(tanMedium, activity, false) { enteredResult -> EnterAtcDialog().show(tanMedium, activity, false) { enteredResult ->
result.set(enteredResult) callback(enteredResult)
tanEnteredLatch.countDown()
} }
} }
} }
try { tanEnteredLatch.await() } catch (ignored: Exception) { }
return result.get()
} }
override fun showTransferMoneyDialog(presenter: BankingPresenter, preselectedBankAccount: BankAccount?, preselectedValues: TransferMoneyData?) { override fun showTransferMoneyDialog(presenter: BankingPresenter, preselectedBankAccount: BankAccount?, preselectedValues: TransferMoneyData?) {

View File

@ -28,7 +28,7 @@ open class EnterAtcDialog : DialogFragment() {
open fun show(tanMedium: TanMedium, activity: AppCompatActivity, open fun show(tanMedium: TanMedium, activity: AppCompatActivity,
fullscreen: Boolean = false, atcEnteredCallback: (EnterTanGeneratorAtcResult?) -> Unit) { fullscreen: Boolean = false, atcEnteredCallback: (EnterTanGeneratorAtcResult) -> Unit) {
this.tanMedium = tanMedium this.tanMedium = tanMedium
this.atcEnteredCallback = atcEnteredCallback this.atcEnteredCallback = atcEnteredCallback

View File

@ -25,24 +25,16 @@ open class RouterJavaFx : IRouter {
AddAccountDialog(presenter).show(messages["add.account.dialog.title"]) AddAccountDialog(presenter).show(messages["add.account.dialog.title"])
} }
override fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter): EnterTanResult { override fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) {
val enteredTan = AtomicReference<EnterTanResult>(null)
val tanEnteredLatch = CountDownLatch(1)
FX.runAndWait { FX.runAndWait {
EnterTanDialog(customer, tanChallenge, presenter) { EnterTanDialog(customer, tanChallenge, presenter) { result ->
enteredTan.set(it) callback(result)
tanEnteredLatch.countDown()
}.show(messages["enter.tan.dialog.title"]) }.show(messages["enter.tan.dialog.title"])
} }
try { tanEnteredLatch.await() } catch (ignored: Exception) { }
return enteredTan.get()
} }
override fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult { override fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
return EnterTanGeneratorAtcResult.userDidNotEnterAtc() callback(EnterTanGeneratorAtcResult.userDidNotEnterAtc()) // TODO: implement EnterAtcDialog
} }
override fun showTransferMoneyDialog(presenter: BankingPresenter, preselectedBankAccount: BankAccount?, preselectedValues: TransferMoneyData?) { override fun showTransferMoneyDialog(presenter: BankingPresenter, preselectedBankAccount: BankAccount?, preselectedValues: TransferMoneyData?) {

View File

@ -9,11 +9,11 @@ import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
interface BankingClientCallback { interface BankingClientCallback {
fun enterTan(customer: Customer, tanChallenge: TanChallenge): EnterTanResult fun enterTan(customer: Customer, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit)
/** /**
* This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator. * 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 fun enterTanGeneratorAtc(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit)
} }

View File

@ -14,9 +14,9 @@ interface IRouter {
fun showAddAccountDialog(presenter: BankingPresenter) fun showAddAccountDialog(presenter: BankingPresenter)
fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter): EnterTanResult fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit)
fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit)
fun showTransferMoneyDialog(presenter: BankingPresenter, preselectedBankAccount: BankAccount?, preselectedValues: TransferMoneyData?) fun showTransferMoneyDialog(presenter: BankingPresenter, preselectedBankAccount: BankAccount?, preselectedValues: TransferMoneyData?)

View File

@ -74,23 +74,25 @@ open class BankingPresenter(
protected val callback: BankingClientCallback = object : BankingClientCallback { protected val callback: BankingClientCallback = object : BankingClientCallback {
override fun enterTan(customer: Customer, tanChallenge: TanChallenge): EnterTanResult { override fun enterTan(customer: Customer, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
if (saveAccountOnNextEnterTanInvocation) { if (saveAccountOnNextEnterTanInvocation) {
persistAccount(customer) persistAccount(customer)
saveAccountOnNextEnterTanInvocation = false saveAccountOnNextEnterTanInvocation = false
} }
val result = router.getTanFromUserFromNonUiThread(customer, tanChallenge, this@BankingPresenter) router.getTanFromUserFromNonUiThread(customer, tanChallenge, this@BankingPresenter) { result ->
if (result.changeTanProcedureTo != null || result.changeTanMediumTo != null) { // then either selected TAN medium or procedure will change -> save account on next call to enterTan() as then changes will be visible
saveAccountOnNextEnterTanInvocation = true
}
if (result.changeTanProcedureTo != null || result.changeTanMediumTo != null) { // then either selected TAN medium or procedure will change -> save account on next call to enterTan() as then changes will be visible callback(result)
saveAccountOnNextEnterTanInvocation = true
} }
return result
} }
override fun enterTanGeneratorAtc(tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult { override fun enterTanGeneratorAtc(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
return router.getAtcFromUserFromNonUiThread(tanMedium) router.getAtcFromUserFromNonUiThread(tanMedium) { result ->
callback(result)
}
} }
} }

View File

@ -167,43 +167,43 @@ open class fints4kBankingClient(
} }
protected open fun createFinTsClientCallback(callback: BankingClientCallback): FinTsClientCallback { protected open fun createFinTsClientCallback(clientCallback: BankingClientCallback): FinTsClientCallback {
return object : FinTsClientCallback { return object : FinTsClientCallback {
override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?): TanProcedure? { override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?, callback: (TanProcedure?) -> Unit) {
return handleAskUserForTanProcedure(supportedTanProcedures, suggestedTanProcedure) handleAskUserForTanProcedure(supportedTanProcedures, suggestedTanProcedure, callback)
} }
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult { override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
return handleEnterTan(customer, tanChallenge, callback) handleEnterTan(customer, tanChallenge, callback, clientCallback)
} }
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult { override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
return handleEnterTanGeneratorAtc(customer, tanMedium, callback) handleEnterTanGeneratorAtc(customer, tanMedium, callback, clientCallback)
} }
} }
} }
protected open fun handleAskUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?): TanProcedure? { protected open fun handleAskUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?, callback: (TanProcedure?) -> Unit) {
// we simply return suggestedTanProcedure as even so it's not user's preferred TAN procedure she still can select it in EnterTanDialog // we simply return suggestedTanProcedure as even so it's not user's preferred TAN procedure she still can select it in EnterTanDialog
return suggestedTanProcedure callback(suggestedTanProcedure)
} }
protected open fun handleEnterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: BankingClientCallback): EnterTanResult { protected open fun handleEnterTan(customer: CustomerData, tanChallenge: TanChallenge, enterTanCallback: (EnterTanResult) -> Unit, clientCallback: BankingClientCallback) {
mapper.updateTanMediaAndProcedures(this@fints4kBankingClient.customer, customer) mapper.updateTanMediaAndProcedures(this@fints4kBankingClient.customer, customer)
val result = callback.enterTan(this@fints4kBankingClient.customer, mapper.mapTanChallenge(tanChallenge)) clientCallback.enterTan(this@fints4kBankingClient.customer, mapper.mapTanChallenge(tanChallenge)) { result ->
enterTanCallback(mapper.mapEnterTanResult(result, customer))
return mapper.mapEnterTanResult(result, customer) }
} }
protected open fun handleEnterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: BankingClientCallback): EnterTanGeneratorAtcResult { protected open fun handleEnterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, enterAtcCallback: (EnterTanGeneratorAtcResult) -> Unit, clientCallback: BankingClientCallback) {
mapper.updateTanMediaAndProcedures(this@fints4kBankingClient.customer, customer) mapper.updateTanMediaAndProcedures(this@fints4kBankingClient.customer, customer)
val result = callback.enterTanGeneratorAtc(mapper.mapTanMedium(tanMedium)) clientCallback.enterTanGeneratorAtc(mapper.mapTanMedium(tanMedium)) { result ->
enterAtcCallback(mapper.mapEnterTanGeneratorAtcResult(result))
return mapper.mapEnterTanGeneratorAtcResult(result) }
} }
} }