Made BankingUiCommon a multi platform project

This commit is contained in:
dankito 2020-07-12 19:31:18 +02:00
parent 588877cb20
commit 52d3b49baa
76 changed files with 478 additions and 326 deletions

View File

@ -1,16 +1,17 @@
package net.dankito.banking.persistence package net.dankito.banking.persistence
import net.dankito.utils.multiplatform.File
import net.dankito.banking.ui.model.Customer import net.dankito.banking.ui.model.Customer
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.util.ISerializer import net.dankito.banking.util.ISerializer
import net.dankito.banking.util.JacksonJsonSerializer import java.io.FileOutputStream
import java.io.File import java.net.URL
open class BankingPersistenceJson( open class BankingPersistenceJson(
protected val jsonFile: File, protected val jsonFile: File,
protected val serializer: ISerializer = JacksonJsonSerializer() protected val serializer: ISerializer
) : IBankingPersistence { ) : IBankingPersistence {
@ -28,7 +29,7 @@ open class BankingPersistenceJson(
} }
override fun readPersistedAccounts(): List<Customer> { override fun readPersistedAccounts(): List<Customer> {
return serializer.deserializeListOr(jsonFile, Customer::class.java, listOf()) return serializer.deserializeListOr(jsonFile, Customer::class, listOf())
} }
@ -36,4 +37,13 @@ open class BankingPersistenceJson(
// done when called saveOrUpdateAccount() // done when called saveOrUpdateAccount()
} }
override fun saveUrlToFile(url: String, file: File) {
URL(url).openConnection().getInputStream().buffered().use { iconInputStream ->
FileOutputStream(file).use { fileOutputStream ->
iconInputStream.copyTo(fileOutputStream)
}
}
}
} }

View File

