diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/IBankingClient.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/IBankingClient.kt new file mode 100644 index 00000000..5530e150 --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/IBankingClient.kt @@ -0,0 +1,23 @@ +package net.dankito.banking.ui + +import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.parameters.GetTransactionsParameter +import net.dankito.banking.ui.model.parameters.TransferMoneyData +import net.dankito.banking.ui.model.responses.AddAccountResponse +import net.dankito.banking.ui.model.responses.BankingClientResponse +import net.dankito.banking.ui.model.responses.GetTransactionsResponse + + +interface IBankingClient { + + fun addAccountAsync(callback: (AddAccountResponse) -> Unit) + + fun getTransactionsAsync( + bankAccount: BankAccount, + parameter: GetTransactionsParameter, + callback: (GetTransactionsResponse) -> Unit + ) + + fun transferMoneyAsync(data: TransferMoneyData, callback: (BankingClientResponse) -> Unit) + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/parameters/GetTransactionsParameter.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/parameters/GetTransactionsParameter.kt new file mode 100644 index 00000000..8219dc0d --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/parameters/GetTransactionsParameter.kt @@ -0,0 +1,14 @@ +package net.dankito.banking.ui.model.parameters + +import java.util.* + + +open class GetTransactionsParameter( + val alsoRetrieveBalance: Boolean = true, + val fromDate: Date? = null, + val toDate: Date? = null +) { + + constructor() : this(true, null, null) // for Java + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/parameters/TransferMoneyData.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/parameters/TransferMoneyData.kt new file mode 100644 index 00000000..5298f99d --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/parameters/TransferMoneyData.kt @@ -0,0 +1,12 @@ +package net.dankito.banking.ui.model.parameters + +import java.math.BigDecimal + + +open class TransferMoneyData( + val creditorName: String, + val creditorIban: String, + val creditorBic: String, + val amount: BigDecimal, + val usage: String +) \ No newline at end of file diff --git a/fints4javaAndroidApp/build.gradle b/fints4javaAndroidApp/build.gradle index ab4f34ed..8efaec66 100644 --- a/fints4javaAndroidApp/build.gradle +++ b/fints4javaAndroidApp/build.gradle @@ -48,8 +48,10 @@ android { } dependencies { + implementation project(':fints4javaBankingClient') + + // TODO: try to get rid of this import implementation project(':fints4javaLib') - implementation project(':BankingUiCommon') api "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" 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 f2d07237..f616c539 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 @@ -8,7 +8,7 @@ import android.support.v7.app.AppCompatActivity import android.support.v7.widget.Toolbar import android.view.Menu import androidx.navigation.findNavController -import net.dankito.banking.fints4java.android.mapper.fints4javaModelMapper +import net.dankito.banking.mapper.fints4javaModelMapper 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.EnterAtcDialog @@ -110,7 +110,7 @@ class MainActivity : AppCompatActivity() { runOnUiThread { // TODO: don't create a fints4javaModelMapper instance here, let MainWindowPresenter do the job - EnterAtcDialog().show(fints4javaModelMapper().mapTanMedium(tanMedium), this@MainActivity, false) { enteredResult -> + EnterAtcDialog().show(net.dankito.banking.mapper.fints4javaModelMapper().mapTanMedium(tanMedium), this@MainActivity, false) { enteredResult -> result.set(enteredResult) tanEnteredLatch.countDown() } 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 640e165a..3964e41c 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 @@ -1,22 +1,22 @@ package net.dankito.banking.fints4java.android.ui +import net.dankito.banking.ui.IBankingClient import net.dankito.banking.ui.model.Account import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.responses.AddAccountResponse +import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.banking.ui.model.responses.GetTransactionsResponse import net.dankito.fints.FinTsClientCallback -import net.dankito.fints.FinTsClientForCustomer import net.dankito.fints.banks.BankFinder import net.dankito.fints.model.BankInfo -import net.dankito.fints.model.BankTransferData import net.dankito.fints.model.CustomerData -import net.dankito.fints.model.GetTransactionsParameter -import net.dankito.fints.model.mapper.BankDataMapper import net.dankito.fints.response.client.FinTsClientResponse import net.dankito.fints.util.IBase64Service import net.dankito.utils.IThreadPool import net.dankito.utils.ThreadPool +import net.dankito.utils.web.client.OkHttpWebClient import java.math.BigDecimal import java.util.* import kotlin.collections.ArrayList @@ -26,32 +26,32 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service, protected val callback: FinTsClientCallback ) { + companion object { + protected const val OneDayMillis = 24 * 60 * 60 * 1000 + } + + protected val bankFinder: BankFinder = BankFinder() protected val threadPool: IThreadPool = ThreadPool() - protected val bankDataMapper = BankDataMapper() - - protected val fints4javaModelMapper = net.dankito.banking.fints4java.android.mapper.fints4javaModelMapper() + protected val fints4javaModelMapper = net.dankito.banking.mapper.fints4javaModelMapper() - protected val accounts = mutableMapOf() + protected val accounts = mutableMapOf() protected val accountAddedListeners = mutableListOf<(Account) -> Unit>() protected val retrievedAccountTransactionsResponseListeners = mutableListOf<(BankAccount, GetTransactionsResponse) -> Unit>() - open fun addAccountAsync(bankInfo: BankInfo, customerId: String, pin: String, - callback: (AddAccountResponse) -> Unit) { + // TODO: move BankInfo out of fints4javaLib + open fun addAccountAsync(bankInfo: BankInfo, customerId: String, pin: String, callback: (AddAccountResponse) -> Unit) { - val bank = bankDataMapper.mapFromBankInfo(bankInfo) - val customer = CustomerData(customerId, pin) - val newClient = FinTsClientForCustomer(bank, customer, this.callback, base64Service) + val newClient = net.dankito.banking.fints4javaBankingClient(bankInfo, customerId, pin, OkHttpWebClient(), base64Service, threadPool, this.callback) newClient.addAccountAsync { response -> - val account = fints4javaModelMapper.mapAccount(customer, bank) - val mappedResponse = fints4javaModelMapper.mapResponse(account, response) + val account = response.account if (response.isSuccessful) { accounts.put(account, newClient) @@ -60,12 +60,12 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service, if (response.supportsRetrievingTransactionsOfLast90DaysWithoutTan) { account.bankAccounts.forEach { bankAccount -> - retrievedAccountTransactions(bankAccount, mappedResponse) + retrievedAccountTransactions(bankAccount, response) } } } - callback(mappedResponse) + callback(response) } } @@ -88,13 +88,11 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service, callback: (GetTransactionsResponse) -> Unit) { getClientForAccount(bankAccount.account)?.let { client -> - client.getTransactionsAsync(GetTransactionsParameter(true, fromDate)) { response -> + client.getTransactionsAsync(bankAccount, net.dankito.banking.ui.model.parameters.GetTransactionsParameter(true, fromDate)) { response -> - val mappedResponse = fints4javaModelMapper.mapResponse(bankAccount.account, response) + retrievedAccountTransactions(bankAccount, response) - retrievedAccountTransactions(bankAccount, mappedResponse) - - callback(mappedResponse) + callback(response) } } } @@ -104,7 +102,7 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service, account.bankAccounts.forEach { bankAccount -> val today = Date() // TODO: still don't know where this bug is coming from that bank returns a transaction dated at end of year val lastRetrievedTransactionDate = bankAccount.bookedTransactions.firstOrNull { it.bookingDate <= today }?.bookingDate - val fromDate = lastRetrievedTransactionDate?.let { Date(it.time - 24 * 60 * 60 * 1000) } // on day before last received transaction + val fromDate = lastRetrievedTransactionDate?.let { Date(it.time - OneDayMillis) } // one day before last received transaction getAccountTransactionsAsync(bankAccount, fromDate, callback) } @@ -135,9 +133,9 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service, } - open fun transferMoneyAsync(bankAccount: BankAccount, bankTransferData: BankTransferData, callback: (FinTsClientResponse) -> Unit) { + open fun transferMoneyAsync(bankAccount: BankAccount, data: TransferMoneyData, callback: (BankingClientResponse) -> Unit) { getClientForAccount(bankAccount.account)?.let { client -> - client.doBankTransferAsync(bankTransferData, callback) + client.transferMoneyAsync(data, callback) } } @@ -180,13 +178,14 @@ open class MainWindowPresenter(protected val base64Service: IBase64Service, } - protected open fun getClientForAccount(account: Account): FinTsClientForCustomer? { + protected open fun getClientForAccount(account: Account): IBankingClient? { accounts.get(account)?.let { client -> - account.selectedTanProcedure?.let { selectedTanProcedure -> - client.customer.selectedTanProcedure = fints4javaModelMapper.mapTanProcedureBack(selectedTanProcedure) - } + // TODO: is this code still needed after updating data model is implemented? +// account.selectedTanProcedure?.let { selectedTanProcedure -> +// client.customer.selectedTanProcedure = fints4javaModelMapper.mapTanProcedureBack(selectedTanProcedure) +// } - return client // TODO: return IBankingClient + return client } return null diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/BankTransferDialog.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/BankTransferDialog.kt index 810e2946..47807e4e 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/BankTransferDialog.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/BankTransferDialog.kt @@ -14,10 +14,11 @@ import kotlinx.android.synthetic.main.dialog_bank_transfer.view.* import net.dankito.banking.fints4java.android.R import net.dankito.banking.fints4java.android.ui.MainWindowPresenter import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.parameters.TransferMoneyData +import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.fints.messages.segmente.implementierte.sepa.ISepaMessageCreator import net.dankito.fints.messages.segmente.implementierte.sepa.SepaMessageCreator import net.dankito.fints.model.BankTransferData -import net.dankito.fints.response.client.FinTsClientResponse import net.dankito.utils.android.extensions.asActivity import java.math.BigDecimal @@ -109,7 +110,7 @@ open class BankTransferDialog : DialogFragment() { protected open fun transferMoney() { getEnteredAmount()?.let { amount -> // should only come at this stage when a valid amount has been entered - val transferData = BankTransferData( + val data = TransferMoneyData( edtxtRemitteeName.text.toString(), edtxtRemitteeIban.text.toString(), edtxtRemitteeBic.text.toString(), @@ -117,15 +118,15 @@ open class BankTransferDialog : DialogFragment() { edtxtUsage.text.toString() ) - presenter.transferMoneyAsync(bankAccount, transferData) { + presenter.transferMoneyAsync(bankAccount, data) { context?.asActivity()?.runOnUiThread { - handleTransferMoneyResultOnUiThread(it, transferData) + handleTransferMoneyResultOnUiThread(data, it) } } } } - protected open fun handleTransferMoneyResultOnUiThread(response: FinTsClientResponse, transferData: BankTransferData) { + protected open fun handleTransferMoneyResultOnUiThread(transferData: TransferMoneyData, response: BankingClientResponse) { context?.let { context -> val message = if (response.isSuccessful) { context.getString(R.string.dialog_bank_transfer_message_transfer_successful, @@ -134,7 +135,7 @@ open class BankTransferDialog : DialogFragment() { else { context.getString(R.string.dialog_bank_transfer_message_transfer_failed, String.format("%.02f", transferData.amount), "€", transferData.creditorName, // TODO: where to get currency from? - presenter.getErrorToShowToUser(response) + response.errorToShowToUser ) } 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 index 707bbca3..1a64c68e 100644 --- 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 @@ -15,7 +15,7 @@ import android.widget.Spinner import kotlinx.android.synthetic.main.dialog_enter_tan.view.* import kotlinx.android.synthetic.main.view_tan_image.view.* import net.dankito.banking.fints4java.android.R -import net.dankito.banking.fints4java.android.mapper.fints4javaModelMapper +import net.dankito.banking.mapper.fints4javaModelMapper import net.dankito.banking.fints4java.android.ui.MainWindowPresenter import net.dankito.banking.fints4java.android.ui.adapter.TanMediumAdapter import net.dankito.banking.fints4java.android.ui.adapter.TanProceduresAdapter @@ -96,7 +96,7 @@ open class EnterTanDialog : DialogFragment() { spinner.onItemSelectedListener = ListItemSelectedListener(adapter) { newSelectedTanProcedure -> if (newSelectedTanProcedure != selectedTanProcedure) { - val mappedTanProcedure = fints4javaModelMapper().mapTanProcedureBack(newSelectedTanProcedure) // TODO: move to MainWindowPresenter + val mappedTanProcedure = net.dankito.banking.mapper.fints4javaModelMapper().mapTanProcedureBack(newSelectedTanProcedure) // TODO: move to MainWindowPresenter tanEnteredCallback(EnterTanResult.userAsksToChangeTanProcedure(mappedTanProcedure)) // TODO: find a way to update account.selectedTanProcedure afterwards diff --git a/fints4javaBankingClient/build.gradle b/fints4javaBankingClient/build.gradle new file mode 100644 index 00000000..186e29b8 --- /dev/null +++ b/fints4javaBankingClient/build.gradle @@ -0,0 +1,23 @@ +apply plugin: 'java-library' +apply plugin: 'kotlin' + + +sourceCompatibility = "1.7" +targetCompatibility = "1.7" + +compileKotlin { + kotlinOptions.jvmTarget = "1.6" +} +compileTestKotlin { + kotlinOptions.jvmTarget = "1.6" +} + + +dependencies { + api project(':BankingUiCommon') + implementation project(':fints4javaLib') + + + testImplementation "junit:junit:$junitVersion" + testImplementation "org.assertj:assertj-core:$assertJVersion" +} \ No newline at end of file diff --git a/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/fints4javaBankingClient.kt b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/fints4javaBankingClient.kt new file mode 100644 index 00000000..a775b450 --- /dev/null +++ b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/fints4javaBankingClient.kt @@ -0,0 +1,70 @@ +package net.dankito.banking + +import net.dankito.banking.ui.IBankingClient +import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.parameters.GetTransactionsParameter +import net.dankito.banking.ui.model.parameters.TransferMoneyData +import net.dankito.banking.ui.model.responses.AddAccountResponse +import net.dankito.banking.ui.model.responses.BankingClientResponse +import net.dankito.banking.ui.model.responses.GetTransactionsResponse +import net.dankito.fints.FinTsClientCallback +import net.dankito.fints.FinTsClientForCustomer +import net.dankito.fints.model.BankInfo +import net.dankito.fints.model.BankTransferData +import net.dankito.fints.model.CustomerData +import net.dankito.fints.model.mapper.BankDataMapper +import net.dankito.fints.util.IBase64Service +import net.dankito.utils.IThreadPool +import net.dankito.utils.ThreadPool +import net.dankito.utils.web.client.IWebClient +import net.dankito.utils.web.client.OkHttpWebClient + + +open class fints4javaBankingClient( + bankInfo: BankInfo, + customerId: String, + pin: String, + webClient: IWebClient = OkHttpWebClient(), + base64Service: IBase64Service, + threadPool: IThreadPool = ThreadPool(), + callback: FinTsClientCallback + +) : IBankingClient { + + protected val mapper = net.dankito.banking.mapper.fints4javaModelMapper() + + protected val bankDataMapper = BankDataMapper() + + protected val bank = bankDataMapper.mapFromBankInfo(bankInfo) + + protected val customer = CustomerData(customerId, pin) + + protected val client = FinTsClientForCustomer(bank, customer, webClient, base64Service, threadPool, callback) + + + override fun addAccountAsync(callback: (AddAccountResponse) -> Unit) { + client.addAccountAsync { response -> + val account = mapper.mapAccount(customer, bank) + val mappedResponse = mapper.mapResponse(account, response) + + callback(mappedResponse) + } + } + + override fun getTransactionsAsync(bankAccount: BankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) { + client.getTransactionsAsync(net.dankito.fints.model.GetTransactionsParameter(parameter.alsoRetrieveBalance, parameter.fromDate, parameter.toDate)) { response -> + + val mappedResponse = mapper.mapResponse(bankAccount, response) + + callback(mappedResponse) + } + } + + override fun transferMoneyAsync(data: TransferMoneyData, callback: (BankingClientResponse) -> Unit) { + val mappedData = BankTransferData(data.creditorName, data.creditorIban, data.creditorBic, data.amount, data.usage) + + client.doBankTransferAsync(mappedData) { response -> + callback(mapper.mapResponse(response)) + } + } +} \ No newline at end of file diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/mapper/fints4javaModelMapper.kt b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt similarity index 93% rename from fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/mapper/fints4javaModelMapper.kt rename to fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt index c61709db..63d096a1 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/mapper/fints4javaModelMapper.kt +++ b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt @@ -1,7 +1,8 @@ -package net.dankito.banking.fints4java.android.mapper +package net.dankito.banking.mapper import net.dankito.banking.ui.model.* import net.dankito.banking.ui.model.responses.AddAccountResponse +import net.dankito.banking.ui.model.responses.BankingClientResponse 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 @@ -129,7 +130,11 @@ open class fints4javaModelMapper { } - fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse { + open fun mapResponse(response: FinTsClientResponse): BankingClientResponse { + return BankingClientResponse(response.isSuccessful, mapErrorToShowToUser(response)) + } + + open fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse { var bookedTransactions = mapOf>() var balances = mapOf() @@ -145,8 +150,7 @@ open class fints4javaModelMapper { balances) } - fun mapResponse(account: Account, response: net.dankito.fints.response.client.GetTransactionsResponse): GetTransactionsResponse { - val bankAccount = account.bankAccounts.first() // TODO: set bank account also on net.dankito.fints.response.client.GetTransactionsResponse + open fun mapResponse(bankAccount: BankAccount, response: net.dankito.fints.response.client.GetTransactionsResponse): GetTransactionsResponse { return GetTransactionsResponse(response.isSuccessful, mapErrorToShowToUser(response), mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions)), diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientForCustomer.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientForCustomer.kt index f260ef23..69fb0702 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientForCustomer.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClientForCustomer.kt @@ -18,13 +18,13 @@ import net.dankito.utils.web.client.OkHttpWebClient open class FinTsClientForCustomer( val bank: BankData, val customer: CustomerData, - callback: FinTsClientCallback, - base64Service: IBase64Service, webClient: IWebClient = OkHttpWebClient(), + base64Service: IBase64Service, + threadPool: IThreadPool = ThreadPool(), + callback: FinTsClientCallback, messageBuilder: MessageBuilder = MessageBuilder(), responseParser: ResponseParser = ResponseParser(), mt940Parser: IAccountTransactionsParser = Mt940AccountTransactionsParser(), - threadPool: IThreadPool = ThreadPool(), product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "0.1") // TODO: get version dynamically ) {