Implemented saving TAN settings like TAN image size and flicker code frequency

This commit is contained in:
dankito 2020-05-24 22:49:50 +02:00
parent 3072517eef
commit e86b17d08b
11 changed files with 353 additions and 64 deletions

View File

@ -79,8 +79,8 @@ class BankingModule(private val applicationContext: Context) {
fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder,
@Named(DatabaseFolderKey) databaseFolder: File, @Named(DataFolderKey) dataFolder: File,
persister: IBankingPersistence, bankIconFinder: IBankIconFinder,
router: IRouter, threadPool: IThreadPool) : BankingPresenter {
return BankingPresenter(bankingClientCreator, bankFinder, databaseFolder, dataFolder, persister, bankIconFinder, router, threadPool)
router: IRouter, serializer: ISerializer, threadPool: IThreadPool) : BankingPresenter {
return BankingPresenter(bankingClientCreator, bankFinder, databaseFolder, dataFolder, persister, bankIconFinder, router, serializer, threadPool)
}
@Provides

View File

@ -1,7 +1,6 @@
package net.dankito.banking.ui.android.dialogs
import android.content.Context
import android.graphics.BitmapFactory
import android.os.Bundle
import android.os.Handler
import android.text.InputType
@ -13,8 +12,9 @@ import android.widget.Spinner
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import kotlinx.android.synthetic.main.dialog_enter_tan.*
import kotlinx.android.synthetic.main.dialog_enter_tan.view.*
import kotlinx.android.synthetic.main.view_tan_image.view.*
import kotlinx.android.synthetic.main.dialog_enter_tan.view.flickerCodeView
import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.android.di.BankingComponent
import net.dankito.banking.ui.android.adapter.TanMediumAdapter
@ -30,6 +30,8 @@ import javax.inject.Inject
open class EnterTanDialog : DialogFragment() {
companion object {
val QrCodeTanProcedures = listOf(TanProcedureType.ChipTanQrCode, TanProcedureType.QrCode)
val OpticalTanProcedures = listOf(TanProcedureType.ChipTanFlickercode, TanProcedureType.ChipTanQrCode,
TanProcedureType.ChipTanPhotoTanMatrixCode, TanProcedureType.photoTan, TanProcedureType.QrCode)
@ -146,7 +148,7 @@ open class EnterTanDialog : DialogFragment() {
val flickerCode = (tanChallenge as FlickerCodeTanChallenge).flickerCode
if (flickerCode.decodingSuccessful) {
flickerCodeView.setCode(flickerCode)
flickerCodeView.setCode(flickerCode, presenter.appSettings.flickerCodeSettings)
}
else {
showDecodingTanChallengeFailedErrorDelayed(flickerCode.decodingError)
@ -157,15 +159,14 @@ open class EnterTanDialog : DialogFragment() {
val decodedImage = (tanChallenge as ImageTanChallenge).image
if (decodedImage.decodingSuccessful) {
val bitmap = BitmapFactory.decodeByteArray(decodedImage.imageBytes, 0, decodedImage.imageBytes.size)
rootView.imgTanImageView.setImageBitmap(bitmap)
rootView.tanImageView.setImage(tanChallenge as ImageTanChallenge, if (isQrTan(tanChallenge)) presenter.appSettings.qrCodeSettings else presenter.appSettings.photoTanSettings)
}
else {
showDecodingTanChallengeFailedErrorDelayed(decodedImage.decodingError)
}
}
rootView.edtxtEnteredTan.inputType = InputType.TYPE_CLASS_NUMBER
rootView.edtxtEnteredTan.inputType = InputType.TYPE_CLASS_NUMBER // TODO: is this always true that TAN is a number?
rootView.edtxtEnteredTan.setOnKeyListener { _, keyCode, _ ->
if (keyCode == KeyEvent.KEYCODE_ENTER) {
@ -230,7 +231,33 @@ open class EnterTanDialog : DialogFragment() {
tanEnteredCallback(result)
checkIfAppSettingsChanged()
dismiss()
}
protected open fun checkIfAppSettingsChanged() {
if (flickerCodeView.didTanProcedureSettingsChange) {
presenter.appSettings.flickerCodeSettings = flickerCodeView.tanProcedureSettings
presenter.appSettingsChanged()
}
if (tanImageView.didTanProcedureSettingsChange) {
if (isQrTan(tanChallenge)) {
presenter.appSettings.qrCodeSettings = tanImageView.tanProcedureSettings
}
else {
presenter.appSettings.photoTanSettings = tanImageView.tanProcedureSettings
}
presenter.appSettingsChanged()
}
}
protected open fun isQrTan(tanChallenge: TanChallenge): Boolean {
return QrCodeTanProcedures.contains(tanChallenge.tanProcedure.type)
}
}

View File

@ -12,21 +12,24 @@ import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.model.tan.FlickerCode
import net.dankito.banking.ui.util.FlickerCodeAnimator
import net.dankito.banking.fints.tan.Bit
import net.dankito.banking.ui.model.settings.ITanView
import net.dankito.banking.ui.model.settings.TanProcedureSettings
import net.dankito.utils.android.extensions.asActivity
open class ChipTanFlickerCodeView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
) : LinearLayout(context, attrs, defStyleAttr), ITanView {
companion object {
const val FrequencyStepSize = 2
const val MinFrequency = 2
const val MaxFrequency = 40
const val DefaultFrequency = 30
const val StripesHeightStepSize = 7
const val StripesWidthStepSize = 2
const val StripesRightMarginStepSize = 1
const val SpaceBetweenStripesStepSize = 1
}
@ -48,13 +51,20 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
protected var stripesHeight = 360
protected var stripesWidth = 120
protected var stripesMarginRight = 30.0 // set back to 30
protected var spaceBetweenStripes = 30
protected var currentFrequency = 30
protected var currentFrequency = DefaultFrequency
protected var isFlickerCodePaused = false
override var didTanProcedureSettingsChange: Boolean = false
protected set
override var tanProcedureSettings: TanProcedureSettings? = null
protected set
init {
setupUi(context)
}
@ -82,12 +92,17 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
stripesHeight = stripe1.layoutParams.height
stripesWidth = stripe1.layoutParams.width
(stripe1.layoutParams as? MarginLayoutParams)?.let { stripesMarginRight = it.rightMargin.toDouble() }
(stripe1.layoutParams as? MarginLayoutParams)?.let { spaceBetweenStripes = it.rightMargin }
tanGeneratorLeftMarker = rootView.findViewById(R.id.tanGeneratorLeftMarker)
tanGeneratorRightMarker = rootView.findViewById(R.id.tanGeneratorRightMarker)
setMarkerPositionAfterStripesLayoutSet()
tanProcedureSettings?.let {
setSize(it.width, it.height, it.space)
setFrequency(it.frequency)
}
}
@ -100,22 +115,30 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
open fun decreaseSize() {
stripesHeight -= StripesHeightStepSize
stripesWidth -= StripesWidthStepSize
stripesMarginRight -= StripesRightMarginStepSize
setWidth(context)
setSize(
stripesWidth - StripesWidthStepSize,
stripesHeight - StripesHeightStepSize,
spaceBetweenStripes - SpaceBetweenStripesStepSize
)
}
open fun increaseSize() {
stripesHeight += StripesHeightStepSize
stripesWidth += StripesWidthStepSize
stripesMarginRight += StripesRightMarginStepSize
setWidth(context)
setSize(
stripesWidth + StripesWidthStepSize,
stripesHeight + StripesHeightStepSize,
spaceBetweenStripes + SpaceBetweenStripesStepSize
)
}
protected open fun setWidth(context: Context) {
open fun setSize(width: Int, height: Int, spaceBetweenStripes: Int) {
this.stripesWidth = width
this.stripesHeight = height
this.spaceBetweenStripes = spaceBetweenStripes
applySize()
}
protected open fun applySize() {
allStripes.forEach { stripe ->
val params = stripe.layoutParams
params.height = stripesHeight
@ -123,7 +146,7 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
(params as? MarginLayoutParams)?.let { marginParams ->
if (marginParams.rightMargin > 0) { // don't set a margin right on fifth stripe
marginParams.rightMargin = stripesMarginRight.toInt()
marginParams.rightMargin = spaceBetweenStripes
}
}
@ -133,6 +156,8 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
requestLayout()
setMarkerPositionAfterStripesLayoutSet()
tanProcedureSettingsChanged()
}
protected open fun setMarkerPositionAfterStripesLayoutSet() {
@ -148,22 +173,28 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
open fun decreaseFrequency() {
if (currentFrequency - FrequencyStepSize >= MinFrequency) {
currentFrequency -= FrequencyStepSize
setFrequency(currentFrequency)
setFrequency(currentFrequency - FrequencyStepSize)
}
}
open fun increaseFrequency() {
if (currentFrequency + FrequencyStepSize <= MaxFrequency) {
currentFrequency += FrequencyStepSize
setFrequency(currentFrequency)
setFrequency(currentFrequency + FrequencyStepSize)
}
}
open fun setFrequency(frequency: Int) {
currentFrequency = frequency
animator.setFrequency(frequency)
tanProcedureSettingsChanged()
}
protected open fun tanProcedureSettingsChanged() {
tanProcedureSettings = TanProcedureSettings(stripesWidth, stripesHeight, spaceBetweenStripes, currentFrequency)
didTanProcedureSettingsChange = true // we don't check if settings really changed, it's not that important
}
@ -181,10 +212,19 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
}
open fun setCode(flickerCode: FlickerCode) {
open fun setCode(flickerCode: FlickerCode, tanProcedureSettings: TanProcedureSettings?) {
animator.stop()
setFrequency(currentFrequency)
tanProcedureSettings?.let {
setSize(it.width, it.height, it.space)
setFrequency(it.frequency)
}
?: run {
setFrequency(DefaultFrequency)
}
this.tanProcedureSettings = tanProcedureSettings
this.didTanProcedureSettingsChange = false
animator.animateFlickerCode(flickerCode) { step ->
context.asActivity()?.runOnUiThread {

View File

@ -1,6 +1,7 @@
package net.dankito.banking.ui.android.views
import android.content.Context
import android.graphics.BitmapFactory
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
@ -8,11 +9,14 @@ import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_tan_image.view.*
import kotlinx.android.synthetic.main.view_tan_image_size_controls.view.*
import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.model.settings.ITanView
import net.dankito.banking.ui.model.settings.TanProcedureSettings
import net.dankito.banking.ui.model.tan.ImageTanChallenge
open class TanImageView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
) : LinearLayout(context, attrs, defStyleAttr), ITanView {
companion object {
const val ChangeSizeStepSizeDp = 10f
@ -26,6 +30,13 @@ open class TanImageView @JvmOverloads constructor(
protected lateinit var imgTanImageView: ImageView
override var didTanProcedureSettingsChange: Boolean = false
protected set
override var tanProcedureSettings: TanProcedureSettings? = null
protected set
init {
MinHeight = convertDpToPx(context, 150f).toInt()
MaxHeight = convertDpToPx(context, 500f).toInt()
@ -44,6 +55,17 @@ open class TanImageView @JvmOverloads constructor(
}
open fun setImage(challenge: ImageTanChallenge, tanProcedureSettings: TanProcedureSettings?) {
val decodedImage = challenge.image
val bitmap = BitmapFactory.decodeByteArray(decodedImage.imageBytes, 0, decodedImage.imageBytes.size)
rootView.imgTanImageView.setImageBitmap(bitmap)
tanProcedureSettings?.let {
setWidthAndHeight(it.width)
}
}
open fun decreaseSize() {
changeSizeBy(ChangeSizeStepSizeDp * -1)
}
@ -56,16 +78,30 @@ open class TanImageView @JvmOverloads constructor(
val params = imgTanImageView.layoutParams
val newWidthAndHeight = params.height + convertDpToPx(context, changeSizeBy).toInt()
setWidthAndHeight(newWidthAndHeight)
}
protected open fun setWidthAndHeight(newWidthAndHeight: Int) {
if (newWidthAndHeight in MinHeight..MaxHeight) {
val params = imgTanImageView.layoutParams
params.height = newWidthAndHeight
params.width = newWidthAndHeight
imgTanImageView.layoutParams = params // TODO: needed?
requestLayout()
tanProcedureSettingsChanged(newWidthAndHeight)
}
}
protected open fun tanProcedureSettingsChanged(newWidthAndHeight: Int) {
tanProcedureSettings = TanProcedureSettings(newWidthAndHeight, newWidthAndHeight)
didTanProcedureSettingsChange = true // we don't check if settings really changed, it's not that important
}
/**
* This method converts dp unit to equivalent pixels, depending on device density.
*

View File

@ -11,6 +11,7 @@ import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.tan.*
import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.utils.extensions.htmlToPlainText
import net.dankito.utils.javafx.ui.dialogs.Window
import tornadofx.*
@ -23,6 +24,8 @@ open class EnterTanDialog(
) : Window() {
companion object {
val QrCodeTanProcedures = listOf(TanProcedureType.ChipTanQrCode, TanProcedureType.QrCode)
private val ButtonHeight = 40.0
private val ButtonWidth = 150.0
}
@ -30,6 +33,10 @@ open class EnterTanDialog(
protected val dialogService = JavaFxDialogService()
protected var flickerCodeView: ChipTanFlickerCodeView? = null
protected var tanImageView: TanImageView? = null
protected val tanProceduresWithoutUnsupported = account.supportedTanProcedures.filterNot { it.type == TanProcedureType.ChipTanUsb } // USB tan generators are not supported
@ -93,7 +100,9 @@ open class EnterTanDialog(
marginBottom = 12.0
}
add(ChipTanFlickerCodeView(flickerCode))
add(ChipTanFlickerCodeView(flickerCode, presenter.appSettings.flickerCodeSettings).apply {
flickerCodeView = this
})
}
}
else {
@ -104,7 +113,8 @@ open class EnterTanDialog(
(challenge as? ImageTanChallenge)?.let { imageTanChallenge ->
val decodedImage = imageTanChallenge.image
if (decodedImage.decodingSuccessful) {
add(TanImageView(decodedImage).apply {
add(TanImageView(decodedImage, if (isQrTan(challenge)) presenter.appSettings.qrCodeSettings else presenter.appSettings.photoTanSettings).apply {
tanImageView = this
vboxConstraints {
marginLeftRight(30.0)
@ -216,6 +226,8 @@ open class EnterTanDialog(
else {
tanEnteredCallback(EnterTanResult.userEnteredTan(enteredTan.value))
checkIfAppSettingsChanged()
close()
}
}
@ -226,4 +238,28 @@ open class EnterTanDialog(
close()
}
protected open fun checkIfAppSettingsChanged() {
if (flickerCodeView?.didTanProcedureSettingsChange == true) {
presenter.appSettings.flickerCodeSettings = flickerCodeView?.tanProcedureSettings
presenter.appSettingsChanged()
}
if (tanImageView?.didTanProcedureSettingsChange == true) {
if (isQrTan(challenge)) {
presenter.appSettings.qrCodeSettings = tanImageView?.tanProcedureSettings
}
else {
presenter.appSettings.photoTanSettings = tanImageView?.tanProcedureSettings
}
presenter.appSettingsChanged()
}
}
protected open fun isQrTan(tanChallenge: TanChallenge): Boolean {
return QrCodeTanProcedures.contains(tanChallenge.tanProcedure.type)
}
}

View File

@ -9,13 +9,18 @@ import net.dankito.banking.ui.util.FlickerCodeAnimator
import net.dankito.banking.fints.tan.Bit
import net.dankito.banking.javafx.dialogs.tan.controls.ChipTanFlickerCodeStripeView
import net.dankito.banking.javafx.dialogs.tan.controls.TanGeneratorMarkerView
import net.dankito.banking.ui.model.settings.ITanView
import net.dankito.banking.ui.model.settings.TanProcedureSettings
import net.dankito.utils.javafx.ui.extensions.fixedHeight
import net.dankito.utils.javafx.ui.extensions.fixedWidth
import net.dankito.utils.javafx.ui.extensions.setBackgroundToColor
import tornadofx.*
open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View() {
open class ChipTanFlickerCodeView(
protected val flickerCode: FlickerCode,
tanProcedureSettings: TanProcedureSettings?
): View(), ITanView {
companion object {
const val ChangeSizeStripeHeightStep = 7.0
@ -26,6 +31,7 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
const val MaxFlickerCodeViewWidth = 1000.0 // what is a senseful value?
const val ChangeFrequencyStep = 5
const val DefaultFrequency = 30
const val IconWidth = 26.0
const val IconHeight = 26.0
@ -34,9 +40,9 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
protected val flickerCodeLeftRightMargin = SimpleDoubleProperty(31.0)
protected val stripeHeight = SimpleDoubleProperty(127.0)
protected val stripeWidth = SimpleDoubleProperty(42.0)
protected val spaceBetweenStripes = SimpleDoubleProperty(10.0)
protected val stripesHeight = SimpleDoubleProperty(tanProcedureSettings?.height?.toDouble() ?: 127.0)
protected val stripesWidth = SimpleDoubleProperty(tanProcedureSettings?.width?.toDouble() ?: 42.0)
protected val spaceBetweenStripes = SimpleDoubleProperty(tanProcedureSettings?.space?.toDouble() ?: 10.0)
protected val flickerCodeViewWidth = SimpleDoubleProperty()
@ -52,15 +58,22 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
protected val isMinFrequencyReached = SimpleBooleanProperty(false)
protected val isMaxFrequencyReached = SimpleBooleanProperty(false)
protected var currentFrequency = 20
protected var currentFrequency = tanProcedureSettings?.frequency ?: DefaultFrequency
protected val animator = FlickerCodeAnimator()
override var didTanProcedureSettingsChange: Boolean = false
protected set
override var tanProcedureSettings: TanProcedureSettings? = tanProcedureSettings
protected set
init {
flickerCodeViewWidth.bind(stripeWidth.add(spaceBetweenStripes).multiply(4).add(stripeWidth).add(flickerCodeLeftRightMargin).add(flickerCodeLeftRightMargin))
flickerCodeViewWidth.bind(stripesWidth.add(spaceBetweenStripes).multiply(4).add(stripesWidth).add(flickerCodeLeftRightMargin).add(flickerCodeLeftRightMargin))
animator.setFrequency(currentFrequency)
setFrequency(currentFrequency)
}
@ -122,13 +135,13 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
add(TanGeneratorMarkerView().apply {
setLeftMarkerPosition(this)
stripeWidth.addListener { _, _, _ -> setLeftMarkerPosition(this) }
stripesWidth.addListener { _, _, _ -> setLeftMarkerPosition(this) }
})
add(TanGeneratorMarkerView().apply {
setRightMarkerPosition(this)
stripeWidth.addListener { _, _, _ -> setRightMarkerPosition(this) }
stripesWidth.addListener { _, _, _ -> setRightMarkerPosition(this) }
})
vboxConstraints {
@ -139,11 +152,11 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
hbox {
minHeight = 150.0
add(ChipTanFlickerCodeStripeView(stripe1, stripeWidth, stripeHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe2, stripeWidth, stripeHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe3, stripeWidth, stripeHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe4, stripeWidth, stripeHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe5, stripeWidth, stripeHeight, SimpleDoubleProperty(0.0)))
add(ChipTanFlickerCodeStripeView(stripe1, stripesWidth, stripesHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe2, stripesWidth, stripesHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe3, stripesWidth, stripesHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe4, stripesWidth, stripesHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe5, stripesWidth, stripesHeight, SimpleDoubleProperty(0.0)))
}
vboxConstraints {
@ -153,6 +166,10 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
}
}
updateMinAndMaxSizeReached()
updateMinAndMaxFrequencyReached()
animator.animateFlickerCode(flickerCode) { step ->
runLater {
paintFlickerCode(step)
@ -171,22 +188,23 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
protected open fun setLeftMarkerPosition(component: UIComponent) {
component.root.anchorpaneConstraints {
leftAnchor = (stripeWidth.value / 2)
leftAnchor = (stripesWidth.value / 2)
}
}
protected open fun setRightMarkerPosition(component: UIComponent) {
component.root.anchorpaneConstraints {
rightAnchor = (stripeWidth.value / 2)
rightAnchor = (stripesWidth.value / 2)
}
}
protected open fun increaseSize() {
if (isMaxSizeReached.value == false) {
stripeHeight.value = stripeHeight.value + ChangeSizeStripeHeightStep
stripeWidth.value = stripeWidth.value + ChangeSizeStripeWidthStep
spaceBetweenStripes.value = spaceBetweenStripes.value + ChangeSizeSpaceBetweenStripesStep
setSize(stripesWidth.value + ChangeSizeStripeWidthStep, stripesHeight.value + ChangeSizeStripeHeightStep,
spaceBetweenStripes.value + ChangeSizeSpaceBetweenStripesStep)
tanProcedureSettingsChanged()
}
updateMinAndMaxSizeReached()
@ -194,16 +212,27 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
protected open fun decreaseSize() {
if (isMinSizeReached.value == false) {
stripeHeight.value = stripeHeight.value - ChangeSizeStripeHeightStep
stripeWidth.value = stripeWidth.value - ChangeSizeStripeWidthStep
spaceBetweenStripes.value = spaceBetweenStripes.value - ChangeSizeSpaceBetweenStripesStep
setSize(stripesWidth.value - ChangeSizeStripeWidthStep, stripesHeight.value - ChangeSizeStripeHeightStep,
spaceBetweenStripes.value - ChangeSizeSpaceBetweenStripesStep)
tanProcedureSettingsChanged()
}
updateMinAndMaxSizeReached()
}
open fun setSize(width: Double, height: Double, spaceBetweenStripes: Double) {
this.stripesWidth.value = width
this.stripesHeight.value = height
this.spaceBetweenStripes.value = spaceBetweenStripes
tanProcedureSettingsChanged()
updateMinAndMaxSizeReached()
}
protected open fun updateMinAndMaxSizeReached() {
val flickerCodeWidth = stripeWidth.value * 5 + spaceBetweenStripes.value * 4
val flickerCodeWidth = stripesWidth.value * 5 + spaceBetweenStripes.value * 4
isMinSizeReached.value = flickerCodeWidth < MinFlickerCodeViewWidth
isMaxSizeReached.value = flickerCodeWidth > MaxFlickerCodeViewWidth
@ -213,8 +242,7 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
if (isMaxFrequencyReached.value == false
&& (currentFrequency + ChangeFrequencyStep) <= FlickerCodeAnimator.MaxFrequency) {
currentFrequency += ChangeFrequencyStep
animator.setFrequency(currentFrequency)
setFrequency(currentFrequency + ChangeFrequencyStep)
}
updateMinAndMaxFrequencyReached()
@ -224,16 +252,33 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
if (isMinFrequencyReached.value == false
&& (currentFrequency - ChangeFrequencyStep) >= FlickerCodeAnimator.MinFrequency) {
currentFrequency -= ChangeFrequencyStep
animator.setFrequency(currentFrequency)
setFrequency(currentFrequency - ChangeFrequencyStep)
}
updateMinAndMaxFrequencyReached()
}
protected open fun setFrequency(frequency: Int) {
currentFrequency = frequency
animator.setFrequency(currentFrequency)
updateMinAndMaxFrequencyReached()
tanProcedureSettingsChanged()
}
protected open fun updateMinAndMaxFrequencyReached() {
isMaxFrequencyReached.value = (currentFrequency + ChangeFrequencyStep) > FlickerCodeAnimator.MaxFrequency
isMinFrequencyReached.value = (currentFrequency - ChangeFrequencyStep) < FlickerCodeAnimator.MinFrequency
}
protected open fun tanProcedureSettingsChanged() {
tanProcedureSettings = TanProcedureSettings(stripesWidth.value.toInt(), stripesHeight.value.toInt(),
spaceBetweenStripes.value.toInt(), currentFrequency)
didTanProcedureSettingsChange = true // we don't check if settings really changed, it's not that important
}
}

View File

@ -4,13 +4,18 @@ import javafx.beans.property.SimpleBooleanProperty
import javafx.geometry.Pos
import javafx.scene.image.Image
import javafx.scene.image.ImageView
import net.dankito.banking.ui.model.settings.ITanView
import net.dankito.banking.ui.model.settings.TanProcedureSettings
import net.dankito.banking.ui.model.tan.TanImage
import net.dankito.utils.javafx.ui.extensions.updateWindowSize
import tornadofx.*
import java.io.ByteArrayInputStream
open class TanImageView(protected val tanImage: TanImage) : View() {
open class TanImageView(
protected val tanImage: TanImage,
tanProcedureSettings: TanProcedureSettings?
) : View(), ITanView {
companion object {
private const val ChangeSizeStepSize = 10.0
@ -29,6 +34,13 @@ open class TanImageView(protected val tanImage: TanImage) : View() {
protected var tanImageView: ImageView by singleAssign()
override var didTanProcedureSettingsChange: Boolean = false
protected set
override var tanProcedureSettings: TanProcedureSettings? = tanProcedureSettings
protected set
override val root = vbox {
add(TanImageSizeView(IconHeight, IconWidth, isMinSizeReached, isMaxSizeReached, { decreaseSize() }, { increaseSize() } ))
@ -45,6 +57,12 @@ open class TanImageView(protected val tanImage: TanImage) : View() {
marginBottom = 4.0
}
}
tanProcedureSettings?.let {
runLater {
setWidthAndHeight(it.width.toDouble())
}
}
}
@ -59,14 +77,26 @@ open class TanImageView(protected val tanImage: TanImage) : View() {
protected open fun changeSizeBy(changeSizeBy: Double) {
val newWidthAndHeight = tanImageView.fitHeight + changeSizeBy
setWidthAndHeight(newWidthAndHeight)
}
protected open fun setWidthAndHeight(newWidthAndHeight: Double) {
if (newWidthAndHeight in MinHeight..MaxHeight) {
tanImageView.fitHeight = newWidthAndHeight
updateWindowSize()
tanProcedureSettingsChanged(newWidthAndHeight.toInt())
}
isMinSizeReached.value = tanImageView.fitHeight <= MinHeight
isMaxSizeReached.value = tanImageView.fitHeight >= MaxHeight
}
protected open fun tanProcedureSettingsChanged(newWidthAndHeight: Int) {
tanProcedureSettings = TanProcedureSettings(newWidthAndHeight, newWidthAndHeight)
didTanProcedureSettingsChange = true // we don't check if settings really changed, it's not that important
}
}

View File

@ -0,0 +1,12 @@
package net.dankito.banking.ui.model.settings
open class AppSettings(
var flickerCodeSettings: TanProcedureSettings? = null,
var qrCodeSettings: TanProcedureSettings? = null,
var photoTanSettings: TanProcedureSettings? = null
) {
internal constructor() : this(null, null, null) // for object deserializers
}

View File

@ -0,0 +1,10 @@
package net.dankito.banking.ui.model.settings
interface ITanView {
val didTanProcedureSettingsChange: Boolean
val tanProcedureSettings: TanProcedureSettings?
}

View File

@ -0,0 +1,18 @@
package net.dankito.banking.ui.model.settings
open class TanProcedureSettings(
var width: Int,
var height: Int,
var space: Int = -1, // only needed for flicker code view
var frequency: Int = -1 // only needed for flicker code view
) {
internal constructor() : this(0, 0) // for object deserializers
override fun toString(): String {
return "Size $width x $height, frequency $frequency"
}
}

View File

@ -17,10 +17,13 @@ import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
import net.dankito.banking.util.IBankIconFinder
import net.dankito.banking.fints.banks.IBankFinder
import net.dankito.banking.fints.model.BankInfo
import net.dankito.banking.ui.model.settings.AppSettings
import net.dankito.utils.IThreadPool
import net.dankito.utils.ThreadPool
import net.dankito.utils.extensions.containsExactly
import net.dankito.utils.extensions.ofMaxLength
import net.dankito.utils.serialization.ISerializer
import net.dankito.utils.serialization.JacksonJsonSerializer
import org.slf4j.LoggerFactory
import java.io.File
import java.io.FileOutputStream
@ -39,6 +42,7 @@ open class BankingPresenter(
protected val persister: IBankingPersistence,
protected val bankIconFinder: IBankIconFinder,
protected val router: IRouter,
protected val serializer: ISerializer = JacksonJsonSerializer(),
protected val threadPool: IThreadPool = ThreadPool()
) {
@ -93,6 +97,7 @@ open class BankingPresenter(
init {
threadPool.runAsync {
readAppSettings()
readPersistedAccounts()
}
@ -543,6 +548,36 @@ open class BankingPresenter(
}
var appSettings: AppSettings = AppSettings()
protected set
open fun appSettingsChanged() {
persistAppSettings()
}
protected open fun persistAppSettings() {
try {
serializer.serializeObject(appSettings, getAppSettingsFile())
} catch (e: Exception) {
log.error("Could not persist AppSettings to file ${getAppSettingsFile()}", e)
}
}
protected open fun readAppSettings() {
try {
serializer.deserializeObject(getAppSettingsFile(), AppSettings::class.java)?.let {
appSettings = it
}
} catch (e: Exception) {
log.error("Could not read AppSettings from file ${getAppSettingsFile()}", e)
}
}
protected open fun getAppSettingsFile(): File {
return File(dataFolder, "app_settings.json")
}
open fun addAccountsChangedListener(listener: (List<Account>) -> Unit): Boolean {
return accountsChangedListeners.add(listener)
}