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

View File

@ -11,17 +11,17 @@ open class SimpleFinTsClientCallback(
) : FinTsClientCallback {
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 {
return enterTan?.invoke(customer, tanChallenge) ?: EnterTanResult.userDidNotEnterTan()
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
callback(enterTan?.invoke(customer, tanChallenge) ?: EnterTanResult.userDidNotEnterTan())
}
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
return enterTanGeneratorAtc?.invoke(customer, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc()
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
callback(enterTanGeneratorAtc?.invoke(customer, tanMedium) ?: EnterTanGeneratorAtcResult.userDidNotEnterAtc())
}
}

View File

@ -46,18 +46,18 @@ open class FinTsClientTestBase {
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
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
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} " +
"(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 {
val enteredTan = AtomicReference<EnterTanResult>(null)
val tanEnteredLatch = CountDownLatch(1)
activityTracker.currentOrNextActivity { activity ->
override fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) {
activityTracker.currentOrNextActivity { activity ->
activity.runOnUiThread {
EnterTanDialog().show(customer, tanChallenge, activity, false) {
enteredTan.set(it)
tanEnteredLatch.countDown()
EnterTanDialog().show(customer, tanChallenge, activity, false) { result ->
callback(result)
}
}
}
try { tanEnteredLatch.await() } catch (ignored: Exception) { }
return enteredTan.get()
}
override fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
val result = AtomicReference<EnterTanGeneratorAtcResult>(null)
val tanEnteredLatch = CountDownLatch(1)
override fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
activityTracker.currentOrNextActivity { activity ->
activity.runOnUiThread {
EnterAtcDialog().show(tanMedium, activity, false) { enteredResult ->
result.set(enteredResult)
tanEnteredLatch.countDown()
callback(enteredResult)
}
}
}
try { tanEnteredLatch.await() } catch (ignored: Exception) { }
return result.get()
}
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,
fullscreen: Boolean = false, atcEnteredCallback: (EnterTanGeneratorAtcResult?) -> Unit) {
fullscreen: Boolean = false, atcEnteredCallback: (EnterTanGeneratorAtcResult) -> Unit) {
this.tanMedium = tanMedium
this.atcEnteredCallback = atcEnteredCallback

View File

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

View File

@ -74,23 +74,25 @@ open class BankingPresenter(
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) {
persistAccount(customer)
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
saveAccountOnNextEnterTanInvocation = true
callback(result)
}
return result
}
override fun enterTanGeneratorAtc(tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
return router.getAtcFromUserFromNonUiThread(tanMedium)
override fun enterTanGeneratorAtc(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
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 {
override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?): TanProcedure? {
return handleAskUserForTanProcedure(supportedTanProcedures, suggestedTanProcedure)
override fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>, suggestedTanProcedure: TanProcedure?, callback: (TanProcedure?) -> Unit) {
handleAskUserForTanProcedure(supportedTanProcedures, suggestedTanProcedure, callback)
}
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): EnterTanResult {
return handleEnterTan(customer, tanChallenge, callback)
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
handleEnterTan(customer, tanChallenge, callback, clientCallback)
}
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
return handleEnterTanGeneratorAtc(customer, tanMedium, callback)
override fun enterTanGeneratorAtc(customer: CustomerData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) {
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
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)
val result = callback.enterTan(this@fints4kBankingClient.customer, mapper.mapTanChallenge(tanChallenge))
return mapper.mapEnterTanResult(result, customer)
clientCallback.enterTan(this@fints4kBankingClient.customer, mapper.mapTanChallenge(tanChallenge)) { result ->
enterTanCallback(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)
val result = callback.enterTanGeneratorAtc(mapper.mapTanMedium(tanMedium))
return mapper.mapEnterTanGeneratorAtcResult(result)
clientCallback.enterTanGeneratorAtc(mapper.mapTanMedium(tanMedium)) { result ->
enterAtcCallback(mapper.mapEnterTanGeneratorAtcResult(result))
}
}
}