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, fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder,
@Named(DatabaseFolderKey) databaseFolder: File, @Named(DataFolderKey) dataFolder: File, @Named(DatabaseFolderKey) databaseFolder: File, @Named(DataFolderKey) dataFolder: File,
persister: IBankingPersistence, bankIconFinder: IBankIconFinder, persister: IBankingPersistence, bankIconFinder: IBankIconFinder,
router: IRouter, threadPool: IThreadPool) : BankingPresenter { router: IRouter, serializer: ISerializer, threadPool: IThreadPool) : BankingPresenter {
return BankingPresenter(bankingClientCreator, bankFinder, databaseFolder, dataFolder, persister, bankIconFinder, router, threadPool) return BankingPresenter(bankingClientCreator, bankFinder, databaseFolder, dataFolder, persister, bankIconFinder, router, serializer, threadPool)
} }
@Provides @Provides

View File

@ -1,7 +1,6 @@
package net.dankito.banking.ui.android.dialogs package net.dankito.banking.ui.android.dialogs
import android.content.Context import android.content.Context
import android.graphics.BitmapFactory
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.text.InputType import android.text.InputType
@ -13,8 +12,9 @@ import android.widget.Spinner
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment 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.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.R
import net.dankito.banking.ui.android.di.BankingComponent import net.dankito.banking.ui.android.di.BankingComponent
import net.dankito.banking.ui.android.adapter.TanMediumAdapter import net.dankito.banking.ui.android.adapter.TanMediumAdapter
@ -30,6 +30,8 @@ import javax.inject.Inject
open class EnterTanDialog : DialogFragment() { open class EnterTanDialog : DialogFragment() {
companion object { companion object {
val QrCodeTanProcedures = listOf(TanProcedureType.ChipTanQrCode, TanProcedureType.QrCode)
val OpticalTanProcedures = listOf(TanProcedureType.ChipTanFlickercode, TanProcedureType.ChipTanQrCode, val OpticalTanProcedures = listOf(TanProcedureType.ChipTanFlickercode, TanProcedureType.ChipTanQrCode,
TanProcedureType.ChipTanPhotoTanMatrixCode, TanProcedureType.photoTan, TanProcedureType.QrCode) TanProcedureType.ChipTanPhotoTanMatrixCode, TanProcedureType.photoTan, TanProcedureType.QrCode)
@ -146,7 +148,7 @@ open class EnterTanDialog : DialogFragment() {
val flickerCode = (tanChallenge as FlickerCodeTanChallenge).flickerCode val flickerCode = (tanChallenge as FlickerCodeTanChallenge).flickerCode
if (flickerCode.decodingSuccessful) { if (flickerCode.decodingSuccessful) {
flickerCodeView.setCode(flickerCode) flickerCodeView.setCode(flickerCode, presenter.appSettings.flickerCodeSettings)
} }
else { else {
showDecodingTanChallengeFailedErrorDelayed(flickerCode.decodingError) showDecodingTanChallengeFailedErrorDelayed(flickerCode.decodingError)
@ -157,15 +159,14 @@ open class EnterTanDialog : DialogFragment() {
val decodedImage = (tanChallenge as ImageTanChallenge).image val decodedImage = (tanChallenge as ImageTanChallenge).image
if (decodedImage.decodingSuccessful) { if (decodedImage.decodingSuccessful) {
val bitmap = BitmapFactory.decodeByteArray(decodedImage.imageBytes, 0, decodedImage.imageBytes.size) rootView.tanImageView.setImage(tanChallenge as ImageTanChallenge, if (isQrTan(tanChallenge)) presenter.appSettings.qrCodeSettings else presenter.appSettings.photoTanSettings)
rootView.imgTanImageView.setImageBitmap(bitmap)
} }
else { else {
showDecodingTanChallengeFailedErrorDelayed(decodedImage.decodingError) 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, _ -> rootView.edtxtEnteredTan.setOnKeyListener { _, keyCode, _ ->
if (keyCode == KeyEvent.KEYCODE_ENTER) { if (keyCode == KeyEvent.KEYCODE_ENTER) {
@ -230,7 +231,33 @@ open class EnterTanDialog : DialogFragment() {
tanEnteredCallback(result) tanEnteredCallback(result)
checkIfAppSettingsChanged()
dismiss() 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.model.tan.FlickerCode
import net.dankito.banking.ui.util.FlickerCodeAnimator import net.dankito.banking.ui.util.FlickerCodeAnimator
import net.dankito.banking.fints.tan.Bit 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 import net.dankito.utils.android.extensions.asActivity
open class ChipTanFlickerCodeView @JvmOverloads constructor( open class ChipTanFlickerCodeView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) { ) : LinearLayout(context, attrs, defStyleAttr), ITanView {
companion object { companion object {
const val FrequencyStepSize = 2 const val FrequencyStepSize = 2
const val MinFrequency = 2 const val MinFrequency = 2
const val MaxFrequency = 40 const val MaxFrequency = 40
const val DefaultFrequency = 30
const val StripesHeightStepSize = 7 const val StripesHeightStepSize = 7
const val StripesWidthStepSize = 2 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 stripesHeight = 360
protected var stripesWidth = 120 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 protected var isFlickerCodePaused = false
override var didTanProcedureSettingsChange: Boolean = false
protected set
override var tanProcedureSettings: TanProcedureSettings? = null
protected set
init { init {
setupUi(context) setupUi(context)
} }
@ -82,12 +92,17 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
stripesHeight = stripe1.layoutParams.height stripesHeight = stripe1.layoutParams.height
stripesWidth = stripe1.layoutParams.width 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) tanGeneratorLeftMarker = rootView.findViewById(R.id.tanGeneratorLeftMarker)
tanGeneratorRightMarker = rootView.findViewById(R.id.tanGeneratorRightMarker) tanGeneratorRightMarker = rootView.findViewById(R.id.tanGeneratorRightMarker)
setMarkerPositionAfterStripesLayoutSet() 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() { open fun decreaseSize() {
stripesHeight -= StripesHeightStepSize setSize(
stripesWidth -= StripesWidthStepSize stripesWidth - StripesWidthStepSize,
stripesMarginRight -= StripesRightMarginStepSize stripesHeight - StripesHeightStepSize,
spaceBetweenStripes - SpaceBetweenStripesStepSize
setWidth(context) )
} }
open fun increaseSize() { open fun increaseSize() {
stripesHeight += StripesHeightStepSize setSize(
stripesWidth += StripesWidthStepSize stripesWidth + StripesWidthStepSize,
stripesMarginRight += StripesRightMarginStepSize stripesHeight + StripesHeightStepSize,
spaceBetweenStripes + SpaceBetweenStripesStepSize
setWidth(context) )
} }
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 -> allStripes.forEach { stripe ->
val params = stripe.layoutParams val params = stripe.layoutParams
params.height = stripesHeight params.height = stripesHeight
@ -123,7 +146,7 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
(params as? MarginLayoutParams)?.let { marginParams -> (params as? MarginLayoutParams)?.let { marginParams ->
if (marginParams.rightMargin > 0) { // don't set a margin right on fifth stripe 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() requestLayout()
setMarkerPositionAfterStripesLayoutSet() setMarkerPositionAfterStripesLayoutSet()
tanProcedureSettingsChanged()
} }
protected open fun setMarkerPositionAfterStripesLayoutSet() { protected open fun setMarkerPositionAfterStripesLayoutSet() {
@ -148,22 +173,28 @@ open class ChipTanFlickerCodeView @JvmOverloads constructor(
open fun decreaseFrequency() { open fun decreaseFrequency() {
if (currentFrequency - FrequencyStepSize >= MinFrequency) { if (currentFrequency - FrequencyStepSize >= MinFrequency) {
currentFrequency -= FrequencyStepSize setFrequency(currentFrequency - FrequencyStepSize)
setFrequency(currentFrequency)
} }
} }
open fun increaseFrequency() { open fun increaseFrequency() {
if (currentFrequency + FrequencyStepSize <= MaxFrequency) { if (currentFrequency + FrequencyStepSize <= MaxFrequency) {
currentFrequency += FrequencyStepSize setFrequency(currentFrequency + FrequencyStepSize)
setFrequency(currentFrequency)
} }
} }
open fun setFrequency(frequency: Int) { open fun setFrequency(frequency: Int) {
currentFrequency = frequency
animator.setFrequency(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() 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 -> animator.animateFlickerCode(flickerCode) { step ->
context.asActivity()?.runOnUiThread { context.asActivity()?.runOnUiThread {

View File

@ -1,6 +1,7 @@
package net.dankito.banking.ui.android.views package net.dankito.banking.ui.android.views
import android.content.Context import android.content.Context
import android.graphics.BitmapFactory
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.ImageView 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.view.*
import kotlinx.android.synthetic.main.view_tan_image_size_controls.view.* import kotlinx.android.synthetic.main.view_tan_image_size_controls.view.*
import net.dankito.banking.ui.android.R 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( open class TanImageView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) { ) : LinearLayout(context, attrs, defStyleAttr), ITanView {
companion object { companion object {
const val ChangeSizeStepSizeDp = 10f const val ChangeSizeStepSizeDp = 10f
@ -26,6 +30,13 @@ open class TanImageView @JvmOverloads constructor(
protected lateinit var imgTanImageView: ImageView protected lateinit var imgTanImageView: ImageView
override var didTanProcedureSettingsChange: Boolean = false
protected set
override var tanProcedureSettings: TanProcedureSettings? = null
protected set
init { init {
MinHeight = convertDpToPx(context, 150f).toInt() MinHeight = convertDpToPx(context, 150f).toInt()
MaxHeight = convertDpToPx(context, 500f).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() { open fun decreaseSize() {
changeSizeBy(ChangeSizeStepSizeDp * -1) changeSizeBy(ChangeSizeStepSizeDp * -1)
} }
@ -56,16 +78,30 @@ open class TanImageView @JvmOverloads constructor(
val params = imgTanImageView.layoutParams val params = imgTanImageView.layoutParams
val newWidthAndHeight = params.height + convertDpToPx(context, changeSizeBy).toInt() val newWidthAndHeight = params.height + convertDpToPx(context, changeSizeBy).toInt()
setWidthAndHeight(newWidthAndHeight)
}
protected open fun setWidthAndHeight(newWidthAndHeight: Int) {
if (newWidthAndHeight in MinHeight..MaxHeight) { if (newWidthAndHeight in MinHeight..MaxHeight) {
val params = imgTanImageView.layoutParams
params.height = newWidthAndHeight params.height = newWidthAndHeight
params.width = newWidthAndHeight params.width = newWidthAndHeight
imgTanImageView.layoutParams = params // TODO: needed? imgTanImageView.layoutParams = params // TODO: needed?
requestLayout() 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. * 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.responses.BankingClientResponse
import net.dankito.banking.ui.model.tan.* import net.dankito.banking.ui.model.tan.*
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.utils.extensions.htmlToPlainText
import net.dankito.utils.javafx.ui.dialogs.Window import net.dankito.utils.javafx.ui.dialogs.Window
import tornadofx.* import tornadofx.*
@ -23,6 +24,8 @@ open class EnterTanDialog(
) : Window() { ) : Window() {
companion object { companion object {
val QrCodeTanProcedures = listOf(TanProcedureType.ChipTanQrCode, TanProcedureType.QrCode)
private val ButtonHeight = 40.0 private val ButtonHeight = 40.0
private val ButtonWidth = 150.0 private val ButtonWidth = 150.0
} }
@ -30,6 +33,10 @@ open class EnterTanDialog(
protected val dialogService = JavaFxDialogService() 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 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 marginBottom = 12.0
} }
add(ChipTanFlickerCodeView(flickerCode)) add(ChipTanFlickerCodeView(flickerCode, presenter.appSettings.flickerCodeSettings).apply {
flickerCodeView = this
})
} }
} }
else { else {
@ -104,7 +113,8 @@ open class EnterTanDialog(
(challenge as? ImageTanChallenge)?.let { imageTanChallenge -> (challenge as? ImageTanChallenge)?.let { imageTanChallenge ->
val decodedImage = imageTanChallenge.image val decodedImage = imageTanChallenge.image
if (decodedImage.decodingSuccessful) { if (decodedImage.decodingSuccessful) {
add(TanImageView(decodedImage).apply { add(TanImageView(decodedImage, if (isQrTan(challenge)) presenter.appSettings.qrCodeSettings else presenter.appSettings.photoTanSettings).apply {
tanImageView = this
vboxConstraints { vboxConstraints {
marginLeftRight(30.0) marginLeftRight(30.0)
@ -216,6 +226,8 @@ open class EnterTanDialog(
else { else {
tanEnteredCallback(EnterTanResult.userEnteredTan(enteredTan.value)) tanEnteredCallback(EnterTanResult.userEnteredTan(enteredTan.value))
checkIfAppSettingsChanged()
close() close()
} }
} }
@ -226,4 +238,28 @@ open class EnterTanDialog(
close() 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.fints.tan.Bit
import net.dankito.banking.javafx.dialogs.tan.controls.ChipTanFlickerCodeStripeView import net.dankito.banking.javafx.dialogs.tan.controls.ChipTanFlickerCodeStripeView
import net.dankito.banking.javafx.dialogs.tan.controls.TanGeneratorMarkerView 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.fixedHeight
import net.dankito.utils.javafx.ui.extensions.fixedWidth import net.dankito.utils.javafx.ui.extensions.fixedWidth
import net.dankito.utils.javafx.ui.extensions.setBackgroundToColor import net.dankito.utils.javafx.ui.extensions.setBackgroundToColor
import tornadofx.* import tornadofx.*
open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View() { open class ChipTanFlickerCodeView(
protected val flickerCode: FlickerCode,
tanProcedureSettings: TanProcedureSettings?
): View(), ITanView {
companion object { companion object {
const val ChangeSizeStripeHeightStep = 7.0 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 MaxFlickerCodeViewWidth = 1000.0 // what is a senseful value?
const val ChangeFrequencyStep = 5 const val ChangeFrequencyStep = 5
const val DefaultFrequency = 30
const val IconWidth = 26.0 const val IconWidth = 26.0
const val IconHeight = 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 flickerCodeLeftRightMargin = SimpleDoubleProperty(31.0)
protected val stripeHeight = SimpleDoubleProperty(127.0) protected val stripesHeight = SimpleDoubleProperty(tanProcedureSettings?.height?.toDouble() ?: 127.0)
protected val stripeWidth = SimpleDoubleProperty(42.0) protected val stripesWidth = SimpleDoubleProperty(tanProcedureSettings?.width?.toDouble() ?: 42.0)
protected val spaceBetweenStripes = SimpleDoubleProperty(10.0) protected val spaceBetweenStripes = SimpleDoubleProperty(tanProcedureSettings?.space?.toDouble() ?: 10.0)
protected val flickerCodeViewWidth = SimpleDoubleProperty() protected val flickerCodeViewWidth = SimpleDoubleProperty()
@ -52,15 +58,22 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
protected val isMinFrequencyReached = SimpleBooleanProperty(false) protected val isMinFrequencyReached = SimpleBooleanProperty(false)
protected val isMaxFrequencyReached = SimpleBooleanProperty(false) protected val isMaxFrequencyReached = SimpleBooleanProperty(false)
protected var currentFrequency = 20 protected var currentFrequency = tanProcedureSettings?.frequency ?: DefaultFrequency
protected val animator = FlickerCodeAnimator() protected val animator = FlickerCodeAnimator()
override var didTanProcedureSettingsChange: Boolean = false
protected set
override var tanProcedureSettings: TanProcedureSettings? = tanProcedureSettings
protected set
init { 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 { add(TanGeneratorMarkerView().apply {
setLeftMarkerPosition(this) setLeftMarkerPosition(this)
stripeWidth.addListener { _, _, _ -> setLeftMarkerPosition(this) } stripesWidth.addListener { _, _, _ -> setLeftMarkerPosition(this) }
}) })
add(TanGeneratorMarkerView().apply { add(TanGeneratorMarkerView().apply {
setRightMarkerPosition(this) setRightMarkerPosition(this)
stripeWidth.addListener { _, _, _ -> setRightMarkerPosition(this) } stripesWidth.addListener { _, _, _ -> setRightMarkerPosition(this) }
}) })
vboxConstraints { vboxConstraints {
@ -139,11 +152,11 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
hbox { hbox {
minHeight = 150.0 minHeight = 150.0
add(ChipTanFlickerCodeStripeView(stripe1, stripeWidth, stripeHeight, spaceBetweenStripes)) add(ChipTanFlickerCodeStripeView(stripe1, stripesWidth, stripesHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe2, stripeWidth, stripeHeight, spaceBetweenStripes)) add(ChipTanFlickerCodeStripeView(stripe2, stripesWidth, stripesHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe3, stripeWidth, stripeHeight, spaceBetweenStripes)) add(ChipTanFlickerCodeStripeView(stripe3, stripesWidth, stripesHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe4, stripeWidth, stripeHeight, spaceBetweenStripes)) add(ChipTanFlickerCodeStripeView(stripe4, stripesWidth, stripesHeight, spaceBetweenStripes))
add(ChipTanFlickerCodeStripeView(stripe5, stripeWidth, stripeHeight, SimpleDoubleProperty(0.0))) add(ChipTanFlickerCodeStripeView(stripe5, stripesWidth, stripesHeight, SimpleDoubleProperty(0.0)))
} }
vboxConstraints { vboxConstraints {
@ -153,6 +166,10 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
} }
} }
updateMinAndMaxSizeReached()
updateMinAndMaxFrequencyReached()
animator.animateFlickerCode(flickerCode) { step -> animator.animateFlickerCode(flickerCode) { step ->
runLater { runLater {
paintFlickerCode(step) paintFlickerCode(step)
@ -171,22 +188,23 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
protected open fun setLeftMarkerPosition(component: UIComponent) { protected open fun setLeftMarkerPosition(component: UIComponent) {
component.root.anchorpaneConstraints { component.root.anchorpaneConstraints {
leftAnchor = (stripeWidth.value / 2) leftAnchor = (stripesWidth.value / 2)
} }
} }
protected open fun setRightMarkerPosition(component: UIComponent) { protected open fun setRightMarkerPosition(component: UIComponent) {
component.root.anchorpaneConstraints { component.root.anchorpaneConstraints {
rightAnchor = (stripeWidth.value / 2) rightAnchor = (stripesWidth.value / 2)
} }
} }
protected open fun increaseSize() { protected open fun increaseSize() {
if (isMaxSizeReached.value == false) { if (isMaxSizeReached.value == false) {
stripeHeight.value = stripeHeight.value + ChangeSizeStripeHeightStep setSize(stripesWidth.value + ChangeSizeStripeWidthStep, stripesHeight.value + ChangeSizeStripeHeightStep,
stripeWidth.value = stripeWidth.value + ChangeSizeStripeWidthStep spaceBetweenStripes.value + ChangeSizeSpaceBetweenStripesStep)
spaceBetweenStripes.value = spaceBetweenStripes.value + ChangeSizeSpaceBetweenStripesStep
tanProcedureSettingsChanged()
} }
updateMinAndMaxSizeReached() updateMinAndMaxSizeReached()
@ -194,16 +212,27 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
protected open fun decreaseSize() { protected open fun decreaseSize() {
if (isMinSizeReached.value == false) { if (isMinSizeReached.value == false) {
stripeHeight.value = stripeHeight.value - ChangeSizeStripeHeightStep setSize(stripesWidth.value - ChangeSizeStripeWidthStep, stripesHeight.value - ChangeSizeStripeHeightStep,
stripeWidth.value = stripeWidth.value - ChangeSizeStripeWidthStep spaceBetweenStripes.value - ChangeSizeSpaceBetweenStripesStep)
spaceBetweenStripes.value = spaceBetweenStripes.value - ChangeSizeSpaceBetweenStripesStep
tanProcedureSettingsChanged()
} }
updateMinAndMaxSizeReached() 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() { protected open fun updateMinAndMaxSizeReached() {
val flickerCodeWidth = stripeWidth.value * 5 + spaceBetweenStripes.value * 4 val flickerCodeWidth = stripesWidth.value * 5 + spaceBetweenStripes.value * 4
isMinSizeReached.value = flickerCodeWidth < MinFlickerCodeViewWidth isMinSizeReached.value = flickerCodeWidth < MinFlickerCodeViewWidth
isMaxSizeReached.value = flickerCodeWidth > MaxFlickerCodeViewWidth isMaxSizeReached.value = flickerCodeWidth > MaxFlickerCodeViewWidth
@ -213,8 +242,7 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
if (isMaxFrequencyReached.value == false if (isMaxFrequencyReached.value == false
&& (currentFrequency + ChangeFrequencyStep) <= FlickerCodeAnimator.MaxFrequency) { && (currentFrequency + ChangeFrequencyStep) <= FlickerCodeAnimator.MaxFrequency) {
currentFrequency += ChangeFrequencyStep setFrequency(currentFrequency + ChangeFrequencyStep)
animator.setFrequency(currentFrequency)
} }
updateMinAndMaxFrequencyReached() updateMinAndMaxFrequencyReached()
@ -224,16 +252,33 @@ open class ChipTanFlickerCodeView(protected val flickerCode: FlickerCode): View(
if (isMinFrequencyReached.value == false if (isMinFrequencyReached.value == false
&& (currentFrequency - ChangeFrequencyStep) >= FlickerCodeAnimator.MinFrequency) { && (currentFrequency - ChangeFrequencyStep) >= FlickerCodeAnimator.MinFrequency) {
currentFrequency -= ChangeFrequencyStep setFrequency(currentFrequency - ChangeFrequencyStep)
animator.setFrequency(currentFrequency)
} }
updateMinAndMaxFrequencyReached() updateMinAndMaxFrequencyReached()
} }
protected open fun setFrequency(frequency: Int) {
currentFrequency = frequency
animator.setFrequency(currentFrequency)
updateMinAndMaxFrequencyReached()
tanProcedureSettingsChanged()
}
protected open fun updateMinAndMaxFrequencyReached() { protected open fun updateMinAndMaxFrequencyReached() {
isMaxFrequencyReached.value = (currentFrequency + ChangeFrequencyStep) > FlickerCodeAnimator.MaxFrequency isMaxFrequencyReached.value = (currentFrequency + ChangeFrequencyStep) > FlickerCodeAnimator.MaxFrequency
isMinFrequencyReached.value = (currentFrequency - ChangeFrequencyStep) < FlickerCodeAnimator.MinFrequency 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.geometry.Pos
import javafx.scene.image.Image import javafx.scene.image.Image
import javafx.scene.image.ImageView 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.banking.ui.model.tan.TanImage
import net.dankito.utils.javafx.ui.extensions.updateWindowSize import net.dankito.utils.javafx.ui.extensions.updateWindowSize
import tornadofx.* import tornadofx.*
import java.io.ByteArrayInputStream 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 { companion object {
private const val ChangeSizeStepSize = 10.0 private const val ChangeSizeStepSize = 10.0
@ -29,6 +34,13 @@ open class TanImageView(protected val tanImage: TanImage) : View() {
protected var tanImageView: ImageView by singleAssign() protected var tanImageView: ImageView by singleAssign()
override var didTanProcedureSettingsChange: Boolean = false
protected set
override var tanProcedureSettings: TanProcedureSettings? = tanProcedureSettings
protected set
override val root = vbox { override val root = vbox {
add(TanImageSizeView(IconHeight, IconWidth, isMinSizeReached, isMaxSizeReached, { decreaseSize() }, { increaseSize() } )) add(TanImageSizeView(IconHeight, IconWidth, isMinSizeReached, isMaxSizeReached, { decreaseSize() }, { increaseSize() } ))
@ -45,6 +57,12 @@ open class TanImageView(protected val tanImage: TanImage) : View() {
marginBottom = 4.0 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) { protected open fun changeSizeBy(changeSizeBy: Double) {
val newWidthAndHeight = tanImageView.fitHeight + changeSizeBy val newWidthAndHeight = tanImageView.fitHeight + changeSizeBy
setWidthAndHeight(newWidthAndHeight)
}
protected open fun setWidthAndHeight(newWidthAndHeight: Double) {
if (newWidthAndHeight in MinHeight..MaxHeight) { if (newWidthAndHeight in MinHeight..MaxHeight) {
tanImageView.fitHeight = newWidthAndHeight tanImageView.fitHeight = newWidthAndHeight
updateWindowSize() updateWindowSize()
tanProcedureSettingsChanged(newWidthAndHeight.toInt())
} }
isMinSizeReached.value = tanImageView.fitHeight <= MinHeight isMinSizeReached.value = tanImageView.fitHeight <= MinHeight
isMaxSizeReached.value = tanImageView.fitHeight >= MaxHeight 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.util.IBankIconFinder
import net.dankito.banking.fints.banks.IBankFinder import net.dankito.banking.fints.banks.IBankFinder
import net.dankito.banking.fints.model.BankInfo import net.dankito.banking.fints.model.BankInfo
import net.dankito.banking.ui.model.settings.AppSettings
import net.dankito.utils.IThreadPool import net.dankito.utils.IThreadPool
import net.dankito.utils.ThreadPool import net.dankito.utils.ThreadPool
import net.dankito.utils.extensions.containsExactly import net.dankito.utils.extensions.containsExactly
import net.dankito.utils.extensions.ofMaxLength import net.dankito.utils.extensions.ofMaxLength
import net.dankito.utils.serialization.ISerializer
import net.dankito.utils.serialization.JacksonJsonSerializer
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -39,6 +42,7 @@ open class BankingPresenter(
protected val persister: IBankingPersistence, protected val persister: IBankingPersistence,
protected val bankIconFinder: IBankIconFinder, protected val bankIconFinder: IBankIconFinder,
protected val router: IRouter, protected val router: IRouter,
protected val serializer: ISerializer = JacksonJsonSerializer(),
protected val threadPool: IThreadPool = ThreadPool() protected val threadPool: IThreadPool = ThreadPool()
) { ) {
@ -93,6 +97,7 @@ open class BankingPresenter(
init { init {
threadPool.runAsync { threadPool.runAsync {
readAppSettings()
readPersistedAccounts() 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 { open fun addAccountsChangedListener(listener: (List<Account>) -> Unit): Boolean {
return accountsChangedListeners.add(listener) return accountsChangedListeners.add(listener)
} }