@ -78,9 +78,7 @@ android {
} }
dependencies { dependencies {
implementation project(":fints4k"), { implementation project(":fints4k")
exclude group: "com.soywiz.korlibs.klock", module: "klock-android" // klock-jvm already adds required extensions, to avoid duplicates
}
implementation project(':BankingUiCommon') implementation project(':BankingUiCommon')

View File

@ -4,9 +4,9 @@ import android.content.Context
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import net.dankito.utils.multiplatform.File
import net.dankito.banking.ui.android.RouterAndroid import net.dankito.banking.ui.android.RouterAndroid
import net.dankito.banking.ui.android.util.CurrentActivityTracker import net.dankito.banking.ui.android.util.CurrentActivityTracker
import net.dankito.banking.ui.android.util.Base64ServiceAndroid
import net.dankito.banking.fints4kBankingClientCreator import net.dankito.banking.fints4kBankingClientCreator
import net.dankito.banking.persistence.IBankingPersistence import net.dankito.banking.persistence.IBankingPersistence
import net.dankito.banking.persistence.LuceneBankingPersistence import net.dankito.banking.persistence.LuceneBankingPersistence
@ -17,6 +17,7 @@ import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.bankfinder.IBankFinder import net.dankito.banking.bankfinder.IBankFinder
import net.dankito.banking.bankfinder.LuceneBankFinder import net.dankito.banking.bankfinder.LuceneBankFinder
import net.dankito.utils.multiplatform.toFile
import net.dankito.banking.util.* import net.dankito.banking.util.*
import net.dankito.banking.util.extraction.IInvoiceDataExtractor import net.dankito.banking.util.extraction.IInvoiceDataExtractor
import net.dankito.banking.util.extraction.ITextExtractorRegistry import net.dankito.banking.util.extraction.ITextExtractorRegistry
@ -28,7 +29,6 @@ import net.dankito.text.extraction.pdf.iText2PdfTextExtractor
import net.dankito.utils.ThreadPool import net.dankito.utils.ThreadPool
import net.dankito.utils.web.client.IWebClient import net.dankito.utils.web.client.IWebClient
import net.dankito.utils.web.client.OkHttpWebClient import net.dankito.utils.web.client.OkHttpWebClient
import java.io.File
import javax.inject.Named import javax.inject.Named
import javax.inject.Singleton import javax.inject.Singleton
@ -59,7 +59,7 @@ class BankingModule(private val applicationContext: Context) {
@Singleton @Singleton
@Named(DataFolderKey) @Named(DataFolderKey)
fun provideDataFolder(applicationContext: Context) : File { fun provideDataFolder(applicationContext: Context) : File {
return ensureFolderExists(applicationContext.filesDir, "data") return ensureFolderExists(applicationContext.filesDir.toFile(), "data")
} }
@Provides @Provides
@ -93,7 +93,7 @@ class BankingModule(private val applicationContext: Context) {
textExtractorRegistry: ITextExtractorRegistry, router: IRouter, invoiceDataExtractor: IInvoiceDataExtractor, textExtractorRegistry: ITextExtractorRegistry, router: IRouter, invoiceDataExtractor: IInvoiceDataExtractor,
serializer: ISerializer, asyncRunner: IAsyncRunner) : BankingPresenter { serializer: ISerializer, asyncRunner: IAsyncRunner) : BankingPresenter {
return BankingPresenter(bankingClientCreator, bankFinder, dataFolder, persister, return BankingPresenter(bankingClientCreator, bankFinder, dataFolder, persister,
remitteeSearcher, bankIconFinder, textExtractorRegistry, router, invoiceDataExtractor, serializer, asyncRunner) router, remitteeSearcher, bankIconFinder, textExtractorRegistry, invoiceDataExtractor, serializer, asyncRunner)
} }
@Provides @Provides
@ -110,8 +110,8 @@ class BankingModule(private val applicationContext: Context) {
@Provides @Provides
@Singleton @Singleton
fun provideBankingClientCreator() : IBankingClientCreator { fun provideBankingClientCreator(serializer: ISerializer) : IBankingClientCreator {
return fints4kBankingClientCreator() return fints4kBankingClientCreator(serializer)
} }
@Provides @Provides

View File

@ -24,7 +24,6 @@ import net.dankito.banking.ui.android.extensions.closePopupOnBackButtonPress
import net.dankito.banking.ui.android.listener.ListItemSelectedListener import net.dankito.banking.ui.android.listener.ListItemSelectedListener
import net.dankito.banking.ui.android.util.StandardAutocompleteCallback import net.dankito.banking.ui.android.util.StandardAutocompleteCallback
import net.dankito.banking.ui.android.util.StandardTextWatcher import net.dankito.banking.ui.android.util.StandardTextWatcher
import net.dankito.banking.search.IRemitteeSearcher
import net.dankito.banking.search.Remittee import net.dankito.banking.search.Remittee
import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.parameters.TransferMoneyData
@ -32,6 +31,7 @@ import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.util.InputValidator import net.dankito.banking.util.InputValidator
import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.bankfinder.BankInfo
import net.dankito.utils.multiplatform.toBigDecimal
import net.dankito.utils.android.extensions.asActivity import net.dankito.utils.android.extensions.asActivity
import java.math.BigDecimal import java.math.BigDecimal
import java.text.DecimalFormatSymbols import java.text.DecimalFormatSymbols
@ -242,7 +242,7 @@ open class TransferMoneyDialog : DialogFragment() {
inputValidator.convertToAllowedSepaCharacters(edtxtRemitteeName.text.toString()), inputValidator.convertToAllowedSepaCharacters(edtxtRemitteeName.text.toString()),
edtxtRemitteeIban.text.toString().replace(" ", ""), edtxtRemitteeIban.text.toString().replace(" ", ""),
edtxtRemitteeBic.text.toString().replace(" ", ""), edtxtRemitteeBic.text.toString().replace(" ", ""),
amount, amount.toBigDecimal(),
inputValidator.convertToAllowedSepaCharacters(edtxtUsage.text.toString()), inputValidator.convertToAllowedSepaCharacters(edtxtUsage.text.toString()),
chkbxInstantPayment.isChecked chkbxInstantPayment.isChecked
) )

View File

@ -1,22 +0,0 @@
package net.dankito.banking.ui.android.util
import android.util.Base64
import net.dankito.banking.util.IBase64Service
import java.nio.charset.Charset
open class Base64ServiceAndroid : IBase64Service {
override fun encode(text: String, charset: Charset): String {
val bytes = text.toByteArray(charset)
return Base64.encodeToString(bytes, Base64.NO_WRAP)
}
override fun decode(base64: String, charset: Charset): String {
val decoded = Base64.decode(base64, Base64.NO_WRAP)
return String(decoded, charset)
}
}

View File

@ -7,6 +7,7 @@ import androidx.fragment.app.FragmentActivity
import com.github.clans.fab.FloatingActionButton import com.github.clans.fab.FloatingActionButton
import com.github.clans.fab.FloatingActionMenu import com.github.clans.fab.FloatingActionMenu
import kotlinx.android.synthetic.main.view_floating_action_button_main.view.* import kotlinx.android.synthetic.main.view_floating_action_button_main.view.*
import net.dankito.utils.multiplatform.toFile
import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType
@ -78,7 +79,7 @@ open class MainActivityFloatingActionMenuButton(
selectedFile?.let { selectedFile?.let {
lastSelectedFolder = selectedFile.parentFile lastSelectedFolder = selectedFile.parentFile
val result = presenter.transferMoneyWithDataFromPdf(selectedFile) val result = presenter.transferMoneyWithDataFromPdf(selectedFile.toFile())
if (result.type != ExtractTransferMoneyDataFromPdfResultType.Success) { if (result.type != ExtractTransferMoneyDataFromPdfResultType.Success) {
showTransferMoneyWithDataFromPdfError(activity, selectedFile, result) showTransferMoneyWithDataFromPdfError(activity, selectedFile, result)

View File

@ -1,17 +1,18 @@
package net.dankito.banking.ui.javafx.dialogs.mainwindow package net.dankito.banking.ui.javafx.dialogs.mainwindow
import javafx.scene.control.SplitPane import javafx.scene.control.SplitPane
import net.dankito.utils.multiplatform.File
import net.dankito.banking.fints4kBankingClientCreator import net.dankito.banking.fints4kBankingClientCreator
import net.dankito.banking.ui.javafx.RouterJavaFx import net.dankito.banking.ui.javafx.RouterJavaFx
import net.dankito.banking.ui.javafx.controls.AccountTransactionsView import net.dankito.banking.ui.javafx.controls.AccountTransactionsView
import net.dankito.banking.ui.javafx.controls.AccountsView import net.dankito.banking.ui.javafx.controls.AccountsView
import net.dankito.banking.ui.javafx.dialogs.mainwindow.controls.MainMenuBar import net.dankito.banking.ui.javafx.dialogs.mainwindow.controls.MainMenuBar
import net.dankito.banking.ui.javafx.util.Base64ServiceJava8
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.util.BankIconFinder import net.dankito.banking.util.BankIconFinder
import net.dankito.banking.bankfinder.LuceneBankFinder import net.dankito.banking.bankfinder.LuceneBankFinder
import net.dankito.banking.persistence.LuceneBankingPersistence import net.dankito.banking.persistence.LuceneBankingPersistence
import net.dankito.banking.search.LuceneRemitteeSearcher import net.dankito.banking.search.LuceneRemitteeSearcher
import net.dankito.banking.util.JacksonJsonSerializer
import net.dankito.banking.util.extraction.JavaTextExtractorRegistry import net.dankito.banking.util.extraction.JavaTextExtractorRegistry
import net.dankito.text.extraction.TextExtractorRegistry import net.dankito.text.extraction.TextExtractorRegistry
import net.dankito.text.extraction.TikaTextExtractor import net.dankito.text.extraction.TikaTextExtractor
@ -19,10 +20,8 @@ import net.dankito.text.extraction.image.Tesseract4CommandlineImageTextExtractor
import net.dankito.text.extraction.image.model.OcrLanguage import net.dankito.text.extraction.image.model.OcrLanguage
import net.dankito.text.extraction.image.model.TesseractConfig import net.dankito.text.extraction.image.model.TesseractConfig
import net.dankito.text.extraction.pdf.* import net.dankito.text.extraction.pdf.*
import net.dankito.utils.web.client.OkHttpWebClient
import tornadofx.* import tornadofx.*
import tornadofx.FX.Companion.messages import tornadofx.FX.Companion.messages
import java.io.File
class MainWindow : View(messages["application.title"]) { class MainWindow : View(messages["application.title"]) {
@ -33,6 +32,8 @@ class MainWindow : View(messages["application.title"]) {
private val indexFolder = ensureFolderExists(dataFolder, "index") private val indexFolder = ensureFolderExists(dataFolder, "index")
private val serializer = JacksonJsonSerializer()
private val tesseractTextExtractor = Tesseract4CommandlineImageTextExtractor(TesseractConfig(listOf(OcrLanguage.English, OcrLanguage.German))) private val tesseractTextExtractor = Tesseract4CommandlineImageTextExtractor(TesseractConfig(listOf(OcrLanguage.English, OcrLanguage.German)))
private val textExtractorRegistry = JavaTextExtractorRegistry(TextExtractorRegistry(pdffontsPdfTypeDetector(), listOf( private val textExtractorRegistry = JavaTextExtractorRegistry(TextExtractorRegistry(pdffontsPdfTypeDetector(), listOf(
@ -41,12 +42,12 @@ class MainWindow : View(messages["application.title"]) {
tesseractTextExtractor, TikaTextExtractor() tesseractTextExtractor, TikaTextExtractor()
))) )))
private val presenter = BankingPresenter(fints4kBankingClientCreator(), private val presenter = BankingPresenter(fints4kBankingClientCreator(serializer),
LuceneBankFinder(indexFolder), dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), LuceneBankFinder(indexFolder), dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder),
LuceneRemitteeSearcher(indexFolder), BankIconFinder(), textExtractorRegistry, RouterJavaFx()) RouterJavaFx(), LuceneRemitteeSearcher(indexFolder), BankIconFinder(), textExtractorRegistry)
// private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder), // private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder),
// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), LuceneRemitteeSearcher(indexFolder), // dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), RouterJavaFx(), LuceneRemitteeSearcher(indexFolder),
// BankIconFinder(), textExtractorRegistry, RouterJavaFx()) // BankIconFinder(), textExtractorRegistry)

View File

@ -5,6 +5,7 @@ import javafx.scene.input.KeyCode
import javafx.scene.input.KeyCodeCombination import javafx.scene.input.KeyCodeCombination
import javafx.scene.input.KeyCombination import javafx.scene.input.KeyCombination
import javafx.stage.FileChooser import javafx.stage.FileChooser
import net.dankito.utils.multiplatform.toFile
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
@ -79,7 +80,7 @@ open class MainMenuBar(protected val presenter: BankingPresenter) : View() {
fileChooser.showOpenDialog(currentStage)?.let { pdfFile -> fileChooser.showOpenDialog(currentStage)?.let { pdfFile ->
lastSelectedFolder = pdfFile.parentFile lastSelectedFolder = pdfFile.parentFile
val result = presenter.transferMoneyWithDataFromPdf(pdfFile) val result = presenter.transferMoneyWithDataFromPdf(pdfFile.toFile())
if (result.type != ExtractTransferMoneyDataFromPdfResultType.Success) { if (result.type != ExtractTransferMoneyDataFromPdfResultType.Success) {
showTransferMoneyWithDataFromPdfError(pdfFile, result) showTransferMoneyWithDataFromPdfError(pdfFile, result)

View File

@ -19,6 +19,7 @@ import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.util.InputValidator import net.dankito.banking.util.InputValidator
import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.bankfinder.BankInfo
import net.dankito.banking.search.Remittee import net.dankito.banking.search.Remittee
import net.dankito.utils.multiplatform.toBigDecimal
import net.dankito.banking.ui.javafx.extensions.focusNextControl import net.dankito.banking.ui.javafx.extensions.focusNextControl
import net.dankito.utils.javafx.ui.controls.AutoCompletionSearchTextField import net.dankito.utils.javafx.ui.controls.AutoCompletionSearchTextField
import net.dankito.utils.javafx.ui.controls.autocompletionsearchtextfield import net.dankito.utils.javafx.ui.controls.autocompletionsearchtextfield
@ -352,7 +353,7 @@ open class TransferMoneyDialog @JvmOverloads constructor(
inputValidator.convertToAllowedSepaCharacters(remitteeName.value), inputValidator.convertToAllowedSepaCharacters(remitteeName.value),
remitteeIban.value.replace(" ", ""), remitteeIban.value.replace(" ", ""),
remitteeBic.value.replace(" ", ""), remitteeBic.value.replace(" ", ""),
amount.value.toBigDecimal(), amount.value.toBigDecimal().toBigDecimal(),
inputValidator.convertToAllowedSepaCharacters(usage.value), inputValidator.convertToAllowedSepaCharacters(usage.value),
instantPayment.value instantPayment.value
) )

View File

@ -1,27 +1,62 @@
apply plugin: 'java-library' plugins {
apply plugin: 'kotlin' id "org.jetbrains.kotlin.multiplatform"
id "com.android.library"
id "maven-publish"
}
ext.artifactName = "banking-ui-common" ext.artifactName = "banking-ui-common"
sourceCompatibility = "1.7" kotlin {
targetCompatibility = "1.7" jvm {
compilations.main.kotlinOptions {
compileKotlin { jvmTarget = "1.6"
kotlinOptions.jvmTarget = "1.6" }
}
targets {
// Select iOS target for real device or emulator.
final def iOSIsRealDevice = System.getenv('SDK_NAME')?.startsWith("iphoneos")
final def iOSTarget = iOSIsRealDevice ? presets.iosArm64 : presets.iosX64
// iOS target.
fromPreset(iOSTarget, 'ios') {
binaries {
framework {
baseName = "BankingUiCommon"
}
}
} }
compileTestKotlin {
kotlinOptions.jvmTarget = "1.6"
} }
sourceSets {
commonMain {
dependencies { dependencies {
api "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" api kotlin("stdlib-common")
api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlinCoroutinesVersion"
api "net.dankito.utils:java-utils:$javaUtilsVersion" // TODO: try to get rid of this import
api project(":fints4k")
api project(":BankFinder") api project(":BankFinder")
}
}
commonTest {
dependencies {
implementation kotlin("test-common")
implementation kotlin("test-annotations-common")
implementation "ch.tutteli.atrium:atrium-fluent-en_GB:$atriumVersion"
}
}
jvmMain {
dependencies {
api "net.dankito.utils:java-utils:$javaUtilsVersion"
api "net.dankito.text.extraction:text-extractor-common:$textExtractorVersion" api "net.dankito.text.extraction:text-extractor-common:$textExtractorVersion"
api "net.dankito.text.extraction:text-info-extractor:$textInfoExtractorVersion" api "net.dankito.text.extraction:text-info-extractor:$textInfoExtractorVersion"
@ -29,13 +64,102 @@ dependencies {
implementation "net.dankito.utils:favicon-finder:1.0.0-SNAPSHOT" implementation "net.dankito.utils:favicon-finder:1.0.0-SNAPSHOT"
implementation "org.jsoup:jsoup:1.13.1" implementation "org.jsoup:jsoup:1.13.1"
}
// TODO: try to get rid of this import }
api project(':fints4k')
jvmTest {
dependencies {
testImplementation "junit:junit:$junitVersion" implementation kotlin("test-junit")
testImplementation "org.assertj:assertj-core:$assertJVersion"
implementation "junit:junit:$junitVersion"
testImplementation "org.slf4j:slf4j-simple:$slf4jVersion" // implementation "org.junit.jupiter:junit-jupiter:$junit5Version"
// runtimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit5Version"
implementation "org.assertj:assertj-core:$assertJVersion"
implementation "org.mockito:mockito-core:$mockitoVersion"
implementation "ch.tutteli.atrium:atrium-api-fluent-en_GB-jdk8:$atriumVersion"
implementation "org.slf4j:slf4j-simple:$slf4jVersion"
}
}
iosMain {
dependencies {
api "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion"
api "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$kotlinCoroutinesVersion"
}
}
}
}
// Task to generate iOS framework for xcode projects.
task packForXCode(type: Sync) {
final File frameworkDir = new File(buildDir, "xcode-frameworks")
final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'
final def framework = kotlin.targets.ios.binaries.getFramework("", mode)
inputs.property "mode", mode
dependsOn framework.linkTask
from { framework.outputFile.parentFile }
into frameworkDir
doLast {
new File(frameworkDir, 'gradlew').with {
text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$@\n"
setExecutable(true)
}
}
}
// Run packForXCode when building.
tasks.build.dependsOn packForXCode
android {
compileSdkVersion androidCompileSdkVersion
defaultConfig {
minSdkVersion androidMinSdkVersion
targetSdkVersion androidTargetSdkVersion
versionName version
versionCode appVersionCode
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
pickFirst 'META-INF/ktor-http.kotlin_module'
pickFirst 'META-INF/kotlinx-io.kotlin_module'
pickFirst 'META-INF/atomicfu.kotlin_module'
pickFirst 'META-INF/ktor-utils.kotlin_module'
pickFirst 'META-INF/kotlinx-coroutines-io.kotlin_module'
pickFirst 'META-INF/ktor-client-core.kotlin_module'
pickFirst 'META-INF/DEPENDENCIES'
pickFirst 'META-INF/NOTICE'
pickFirst 'META-INF/LICENSE'
pickFirst 'META-INF/LICENSE.txt'
pickFirst 'META-INF/NOTICE.txt'
}
lintOptions {
abortOnError false
}
} }

View File

@ -1,5 +1,6 @@
package net.dankito.banking.persistence package net.dankito.banking.persistence
import net.dankito.utils.multiplatform.File
import net.dankito.banking.ui.model.Customer import net.dankito.banking.ui.model.Customer
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.ui.model.BankAccount
@ -16,4 +17,6 @@ interface IBankingPersistence {
fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List<AccountTransaction>) fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List<AccountTransaction>)
fun saveUrlToFile(url: String, file: File)
} }

View File

@ -0,0 +1,33 @@
package net.dankito.banking.persistence
import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.Customer
import net.dankito.utils.multiplatform.File
open class NoOpBankingPersistence : IBankingPersistence {
override fun saveOrUpdateAccount(customer: Customer, allCustomers: List<Customer>) {
}
override fun deleteAccount(customer: Customer, allCustomers: List<Customer>) {
}
override fun readPersistedAccounts(): List<Customer> {
return listOf()
}
override fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List<AccountTransaction>) {
}
override fun saveUrlToFile(url: String, file: File) {
}
}

View File

@ -0,0 +1,10 @@
package net.dankito.banking.search
open class NoOpRemitteeSearcher : IRemitteeSearcher {
override fun findRemittees(query: String): List<Remittee> {
return listOf()
}
}

View File

@ -1,8 +1,8 @@
package net.dankito.banking.ui package net.dankito.banking.ui
import net.dankito.utils.multiplatform.File
import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.bankfinder.BankInfo
import net.dankito.banking.util.IAsyncRunner import net.dankito.banking.util.IAsyncRunner
import java.io.File
interface IBankingClientCreator { interface IBankingClientCreator {

View File

@ -1,14 +1,14 @@
package net.dankito.banking.ui.model package net.dankito.banking.ui.model
import com.fasterxml.jackson.annotation.JsonIdentityInfo //import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators //import com.fasterxml.jackson.annotation.ObjectIdGenerators
import java.math.BigDecimal import net.dankito.utils.multiplatform.BigDecimal
import java.text.DateFormat import net.dankito.utils.multiplatform.Date
import java.text.SimpleDateFormat import net.dankito.utils.multiplatform.DateFormatStyle
import java.util.* import net.dankito.utils.multiplatform.DateFormatter
@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references //@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references
open class AccountTransaction( open class AccountTransaction(
open val bankAccount: BankAccount, open val bankAccount: BankAccount,
open val amount: BigDecimal, open val amount: BigDecimal,
@ -50,7 +50,7 @@ open class AccountTransaction(
) { ) {
companion object { companion object {
val IdDateFormat = SimpleDateFormat("yyyy.MM.dd") val IdDateFormat = DateFormatter("yyyy.MM.dd")
} }
@ -62,7 +62,7 @@ open class AccountTransaction(
0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null) 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null)
// for object deserializers // for object deserializers
internal constructor() : this(BankAccount(), BigDecimal.ZERO, "","", Date(), null, null, null, null, Date(), 0, null, BigDecimal.ZERO, BigDecimal.ZERO, internal constructor() : this(BankAccount(), BigDecimal.Zero, "","", Date(), null, null, null, null, Date(), 0, null, BigDecimal.Zero, BigDecimal.Zero,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, "", "", null, null, "", null) null, "", "", null, null, "", null)
@ -79,7 +79,7 @@ open class AccountTransaction(
if (other !is AccountTransaction) return false if (other !is AccountTransaction) return false
if (bankAccount != other.bankAccount) return false if (bankAccount != other.bankAccount) return false
if (amount.compareTo(other.amount) != 0) return false if (amount != other) return false // TODO: does this work?
if (currency != other.currency) return false if (currency != other.currency) return false
if (unparsedUsage != other.unparsedUsage) return false if (unparsedUsage != other.unparsedUsage) return false
if (bookingDate != other.bookingDate) return false if (bookingDate != other.bookingDate) return false
@ -108,7 +108,7 @@ open class AccountTransaction(
override fun toString(): String { override fun toString(): String {
return "${DateFormat.getDateInstance(DateFormat.MEDIUM).format(valueDate)} $amount $otherPartyName: $usage" return "${DateFormatter(DateFormatStyle.Medium).format(valueDate)} $amount $otherPartyName: $usage"
} }
} }

View File

@ -1,12 +1,14 @@
package net.dankito.banking.ui.model package net.dankito.banking.ui.model
import com.fasterxml.jackson.annotation.JsonIdentityInfo //import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators //import com.fasterxml.jackson.annotation.ObjectIdGenerators
import java.math.BigDecimal import net.dankito.utils.multiplatform.BigDecimal
import java.util.* import net.dankito.utils.multiplatform.Date
import net.dankito.utils.multiplatform.UUID
import kotlin.jvm.JvmOverloads
@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references //@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references
open class BankAccount @JvmOverloads constructor( open class BankAccount @JvmOverloads constructor(
open val customer: Customer, open val customer: Customer,
open val identifier: String, open val identifier: String,
@ -14,7 +16,7 @@ open class BankAccount @JvmOverloads constructor(
open var iban: String?, open var iban: String?,
open var subAccountNumber: String?, open var subAccountNumber: String?,
open val customerId: String, open val customerId: String,
open var balance: BigDecimal = BigDecimal.ZERO, open var balance: BigDecimal = BigDecimal.Zero,
open var currency: String = "EUR", open var currency: String = "EUR",
open var type: BankAccountType = BankAccountType.Girokonto, open var type: BankAccountType = BankAccountType.Girokonto,
open val productName: String? = null, open val productName: String? = null,
@ -30,7 +32,7 @@ open class BankAccount @JvmOverloads constructor(
internal constructor() : this(Customer(), "", "", null, null, "") // for object deserializers internal constructor() : this(Customer(), "", "", null, null, "") // for object deserializers
open var id: String = UUID.randomUUID().toString() open var id: String = UUID.random()
open val displayName: String open val displayName: String

View File

@ -1,15 +1,16 @@
package net.dankito.banking.ui.model package net.dankito.banking.ui.model
import com.fasterxml.jackson.annotation.JsonIdentityInfo //import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators //import com.fasterxml.jackson.annotation.ObjectIdGenerators
import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.utils.multiplatform.UUID
import net.dankito.utils.multiplatform.sum
import net.dankito.banking.ui.model.tan.TanMedium import net.dankito.banking.ui.model.tan.TanMedium
import net.dankito.banking.ui.model.tan.TanMediumStatus import net.dankito.banking.ui.model.tan.TanMediumStatus
import net.dankito.banking.ui.model.tan.TanProcedure import net.dankito.banking.ui.model.tan.TanProcedure
import java.math.BigDecimal
import java.util.*
@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references //@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references
open class Customer( open class Customer(
val bankCode: String, val bankCode: String,
val customerId: String, val customerId: String,
@ -27,7 +28,7 @@ open class Customer(
internal constructor() : this("", "", "", "", "", "", "") // for object deserializers internal constructor() : this("", "", "", "", "", "", "") // for object deserializers
var id: String = UUID.randomUUID().toString() var id: String = UUID.random()
var supportedTanProcedures: List<TanProcedure> = listOf() var supportedTanProcedures: List<TanProcedure> = listOf()
@ -44,7 +45,7 @@ open class Customer(
get() = bankName get() = bankName
val balance: BigDecimal val balance: BigDecimal
get() = accounts.map { it.balance }.fold(BigDecimal.ZERO) { acc, e -> acc + e } get() = accounts.map { it.balance }.sum()
val transactions: List<AccountTransaction> val transactions: List<AccountTransaction>
get() = accounts.flatMap { it.bookedTransactions } get() = accounts.flatMap { it.bookedTransactions }

View File

@ -1,6 +1,6 @@
package net.dankito.banking.ui.model package net.dankito.banking.ui.model
import java.util.* import net.dankito.utils.multiplatform.Date
open class MessageLogEntry( open class MessageLogEntry(

View File

@ -1,7 +1,7 @@
package net.dankito.banking.ui.model.parameters package net.dankito.banking.ui.model.parameters
import net.dankito.utils.multiplatform.Date
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.AccountTransaction
import java.util.*
open class GetTransactionsParameter( open class GetTransactionsParameter(

View File

@ -1,7 +1,7 @@
package net.dankito.banking.ui.model.parameters package net.dankito.banking.ui.model.parameters
import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.AccountTransaction
import java.math.BigDecimal
open class TransferMoneyData( open class TransferMoneyData(
@ -20,7 +20,7 @@ open class TransferMoneyData(
transaction.otherPartyName ?: "", transaction.otherPartyName ?: "",
transaction.otherPartyAccountId ?: "", transaction.otherPartyAccountId ?: "",
transaction.otherPartyBankCode ?: "", transaction.otherPartyBankCode ?: "",
BigDecimal.ZERO, BigDecimal.Zero,
"" ""
) )
} }

View File

@ -1,9 +1,9 @@
package net.dankito.banking.ui.model.responses package net.dankito.banking.ui.model.responses
import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.banking.ui.model.Customer import net.dankito.banking.ui.model.Customer
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.ui.model.BankAccount
import java.math.BigDecimal
open class AddAccountResponse( open class AddAccountResponse(

View File

@ -1,8 +1,8 @@
package net.dankito.banking.ui.model.responses package net.dankito.banking.ui.model.responses
import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.ui.model.BankAccount
import java.math.BigDecimal
open class GetTransactionsResponse( open class GetTransactionsResponse(

View File

@ -17,6 +17,7 @@ import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
import net.dankito.banking.bankfinder.IBankFinder import net.dankito.banking.bankfinder.IBankFinder
import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.bankfinder.BankInfo
import net.dankito.banking.search.IRemitteeSearcher import net.dankito.banking.search.IRemitteeSearcher
import net.dankito.banking.search.NoOpRemitteeSearcher
import net.dankito.banking.search.Remittee import net.dankito.banking.search.Remittee
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType
@ -25,14 +26,10 @@ import net.dankito.banking.ui.model.settings.AppSettings
import net.dankito.banking.util.* import net.dankito.banking.util.*
import net.dankito.banking.util.extraction.IInvoiceDataExtractor import net.dankito.banking.util.extraction.IInvoiceDataExtractor
import net.dankito.banking.util.extraction.ITextExtractorRegistry import net.dankito.banking.util.extraction.ITextExtractorRegistry
import net.dankito.banking.util.extraction.JavaInvoiceDataExtractor import net.dankito.banking.util.extraction.NoOpInvoiceDataExtractor
import org.slf4j.LoggerFactory import net.dankito.banking.util.extraction.NoOpTextExtractorRegistry
import java.io.File import net.dankito.utils.multiplatform.*
import java.io.FileOutputStream import net.dankito.utils.multiplatform.log.LoggerFactory
import java.math.BigDecimal
import java.net.URL
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -41,21 +38,21 @@ open class BankingPresenter(
protected val bankFinder: IBankFinder, protected val bankFinder: IBankFinder,
protected val dataFolder: File, protected val dataFolder: File,
protected val persister: IBankingPersistence, protected val persister: IBankingPersistence,
protected val remitteeSearcher: IRemitteeSearcher,
protected val bankIconFinder: IBankIconFinder,
protected val textExtractorRegistry: ITextExtractorRegistry,
protected val router: IRouter, protected val router: IRouter,
protected val invoiceDataExtractor: IInvoiceDataExtractor = JavaInvoiceDataExtractor(), protected val remitteeSearcher: IRemitteeSearcher = NoOpRemitteeSearcher(),
protected val serializer: ISerializer = JacksonJsonSerializer(), protected val bankIconFinder: IBankIconFinder = NoOpBankIconFinder(),
protected val textExtractorRegistry: ITextExtractorRegistry = NoOpTextExtractorRegistry(),
protected val invoiceDataExtractor: IInvoiceDataExtractor = NoOpInvoiceDataExtractor(),
protected val serializer: ISerializer = NoOpSerializer(),
protected val asyncRunner: IAsyncRunner = CoroutinesAsyncRunner() protected val asyncRunner: IAsyncRunner = CoroutinesAsyncRunner()
) { ) {
companion object { companion object {
protected const val OneDayMillis = 24 * 60 * 60 * 1000 protected const val OneDayMillis = 24 * 60 * 60 * 1000
protected val MessageLogEntryDateFormat = SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS") protected val MessageLogEntryDateFormatter = DateFormatter("yyyy.MM.dd HH:mm:ss.SSS")
private val log = LoggerFactory.getLogger(BankingPresenter::class.java) private val log = LoggerFactory.getLogger(BankingPresenter::class)
} }
@ -127,7 +124,7 @@ open class BankingPresenter(
try { try {
newClient.restoreData() newClient.restoreData()
} catch (e: Exception) { } catch (e: Exception) {
log.error("Could not deserialize customer data of $customer", e) log.error(e) { "Could not deserialize customer data of $customer" }
// TODO: show error message to user // TODO: show error message to user
} }
@ -138,7 +135,7 @@ open class BankingPresenter(
selectedAllBankAccounts() // TODO: save last selected bank account(s) selectedAllBankAccounts() // TODO: save last selected bank account(s)
} catch (e: Exception) { } catch (e: Exception) {
log.error("Could not deserialize persisted accounts with persister $persister", e) log.error(e) { "Could not deserialize persisted accounts with persister $persister" }
} }
} }
@ -194,14 +191,14 @@ open class BankingPresenter(
bankIconFinder.findIconForBank(customer.bankName)?.let { bankIconUrl -> bankIconFinder.findIconForBank(customer.bankName)?.let { bankIconUrl ->
val bankIconFile = saveBankIconToDisk(customer, bankIconUrl) val bankIconFile = saveBankIconToDisk(customer, bankIconUrl)
customer.iconUrl = "file://" + bankIconFile.absolutePath // without 'file://' Android will not find it customer.iconUrl = "file://" + bankIconFile.getAbsolutePath() // without 'file://' Android will not find it
persistAccount(customer) persistAccount(customer)
callAccountsChangedListeners() callAccountsChangedListeners()
} }
} catch (e: Exception) { } catch (e: Exception) {
log.error("Could not get icon for bank ${customer.bankName}", e) log.error(e) { "Could not get icon for bank ${customer.bankName}" }
} }
} }
@ -212,28 +209,24 @@ open class BankingPresenter(
val extension = getIconFileExtension(bankIconUrl) val extension = getIconFileExtension(bankIconUrl)
val bankIconFile = File(bankIconsDir, customer.bankCode + if (extension != null) (".$extension") else "") val bankIconFile = File(bankIconsDir, customer.bankCode + if (extension != null) (".$extension") else "")
URL(bankIconUrl).openConnection().getInputStream().buffered().use { iconInputStream -> persister.saveUrlToFile(bankIconUrl, bankIconFile)
FileOutputStream(bankIconFile).use { fileOutputStream ->
iconInputStream.copyTo(fileOutputStream)
}
}
return bankIconFile return bankIconFile
} }
protected open fun getIconFileExtension(bankIconUrl: String): String? { protected open fun getIconFileExtension(bankIconUrl: String): String? {
try { try {
var iconFilename = File(bankIconUrl).name var iconFilename = File(bankIconUrl).filename
if (iconFilename.contains('?')) { if (iconFilename.contains('?')) {
iconFilename = iconFilename.substring(0, iconFilename.indexOf('?')) iconFilename = iconFilename.substring(0, iconFilename.indexOf('?'))
} }
val extension = File(iconFilename).extension val extension = File(iconFilename).fileExtension
if (extension.isNotBlank()) { if (extension.isNotBlank()) {
return extension return extension
} }
} catch (e: Exception) { } catch (e: Exception) {
log.info("Could not get icon file extension from url '$bankIconUrl'", e) log.info(e) { "Could not get icon file extension from url '$bankIconUrl'" }
} }
return null return null
@ -308,7 +301,7 @@ open class BankingPresenter(
} }
protected open fun updateBankAccountTransactionsAsync(bankAccount: BankAccount, abortIfTanIsRequired: Boolean, callback: (GetTransactionsResponse) -> Unit) { protected open fun updateBankAccountTransactionsAsync(bankAccount: BankAccount, abortIfTanIsRequired: Boolean, callback: (GetTransactionsResponse) -> Unit) {
val fromDate = bankAccount.lastRetrievedTransactionsTimestamp?.let { Date(it.time - OneDayMillis) } // one day before last received transactions val fromDate = bankAccount.lastRetrievedTransactionsTimestamp?.let { Date(it.millisSinceEpoch - OneDayMillis) } // one day before last received transactions
fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback) fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback)
} }
@ -348,7 +341,7 @@ open class BankingPresenter(
} }
open fun formatAmount(amount: BigDecimal): String { open fun formatAmount(amount: BigDecimal): String {
return String.format("%.02f", amount) return amount.format("%.02f")
} }
@ -391,7 +384,7 @@ open class BankingPresenter(
val transferMoneyData = TransferMoneyData("", val transferMoneyData = TransferMoneyData("",
invoiceData.potentialIban ?: "", invoiceData.potentialIban ?: "",
invoiceData.potentialBic ?: "", invoiceData.potentialBic ?: "",
invoiceData.potentialTotalAmount ?: BigDecimal.ZERO, "") invoiceData.potentialTotalAmount ?: BigDecimal.Zero, "")
showTransferMoneyDialog(null, transferMoneyData) showTransferMoneyDialog(null, transferMoneyData)
} }
else { else {
@ -481,7 +474,7 @@ open class BankingPresenter(
} }
return logEntries.map { entry -> return logEntries.map { entry ->
MessageLogEntryDateFormat.format(entry.time) + " " + entry.customer.bankCode + " " + entry.message MessageLogEntryDateFormatter.format(entry.time) + " " + entry.customer.bankCode + " " + entry.message
} }
} }
@ -608,15 +601,15 @@ open class BankingPresenter(
protected open fun getAccountTransactionsForBankAccounts(bankAccounts: Collection<BankAccount>): List<AccountTransaction> { protected open fun getAccountTransactionsForBankAccounts(bankAccounts: Collection<BankAccount>): List<AccountTransaction> {
return bankAccounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate } // TODO: someday add unbooked transactions return bankAccounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate.millisSinceEpoch } // TODO: someday add unbooked transactions
} }
protected open fun getBalanceForAccounts(customers: Collection<Customer>): BigDecimal { protected open fun getBalanceForAccounts(customers: Collection<Customer>): BigDecimal {
return customers.map { it.balance }.fold(BigDecimal.ZERO) { acc, e -> acc + e } return customers.map { it.balance }.sum()
} }
protected open fun sumBalance(singleBalances: Collection<BigDecimal>): BigDecimal { protected open fun sumBalance(singleBalances: Collection<BigDecimal>): BigDecimal {
return singleBalances.fold(BigDecimal.ZERO) { acc, e -> acc + e } return singleBalances.sum()
} }
@ -631,17 +624,17 @@ open class BankingPresenter(
try { try {
serializer.serializeObject(appSettings, getAppSettingsFile()) serializer.serializeObject(appSettings, getAppSettingsFile())
} catch (e: Exception) { } catch (e: Exception) {
log.error("Could not persist AppSettings to file ${getAppSettingsFile()}", e) log.error(e) { "Could not persist AppSettings to file ${getAppSettingsFile()}" }
} }
} }
protected open fun readAppSettings() { protected open fun readAppSettings() {
try { try {
serializer.deserializeObject(getAppSettingsFile(), AppSettings::class.java)?.let { serializer.deserializeObject(getAppSettingsFile(), AppSettings::class)?.let {
appSettings = it appSettings = it
} }
} catch (e: Exception) { } catch (e: Exception) {
log.error("Could not read AppSettings from file ${getAppSettingsFile()}", e) log.error(e) { "Could not read AppSettings from file ${getAppSettingsFile()}" }
} }
} }

View File

@ -0,0 +1,95 @@
package net.dankito.banking.ui.util
import net.dankito.banking.ui.model.tan.FlickerCode
import net.dankito.banking.fints.tan.Bit
import net.dankito.banking.fints.tan.FlickerCanvas
import net.dankito.utils.multiplatform.log.LoggerFactory
import kotlin.jvm.JvmOverloads
import kotlin.jvm.Volatile
open class FlickerCodeAnimator {
companion object {
const val MinFrequency = 2
const val MaxFrequency = 40
const val DefaultFrequency = 20
private val log = LoggerFactory.getLogger(FlickerCodeAnimator::class)
}
protected var currentFrequency: Int = DefaultFrequency
protected var currentStepIndex = 0
@Volatile
protected var isPaused = false
// 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.parsedDataSet).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) {
// if (isPaused == 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 pause() {
this.isPaused = true
}
open fun resume() {
this.isPaused = false
}
open fun stop() {
try {
// if (calculateAnimationThread?.isInterrupted == false) {
// calculateAnimationThread?.interrupt()
// calculateAnimationThread?.join(500)
//
// calculateAnimationThread = null
// }
} catch (e: Exception) {
log.warn(e) { "Could not stop calculateAnimationThread" }
}
}
open fun setFrequency(frequency: Int) {
if (frequency in MinFrequency..MaxFrequency) {
currentFrequency = frequency
}
}
}

View File

@ -1,14 +1,13 @@
package net.dankito.banking.util package net.dankito.banking.util
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
open class CoroutinesAsyncRunner : IAsyncRunner { open class CoroutinesAsyncRunner : IAsyncRunner { // TODO: remove (coroutines in common)
override fun runAsync(runnable: () -> Unit) { override fun runAsync(runnable: () -> Unit) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch {
runnable() runnable()
} }
} }

View File

@ -0,0 +1,16 @@
package net.dankito.banking.util
import net.dankito.utils.multiplatform.File
import kotlin.reflect.KClass
interface ISerializer {
fun serializeObject(obj: Any, outputFile: File)
fun <T : Any> deserializeObject(serializedObjectFile: File, objectClass: KClass<T>, vararg genericParameterTypes: KClass<*>): T?
fun <T : Any> deserializeListOr(serializedObjectFile: File, genericListParameterType: KClass<T>,
defaultValue: List<T> = listOf()) : List<T>
}

View File

@ -2,7 +2,6 @@ package net.dankito.banking.util
import net.dankito.banking.fints.messages.segmente.implementierte.sepa.ISepaMessageCreator import net.dankito.banking.fints.messages.segmente.implementierte.sepa.ISepaMessageCreator
import net.dankito.banking.fints.messages.segmente.implementierte.sepa.SepaMessageCreator import net.dankito.banking.fints.messages.segmente.implementierte.sepa.SepaMessageCreator
import java.util.regex.Pattern
open class InputValidator { open class InputValidator {
@ -22,7 +21,7 @@ open class InputValidator {
* (https://en.wikipedia.org/wiki/International_Bank_Account_Number#Structure) * (https://en.wikipedia.org/wiki/International_Bank_Account_Number#Structure)
*/ */
const val IbanPatternString = "[A-Z]{2}\\d{2}[A-Z0-9]{10,30}" const val IbanPatternString = "[A-Z]{2}\\d{2}[A-Z0-9]{10,30}"
val IbanPattern = Pattern.compile("^" + IbanPatternString + "\$") val IbanPattern = Regex("^" + IbanPatternString + "\$")
/** /**
* The IBAN should not contain spaces when transmitted electronically. When printed it is expressed in groups * The IBAN should not contain spaces when transmitted electronically. When printed it is expressed in groups
@ -30,9 +29,9 @@ open class InputValidator {
* (https://en.wikipedia.org/wiki/International_Bank_Account_Number#Structure) * (https://en.wikipedia.org/wiki/International_Bank_Account_Number#Structure)
*/ */
const val IbanWithSpacesPatternString = "[A-Z]{2}\\d{2}\\s([A-Z0-9]{4}\\s){3}[A-Z0-9\\s]{1,18}" const val IbanWithSpacesPatternString = "[A-Z]{2}\\d{2}\\s([A-Z0-9]{4}\\s){3}[A-Z0-9\\s]{1,18}"
val IbanWithSpacesPattern = Pattern.compile("^" + IbanWithSpacesPatternString + "\$") val IbanWithSpacesPattern = Regex("^" + IbanWithSpacesPatternString + "\$")
val InvalidIbanCharactersPattern = Pattern.compile("[^A-Z0-9 ]") val InvalidIbanCharactersPattern = Regex("[^A-Z0-9 ]")
/** /**
@ -47,12 +46,12 @@ open class InputValidator {
* Where an eight digit code is given, it may be assumed that it refers to the primary office. * Where an eight digit code is given, it may be assumed that it refers to the primary office.
*/ */
const val BicPatternString = "[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:\\b|[A-Z0-9]{03})" const val BicPatternString = "[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:\\b|[A-Z0-9]{03})"
val BicPattern = Pattern.compile("^" + BicPatternString + "$") val BicPattern = Regex("^" + BicPatternString + "$")
val InvalidBicCharactersPattern = Pattern.compile("[^A-Z0-9]") val InvalidBicCharactersPattern = Regex("[^A-Z0-9]")
val InvalidSepaCharactersPattern = Pattern.compile("[^${SepaMessageCreator.AllowedSepaCharacters}]+") val InvalidSepaCharactersPattern = Regex("[^${SepaMessageCreator.AllowedSepaCharacters}]+")
} }
@ -60,7 +59,7 @@ open class InputValidator {
open fun isValidIban(stringToTest: String): Boolean { open fun isValidIban(stringToTest: String): Boolean {
return IbanPattern.matcher(stringToTest.replace(" ", "")).matches() return IbanPattern.matches(stringToTest.replace(" ", ""))
} }
open fun getInvalidIbanCharacters(string: String): String { open fun getInvalidIbanCharacters(string: String): String {
@ -69,7 +68,7 @@ open class InputValidator {
open fun isValidBic(stringToTest: String): Boolean { open fun isValidBic(stringToTest: String): Boolean {
return BicPattern.matcher(stringToTest).matches() return BicPattern.matches(stringToTest)
} }
open fun getInvalidBicCharacters(string: String): String { open fun getInvalidBicCharacters(string: String): String {
@ -114,16 +113,8 @@ open class InputValidator {
} }
open fun getInvalidCharacters(string: String, pattern: Pattern): String { open fun getInvalidCharacters(string: String, pattern: Regex): String {
val illegalCharacters = mutableSetOf<String>() return pattern.findAll(string).joinToString("")
val matcher = pattern.matcher(string)
while (matcher.find()) {
illegalCharacters.add(matcher.group())
}
return illegalCharacters.joinToString("")
} }
} }

View File

@ -0,0 +1,21 @@
package net.dankito.banking.util
import net.dankito.utils.multiplatform.File
import kotlin.reflect.KClass
open class NoOpSerializer : ISerializer {
override fun serializeObject(obj: Any, outputFile: File) {
}
override fun <T : Any> deserializeObject(serializedObjectFile: File, objectClass: KClass<T>, vararg genericParameterTypes: KClass<*>): T? {
return null
}
override fun <T : Any> deserializeListOr(serializedObjectFile: File, genericListParameterType: KClass<T>, defaultValue: List<T>): List<T> {
return defaultValue
}
}

View File

@ -1,7 +1,5 @@
package net.dankito.banking.util.extraction package net.dankito.banking.util.extraction
import java.lang.Exception
open class ExtractionResult( open class ExtractionResult(
open val couldExtractText: Boolean, open val couldExtractText: Boolean,

View File

@ -1,6 +1,6 @@
package net.dankito.banking.util.extraction package net.dankito.banking.util.extraction
import java.io.File import net.dankito.utils.multiplatform.File
interface ITextExtractorRegistry { interface ITextExtractorRegistry {

View File

@ -1,6 +1,6 @@
package net.dankito.banking.util.extraction package net.dankito.banking.util.extraction
import java.math.BigDecimal import net.dankito.utils.multiplatform.BigDecimal
open class InvoiceData( open class InvoiceData(

View File

@ -1,6 +1,6 @@
package net.dankito.banking.util.extraction package net.dankito.banking.util.extraction
import java.io.File import net.dankito.utils.multiplatform.File
open class NoOpTextExtractorRegistry : ITextExtractorRegistry { open class NoOpTextExtractorRegistry : ITextExtractorRegistry {

View File

@ -0,0 +1,24 @@
package net.dankito.banking.util
import net.dankito.utils.multiplatform.File
import kotlin.reflect.KClass
open class JacksonJsonSerializer(
protected val serializer: net.dankito.utils.serialization.ISerializer = net.dankito.utils.serialization.JacksonJsonSerializer()
) : ISerializer {
override fun serializeObject(obj: Any, outputFile: File) {
return serializer.serializeObject(obj, outputFile)
}
override fun <T : Any> deserializeObject(serializedObjectFile: File, objectClass: KClass<T>,
vararg genericParameterTypes: KClass<*>): T? {
return serializer.deserializeObject(serializedObjectFile, objectClass.java, *genericParameterTypes.map { it.java }.toTypedArray())
}
override fun <T : Any> deserializeListOr(serializedObjectFile: File, genericListParameterType: KClass<T>, defaultValue: List<T>): List<T> {
return serializer.deserializeListOr(serializedObjectFile, genericListParameterType.java, defaultValue)
}
}

View File

@ -1,5 +1,6 @@
package net.dankito.banking.util.extraction package net.dankito.banking.util.extraction
import net.dankito.utils.multiplatform.toBigDecimal
import net.dankito.text.extraction.info.invoice.InvoiceDataExtractor import net.dankito.text.extraction.info.invoice.InvoiceDataExtractor

View File

@ -1,8 +1,8 @@
package net.dankito.banking.util.extraction package net.dankito.banking.util.extraction
import net.dankito.utils.multiplatform.File
import net.dankito.text.extraction.TextExtractorRegistry import net.dankito.text.extraction.TextExtractorRegistry
import net.dankito.text.extraction.model.ErrorType import net.dankito.text.extraction.model.ErrorType
import java.io.File
open class JavaTextExtractorRegistry( open class JavaTextExtractorRegistry(

View File

@ -1,6 +1,5 @@
package net.dankito.banking.util package net.dankito.banking.util
import net.dankito.banking.bankfinder.InMemoryBankFinder
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.dankito.banking.ui">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@ -1,94 +0,0 @@
package net.dankito.banking.ui.util
import net.dankito.banking.ui.model.tan.FlickerCode
import net.dankito.banking.fints.tan.Bit
import net.dankito.banking.fints.tan.FlickerCanvas
import org.slf4j.LoggerFactory
import java.util.concurrent.TimeUnit
open class FlickerCodeAnimator { // TODO: move to fints44
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
@Volatile
protected var isPaused = false
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.parsedDataSet).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) {
if (isPaused == 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 pause() {
this.isPaused = true
}
open fun resume() {
this.isPaused = false
}
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

@ -1,15 +0,0 @@
package net.dankito.banking.util
import java.io.File
interface ISerializer {
fun serializeObject(obj: Any, outputFile: File)
fun <T> deserializeObject(serializedObjectFile: File, objectClass: Class<T>, vararg genericParameterTypes: Class<*>): T?
fun <T> deserializeListOr(serializedObjectFile: File, genericListParameterType: Class<T>,
defaultValue: List<T> = listOf()) : List<T>
}

View File

@ -1,24 +0,0 @@
package net.dankito.banking.util
import net.dankito.utils.serialization.JacksonJsonSerializer
import java.io.File
open class JacksonJsonSerializer(
protected val serializer: net.dankito.utils.serialization.ISerializer = JacksonJsonSerializer()
) : ISerializer {
override fun serializeObject(obj: Any, outputFile: File) {
return serializer.serializeObject(obj, outputFile)
}
override fun <T> deserializeObject(serializedObjectFile: File, objectClass: Class<T>,
vararg genericParameterTypes: Class<*>): T? {
return serializer.deserializeObject(serializedObjectFile, objectClass, *genericParameterTypes)
}
override fun <T> deserializeListOr(serializedObjectFile: File, genericListParameterType: Class<T>, defaultValue: List<T>): List<T> {
return serializer.deserializeListOr(serializedObjectFile, genericListParameterType, defaultValue)
}
}

View File

@ -1,20 +0,0 @@
package net.dankito.banking.util
import java.io.File
open class NoOpSerializer : ISerializer {
override fun serializeObject(obj: Any, outputFile: File) {
}
override fun <T> deserializeObject(serializedObjectFile: File, objectClass: Class<T>, vararg genericParameterTypes: Class<*>): T? {
return null
}
override fun <T> deserializeListOr(serializedObjectFile: File, genericListParameterType: Class<T>, defaultValue: List<T>): List<T> {
return defaultValue
}
}

View File

@ -34,24 +34,22 @@ kotlin {
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
implementation kotlin("stdlib-common") api project(":BankingUiCommon")
api project(":fints4k")
implementation project(":BankingUiCommon")
implementation project(":fints4k")
} }
} }
jvmMain { jvmMain {
dependencies { dependencies {
api kotlin("stdlib-jdk7")
} }
} }
iosMain { iosMain {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion"
} }
} }