From d3747674006fc5024d7df64613f85c1701d4ed05 Mon Sep 17 00:00:00 2001 From: dankl Date: Sat, 26 Oct 2019 01:10:51 +0200 Subject: [PATCH] Started EnterTanDialog --- .../fints4java/android/MainActivity.kt | 25 ++- .../android/ui/MainWindowPresenter.kt | 2 +- .../android/ui/dialogs/EnterTanDialog.kt | 70 ++++++++ .../ui/views/ChipTanFlickerCodeStripeView.kt | 56 ++++++ .../ui/views/ChipTanFlickerCodeView.kt | 138 +++++++++++++++ .../android/util/FlickercodeAnimator.kt | 81 +++++++++ .../src/main/res/layout/dialog_enter_tan.xml | 79 +++++++++ .../src/main/res/layout/view_flicker_code.xml | 165 ++++++++++++++++++ .../src/main/res/values/colors.xml | 2 + .../src/main/res/values/dimens.xml | 19 ++ .../src/main/res/values/strings.xml | 9 + .../net/dankito/fints/tan/FlickerCanvas.kt | 6 +- 12 files changed, 648 insertions(+), 4 deletions(-) create mode 100644 fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt create mode 100644 fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeStripeView.kt create mode 100644 fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeView.kt create mode 100644 fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/util/FlickercodeAnimator.kt create mode 100644 fints4javaAndroidApp/src/main/res/layout/dialog_enter_tan.xml create mode 100644 fints4javaAndroidApp/src/main/res/layout/view_flicker_code.xml diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/MainActivity.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/MainActivity.kt index f5d48958..d56680d0 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/MainActivity.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/MainActivity.kt @@ -10,9 +10,13 @@ import android.view.Menu import androidx.navigation.findNavController import net.dankito.banking.fints4java.android.ui.MainWindowPresenter import net.dankito.banking.fints4java.android.ui.dialogs.AddAccountDialog +import net.dankito.banking.fints4java.android.ui.dialogs.EnterTanDialog import net.dankito.fints.FinTsClientCallback import net.dankito.fints.model.TanChallenge import net.dankito.fints.model.TanProcedure +import java.util.concurrent.CountDownLatch +import java.util.concurrent.atomic.AtomicReference + class MainActivity : AppCompatActivity() { @@ -26,7 +30,7 @@ class MainActivity : AppCompatActivity() { } override fun enterTan(tanChallenge: TanChallenge): String? { - return null + return getTanFromUserOffUiThread(tanChallenge) } }) @@ -78,4 +82,23 @@ class MainActivity : AppCompatActivity() { // return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() // } + private fun getTanFromUserOffUiThread(tanChallenge: TanChallenge): String? { + val enteredTan = AtomicReference(null) + val tanEnteredLatch = CountDownLatch(1) + + runOnUiThread { + EnterTanDialog().show(tanChallenge, this@MainActivity, false) { + enteredTan.set(it) + tanEnteredLatch.countDown() + } + } + + try { + tanEnteredLatch.await() + } catch (ignored: Exception) { + } + + return enteredTan.get() + } + } diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/MainWindowPresenter.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/MainWindowPresenter.kt index f6860d6e..764ae37d 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/MainWindowPresenter.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/MainWindowPresenter.kt @@ -48,7 +48,7 @@ open class MainWindowPresenter(callback: FinTsClientCallback) { open fun getAccountTransactionsAsync(bank: BankData, customer: CustomerData, callback: (GetTransactionsResponse) -> Unit) { - finTsClient.tryGetTransactionsOfLast90DaysWithoutTanAsync(bank, customer, callback) + finTsClient.getTransactionsAsync(GetTransactionsParameter(), bank, customer, callback) } diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt new file mode 100644 index 00000000..a5c09fb0 --- /dev/null +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt @@ -0,0 +1,70 @@ +package net.dankito.banking.fints4java.android.ui.dialogs + +import android.os.Bundle +import android.support.v4.app.DialogFragment +import android.support.v7.app.AppCompatActivity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import kotlinx.android.synthetic.main.dialog_enter_tan.view.* +import net.dankito.banking.fints4java.android.R +import net.dankito.fints.model.TanChallenge +import net.dankito.fints.model.TanProcedureType +import net.dankito.fints.tan.FlickercodeDecoder + + +open class EnterTanDialog : DialogFragment() { + + companion object { + const val DialogTag = "EnterTanDialog" + } + + + protected lateinit var tanChallenge: TanChallenge + + protected lateinit var tanEnteredCallback: (String?) -> Unit + + + open fun show(tanChallenge: TanChallenge, activity: AppCompatActivity, + fullscreen: Boolean = false, tanEnteredCallback: (String?) -> Unit) { + + this.tanChallenge = tanChallenge + this.tanEnteredCallback = tanEnteredCallback + + val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.Dialog + setStyle(STYLE_NORMAL, style) + + show(activity.supportFragmentManager, DialogTag) + } + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val rootView = inflater.inflate(R.layout.dialog_enter_tan, container, false) + + setupUI(rootView) + + return rootView + } + + protected open fun setupUI(rootView: View) { + val flickerCodeView = rootView.flickerCodeView + + if (tanChallenge.tanProcedure.type == TanProcedureType.ChipTan) { + flickerCodeView.visibility = View.VISIBLE + flickerCodeView.setCode(FlickercodeDecoder().decodeChallenge(tanChallenge.tanChallenge)) + } + + rootView.txtTanDescriptionToShowToUser.text = tanChallenge.messageToShowToUser + + rootView.btnCancel.setOnClickListener { enteringTanDone(null) } + + rootView.btnEnteringTanDone.setOnClickListener { enteringTanDone(rootView.edtxtEnteredTan.text.toString()) } + } + + protected open fun enteringTanDone(enteredTan: String?) { + tanEnteredCallback(enteredTan) + + dismiss() + } + +} \ No newline at end of file diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeStripeView.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeStripeView.kt new file mode 100644 index 00000000..b76ad202 --- /dev/null +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeStripeView.kt @@ -0,0 +1,56 @@ +package net.dankito.banking.fints4java.android.ui.views + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import android.view.View +import net.dankito.fints.tan.Bit + + +open class ChipTanFlickerCodeStripeView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : View(context, attrs, defStyleAttr) { + + + companion object { + val White = Color.WHITE + val Black = Color.BLACK + } + + + protected var currentColor = 0 + + + init { + drawWhite() + } + + + open fun showStripe(showStripe: Bit) { + if (showStripe == Bit.High) { + drawWhite() + } + else { + drawBlack() + } + } + + open fun drawWhite() { + drawInColor(White) + } + + open fun drawBlack() { + drawInColor(Black) + } + + open fun drawInColor(color: Int) { + if (color != currentColor) { + setBackgroundColor(color) + + currentColor = color + + invalidate() + } + } + +} \ No newline at end of file diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeView.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeView.kt new file mode 100644 index 00000000..3cb247fe --- /dev/null +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeView.kt @@ -0,0 +1,138 @@ +package net.dankito.banking.fints4java.android.ui.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.LinearLayout +import kotlinx.android.synthetic.main.view_flicker_code.view.* +import net.dankito.banking.fints4java.android.R +import net.dankito.banking.fints4java.android.util.FlickercodeAnimator +import net.dankito.fints.tan.Bit +import net.dankito.fints.tan.Flickercode +import net.dankito.utils.android.extensions.asActivity + + +open class ChipTanFlickerCodeView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : LinearLayout(context, attrs, defStyleAttr) { + + companion object { + const val FrequencyStepSize = 2 + const val MinFrequency = 2 + const val MaxFrequency = 40 + } + + + protected lateinit var stripe1: ChipTanFlickerCodeStripeView + protected lateinit var stripe2: ChipTanFlickerCodeStripeView + protected lateinit var stripe3: ChipTanFlickerCodeStripeView + protected lateinit var stripe4: ChipTanFlickerCodeStripeView + protected lateinit var stripe5: ChipTanFlickerCodeStripeView + + protected val animator = FlickercodeAnimator() + + + protected var currentWidth = 264f + + protected var currentFrequency = 30 + + + init { + setupUi(context) + } + + private fun setupUi(context: Context) { + val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + val rootView = inflater.inflate(R.layout.view_flicker_code, this, true) + + rootView.btnIncreaseSize.setOnClickListener { increaseSize() } + rootView.btnDecreaseSize.setOnClickListener { decreaseSize() } + + rootView.btnIncreaseSpeed.setOnClickListener { increaseFrequency() } + rootView.btnDecreaseSpeed.setOnClickListener { decreaseFrequency() } + + stripe1 = rootView.findViewById(R.id.flickerCodeStripe1) + stripe2 = rootView.findViewById(R.id.flickerCodeStripe2) + stripe3 = rootView.findViewById(R.id.flickerCodeStripe3) + stripe4 = rootView.findViewById(R.id.flickerCodeStripe4) + stripe5 = rootView.findViewById(R.id.flickerCodeStripe5) + } + + + + override fun onDetachedFromWindow() { + animator.stop() + + super.onDetachedFromWindow() + } + + + + open fun increaseSize() { + currentWidth += 10 + + setWidth(context) + } + + open fun decreaseSize() { + currentWidth -= 10 + + setWidth(context) + } + + protected open fun setWidth(context: Context) { + // TODO +// val params = stripesView.layoutParams +//// params.width = convertDpToPx(context, currentWidth).toInt() +// params.width = LayoutParams.WRAP_CONTENT +// params.height = LayoutParams.WRAP_CONTENT // TODO: needed? +// (params as? LinearLayoutCompat.LayoutParams)?.gravity = Gravity.CENTER_HORIZONTAL +// stripesView.layoutParams = params +// +// stripesView.requestLayout() + } + + + open fun increaseFrequency() { + if (currentFrequency + FrequencyStepSize <= MaxFrequency) { + currentFrequency += FrequencyStepSize + + setFrequency(currentFrequency) + } + } + + open fun decreaseFrequency() { + if (currentFrequency - FrequencyStepSize >= MinFrequency) { + currentFrequency -= FrequencyStepSize + + setFrequency(currentFrequency) + } + } + + open fun setFrequency(frequency: Int) { + animator.setFrequency(frequency) + } + + + open fun setCode(flickercode: Flickercode) { + animator.stop() + + setFrequency(currentFrequency) + + animator.animateFlickercode(flickercode) { step -> + context.asActivity()?.runOnUiThread { + showStepOnUiThread(step) + } + } + } + + protected open fun showStepOnUiThread(step: Array) { + + stripe1.showStripe(step[0]) + stripe2.showStripe(step[1]) + stripe3.showStripe(step[2]) + stripe4.showStripe(step[3]) + stripe5.showStripe(step[4]) + } + +} \ No newline at end of file diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/util/FlickercodeAnimator.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/util/FlickercodeAnimator.kt new file mode 100644 index 00000000..b0cc1134 --- /dev/null +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/util/FlickercodeAnimator.kt @@ -0,0 +1,81 @@ +package net.dankito.banking.fints4java.android.util + +import net.dankito.fints.tan.Bit +import net.dankito.fints.tan.FlickerCanvas +import net.dankito.fints.tan.Flickercode +import org.slf4j.LoggerFactory +import java.util.concurrent.TimeUnit + + +open class FlickercodeAnimator { // TODO: move to fints4javaLib + + companion object { + const val MinFrequency = 2 + const val MaxFrequency = 40 + const val DefaultFrequency = 20 + + private val log = LoggerFactory.getLogger(FlickercodeAnimator::class.java) + } + + + protected var currentFrequency: Int = DefaultFrequency + + protected var currentStepIndex = 0 + + protected var calculateAnimationThread: Thread? = null + + + + @JvmOverloads + open fun animateFlickercode(flickercode: Flickercode, frequency: Int = DefaultFrequency, showStep: (Array) -> Unit) { + currentFrequency = frequency + currentStepIndex = 0 + val steps = FlickerCanvas(flickercode.rendered).steps + + stop() // stop may still running previous animation + + calculateAnimationThread = Thread({ calculateAnimation(steps, showStep) }, "CalculateFlickercodeAnimation") + + calculateAnimationThread?.start() + } + + protected open fun calculateAnimation(steps: List>, showStep: (Array) -> Unit) { + while (Thread.currentThread().isInterrupted == false) { + val nextStep = steps[currentStepIndex] + + showStep(nextStep) + + currentStepIndex++ + if (currentStepIndex >= steps.size) { + currentStepIndex = 0 // all steps shown, start again from beginning + } + + try { + TimeUnit.MILLISECONDS.sleep(1000L / currentFrequency) + } catch (ignored: Exception) { + Thread.currentThread().interrupt() + } + } + } + + open fun stop() { + try { + if (calculateAnimationThread?.isInterrupted == false) { + calculateAnimationThread?.interrupt() + calculateAnimationThread?.join(500) + + calculateAnimationThread = null + } + } catch (e: Exception) { + log.warn("Could not stop calculateAnimationThread", e) + } + } + + + open fun setFrequency(frequency: Int) { + if (frequency in MinFrequency..MaxFrequency) { + currentFrequency = frequency + } + } + +} \ No newline at end of file diff --git a/fints4javaAndroidApp/src/main/res/layout/dialog_enter_tan.xml b/fints4javaAndroidApp/src/main/res/layout/dialog_enter_tan.xml new file mode 100644 index 00000000..a1af6603 --- /dev/null +++ b/fints4javaAndroidApp/src/main/res/layout/dialog_enter_tan.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + +