Implemented changing TAN procedure and TAN medium

This commit is contained in:
dankl 2020-01-08 23:22:49 +01:00 committed by dankito
parent ce58ef60ca
commit b0ee9bb0d5
7 changed files with 123 additions and 41 deletions

View File

@ -28,6 +28,11 @@ add.account.dialog.successfully.added.account.bank.supports.retrieving.transacti
enter.tan.dialog.title=TAN required enter.tan.dialog.title=TAN required
enter.tan.dialog.select.tan.procedure=TAN procedure:
enter.tan.dialog.select.tan.medium=TAN medium:
enter.tan.dialog.size.label=Size: enter.tan.dialog.size.label=Size:
enter.tan.dialog.frequency.label=Speed: enter.tan.dialog.frequency.label=Speed:
enter.tan.dialog.enter.tan.label=TAN: enter.tan.dialog.enter.tan.label=TAN:
enter.tan.dialog.error.could.not.decode.tan.image=Could not decode flicker code or QR code / PhotoTan. Most likely an internal error:\n%s.
enter.tan.dialog.tan.medium.successfully.changed=TAN medium successfully changed to \'%s\'.
enter.tan.dialog.tan.error.changing.tan.medium=Could not change TAN medium to \'%s\':\n%s.

View File

@ -28,6 +28,11 @@ add.account.dialog.successfully.added.account.bank.supports.retrieving.transacti
enter.tan.dialog.title=TAN wird benötigt enter.tan.dialog.title=TAN wird benötigt
enter.tan.dialog.select.tan.procedure=TAN Verfahren:
enter.tan.dialog.select.tan.medium=TAN Medium:
enter.tan.dialog.size.label=Größe: enter.tan.dialog.size.label=Größe:
enter.tan.dialog.frequency.label=Geschwindigkeit: enter.tan.dialog.frequency.label=Geschwindigkeit:
enter.tan.dialog.enter.tan.label=TAN: enter.tan.dialog.enter.tan.label=TAN:
enter.tan.dialog.error.could.not.decode.tan.image=Flickercode bzw. QR-Code / PhotoTan konnte nicht dekodiert werden. Höchst wahrscheinlich ein interner Fehler:\n%s.
enter.tan.dialog.tan.medium.successfully.changed=TAN Medium erfolgreich geändert zu \'%s\'.
enter.tan.dialog.tan.error.changing.tan.medium=TAN Medium konnte nicht geändert werden zu \'%s\':\n%s.

View File

