Implemented retrieving and displaying account transactions

This commit is contained in:
dankito 2022-02-16 00:16:44 +01:00
parent a6a24311d7
commit a6fd9a4d42
15 changed files with 285 additions and 28 deletions

View File

@ -35,10 +35,17 @@ android {
}
dependencies {
implementation project(":fints4k")
implementation "net.dankito.utils:android-utils:1.1.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0-rc02'
implementation 'androidx.navigation:navigation-ui-ktx:2.0.0-rc02'
implementation "org.slf4j:slf4j-android:1.7.32"
}

View File

@ -2,6 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.codinux.banking.fints4k.android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"

View File

@ -1,11 +1,14 @@
package net.codinux.banking.fints4k.android
import android.os.Bundle
import android.view.ContextThemeWrapper
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import net.codinux.banking.fints4k.android.adapter.AccountTransactionsListRecyclerAdapter
import net.codinux.banking.fints4k.android.databinding.FragmentFirstBinding
/**
@ -15,6 +18,8 @@ class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
private val accountTransactionsAdapter = AccountTransactionsListRecyclerAdapter()
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
@ -32,8 +37,17 @@ class FirstFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.buttonFirst.setOnClickListener {
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
binding.rcyvwAccountTransactions.apply {
layoutManager = LinearLayoutManager(this@FirstFragment.context, LinearLayoutManager.VERTICAL, false)
addItemDecoration(DividerItemDecoration(ContextThemeWrapper(this@FirstFragment.context, R.style.Theme_Fints4kProject), (layoutManager as LinearLayoutManager).orientation))
adapter = accountTransactionsAdapter
}
// TODO: set your credentials here
Presenter().retrieveAccountData("", "", "", "") { response ->
if (response.successful) {
accountTransactionsAdapter.items = response.retrievedData.flatMap { it.bookedTransactions }
}
}
}

View File

@ -0,0 +1,54 @@
package net.codinux.banking.fints4k.android
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.datetime.LocalDate
import net.dankito.banking.fints.FinTsClientDeprecated
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
import net.dankito.banking.fints.model.AddAccountParameter
import net.dankito.banking.fints.response.client.AddAccountResponse
import net.dankito.utils.multiplatform.extensions.millisSinceEpochAtSystemDefaultTimeZone
import org.slf4j.LoggerFactory
import java.math.BigDecimal
import java.text.DateFormat
import java.util.*
class Presenter {
companion object {
val ValueDateFormat = DateFormat.getDateInstance(DateFormat.SHORT)
private val log = LoggerFactory.getLogger(Presenter::class.java)
}
private val fintsClient = FinTsClientDeprecated(SimpleFinTsClientCallback())
fun retrieveAccountData(bankCode: String, customerId: String, pin: String, finTs3ServerAddress: String, retrievedResult: (AddAccountResponse) -> Unit) {
fintsClient.addAccountAsync(AddAccountParameter(bankCode, customerId, pin, finTs3ServerAddress)) { response ->
log.info("Retrieved response from ${response.bank.bankName} for ${response.bank.customerName}")
GlobalScope.launch(Dispatchers.Main) {
retrievedResult(response)
}
}
}
fun formatDate(date: LocalDate): String {
try {
return ValueDateFormat.format(Date(date.millisSinceEpochAtSystemDefaultTimeZone))
} catch (e: Exception) {
log.error("Could not format date $date", e)
}
return date.toString()
}
fun formatAmount(amount: BigDecimal): String {
return String.format("%.02f", amount)
}
}

View File

@ -0,0 +1,46 @@
package net.codinux.banking.fints4k.android.adapter
import android.view.View
import net.codinux.banking.fints4k.android.Presenter
import net.codinux.banking.fints4k.android.R
import net.codinux.banking.fints4k.android.adapter.viewholder.AccountTransactionsViewHolder
import net.dankito.banking.fints.model.AccountTransaction
import net.dankito.banking.fints.util.toBigDecimal
import net.dankito.utils.android.extensions.setTextColorToColorResource
import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter
import org.slf4j.LoggerFactory
import java.math.BigDecimal
class AccountTransactionsListRecyclerAdapter : ListRecyclerAdapter<AccountTransaction, AccountTransactionsViewHolder>() {
private val presenter = Presenter() // TOOD: inject
private val log = LoggerFactory.getLogger(AccountTransactionsListRecyclerAdapter::class.java)
override fun getListItemLayoutId() = R.layout.list_item_account_transaction
override fun createViewHolder(itemView: View): AccountTransactionsViewHolder {
return AccountTransactionsViewHolder(itemView)
}
override fun bindItemToView(viewHolder: AccountTransactionsViewHolder, item: AccountTransaction) {
try {
viewHolder.txtvwBookingText.text = item.bookingText ?: ""
viewHolder.txtvwOtherPartyName.visibility = if (item.showOtherPartyName) View.VISIBLE else View.GONE
viewHolder.txtvwOtherPartyName.text = item.otherPartyName ?: ""
viewHolder.txtvwReference.text = item.reference
viewHolder.txtvwDate.text = presenter.formatDate(item.valueDate)
val amount = item.amount.toBigDecimal()
viewHolder.txtvwAmount.text = presenter.formatAmount(amount)
viewHolder.txtvwAmount.setTextColorToColorResource(if (amount >= BigDecimal.ZERO) R.color.positiveAmount else R.color.negativeAmount)
} catch (e: Exception) {
log.error("Could not display account transaction $item", e)
}
}
}

View File

@ -0,0 +1,20 @@
package net.codinux.banking.fints4k.android.adapter.viewholder
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import net.codinux.banking.fints4k.android.R
class AccountTransactionsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val txtvwBookingText: TextView = itemView.findViewById(R.id.txtvwBookingText)
val txtvwOtherPartyName: TextView = itemView.findViewById(R.id.txtvwOtherPartyName)
val txtvwReference: TextView = itemView.findViewById(R.id.txtvwReference)
val txtvwAmount: TextView = itemView.findViewById(R.id.txtvwAmount)
val txtvwDate: TextView = itemView.findViewById(R.id.txtvwDate)
}

View File

@ -4,6 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_content_padding"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<fragment

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@ -7,25 +7,13 @@
android:layout_height="match_parent"
tools:context=".FirstFragment">
<TextView
android:id="@+id/textview_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_first_fragment"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/button_first"
/>
<Button
android:id="@+id/button_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next"
app:layout_constraintTop_toBottomOf="@id/textview_first"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView android:id="@+id/rcyvwAccountTransactions"
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
>
</androidx.recyclerview.widget.RecyclerView>
</RelativeLayout>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_account_transaction_height"
android:padding="@dimen/list_item_account_transaction_padding"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@+id/lytAmountAndDate"
android:layout_toStartOf="@+id/lytAmountAndDate"
android:layout_marginLeft="@dimen/list_item_account_transaction_transaction_text_margin_left_and_right"
android:layout_marginStart="@dimen/list_item_account_transaction_transaction_text_margin_left_and_right"
android:layout_marginRight="@dimen/list_item_account_transaction_transaction_text_margin_left_and_right"
android:layout_marginEnd="@dimen/list_item_account_transaction_transaction_text_margin_left_and_right"
android:gravity="center_vertical"
>
<TextView
android:id="@+id/txtvwBookingText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/txtvwOtherPartyName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_item_account_transaction_other_party_name_margin_top_and_bottom"
android:layout_marginBottom="@dimen/list_item_account_transaction_other_party_name_margin_top_and_bottom"
/>
<TextView
android:id="@+id/txtvwReference"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/lytAmountAndDate"
android:orientation="vertical"
android:layout_width="@dimen/list_item_account_transaction_amount_width"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:gravity="center_vertical"
>
<ImageView
android:id="@+id/imgvwBankIcon"
android:layout_width="@dimen/list_item_account_transaction_bank_icon_width_and_height"
android:layout_height="@dimen/list_item_account_transaction_bank_icon_width_and_height"
android:layout_marginTop="@dimen/list_item_account_transaction_bank_icon_margin_top"
android:layout_marginBottom="@dimen/list_item_account_transaction_bank_icon_margin_bottom"
android:layout_gravity="end"
android:visibility="gone"
/>
<TextView
android:id="@+id/txtvwAmount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
/>
<TextView
android:id="@+id/txtvwDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_item_account_transaction_date_margin_top"
android:layout_marginBottom="@dimen/list_item_account_transaction_date_margin_bottom"
android:gravity="end"
/>
</LinearLayout>
</RelativeLayout>

View File

@ -7,4 +7,9 @@
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="positiveAmount">#43A047</color>
<color name="negativeAmount">#E53935</color>
</resources>

View File

@ -1,3 +1,18 @@
<resources>
<dimen name="activity_content_padding">4dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="list_item_account_transaction_height">100dp</dimen>
<dimen name="list_item_account_transaction_padding">4dp</dimen>
<dimen name="list_item_account_transaction_bank_icon_width_and_height">16dp</dimen>
<dimen name="list_item_account_transaction_bank_icon_margin_top">8dp</dimen>
<dimen name="list_item_account_transaction_bank_icon_margin_bottom">12dp</dimen>
<dimen name="list_item_account_transaction_amount_width">70dp</dimen>
<dimen name="list_item_account_transaction_date_margin_top">12dp</dimen>
<dimen name="list_item_account_transaction_date_margin_bottom">8dp</dimen>
<dimen name="list_item_account_transaction_transaction_text_margin_left_and_right">4dp</dimen>
<dimen name="list_item_account_transaction_other_party_name_margin_top_and_bottom">6dp</dimen>
</resources>

View File

@ -75,8 +75,6 @@ kotlin {
dependencies {
api project(":multiplatform-utils")
implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.3.2"
implementation "co.touchlab:stately-concurrency:1.2.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"

View File

@ -54,6 +54,9 @@ open class AccountTransaction(
null, "", "", null, null, "", null)
open val showOtherPartyName: Boolean
get() = otherPartyName.isNullOrBlank() == false /* && type != "ENTGELTABSCHLUSS" && type != "AUSZAHLUNG" */ // TODO
val reference: String
get() = sepaReference ?: unparsedReference

View File

@ -0,0 +1,14 @@
package net.dankito.banking.fints.util
import net.dankito.banking.fints.model.Amount
import net.dankito.banking.fints.model.Money
import java.math.BigDecimal
fun Amount.toBigDecimal(): BigDecimal {
return BigDecimal(this.string.replace(',', '.'))
}
fun Money.toBigDecimal(): BigDecimal {
return this.amount.toBigDecimal()
}

View File

@ -65,7 +65,7 @@ kotlin {
sourceSets {
commonMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.3.2"
api "org.jetbrains.kotlinx:kotlinx-datetime:0.3.2"
}
}