Started EnterTanDialog

This commit is contained in:
dankl 2019-10-26 01:10:51 +02:00 committed by dankito
parent 2c95f06a13
commit d374767400
12 changed files with 648 additions and 4 deletions

View File

@ -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<String>(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()
}
}

View File

@ -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)
}

View File

@ -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()
}
}

View File

@ -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()
}
}
}

View File

@ -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<Bit>) {
stripe1.showStripe(step[0])
stripe2.showStripe(step[1])
stripe3.showStripe(step[2])
stripe4.showStripe(step[3])
stripe5.showStripe(step[4])
}
}

View File

@ -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<Bit>) -> 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<Array<Bit>>, showStep: (Array<Bit>) -> 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
}
}
}

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/dialog_enter_tan_padding"
>
<net.dankito.banking.fints4java.android.ui.views.ChipTanFlickerCodeView
android:id="@+id/flickerCodeView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
/>
<TextView
android:id="@+id/txtTanDescriptionToShowToUser"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="3"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_enter_tan_enter_tan_height"
android:layout_marginBottom="@dimen/dialog_enter_tan_enter_tan_margin_bottom"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:text="@string/dialog_enter_tan_enter_tan_dialog"
/>
<EditText
android:id="@+id/edtxtEnteredTan"
android:layout_width="@dimen/dialog_enter_tan_enter_tan_width"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/dialog_enter_tan_enter_tan_margin_left"
android:layout_marginStart="@dimen/dialog_enter_tan_enter_tan_margin_left"
/>
</LinearLayout>
<RelativeLayout
android:id="@+id/lytButtonBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
>
<Button
android:id="@+id/btnEnteringTanDone"
android:layout_width="@dimen/dialog_enter_tan_buttons_width"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
style="?android:attr/buttonBarButtonStyle"
android:text="@android:string/ok"
/>
<Button
android:id="@+id/btnCancel"
android:layout_width="@dimen/dialog_enter_tan_buttons_width"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/btnEnteringTanDone"
android:layout_toStartOf="@+id/btnEnteringTanDone"
style="?android:attr/buttonBarButtonStyle"
android:text="@string/cancel"
/>
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/view_flicker_code_size"
/>
<Button
android:id="@+id/btnIncreaseSize"
android:layout_width="@dimen/view_flicker_code_control_buttons_width"
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
android:text="@string/view_flicker_code_increase_size"
/>
<Button
android:id="@+id/btnDecreaseSize"
android:layout_width="@dimen/view_flicker_code_control_buttons_width"
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
android:text="@string/view_flicker_code_decrease_size"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/view_flicker_code_frequency"
/>
<Button
android:id="@+id/btnIncreaseSpeed"
android:layout_width="@dimen/view_flicker_code_control_buttons_width"
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
android:text="@string/view_flicker_code_increase_frequency"
/>
<Button
android:id="@+id/btnDecreaseSpeed"
android:layout_width="@dimen/view_flicker_code_control_buttons_width"
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
android:text="@string/view_flicker_code_decrease_frequency"
/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/black"
android:paddingTop="@dimen/dialog_enter_tan_flicker_view_margin_top_bottom"
android:paddingBottom="@dimen/dialog_enter_tan_flicker_view_margin_top_bottom"
>
<!-- TODO: really needed? -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/view_tan_generator_marker_margin_bottom"
>
<View
android:layout_width="@dimen/view_tan_generator_marker_width"
android:layout_height="@dimen/view_tan_generator_marker_height"
android:background="@color/view_tan_generator_marker_background"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="236px"
android:layout_marginStart="236px"
/>
<View
android:layout_width="@dimen/view_tan_generator_marker_width"
android:layout_height="@dimen/view_tan_generator_marker_height"
android:background="@color/view_tan_generator_marker_background"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="236px"
android:layout_marginEnd="236px"
/>
</RelativeLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
>
<net.dankito.banking.fints4java.android.ui.views.ChipTanFlickerCodeStripeView
android:id="@+id/flickerCodeStripe1"
android:layout_width="@dimen/view_flicker_code_stripe_width"
android:layout_height="@dimen/view_flicker_code_stripe_height"
android:layout_marginRight="@dimen/view_flicker_code_stripe_margin_right"
android:layout_marginEnd="@dimen/view_flicker_code_stripe_margin_right"
/>
<net.dankito.banking.fints4java.android.ui.views.ChipTanFlickerCodeStripeView
android:id="@+id/flickerCodeStripe2"
android:layout_width="@dimen/view_flicker_code_stripe_width"
android:layout_height="@dimen/view_flicker_code_stripe_height"
android:layout_marginRight="@dimen/view_flicker_code_stripe_margin_right"
android:layout_marginEnd="@dimen/view_flicker_code_stripe_margin_right"
/>
<net.dankito.banking.fints4java.android.ui.views.ChipTanFlickerCodeStripeView
android:id="@+id/flickerCodeStripe3"
android:layout_width="@dimen/view_flicker_code_stripe_width"
android:layout_height="@dimen/view_flicker_code_stripe_height"
android:layout_marginRight="@dimen/view_flicker_code_stripe_margin_right"
android:layout_marginEnd="@dimen/view_flicker_code_stripe_margin_right"
/>
<net.dankito.banking.fints4java.android.ui.views.ChipTanFlickerCodeStripeView
android:id="@+id/flickerCodeStripe4"
android:layout_width="@dimen/view_flicker_code_stripe_width"
android:layout_height="@dimen/view_flicker_code_stripe_height"
android:layout_marginRight="@dimen/view_flicker_code_stripe_margin_right"
android:layout_marginEnd="@dimen/view_flicker_code_stripe_margin_right"
/>
<net.dankito.banking.fints4java.android.ui.views.ChipTanFlickerCodeStripeView
android:id="@+id/flickerCodeStripe5"
android:layout_width="@dimen/view_flicker_code_stripe_width"
android:layout_height="@dimen/view_flicker_code_stripe_height"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -4,4 +4,6 @@
<color name="positiveAmount">#00FF00</color>
<color name="negativeAmount">#FF0000</color>
<color name="view_tan_generator_marker_background">@android:color/darker_gray</color>
</resources>