@ -21,40 +21,32 @@ import java.io.StringWriter
open class JavaFxDialogService { open class JavaFxDialogService {
open fun showInfoMessage(infoMessage: CharSequence, alertTitle: CharSequence?) { open fun showInfoMessage(infoMessage: CharSequence, alertTitle: CharSequence? = null, owner: Stage? = FX.primaryStage) {
showInfoMessage(infoMessage, alertTitle, FX.primaryStage)
}
open fun showInfoMessage(infoMessage: CharSequence, alertTitle: CharSequence?, owner: Stage?) {
Platform.runLater { showInfoMessageOnUiThread(infoMessage, alertTitle, owner) } Platform.runLater { showInfoMessageOnUiThread(infoMessage, alertTitle, owner) }
} }
open fun showInfoMessageOnUiThread(infoMessage: CharSequence, alertTitle: CharSequence?, owner: Stage?) { open fun showInfoMessageOnUiThread(infoMessage: CharSequence, alertTitle: CharSequence? = null, owner: Stage? = FX.primaryStage): ButtonType? {
val alert = createDialog(Alert.AlertType.INFORMATION, infoMessage, alertTitle, owner, ButtonType.OK) val dialog = createDialog(Alert.AlertType.INFORMATION, infoMessage, alertTitle, owner, ButtonType.OK)
alert.showAndWait() return showAndWaitForResult(dialog)
} }
open fun showErrorMessage(errorMessage: CharSequence, alertTitle: CharSequence?, exception: Exception?) { open fun showErrorMessage(errorMessage: CharSequence, alertTitle: CharSequence? = null, exception: Exception? = null, owner: Stage? = FX.primaryStage) {
showErrorMessage(errorMessage, alertTitle, exception, null)
}
open fun showErrorMessage(errorMessage: CharSequence, alertTitle: CharSequence?, exception: Exception?, owner: Stage?) {
Platform.runLater { showErrorMessageOnUiThread(errorMessage, alertTitle, exception, owner) } Platform.runLater { showErrorMessageOnUiThread(errorMessage, alertTitle, exception, owner) }
} }
open fun showErrorMessageOnUiThread(errorMessage: CharSequence, alertTitle: CharSequence?, exception: Exception?, owner: Stage?) { open fun showErrorMessageOnUiThread(errorMessage: CharSequence, alertTitle: CharSequence? = null, exception: Exception? = null, owner: Stage? = FX.primaryStage): ButtonType? {
val alert = createDialog(Alert.AlertType.ERROR, errorMessage, alertTitle, owner, ButtonType.OK) val dialog = createDialog(Alert.AlertType.ERROR, errorMessage, alertTitle, owner, ButtonType.OK)
if (exception != null) { if (exception != null) {
createExpandableException(alert, exception) createExpandableException(dialog, exception)
} }
alert.showAndWait() return showAndWaitForResult(dialog)
} }
protected open fun createExpandableException(alert: Alert, exception: Exception) { protected open fun createExpandableException(dialog: Alert, exception: Exception) {
val sw = StringWriter() val sw = StringWriter()
val pw = PrintWriter(sw) val pw = PrintWriter(sw)
exception.printStackTrace(pw) exception.printStackTrace(pw)
@ -77,13 +69,17 @@ open class JavaFxDialogService {
expContent.add(textArea, 0, 1) expContent.add(textArea, 0, 1)
// Set expandable Exception into the dialog pane. // Set expandable Exception into the dialog pane.
alert.dialogPane.expandableContent = expContent dialog.dialogPane.expandableContent = expContent
} }
open fun showDialog(alertType: Alert.AlertType, message: CharSequence, alertTitle: CharSequence? = null, owner: Stage? = FX.primaryStage, vararg buttons: ButtonType): ButtonType? { open fun showDialog(alertType: Alert.AlertType, message: CharSequence, alertTitle: CharSequence? = null, owner: Stage? = FX.primaryStage, vararg buttons: ButtonType): ButtonType? {
val dialog = createDialog(alertType, message, alertTitle, owner, *buttons) val dialog = createDialog(alertType, message, alertTitle, owner, *buttons)
return showAndWaitForResult(dialog)
}
protected open fun showAndWaitForResult(dialog: Alert): ButtonType? {
val result = dialog.showAndWait() val result = dialog.showAndWait()
return result.map { it }.orElse(null) return result.map { it }.orElse(null)

View File

@ -1,13 +1,14 @@
package net.dankito.banking.ui.javafx.dialogs.tan package net.dankito.banking.ui.javafx.dialogs.tan
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty import javafx.beans.property.SimpleStringProperty
import javafx.geometry.Insets import javafx.geometry.Insets
import javafx.geometry.Pos import javafx.geometry.Pos
import net.dankito.banking.javafx.dialogs.tan.controls.ChipTanFlickerCodeView import net.dankito.banking.javafx.dialogs.tan.controls.ChipTanFlickerCodeView
import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService
import net.dankito.banking.ui.model.Account import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.tan.EnterTanResult import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.tan.FlickerCodeTanChallenge import net.dankito.banking.ui.model.tan.*
import net.dankito.banking.ui.model.tan.TanChallenge
import net.dankito.banking.ui.presenter.MainWindowPresenter import net.dankito.banking.ui.presenter.MainWindowPresenter
import net.dankito.utils.javafx.ui.dialogs.Window import net.dankito.utils.javafx.ui.dialogs.Window
import tornadofx.* import tornadofx.*
@ -26,22 +27,74 @@ open class EnterTanDialog(
} }
protected val dialogService = JavaFxDialogService()
protected val selectedTanProcedure = SimpleObjectProperty<TanProcedure>(account.selectedTanProcedure ?: account.supportedTanProcedures.firstOrNull())
protected val selectedTanMedium = SimpleObjectProperty<TanMedium>(account.tanMediaSorted.firstOrNull())
protected val enteredTan = SimpleStringProperty("") protected val enteredTan = SimpleStringProperty("")
init {
selectedTanProcedure.addListener { _, _, newValue ->
tanEnteredCallback(EnterTanResult.userAsksToChangeTanProcedure(newValue))
close()
}
selectedTanMedium.addListener { _, _, newValue ->
if (newValue.status != TanMediumStatus.Used) {
tanEnteredCallback(EnterTanResult.userAsksToChangeTanMedium(newValue) { response ->
runLater { handleChangeTanMediumResponseOnUiThread(newValue, response) }
})
}
}
}
override val root = vbox { override val root = vbox {
paddingAll = 4.0 paddingAll = 4.0
(challenge as? FlickerCodeTanChallenge)?.let { flickerCodeTanChallenge -> form {
hbox { fieldset {
alignment = Pos.CENTER field(messages["enter.tan.dialog.select.tan.procedure"]) {
combobox(selectedTanProcedure, account.supportedTanProcedures) {
vboxConstraints { cellFormat {
marginLeftRight(30.0) text = it.displayName
marginBottom = 12.0 }
}
} }
add(ChipTanFlickerCodeView(flickerCodeTanChallenge.flickerCode)) if (account.tanMediaSorted.isNotEmpty()) {
field(messages["enter.tan.dialog.select.tan.medium"]) {
combobox(selectedTanMedium, account.tanMediaSorted) {
cellFormat {
text = it.displayName
}
}
}
}
}
}
(challenge as? FlickerCodeTanChallenge)?.let { flickerCodeTanChallenge ->
val flickerCode = flickerCodeTanChallenge.flickerCode
if (flickerCode.decodingSuccessful) {
hbox {
alignment = Pos.CENTER
vboxConstraints {
marginLeftRight(30.0)
marginBottom = 12.0
}
add(ChipTanFlickerCodeView(flickerCode))
}
}
else {
showDecodingTanChallengeFailedError(flickerCode.decodingError)
} }
} }
@ -110,7 +163,26 @@ open class EnterTanDialog(
} }
private fun finishedEnteringTan() { protected open fun showDecodingTanChallengeFailedError(error: Exception?) {
dialogService.showErrorMessage(String.format(messages["enter.tan.dialog.error.could.not.decode.tan.image"], error?.localizedMessage),
null, error, currentStage)
}
protected open fun handleChangeTanMediumResponseOnUiThread(newUsedTanMedium: TanMedium, response: BankingClientResponse) {
if (response.isSuccessful) {
dialogService.showInfoMessageOnUiThread(String.format(messages["enter.tan.dialog.tan.medium.successfully.changed"],
newUsedTanMedium.displayName), null, currentStage)
close()
}
else {
dialogService.showErrorMessageOnUiThread(String.format(messages["enter.tan.dialog.tan.error.changing.tan.medium"],
newUsedTanMedium.displayName, response.errorToShowToUser), null, response.error, currentStage)
}
}
protected open fun finishedEnteringTan() {
if (enteredTan.value.isNullOrEmpty()) { if (enteredTan.value.isNullOrEmpty()) {
cancelledEnteringTan() cancelledEnteringTan()
} }
@ -121,7 +193,7 @@ open class EnterTanDialog(
} }
} }
private fun cancelledEnteringTan() { protected open fun cancelledEnteringTan() {
tanEnteredCallback(EnterTanResult.userDidNotEnterTan()) tanEnteredCallback(EnterTanResult.userDidNotEnterTan())
close() close()

View File

@ -1,6 +1,7 @@
package net.dankito.banking.ui.model package net.dankito.banking.ui.model
import net.dankito.banking.ui.model.tan.TanMedium import net.dankito.banking.ui.model.tan.TanMedium
import net.dankito.banking.ui.model.tan.TanMediumStatus
import net.dankito.banking.ui.model.tan.TanProcedure import net.dankito.banking.ui.model.tan.TanProcedure
import java.math.BigDecimal import java.math.BigDecimal
@ -23,6 +24,9 @@ open class Account(
var tanMedia: List<TanMedium> = listOf() var tanMedia: List<TanMedium> = listOf()
val tanMediaSorted: List<TanMedium>
get() = tanMedia.sortedByDescending { it.status == TanMediumStatus.Used }
val displayName: String val displayName: String
get() = bank.name get() = bank.name

View File

@ -15,13 +15,13 @@ import android.widget.Spinner
import kotlinx.android.synthetic.main.dialog_enter_tan.view.* import kotlinx.android.synthetic.main.dialog_enter_tan.view.*
import kotlinx.android.synthetic.main.view_tan_image.view.* import kotlinx.android.synthetic.main.view_tan_image.view.*
import net.dankito.banking.fints4java.android.R import net.dankito.banking.fints4java.android.R
import net.dankito.banking.ui.presenter.MainWindowPresenter
import net.dankito.banking.fints4java.android.ui.adapter.TanMediumAdapter 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.adapter.TanProceduresAdapter
import net.dankito.banking.fints4java.android.ui.listener.ListItemSelectedListener import net.dankito.banking.fints4java.android.ui.listener.ListItemSelectedListener
import net.dankito.banking.ui.model.Account import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.tan.* import net.dankito.banking.ui.model.tan.*
import net.dankito.banking.ui.presenter.MainWindowPresenter
open class EnterTanDialog : DialogFragment() { open class EnterTanDialog : DialogFragment() {
@ -104,7 +104,7 @@ open class EnterTanDialog : DialogFragment() {
protected open fun setupSelectTanMediumView(rootView: View) { protected open fun setupSelectTanMediumView(rootView: View) {
rootView.lytTanMedium.visibility = View.VISIBLE rootView.lytTanMedium.visibility = View.VISIBLE
tanMediumAdapter.setItems(account.tanMedia.sortedByDescending { it.status == TanMediumStatus.Used }) tanMediumAdapter.setItems(account.tanMediaSorted)
rootView.spnTanMedium.adapter = tanMediumAdapter rootView.spnTanMedium.adapter = tanMediumAdapter
rootView.spnTanMedium.onItemSelectedListener = ListItemSelectedListener(tanMediumAdapter) { selectedTanMedium -> rootView.spnTanMedium.onItemSelectedListener = ListItemSelectedListener(tanMediumAdapter) { selectedTanMedium ->
@ -131,12 +131,12 @@ open class EnterTanDialog : DialogFragment() {
val flickerCodeView = rootView.flickerCodeView val flickerCodeView = rootView.flickerCodeView
flickerCodeView.visibility = View.VISIBLE flickerCodeView.visibility = View.VISIBLE
val flickercode = (tanChallenge as FlickerCodeTanChallenge).flickerCode val flickerCode = (tanChallenge as FlickerCodeTanChallenge).flickerCode
if (flickercode.decodingSuccessful) { if (flickerCode.decodingSuccessful) {
flickerCodeView.setCode(flickercode) flickerCodeView.setCode(flickerCode)
} }
else { else {
showDecodingTanChallengeFailedErrorDelayed(flickercode.decodingError) showDecodingTanChallengeFailedErrorDelayed(flickerCode.decodingError)
} }
} }
else if (tanChallenge is ImageTanChallenge) { else if (tanChallenge is ImageTanChallenge) {

View File

@ -65,7 +65,7 @@
<string name="dialog_enter_tan_select_tan_procedure">TAN procedure</string> <string name="dialog_enter_tan_select_tan_procedure">TAN procedure</string>
<string name="dialog_enter_tan_select_tan_medium">TAN medium</string> <string name="dialog_enter_tan_select_tan_medium">TAN medium</string>
<string name="dialog_enter_tan_enter_tan">Enter TAN:</string> <string name="dialog_enter_tan_enter_tan">Enter TAN:</string>
<string name="dialog_enter_tan_error_could_not_decode_tan_image">Could not decode QR code / PhotoTan. Most likely an internal error:\n%s.</string> <string name="dialog_enter_tan_error_could_not_decode_tan_image">Could not decode flicker code or QR code / PhotoTan. Most likely an internal error:\n%s.</string>
<string name="dialog_enter_tan_tan_medium_successfully_changed">TAN medium successfully changed to \'%s\'.</string> <string name="dialog_enter_tan_tan_medium_successfully_changed">TAN medium successfully changed to \'%s\'.</string>
<string name="dialog_enter_tan_error_changing_tan_medium">Could not change TAN medium to \'%s\':\n%s.</string> <string name="dialog_enter_tan_error_changing_tan_medium">Could not change TAN medium to \'%s\':\n%s.</string>