Implemented showing user's tan media

This commit is contained in:
dankl 2019-12-29 17:55:41 +01:00 committed by dankito
parent be3dba4765
commit 11f115936b
16 changed files with 149 additions and 12 deletions

View File

@ -19,6 +19,8 @@ open class Account(
var selectedTanProcedure: TanProcedure? = null
var tanMedia: List<TanMedium> = listOf()
val balance: BigDecimal
get() = bankAccounts.map { it.balance }.fold(BigDecimal.ZERO) { acc, e -> acc + e }

View File

@ -0,0 +1,14 @@
package net.dankito.banking.ui.model
open class TanMedium(
val displayName: String,
val status: TanMediumStatus,
val originalObject: Any
) {
override fun toString(): String {
return "$displayName $status"
}
}

View File

@ -0,0 +1,10 @@
package net.dankito.banking.ui.model
enum class TanMediumStatus {
Used,
Available
}

View File

@ -12,6 +12,7 @@ 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.CustomerData
import net.dankito.fints.model.TanChallenge
import net.dankito.fints.model.TanProcedure
import java.util.concurrent.CountDownLatch
@ -29,8 +30,8 @@ class MainActivity : AppCompatActivity() {
return supportedTanProcedures.first()
}
override fun enterTan(tanChallenge: TanChallenge): String? {
return getTanFromUserOffUiThread(tanChallenge)
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): String? {
return getTanFromUserOffUiThread(customer, tanChallenge)
}
})
@ -82,12 +83,14 @@ class MainActivity : AppCompatActivity() {
// return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
// }
private fun getTanFromUserOffUiThread(tanChallenge: TanChallenge): String? {
private fun getTanFromUserOffUiThread(customer: CustomerData, tanChallenge: TanChallenge): String? {
val enteredTan = AtomicReference<String>(null)
val tanEnteredLatch = CountDownLatch(1)
val account = presenter.getAccountForCustomer(customer)
runOnUiThread {
EnterTanDialog().show(tanChallenge, this@MainActivity, false) {
EnterTanDialog().show(account, tanChallenge, this@MainActivity, false) {
enteredTan.set(it)
tanEnteredLatch.countDown()
}

View File

@ -4,6 +4,7 @@ import net.dankito.banking.ui.model.*
import net.dankito.banking.ui.model.responses.AddAccountResponse
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
import net.dankito.fints.model.AccountData
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
@ -21,6 +22,7 @@ open class fints4javaModelMapper {
account.bankAccounts = mapBankAccounts(account, customer.accounts)
account.supportedTanProcedures = mapTanProcedures(customer.supportedTanProcedures)
account.tanMedia = mapTanMediums(customer.tanMedia)
return account
}
@ -89,6 +91,29 @@ open class fints4javaModelMapper {
}
open fun mapTanMediums(tanMediums: List<net.dankito.fints.messages.datenelemente.implementierte.tan.TanMedium>): List<TanMedium> {
return tanMediums.map { mapTanMedium(it) }
}
open fun mapTanMedium(tanMedium: net.dankito.fints.messages.datenelemente.implementierte.tan.TanMedium): TanMedium {
val status = if (tanMedium.status.name.contains("Aktiv")) TanMediumStatus.Used else TanMediumStatus.Available
return TanMedium(getDisplayNameForTanMedium(tanMedium), status, tanMedium)
}
protected open fun getDisplayNameForTanMedium(tanMedium: net.dankito.fints.messages.datenelemente.implementierte.tan.TanMedium): String {
if (tanMedium is TanGeneratorTanMedium) {
tanMedium.mediaName?.let { mediaName ->
return "$mediaName ${tanMedium.cardNumber}"
}
return "Card ${tanMedium.cardNumber}" // TODO: translate
}
return tanMedium.mediumClass.name
}
fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse {
var bookedTransactions = mapOf<BankAccount, List<AccountTransaction>>()
var balances = mapOf<BankAccount, BigDecimal>()

View File

@ -187,6 +187,10 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service,
open val balanceOfAllAccounts: BigDecimal
get() = accounts.keys.map { it.balance }.fold(BigDecimal.ZERO) { acc, e -> acc + e }
open fun getAccountForCustomer(customer: CustomerData): Account { // TODO: remove as presenter should not be aware of fints4java objects
return accounts.keys.first { it.customerId == customer.customerId }
}
open fun addAccountAddedListener(listener: (Account) -> Unit) {
accountAddedListeners.add(listener)

View File

@ -0,0 +1,25 @@
package net.dankito.banking.fints4java.android.ui.adapter
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.ui.model.TanMedium
import net.dankito.utils.android.extensions.asActivity
import net.dankito.utils.android.ui.adapter.ListAdapter
open class TanMediumAdapter : ListAdapter<TanMedium>() {
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
val procedure = getItem(position)
val view = convertView ?: parent?.context?.asActivity()?.layoutInflater?.inflate(
R.layout.list_item_tan_medium, parent, false)
view?.findViewById<TextView>(R.id.txtTanMediumDisplayName)?.text = procedure.displayName
return view
}
}

View File

@ -9,6 +9,9 @@ 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.banking.fints4java.android.ui.adapter.TanMediumAdapter
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.TanMediumStatus
import net.dankito.fints.model.TanChallenge
import net.dankito.fints.model.TanProcedureType
import net.dankito.fints.tan.FlickercodeDecoder
@ -21,14 +24,19 @@ open class EnterTanDialog : DialogFragment() {
}
protected lateinit var account: Account
protected lateinit var tanChallenge: TanChallenge
protected lateinit var tanEnteredCallback: (String?) -> Unit
protected val tanMediumAdapter = TanMediumAdapter()
open fun show(tanChallenge: TanChallenge, activity: AppCompatActivity,
open fun show(account: Account, tanChallenge: TanChallenge, activity: AppCompatActivity,
fullscreen: Boolean = false, tanEnteredCallback: (String?) -> Unit) {
this.account = account
this.tanChallenge = tanChallenge
this.tanEnteredCallback = tanEnteredCallback
@ -51,6 +59,12 @@ open class EnterTanDialog : DialogFragment() {
val flickerCodeView = rootView.flickerCodeView
if (tanChallenge.tanProcedure.type == TanProcedureType.ChipTanOptisch) {
if (account.tanMedia.isNotEmpty()) {
rootView.lytTanMedium.visibility = View.VISIBLE
tanMediumAdapter.setItems(account.tanMedia.sortedByDescending { it.status == TanMediumStatus.Used })
rootView.spnTanMedium.adapter = tanMediumAdapter
}
flickerCodeView.visibility = View.VISIBLE
flickerCodeView.setCode(FlickercodeDecoder().decodeChallenge(tanChallenge.tanChallenge))

View File

@ -6,6 +6,32 @@
android:padding="@dimen/dialog_enter_tan_padding"
>
<LinearLayout
android:id="@+id/lytTanMedium"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_enter_tan_tan_medium_height"
android:layout_gravity="center_vertical"
android:visibility="gone"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/dialog_enter_tan_select_tan_medium"
android:layout_marginRight="@dimen/dialog_enter_tan_tan_medium_label_right_margin"
android:layout_marginEnd="@dimen/dialog_enter_tan_tan_medium_label_right_margin"
/>
<Spinner
android:id="@+id/spnTanMedium"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
</LinearLayout>
<net.dankito.banking.fints4java.android.ui.views.ChipTanFlickerCodeView
android:id="@+id/flickerCodeView"
android:layout_width="match_parent"
@ -33,7 +59,7 @@
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:text="@string/dialog_enter_tan_enter_tan_dialog"
android:text="@string/dialog_enter_tan_enter_tan"
/>
<EditText

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/txtTanMediumDisplayName"
style="@style/TextAppearance.AppCompat.Small"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ellipsize="marquee"
/>

View File

@ -49,6 +49,8 @@
<dimen name="view_tan_generator_marker_margin_bottom">6dp</dimen>
<dimen name="dialog_enter_tan_padding">4dp</dimen>
<dimen name="dialog_enter_tan_tan_medium_height">30dp</dimen>
<dimen name="dialog_enter_tan_tan_medium_label_right_margin">8dp</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>

View File

@ -58,6 +58,7 @@
<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>
<string name="dialog_enter_tan_select_tan_medium">TAN medium</string>
<string name="dialog_enter_tan_enter_tan">Enter TAN:</string>
</resources>

View File

@ -582,7 +582,7 @@ open class FinTsClient @JvmOverloads constructor(
if (response.isStrongAuthenticationRequired) {
response.tanResponse?.let { tanResponse ->
// TODO: is this true for all tan procedures?
val enteredTan = callback.enterTan(TanChallenge(tanResponse.challenge ?: "",
val enteredTan = callback.enterTan(customer, TanChallenge(tanResponse.challenge ?: "",
tanResponse.challengeHHD_UC ?: "", customer.selectedTanProcedure))
if (enteredTan == null) {

View File

@ -1,5 +1,6 @@
package net.dankito.fints
import net.dankito.fints.model.CustomerData
import net.dankito.fints.model.TanChallenge
import net.dankito.fints.model.TanProcedure
@ -8,6 +9,6 @@ interface FinTsClientCallback {
fun askUserForTanProcedure(supportedTanProcedures: List<TanProcedure>): TanProcedure?
fun enterTan(tanChallenge: TanChallenge): String?
fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): String?
}

View File

@ -41,7 +41,7 @@ public class JavaShowcase {
@Nullable
@Override
public String enterTan(@NotNull TanChallenge tanChallenge) {
public String enterTan(@NotNull CustomerData customer, @NotNull TanChallenge tanChallenge) {
return null;
}

View File

@ -35,7 +35,7 @@ class FinTsClientTest {
return supportedTanProcedures.first()
}
override fun enterTan(tanChallenge: TanChallenge): String? {
override fun enterTan(customer: CustomerData, tanChallenge: TanChallenge): String? {
didAskUserToEnterTan.set(true)
return null