diff --git a/build.gradle b/build.gradle
index 839be3dd..be65aec7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -58,6 +58,8 @@ ext {
multiDexVersion = "2.0.1"
+ androidXCoreVersion = "1.3.1"
+
appCompatVersion = "1.1.0"
constraintLayoutVersion = "1.1.3"
@@ -66,6 +68,8 @@ ext {
daggerVersion = "2.27"
+ roomVersion = "2.2.5"
+
/* JavaFX */
diff --git a/persistence/database/RoomBankingPersistence/build.gradle b/persistence/database/RoomBankingPersistence/build.gradle
new file mode 100644
index 00000000..7e904f27
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/build.gradle
@@ -0,0 +1,37 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+
+android {
+ compileSdkVersion androidCompileSdkVersion
+ buildToolsVersion androidBuildToolsVersion
+
+ defaultConfig {
+
+ minSdkVersion androidMinSdkVersion
+ targetSdkVersion androidTargetSdkVersion
+
+ versionName version
+ versionCode appVersionCode
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation project(':BankingUiCommon')
+
+ implementation "androidx.room:room-runtime:$roomVersion"
+ kapt "androidx.room:room-compiler:$roomVersion"
+ implementation "androidx.room:room-ktx:$roomVersion"
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/consumer-rules.pro b/persistence/database/RoomBankingPersistence/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/persistence/database/RoomBankingPersistence/proguard-rules.pro b/persistence/database/RoomBankingPersistence/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/AndroidManifest.xml b/persistence/database/RoomBankingPersistence/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..622eda74
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+ /
+
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/BankingDatabase.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/BankingDatabase.kt
new file mode 100644
index 00000000..0ce032c6
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/BankingDatabase.kt
@@ -0,0 +1,25 @@
+package net.dankito.banking.persistence
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
+import net.dankito.banking.persistence.dao.*
+import net.dankito.banking.persistence.model.*
+
+
+@Database(entities = [ Bank::class, BankAccount::class, AccountTransaction::class, TanProcedure::class, TanMedium::class ],
+ version = 1, exportSchema = false)
+@TypeConverters(net.dankito.banking.persistence.TypeConverters::class)
+abstract class BankingDatabase : RoomDatabase() {
+
+ abstract fun bankDao(): BankDao
+
+ abstract fun bankAccountDao(): BankAccountDao
+
+ abstract fun accountTransactionDao(): AccountTransactionDao
+
+ abstract fun tanProcedureDao(): TanProcedureDao
+
+ abstract fun tanMediumDao(): TanMediumDao
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/RoomBankingPersistence.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/RoomBankingPersistence.kt
new file mode 100644
index 00000000..78cd1523
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/RoomBankingPersistence.kt
@@ -0,0 +1,134 @@
+package net.dankito.banking.persistence
+
+import android.content.Context
+import androidx.room.Room
+import net.dankito.banking.persistence.dao.saveOrUpdate
+import net.dankito.banking.persistence.model.*
+import net.dankito.banking.ui.model.IAccountTransaction
+import net.dankito.banking.ui.model.TypedBankAccount
+import net.dankito.banking.ui.model.TypedCustomer
+import net.dankito.banking.ui.model.tan.MobilePhoneTanMedium
+import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
+import net.dankito.banking.util.persistence.doSaveUrlToFile
+import net.dankito.utils.multiplatform.File
+
+
+open class RoomBankingPersistence(applicationContext: Context) : IBankingPersistence {
+
+ protected val db = Room.databaseBuilder(
+ applicationContext,
+ BankingDatabase::class.java, "banking-database"
+ ).build()
+
+
+ override fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List) {
+ (customer as? Bank)?.let { bank ->
+ bank.selectedTanProcedureId = bank.selectedTanProcedure?.technicalId
+
+ db.bankDao().saveOrUpdate(bank)
+
+ // TODO: in this way removed accounts won't be deleted from DB and therefore still be visible to user
+ val accounts = bank.accounts.filterIsInstance()
+ accounts.forEach { it.bankId = bank.id }
+ db.bankAccountDao().saveOrUpdate(accounts)
+
+ // TODO: in this way removed TAN procedures won't be deleted from DB and therefore still be visible to user
+ val tanProcedures = bank.supportedTanProcedures.filterIsInstance()
+ tanProcedures.forEach { it.bankId = bank.id }
+ db.tanProcedureDao().saveOrUpdate(tanProcedures)
+
+ // TODO: in this way removed TAN procedures won't be deleted from DB and therefore still be visible to user
+ val tanMedia = bank.tanMedia.map { map(bank, it) }
+ db.tanMediumDao().saveOrUpdate(tanMedia)
+ }
+ }
+
+ override fun deleteAccount(customer: TypedCustomer, allCustomers: List) {
+ (customer as? Bank)?.let { bank ->
+ db.accountTransactionDao().delete(bank.accounts.flatMap { it.bookedTransactions }.filterIsInstance())
+
+ db.bankAccountDao().delete(bank.accounts.filterIsInstance())
+
+ db.tanProcedureDao().delete(bank.supportedTanProcedures.filterIsInstance())
+ db.tanMediumDao().delete(bank.tanMedia.filterIsInstance())
+
+ db.bankDao().delete(bank)
+ }
+ }
+
+ override fun readPersistedAccounts(): List {
+ val banks = db.bankDao().getAll()
+
+ val accounts = db.bankAccountDao().getAll()
+
+ val transactions = db.accountTransactionDao().getAll()
+
+ val tanProcedures = db.tanProcedureDao().getAll()
+
+ val tanMedia = db.tanMediumDao().getAll()
+
+ banks.forEach { bank ->
+ bank.accounts = accounts.filter { it.bankId == bank.id }
+
+ bank.accounts.filterIsInstance().forEach { account ->
+ account.customer = bank
+
+ account.bookedTransactions = transactions.filter { it.bankAccountId == account.id }
+
+ account.bookedTransactions.filterIsInstance().forEach { transaction ->
+ transaction.bankAccount = account
+ }
+ }
+
+ bank.supportedTanProcedures = tanProcedures.filter { it.bankId == bank.id }
+ bank.selectedTanProcedure = bank.supportedTanProcedures.firstOrNull { it.technicalId == bank.selectedTanProcedureId }
+
+ bank.tanMedia = tanMedia.filter { it.bankId == bank.id }.map { map(it) }
+ }
+
+ return banks
+ }
+
+ override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List) {
+ val accountId = (bankAccount as? BankAccount)?.id ?: bankAccount.technicalId.toLong()
+
+ val mappedTransactions = transactions.filterIsInstance()
+
+ mappedTransactions.forEach { it.bankAccountId = accountId }
+
+ db.accountTransactionDao().saveOrUpdate(mappedTransactions)
+ }
+
+
+ protected open fun map(bank: Bank, tanMedium: net.dankito.banking.ui.model.tan.TanMedium): TanMedium {
+ val type = when (tanMedium) {
+ is TanGeneratorTanMedium -> TanMediumType.TanGeneratorTanMedium
+ is MobilePhoneTanMedium -> TanMediumType.MobilePhoneTanMedium
+ else -> TanMediumType.OtherTanMedium
+ }
+
+ return TanMedium(tanMedium.technicalId, bank.id, type, tanMedium.displayName, tanMedium.status,
+ (tanMedium as? TanGeneratorTanMedium)?.cardNumber, (tanMedium as? MobilePhoneTanMedium)?.phoneNumber)
+ }
+
+ protected open fun map(tanMedium: TanMedium): net.dankito.banking.ui.model.tan.TanMedium {
+ val displayName = tanMedium.displayName
+ val status = tanMedium.status
+
+ val mapped = when (tanMedium.type) {
+ TanMediumType.TanGeneratorTanMedium -> TanGeneratorTanMedium(displayName, status, tanMedium.cardNumber ?: "")
+ TanMediumType.MobilePhoneTanMedium -> MobilePhoneTanMedium(displayName, status, tanMedium.phoneNumber)
+ else -> net.dankito.banking.ui.model.tan.TanMedium(displayName, status)
+ }
+
+ mapped.technicalId = tanMedium.id
+
+ return mapped
+ }
+
+
+ override fun saveUrlToFile(url: String, file: File) {
+ doSaveUrlToFile(url, file)
+ }
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/TypeConverters.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/TypeConverters.kt
new file mode 100644
index 00000000..8536262c
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/TypeConverters.kt
@@ -0,0 +1,91 @@
+package net.dankito.banking.persistence
+
+import androidx.room.TypeConverter
+import net.dankito.banking.persistence.model.TanMediumType
+import net.dankito.banking.ui.model.BankAccountType
+import net.dankito.banking.ui.model.tan.AllowedTanFormat
+import net.dankito.banking.ui.model.tan.TanMediumStatus
+import net.dankito.banking.ui.model.tan.TanProcedureType
+import net.dankito.utils.multiplatform.BigDecimal
+import net.dankito.utils.multiplatform.Date
+
+
+open class TypeConverters {
+
+ @TypeConverter
+ fun fromMultiplatformBigDecimal(value: BigDecimal?): String? {
+ return value?.toPlainString()
+ }
+
+ @TypeConverter
+ fun toMultiplatformBigDecimal(value: String?): BigDecimal? {
+ return value?.let { BigDecimal(value) }
+ }
+
+
+ @TypeConverter
+ fun fromMultiplatformDate(value: Date?): Long? {
+ return value?.millisSinceEpoch
+ }
+
+ @TypeConverter
+ fun toMultiplatformDate(value: Long?): Date? {
+ return value?.let { Date(value) }
+ }
+
+
+ @TypeConverter
+ fun fromBankAccountType(value: BankAccountType): Int {
+ return value.ordinal
+ }
+
+ @TypeConverter
+ fun toBankAccountType(value: Int): BankAccountType {
+ return BankAccountType.values().first { it.ordinal == value }
+ }
+
+
+ @TypeConverter
+ fun fromTanProcedureType(value: TanProcedureType): Int {
+ return value.ordinal
+ }
+
+ @TypeConverter
+ fun toTanProcedureType(value: Int): TanProcedureType {
+ return TanProcedureType.values().first { it.ordinal == value }
+ }
+
+
+ @TypeConverter
+ fun fromAllowedTanFormat(value: AllowedTanFormat): Int {
+ return value.ordinal
+ }
+
+ @TypeConverter
+ fun toAllowedTanFormat(value: Int): AllowedTanFormat {
+ return AllowedTanFormat.values().first { it.ordinal == value }
+ }
+
+
+ @TypeConverter
+ fun fromTanMediumStatus(value: TanMediumStatus): Int {
+ return value.ordinal
+ }
+
+ @TypeConverter
+ fun toTanMediumStatus(value: Int): TanMediumStatus {
+ return TanMediumStatus.values().first { it.ordinal == value }
+ }
+
+
+ @TypeConverter
+ fun fromTanMediumTypes(value: TanMediumType): Int {
+ return value.ordinal
+ }
+
+ @TypeConverter
+ fun toTanMediumType(value: Int): TanMediumType {
+ return TanMediumType.values().first { it.ordinal == value }
+ }
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/AccountTransactionDao.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/AccountTransactionDao.kt
new file mode 100644
index 00000000..3aeab1b8
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/AccountTransactionDao.kt
@@ -0,0 +1,14 @@
+package net.dankito.banking.persistence.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import net.dankito.banking.persistence.model.AccountTransaction
+
+
+@Dao
+interface AccountTransactionDao : BaseDao {
+
+ @Query("SELECT * FROM AccountTransaction")
+ fun getAll(): List
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BankAccountDao.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BankAccountDao.kt
new file mode 100644
index 00000000..3a4e2af6
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BankAccountDao.kt
@@ -0,0 +1,14 @@
+package net.dankito.banking.persistence.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import net.dankito.banking.persistence.model.BankAccount
+
+
+@Dao
+interface BankAccountDao : BaseDao {
+
+ @Query("SELECT * FROM BankAccount")
+ fun getAll(): List
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BankDao.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BankDao.kt
new file mode 100644
index 00000000..ed23ede9
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BankDao.kt
@@ -0,0 +1,13 @@
+package net.dankito.banking.persistence.dao
+
+import androidx.room.*
+import net.dankito.banking.persistence.model.Bank
+
+
+@Dao
+interface BankDao : BaseDao {
+
+ @Query("SELECT * FROM Bank")
+ fun getAll(): List
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BaseDao.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BaseDao.kt
new file mode 100644
index 00000000..794362cb
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BaseDao.kt
@@ -0,0 +1,35 @@
+package net.dankito.banking.persistence.dao
+
+import androidx.room.*
+
+
+interface BaseDao {
+
+ companion object {
+ const val ObjectNotInsertedId = -1L
+
+ const val IdNotSet = 0L
+ }
+
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ fun insert(obj: T): Long
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ fun insert(obj: List): List
+
+
+ @Update(onConflict = OnConflictStrategy.IGNORE)
+ fun update(obj: T)
+
+ @Update(onConflict = OnConflictStrategy.IGNORE)
+ fun update(obj: List)
+
+
+ @Delete
+ fun delete(obj: T)
+
+ @Delete
+ fun delete(obj: List)
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BaseDaoExtensions.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BaseDaoExtensions.kt
new file mode 100644
index 00000000..28dc3c97
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BaseDaoExtensions.kt
@@ -0,0 +1,58 @@
+package net.dankito.banking.persistence.dao
+
+import androidx.room.Transaction
+import net.dankito.banking.persistence.model.*
+
+
+/* Room didn't allow me to add these methods to BaseDao directly (Kapt error), so i defined them as extension methods */
+
+@Transaction
+fun BaseDao.saveOrUpdate(obj: T) {
+ val id = insert(obj)
+
+ if (wasNotInserted(id)) {
+ update(obj)
+ }
+ else {
+ setId(obj, id)
+ }
+}
+
+@Transaction
+fun BaseDao.saveOrUpdate(objList: List) {
+ val ids = insert(objList)
+
+ // i was not allowed to use mapIndexedNotNull()
+ val notInsertedObjects = mutableListOf()
+ ids.forEachIndexed { index, id ->
+ val obj = objList[index]
+
+ if (wasNotInserted(id)) {
+ notInsertedObjects.add(obj)
+ }
+ else {
+ setId(obj, id)
+ }
+ }
+
+ update(notInsertedObjects)
+}
+
+private fun wasNotInserted(id: Long): Boolean {
+ return id == BaseDao.ObjectNotInsertedId
+}
+
+private fun setId(obj: T, id: Long) {
+ if (obj is Bank) {
+ obj.id = id // why doesn't Room set this on itself?
+ obj.technicalId = obj.id.toString()
+ }
+ else if (obj is BankAccount) {
+ obj.id = id // why doesn't Room set this on itself?
+ obj.technicalId = obj.id.toString()
+ }
+ else if (obj is AccountTransaction) {
+ obj.id = id // why doesn't Room set this on itself?
+ obj.technicalId = obj.id.toString()
+ }
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanMediumDao.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanMediumDao.kt
new file mode 100644
index 00000000..676c48b0
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanMediumDao.kt
@@ -0,0 +1,14 @@
+package net.dankito.banking.persistence.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import net.dankito.banking.persistence.model.TanMedium
+
+
+@Dao
+interface TanMediumDao : BaseDao {
+
+ @Query("SELECT * FROM TanMedium")
+ fun getAll(): List
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanProcedureDao.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanProcedureDao.kt
new file mode 100644
index 00000000..9152264e
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanProcedureDao.kt
@@ -0,0 +1,14 @@
+package net.dankito.banking.persistence.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import net.dankito.banking.persistence.model.TanProcedure
+
+
+@Dao
+interface TanProcedureDao : BaseDao {
+
+ @Query("SELECT * FROM TanProcedure")
+ fun getAll(): List
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/AccountTransaction.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/AccountTransaction.kt
new file mode 100644
index 00000000..3006a714
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/AccountTransaction.kt
@@ -0,0 +1,94 @@
+package net.dankito.banking.persistence.model
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import net.dankito.banking.persistence.dao.BaseDao
+import net.dankito.banking.ui.model.IAccountTransaction
+import net.dankito.utils.multiplatform.*
+
+
+@Entity
+open class AccountTransaction(
+ @Ignore
+ override var bankAccount: BankAccount,
+
+ override var amount: BigDecimal,
+ override var currency: String,
+ override var unparsedUsage: String,
+ override var bookingDate: Date,
+ override var otherPartyName: String?,
+ override var otherPartyBankCode: String?,
+ override var otherPartyAccountId: String?,
+ override var bookingText: String?,
+ override var valueDate: Date,
+ override var statementNumber: Int,
+ override var sequenceNumber: Int?,
+ override var openingBalance: BigDecimal?,
+ override var closingBalance: BigDecimal?,
+
+ override var endToEndReference: String?,
+ override var customerReference: String?,
+ override var mandateReference: String?,
+ override var creditorIdentifier: String?,
+ override var originatorsIdentificationCode: String?,
+ override var compensationAmount: String?,
+ override var originalAmount: String?,
+ override var sepaUsage: String?,
+ override var deviantOriginator: String?,
+ override var deviantRecipient: String?,
+ override var usageWithNoSpecialType: String?,
+ override var primaNotaNumber: String?,
+ override var textKeySupplement: String?,
+
+ override var currencyType: String?,
+ override var bookingKey: String,
+ override var referenceForTheAccountOwner: String,
+ override var referenceOfTheAccountServicingInstitution: String?,
+ override var supplementaryDetails: String?,
+
+ override var transactionReferenceNumber: String,
+ override var relatedReferenceNumber: String?
+) : IAccountTransaction {
+
+ // for object deserializers
+ internal constructor() : this(BankAccount(), null, "", BigDecimal.Zero, Date(), null)
+
+ /* convenience constructors for languages not supporting default values */
+
+ constructor(bankAccount: BankAccount, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
+ : this(bankAccount, amount, "EUR", unparsedUsage, valueDate,
+ otherPartyName, null, null, bookingText, valueDate)
+
+
+ constructor(bankAccount: BankAccount, amount: BigDecimal, currency: String, unparsedUsage: String, bookingDate: Date,
+ otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?,
+ bookingText: String?, valueDate: Date)
+ : this(bankAccount, amount, currency, unparsedUsage, bookingDate,
+ otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate,
+ 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null)
+
+
+ @PrimaryKey(autoGenerate = true)
+ open var id: Long = BaseDao.IdNotSet
+
+ override var technicalId: String = buildTransactionIdentifier()
+
+ // Room doesn't allow me to add getters and setters -> have to map it manually
+ open var bankAccountId: Long = BaseDao.ObjectNotInsertedId
+
+
+ override fun equals(other: Any?): Boolean {
+ return doesEqual(other)
+ }
+
+ override fun hashCode(): Int {
+ return calculateHashCode()
+ }
+
+
+ override fun toString(): String {
+ return stringRepresentation
+ }
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/Bank.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/Bank.kt
new file mode 100644
index 00000000..471020a0
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/Bank.kt
@@ -0,0 +1,55 @@
+package net.dankito.banking.persistence.model
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import net.dankito.banking.persistence.dao.BaseDao
+import net.dankito.banking.ui.model.TypedBankAccount
+import net.dankito.banking.ui.model.TypedCustomer
+import net.dankito.banking.ui.model.tan.TanMedium
+import net.dankito.banking.ui.model.tan.TanProcedure
+
+
+@Entity
+open class Bank(
+ override var bankCode: String,
+ override var customerId: String,
+ override var password: String,
+ override var finTsServerAddress: String,
+ override var bankName: String,
+ override var bic: String,
+ override var customerName: String,
+
+ override var userId: String = customerId,
+ override var iconUrl: String? = null,
+
+ @Ignore
+ override var accounts: List = listOf(),
+
+ @Ignore
+ override var supportedTanProcedures: List = listOf(),
+ @Ignore
+ override var selectedTanProcedure: TanProcedure? = null,
+ @Ignore
+ override var tanMedia: List = listOf(),
+
+ @PrimaryKey(autoGenerate = true)
+ open var id: Long = BaseDao.IdNotSet,
+
+ override var technicalId: String = id.toString(),
+
+ override var userSetDisplayName: String? = null,
+ override var displayIndex: Int = 0
+) : TypedCustomer {
+
+ internal constructor() : this("", "", "", "", "", "", "") // for object deserializers
+
+
+ open var selectedTanProcedureId: String? = null
+
+
+ override fun toString(): String {
+ return stringRepresentation
+ }
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/BankAccount.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/BankAccount.kt
new file mode 100644
index 00000000..41cf56a6
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/BankAccount.kt
@@ -0,0 +1,71 @@
+package net.dankito.banking.persistence.model
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import net.dankito.banking.persistence.dao.BaseDao
+import net.dankito.banking.ui.model.*
+import net.dankito.utils.multiplatform.BigDecimal
+import net.dankito.utils.multiplatform.Date
+import net.dankito.utils.multiplatform.UUID
+
+
+@Entity
+open class BankAccount(
+ @Ignore
+ override var customer: TypedCustomer,
+ override var identifier: String,
+ override var accountHolderName: String,
+ override var iban: String?,
+ override var subAccountNumber: String?,
+ override var customerId: String,
+ override var balance: BigDecimal = BigDecimal.Zero,
+ override var currency: String = "EUR",
+ override var type: BankAccountType = BankAccountType.Girokonto,
+ override var productName: String? = null,
+ override var accountLimit: String? = null,
+ override var lastRetrievedTransactionsTimestamp: Date? = null,
+
+ override var supportsRetrievingAccountTransactions: Boolean = false,
+ override var supportsRetrievingBalance: Boolean = false,
+ override var supportsTransferringMoney: Boolean = false,
+ override var supportsInstantPaymentMoneyTransfer: Boolean = false,
+
+ @Ignore
+ override var bookedTransactions: List = listOf(),
+ @Ignore
+ override var unbookedTransactions: List = listOf()
+) : TypedBankAccount {
+
+ internal constructor() : this(Bank(), null, "") // for object deserializers
+
+ /* convenience constructors for languages not supporting default values */
+
+ constructor(customer: TypedCustomer, productName: String?, identifier: String) : this(customer, productName, identifier, BankAccountType.Girokonto)
+
+ constructor(customer: TypedCustomer, productName: String?, identifier: String, type: BankAccountType = BankAccountType.Girokonto, balance: BigDecimal = BigDecimal.Zero)
+ : this(customer, identifier, "", null, null, "", balance, "EUR", type, productName)
+
+
+ @PrimaryKey(autoGenerate = true)
+ open var id: Long = BaseDao.IdNotSet
+
+ override var technicalId: String = UUID.random()
+
+ // Room doesn't allow me to add getters and setters -> have to map it manually
+ open var bankId: Long = BaseDao.ObjectNotInsertedId
+
+
+ override var haveAllTransactionsBeenFetched: Boolean = false
+
+
+ override var userSetDisplayName: String? = null
+
+ override var displayIndex: Int = 0
+
+
+ override fun toString(): String {
+ return stringRepresentation
+ }
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/RoomModelCreator.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/RoomModelCreator.kt
new file mode 100644
index 00000000..db8579dc
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/RoomModelCreator.kt
@@ -0,0 +1,73 @@
+package net.dankito.banking.persistence.model
+
+import net.dankito.banking.ui.model.IAccountTransaction
+import net.dankito.banking.ui.model.TypedBankAccount
+import net.dankito.banking.ui.model.TypedCustomer
+import net.dankito.banking.ui.model.mapper.IModelCreator
+import net.dankito.banking.ui.model.tan.AllowedTanFormat
+import net.dankito.banking.ui.model.tan.TanProcedureType
+import net.dankito.utils.multiplatform.BigDecimal
+import net.dankito.utils.multiplatform.Date
+
+
+open class RoomModelCreator : IModelCreator {
+
+ override fun createCustomer(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String,
+ bic: String, customerName: String, userId: String, iconUrl: String?): TypedCustomer {
+
+ return Bank(bankCode, customerId, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl)
+ }
+
+ override fun createBankAccount(customer: TypedCustomer, productName: String?, identifier: String): TypedBankAccount {
+ return BankAccount(customer, productName, identifier)
+ }
+
+ override fun createTransaction(
+ bankAccount: TypedBankAccount,
+ amount: BigDecimal,
+ currency: String,
+ unparsedUsage: String,
+ bookingDate: Date,
+ otherPartyName: String?,
+ otherPartyBankCode: String?,
+ otherPartyAccountId: String?,
+ bookingText: String?,
+ valueDate: Date,
+ statementNumber: Int,
+ sequenceNumber: Int?,
+ openingBalance: BigDecimal?,
+ closingBalance: BigDecimal?,
+ endToEndReference: String?,
+ customerReference: String?,
+ mandateReference: String?,
+ creditorIdentifier: String?,
+ originatorsIdentificationCode: String?,
+ compensationAmount: String?,
+ originalAmount: String?,
+ sepaUsage: String?,
+ deviantOriginator: String?,
+ deviantRecipient: String?,
+ usageWithNoSpecialType: String?,
+ primaNotaNumber: String?,
+ textKeySupplement: String?,
+ currencyType: String?,
+ bookingKey: String,
+ referenceForTheAccountOwner: String,
+ referenceOfTheAccountServicingInstitution: String?,
+ supplementaryDetails: String?,
+ transactionReferenceNumber: String,
+ relatedReferenceNumber: String?
+ ): IAccountTransaction {
+ return AccountTransaction(bankAccount as BankAccount, amount, currency, unparsedUsage, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId,
+ bookingText, valueDate, statementNumber, sequenceNumber, openingBalance, closingBalance, endToEndReference, customerReference, mandateReference,
+ creditorIdentifier, originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient,
+ usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
+ referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber)
+ }
+
+
+ override fun createTanProcedure(displayName: String, type: TanProcedureType, bankInternalProcedureCode: String, maxTanInputLength: Int?, allowedTanFormat: AllowedTanFormat): net.dankito.banking.ui.model.tan.TanProcedure {
+ return TanProcedure(displayName, type, bankInternalProcedureCode, maxTanInputLength, allowedTanFormat)
+ }
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMedium.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMedium.kt
new file mode 100644
index 00000000..9be6843b
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMedium.kt
@@ -0,0 +1,24 @@
+package net.dankito.banking.persistence.model
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import net.dankito.banking.persistence.dao.BaseDao
+import net.dankito.banking.ui.model.tan.TanMediumStatus
+
+
+@Entity
+open class TanMedium(
+ @PrimaryKey
+ open var id: String,
+ open var bankId: Long,
+
+ open var type: TanMediumType,
+ open var displayName: String,
+ open var status: TanMediumStatus,
+ open var cardNumber: String? = null,
+ open var phoneNumber: String? = null
+) {
+
+ internal constructor() : this("", BaseDao.ObjectNotInsertedId, TanMediumType.OtherTanMedium, "", TanMediumStatus.Available) // for object deserializers
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMediumType.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMediumType.kt
new file mode 100644
index 00000000..719273a4
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMediumType.kt
@@ -0,0 +1,12 @@
+package net.dankito.banking.persistence.model
+
+
+enum class TanMediumType {
+
+ TanGeneratorTanMedium,
+
+ MobilePhoneTanMedium,
+
+ OtherTanMedium
+
+}
\ No newline at end of file
diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanProcedure.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanProcedure.kt
new file mode 100644
index 00000000..7a6a0424
--- /dev/null
+++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanProcedure.kt
@@ -0,0 +1,29 @@
+package net.dankito.banking.persistence.model
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import net.dankito.banking.persistence.dao.BaseDao
+import net.dankito.banking.ui.model.tan.AllowedTanFormat
+import net.dankito.banking.ui.model.tan.TanProcedure
+import net.dankito.banking.ui.model.tan.TanProcedureType
+
+
+@Entity
+open class TanProcedure(
+ displayName: String,
+ type: TanProcedureType,
+ bankInternalProcedureCode: String,
+ maxTanInputLength: Int? = null,
+ allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric
+) : TanProcedure(displayName, type, bankInternalProcedureCode, maxTanInputLength, allowedTanFormat) {
+
+ internal constructor() : this("", TanProcedureType.EnterTan, "") // for object deserializers
+
+
+ @PrimaryKey
+ open var id: String = technicalId
+
+ // Room doesn't allow me to add getters and setters -> have to map it manually
+ open var bankId: Long = BaseDao.ObjectNotInsertedId
+
+}
\ No newline at end of file
diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt
index 32769af6..eb9bed57 100644
--- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt
+++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt
@@ -4,8 +4,7 @@ import net.dankito.banking.persistence.model.CustomerEntity
import net.dankito.banking.ui.model.*
import net.dankito.utils.multiplatform.File
import net.dankito.banking.util.ISerializer
-import java.io.FileOutputStream
-import java.net.URL
+import net.dankito.banking.util.persistence.doSaveUrlToFile
open class BankingPersistenceJson(
@@ -43,11 +42,7 @@ open class BankingPersistenceJson(
override fun saveUrlToFile(url: String, file: File) {
- URL(url).openConnection().getInputStream().buffered().use { iconInputStream ->
- FileOutputStream(file).use { fileOutputStream ->
- iconInputStream.copyTo(fileOutputStream)
- }
- }
+ doSaveUrlToFile(url, file)
}
}
\ No newline at end of file
diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt
index 058cc61e..0b0d55b8 100644
--- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt
+++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt
@@ -70,4 +70,8 @@ open class AccountTransactionEntity(
return calculateHashCode()
}
+ override fun toString(): String {
+ return stringRepresentation
+ }
+
}
\ No newline at end of file
diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt
index 56d24334..6ad2e3bd 100644
--- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt
+++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt
@@ -38,4 +38,9 @@ open class BankAccountEntity(
internal constructor() : this(CustomerEntity(), "", "", null, null, "") // for object deserializers
+
+ override fun toString(): String {
+ return stringRepresentation
+ }
+
}
\ No newline at end of file
diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt
index 9aa46cc8..2bda1e34 100644
--- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt
+++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt
@@ -30,4 +30,9 @@ open class CustomerEntity(
internal constructor() : this("", "", "", "", "", "", "") // for object deserializers
+
+ override fun toString(): String {
+ return stringRepresentation
+ }
+
}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 12207d17..c88874f0 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -18,6 +18,7 @@ include ':hbci4jBankingClient'
include ':BankingPersistenceJson'
include ':LuceneBankingPersistence'
+include ':RoomBankingPersistence'
include ':BankingAndroidApp'
@@ -41,6 +42,7 @@ project(':BankingUiNativeIntegration').projectDir = "$rootDir/ui/BankingUiNative
project(':BankingPersistenceJson').projectDir = "$rootDir/persistence/json/BankingPersistenceJson/" as File
project(':LuceneBankingPersistence').projectDir = "$rootDir/persistence/LuceneBankingPersistence/" as File
+project(':RoomBankingPersistence').projectDir = "$rootDir/persistence/database/RoomBankingPersistence/" as File
diff --git a/ui/BankingAndroidApp/build.gradle b/ui/BankingAndroidApp/build.gradle
index 29f517d5..280f05ce 100644
--- a/ui/BankingAndroidApp/build.gradle
+++ b/ui/BankingAndroidApp/build.gradle
@@ -91,6 +91,7 @@ dependencies {
implementation project(':BankingPersistenceJson')
implementation project(':LuceneBankingPersistence')
+ implementation project(':RoomBankingPersistence')
implementation "net.dankito.text.extraction:itext2-text-extractor:$textExtractorVersion"
diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt
index 5468c0a8..68bf6a36 100644
--- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt
+++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt
@@ -9,7 +9,6 @@ import net.dankito.banking.ui.android.RouterAndroid
import net.dankito.banking.ui.android.util.CurrentActivityTracker
import net.dankito.banking.fints4kBankingClientCreator
import net.dankito.banking.persistence.IBankingPersistence
-import net.dankito.banking.persistence.LuceneBankingPersistence
import net.dankito.banking.search.IRemitteeSearcher
import net.dankito.banking.search.LuceneRemitteeSearcher
import net.dankito.banking.ui.IBankingClientCreator
@@ -17,7 +16,8 @@ import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.bankfinder.IBankFinder
import net.dankito.banking.bankfinder.LuceneBankFinder
-import net.dankito.banking.persistence.mapper.EntitiesModelCreator
+import net.dankito.banking.persistence.RoomBankingPersistence
+import net.dankito.banking.persistence.model.RoomModelCreator
import net.dankito.banking.ui.model.mapper.IModelCreator
import net.dankito.utils.multiplatform.toFile
import net.dankito.banking.util.*
@@ -115,8 +115,8 @@ class BankingModule(private val applicationContext: Context) {
@Provides
@Singleton
- fun provideBankingPersistence(@Named(IndexFolderKey) indexFolder: File, @Named(DatabaseFolderKey) databaseFolder: File, serializer: ISerializer) : IBankingPersistence {
- return LuceneBankingPersistence(indexFolder, databaseFolder, serializer)
+ fun provideBankingPersistence() : IBankingPersistence {
+ return RoomBankingPersistence(applicationContext)
}
@Provides
@@ -169,7 +169,7 @@ class BankingModule(private val applicationContext: Context) {
@Provides
@Singleton
fun provideModelCreator() : IModelCreator {
- return EntitiesModelCreator()
+ return RoomModelCreator()
}
@Provides
diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt
index 9fb41cc0..1b188117 100644
--- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt
+++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt
@@ -77,7 +77,7 @@ open class AccountTransaction(
override fun toString(): String {
- return "${DateFormatter(DateFormatStyle.Medium).format(valueDate)} $amount $otherPartyName: $usage"
+ return stringRepresentation
}
}
\ No newline at end of file
diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt
index 3b3df0a0..27db5f39 100644
--- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt
+++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt
@@ -49,7 +49,7 @@ open class BankAccount @JvmOverloads constructor(
override fun toString(): String {
- return "$accountHolderName ($identifier)"
+ return stringRepresentation
}
}
\ No newline at end of file
diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt
index bf71546f..a6441b9d 100644
--- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt
+++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt
@@ -43,7 +43,7 @@ open class Customer(
override fun toString(): String {
- return "$bankName $customerId"
+ return stringRepresentation
}
}
\ No newline at end of file
diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IAccountTransaction.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IAccountTransaction.kt
index a2594e52..daf92551 100644
--- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IAccountTransaction.kt
+++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IAccountTransaction.kt
@@ -2,6 +2,7 @@ package net.dankito.banking.ui.model
import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.utils.multiplatform.Date
+import net.dankito.utils.multiplatform.DateFormatStyle
import net.dankito.utils.multiplatform.DateFormatter
@@ -106,5 +107,8 @@ interface IAccountTransaction {
result = 31 * result + valueDate.hashCode()
return result
}
+
+ val stringRepresentation: String
+ get() = "${DateFormatter(DateFormatStyle.Medium).format(valueDate)} $amount $otherPartyName: $usage"
}
\ No newline at end of file
diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankAccount.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankAccount.kt
index d1ffa9f4..63ab391b 100644
--- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankAccount.kt
+++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankAccount.kt
@@ -52,5 +52,9 @@ interface IBankAccount : OrderedDisplayable {
this.unbookedTransactions = uniqueUnbookedTransactions.toList()
}
+
+
+ val stringRepresentation: String
+ get() = "$accountHolderName ($identifier)"
}
\ No newline at end of file
diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/ICustomer.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/ICustomer.kt
index 29ed0a76..475d9c6f 100644
--- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/ICustomer.kt
+++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/ICustomer.kt
@@ -53,4 +53,8 @@ interface ICustomer, TAccountTransac
val tanMediaSorted: List
get() = tanMedia.sortedByDescending { it.status == TanMediumStatus.Used }
+
+ val stringRepresentation: String
+ get() = "$bankName $customerId"
+
}
\ No newline at end of file
diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt
index 3de107cb..ffe9e37d 100644
--- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt
+++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt
@@ -1,6 +1,7 @@
package net.dankito.banking.ui.model.mapper
import net.dankito.banking.ui.model.*
+import net.dankito.banking.ui.model.tan.*
import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.utils.multiplatform.Date
@@ -54,4 +55,22 @@ interface IModelCreator {
relatedReferenceNumber: String?
) : IAccountTransaction
+
+ fun createTanProcedure(displayName: String, type: TanProcedureType, bankInternalProcedureCode: String,
+ maxTanInputLength: Int? = null, allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric): TanProcedure {
+ return TanProcedure(displayName, type, bankInternalProcedureCode, maxTanInputLength)
+ }
+
+ fun createTanMedium(displayName: String, status: TanMediumStatus): TanMedium {
+ return TanMedium(displayName, status)
+ }
+
+ fun createTanGeneratorTanMedium(displayName: String, status: TanMediumStatus, cardNumber: String): TanGeneratorTanMedium {
+ return TanGeneratorTanMedium(displayName, status, cardNumber)
+ }
+
+ fun createMobilePhoneTanMedium(displayName: String, status: TanMediumStatus, phoneNumber: String?): MobilePhoneTanMedium {
+ return MobilePhoneTanMedium(displayName, status, phoneNumber)
+ }
+
}
\ No newline at end of file
diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanMedium.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanMedium.kt
index ee0c8c01..9225f6e7 100644
--- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanMedium.kt
+++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanMedium.kt
@@ -6,7 +6,7 @@ import net.dankito.utils.multiplatform.UUID
open class TanMedium(
override val displayName: String,
- val status: TanMediumStatus
+ open val status: TanMediumStatus
) : Displayable {
diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanProcedure.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanProcedure.kt
index bcd27265..326cac21 100644
--- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanProcedure.kt
+++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanProcedure.kt
@@ -2,21 +2,23 @@ package net.dankito.banking.ui.model.tan
import net.dankito.banking.ui.model.Displayable
import net.dankito.utils.multiplatform.UUID
+import kotlin.jvm.Transient
open class TanProcedure(
override val displayName: String,
- val type: TanProcedureType,
- val bankInternalProcedureCode: String,
- val maxTanInputLength: Int? = null,
- val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric
+ open val type: TanProcedureType,
+ open val bankInternalProcedureCode: String,
+ open val maxTanInputLength: Int? = null,
+ open val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric
) : Displayable {
internal constructor() : this("", TanProcedureType.EnterTan, "") // for object deserializers
- val isNumericTan: Boolean = allowedTanFormat == AllowedTanFormat.Numeric
+ @Transient
+ open val isNumericTan: Boolean = allowedTanFormat == AllowedTanFormat.Numeric
open var technicalId: String = UUID.random()
diff --git a/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/IBankingPersistenceExtensions.kt b/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/IBankingPersistenceExtensions.kt
new file mode 100644
index 00000000..2529c6d2
--- /dev/null
+++ b/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/IBankingPersistenceExtensions.kt
@@ -0,0 +1,15 @@
+package net.dankito.banking.util.persistence
+
+import net.dankito.banking.persistence.IBankingPersistence
+import net.dankito.utils.multiplatform.File
+import java.io.FileOutputStream
+import java.net.URL
+
+
+fun IBankingPersistence.doSaveUrlToFile(url: String, file: File) {
+ URL(url).openConnection().getInputStream().buffered().use { iconInputStream ->
+ FileOutputStream(file).use { fileOutputStream ->
+ iconInputStream.copyTo(fileOutputStream)
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt
index 75cec669..d8017c59 100644
--- a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt
+++ b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt
@@ -253,7 +253,7 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
account.selectedTanProcedure = null
}
- account.tanMedia = mapTanMediums(bank.tanMedia)
+ account.tanMedia = mapTanMedia(bank.tanMedia)
}
@@ -262,7 +262,7 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
open fun mapTanProcedure(tanProcedure: net.dankito.banking.fints.model.TanProcedure): TanProcedure {
- return TanProcedure(
+ return modelCreator.createTanProcedure(
tanProcedure.displayName,
mapTanProcedureType(tanProcedure.type),
tanProcedure.securityFunction.code,
@@ -306,40 +306,34 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
- open fun mapTanMediums(tanMediums: List): List {
+ open fun mapTanMedia(tanMediums: List): List {
return tanMediums.map { mapTanMedium(it) }
}
open fun mapTanMedium(tanMedium: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium): TanMedium {
+ val displayName = getDisplayNameForTanMedium(tanMedium)
+ val status = mapTanMediumStatus(tanMedium)
+
// TODO: irgendwas ging hier schief
if (tanMedium is net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium) {
- return mapTanMedium(tanMedium)
+ return mapTanMedium(tanMedium, displayName, status)
}
if (tanMedium is net.dankito.banking.fints.messages.datenelemente.implementierte.tan.MobilePhoneTanMedium) {
- return mapTanMedium(tanMedium)
+ return modelCreator.createMobilePhoneTanMedium(displayName, status, tanMedium.phoneNumber ?: tanMedium.concealedPhoneNumber)
}
- return TanMedium(
- getDisplayNameForTanMedium(tanMedium),
- mapTanMediumStatus(tanMedium)
- )
+ return modelCreator.createTanMedium(displayName, status)
}
open fun mapTanMedium(tanMedium: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium): TanGeneratorTanMedium {
- return TanGeneratorTanMedium(
- getDisplayNameForTanMedium(tanMedium),
- mapTanMediumStatus(tanMedium),
- tanMedium.cardNumber
- )
+ return mapTanMedium(tanMedium, getDisplayNameForTanMedium(tanMedium), mapTanMediumStatus(tanMedium))
}
- open fun mapTanMedium(tanMedium: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.MobilePhoneTanMedium): MobilePhoneTanMedium {
- return MobilePhoneTanMedium(
- getDisplayNameForTanMedium(tanMedium),
- mapTanMediumStatus(tanMedium),
- tanMedium.phoneNumber ?: tanMedium.concealedPhoneNumber
- )
+ protected open fun mapTanMedium(tanMedium: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium,
+ displayName: String, status: TanMediumStatus): TanGeneratorTanMedium {
+
+ return modelCreator.createTanGeneratorTanMedium(displayName, status, tanMedium.cardNumber)
}
protected open fun getDisplayNameForTanMedium(tanMedium: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium): String {
diff --git a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt
index 314177aa..65548fa8 100644
--- a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt
+++ b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt
@@ -106,15 +106,15 @@ open class hbci4jModelMapper(
// TODO: implement all TAN procedures
displayNameLowerCase.contains("chiptan") -> {
if (displayNameLowerCase.contains("qr")) {
- net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.ChipTanQrCode, code)
+ modelCreator.createTanProcedure(displayName, TanProcedureType.ChipTanQrCode, code)
}
else {
- net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.ChipTanFlickercode, code)
+ modelCreator.createTanProcedure(displayName, TanProcedureType.ChipTanFlickercode, code)
}
}
- displayNameLowerCase.contains("sms") -> net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.SmsTan, code)
- displayNameLowerCase.contains("push") -> net.dankito.banking.ui.model.tan.TanProcedure(displayName, TanProcedureType.AppTan, code)
+ displayNameLowerCase.contains("sms") -> modelCreator.createTanProcedure(displayName, TanProcedureType.SmsTan, code)
+ displayNameLowerCase.contains("push") -> modelCreator.createTanProcedure(displayName, TanProcedureType.AppTan, code)
// we filter out iTAN and Einschritt-Verfahren as they are not permitted anymore according to PSD2
else -> null