From e86b17d08b580fd2e33e82cc90b04325b20c0404 Mon Sep 17 00:00:00 2001 From: dankito Date: Sun, 24 May 2020 22:49:50 +0200 Subject: [PATCH] Implemented saving TAN settings like TAN image size and flicker code frequency --- .../banking/ui/android/di/BankingModule.kt | 4 +- .../ui/android/dialogs/EnterTanDialog.kt | 39 ++++++-- .../android/views/ChipTanFlickerCodeView.kt | 90 ++++++++++++----- .../banking/ui/android/views/TanImageView.kt | 38 ++++++- .../ui/javafx/dialogs/tan/EnterTanDialog.kt | 40 +++++++- .../tan/controls/ChipTanFlickerCodeView.kt | 99 ++++++++++++++----- .../dialogs/tan/controls/TanImageView.kt | 32 +++++- .../banking/ui/model/settings/AppSettings.kt | 12 +++ .../banking/ui/model/settings/ITanView.kt | 10 ++ .../ui/model/settings/TanProcedureSettings.kt | 18 ++++ .../banking/ui/presenter/BankingPresenter.kt | 35 +++++++ 11 files changed, 353 insertions(+), 64 deletions(-) create mode 100644 ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/AppSettings.kt create mode 100644 ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/ITanView.kt create mode 100644 ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/TanProcedureSettings.kt diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt index cc2d073a..4657d165 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt @@ -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 diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterTanDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterTanDialog.kt index 47e45669..760577e9 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterTanDialog.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterTanDialog.kt @@ -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) + } + } \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeView.kt index 5a6855da..1c4729a3 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeView.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeView.kt @@ -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 { diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/TanImageView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/TanImageView.kt index a90fe405..c9bc4044 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/TanImageView.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/TanImageView.kt @@ -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. * diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/EnterTanDialog.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/EnterTanDialog.kt index 93a12a3b..a0bc7d95 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/EnterTanDialog.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/EnterTanDialog.kt @@ -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) + } + } \ No newline at end of file diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/controls/ChipTanFlickerCodeView.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/controls/ChipTanFlickerCodeView.kt index 3db4e3cb..e688d58e 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/controls/ChipTanFlickerCodeView.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/controls/ChipTanFlickerCodeView.kt @@ -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 + } + } \ No newline at end of file diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/controls/TanImageView.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/controls/TanImageView.kt index b02a8a47..03c21364 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/controls/TanImageView.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/controls/TanImageView.kt @@ -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 + } + } \ No newline at end of file diff --git a/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/AppSettings.kt b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/AppSettings.kt new file mode 100644 index 00000000..70f189c2 --- /dev/null +++ b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/AppSettings.kt @@ -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 + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/ITanView.kt b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/ITanView.kt new file mode 100644 index 00000000..d1591f5e --- /dev/null +++ b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/ITanView.kt @@ -0,0 +1,10 @@ +package net.dankito.banking.ui.model.settings + + +interface ITanView { + + val didTanProcedureSettingsChange: Boolean + + val tanProcedureSettings: TanProcedureSettings? + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/TanProcedureSettings.kt b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/TanProcedureSettings.kt new file mode 100644 index 00000000..2c323af0 --- /dev/null +++ b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/settings/TanProcedureSettings.kt @@ -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" + } + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/presenter/BankingPresenter.kt b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/presenter/BankingPresenter.kt index ef9024ce..d7d7c446 100644 --- a/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/presenter/BankingPresenter.kt +++ b/ui/BankingUiCommon/src/main/java/net/dankito/banking/ui/presenter/BankingPresenter.kt @@ -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) -> Unit): Boolean { return accountsChangedListeners.add(listener) }