View File

@ -31,4 +31,23 @@
<dimen name="dialog_bank_transfer_autocomplete_fields_height">50dp</dimen>
<dimen name="dialog_bank_transfer_buttons_width">120dp</dimen>
<dimen name="view_flicker_code_stripe_height">120dp</dimen>
<dimen name="view_flicker_code_control_buttons_height">40dp</dimen>
<dimen name="view_flicker_code_control_buttons_width">40dp</dimen>
<dimen name="view_flicker_code_stripe_width">40dp</dimen>
<dimen name="view_flicker_code_stripe_margin_right">10dp</dimen>
<dimen name="view_tan_generator_marker_height">20dp</dimen>
<dimen name="view_tan_generator_marker_width">8dp</dimen>
<dimen name="view_tan_generator_marker_margin_bottom">6dp</dimen>
<dimen name="dialog_enter_tan_padding">4dp</dimen>
<dimen name="dialog_enter_tan_flicker_view_height">175dp</dimen>
<dimen name="dialog_enter_tan_flicker_view_margin_top_bottom">20dp</dimen>
<dimen name="dialog_enter_tan_enter_tan_height">50dp</dimen>
<dimen name="dialog_enter_tan_enter_tan_width">110dp</dimen>
<dimen name="dialog_enter_tan_enter_tan_margin_left">6dp</dimen>
<dimen name="dialog_enter_tan_enter_tan_margin_bottom">8dp</dimen>
<dimen name="dialog_enter_tan_buttons_width">120dp</dimen>
</resources>

View File

@ -25,4 +25,13 @@
<string name="dialog_bank_transfer_usage">Usage:</string>
<string name="dialog_bank_transfer_transfer">Transfer</string>
<string name="view_flicker_code_size">Size:</string>
<string name="view_flicker_code_increase_size">+</string>
<string name="view_flicker_code_decrease_size">-</string>
<string name="view_flicker_code_frequency">Frequency:</string>
<string name="view_flicker_code_increase_frequency">+</string>
<string name="view_flicker_code_decrease_frequency">-</string>
<string name="dialog_enter_tan_enter_tan_dialog">Enter TAN:</string>
</resources>

View File

@ -14,6 +14,9 @@ open class FlickerCanvas(var code: String) {
var clock = Bit.High
var bitarray = mutableListOf<MutableList<Bit>>()
val steps: List<Array<Bit>>
fun reset() {
halfbyteid = 0
clock = Bit.High
@ -53,8 +56,7 @@ open class FlickerCanvas(var code: String) {
steps.add(step())
} while (halfbyteid > 0 || clock == Bit.Low)
log.info("Steps:")
steps.forEach { step -> log.info(step.joinToString("")) }
this.steps = steps
}
fun step(): Array<Bit> {