diff --git a/build.gradle b/build.gradle index 0a418aa8..3892b7e0 100644 --- a/build.gradle +++ b/build.gradle @@ -2,99 +2,12 @@ ext { appVersionName = '1.0.0-Alpha-10' - appVersionCode = 10 - /* MPP / basic dependencies */ - kotlinVersion = '1.4.21' - kotlinCoroutinesVersion = "1.3.7" - - - ktorVersion = "1.4.2" - javaUtilsVersion = '1.0.18' - luceneUtilsVersion = "0.6.0" - - - textExtractorVersion = "0.6.0" - - textInfoExtractorVersion = "1.0.1" - - commonsCsvVersion = "1.8" - - - hbci4jVersion = '3.1.37' - - - /* iOS */ - - iOSIsRealDevice = false - embedBitcodeValue = "marker" // Use "marker" to embed the bitcode marker (for debug builds) -// embedBitcodeValue = "bitcode" // for release binaries - - - /* Java */ - - faviconFinderVersion = "1.0.0" - - - /* Android */ - - androidCompileSdkVersion = 30 - - androidBuildToolsVersion = "30.0.3" - - androidMinSdkVersion = 21 // TODO: fix SSLv3 / TLS and set back to 16 - - androidTargetSdkVersion = 30 - - - fileChooserDialogVersion = "1.3.1-androidx" - - androidUtilsVersion = '1.1.2' - - fastAdapterVersion = "5.2.4" - - materialDrawerVersion = "8.1.6" - - clansFloatingActionButtonVersion = '1.6.4' - - autocompleteVersion = "1.1.0" - - zxingVersion = "3.3.0" - - scytaleVersion = "1.0.1" - - multiDexVersion = "2.0.1" - - appCompatVersion = "1.1.0" - - androidXCoreVersion = "1.3.1" - - androidXNavigationVersion = "2.3.0" - - androidXBiometricVersion = "1.0.1" - - constraintLayoutVersion = "1.1.3" - - materialComponentsVersion = "1.1.0" - - daggerVersion = "2.27" - - roomVersion = "2.2.5" - - sqlCipherVersion = "4.4.0" - - bcryptVersion = "0.9.0" - - - /* JavaFX */ - - javaFxUtilsVersion = '1.0.9' - /* Test */ @@ -113,17 +26,13 @@ ext { } buildscript { - ext.kotlin_version = '1.3.72' -// ext.kotlin_version = '1.4.10' - repositories { + mavenCentral() google() - jcenter() } + dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" -// classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" // Nexus staging plugin has to be downgraded to 0.10.0 to be applicable to sub projects, see https://github.com/UweTrottmann/SeriesGuide/commit/ca33e8ad2fa6cc5c426450c8aef3417ba073ca7f classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.10.0" @@ -142,10 +51,9 @@ allprojects { mavenLocal() mavenCentral() google() - jcenter() } - group 'net.dankito.banking' + group 'net.codinux.banking' version appVersionName } @@ -154,26 +62,6 @@ allprojects { task jarAll { dependsOn = [ "common:jvmJar", - "fints4k:jvm6Jar", - "fints4k-jvm:jar", - "BankFinder:jvmJar", - "EpcQrCodeParser:jvmJar", - "BankingUiCommon:jvmJar", - "fints4kBankingClient:jvmJar", - "BankingUiCommon:jvmJar", - "BankingJavaFxControls:jar", - "BankingJavaFxApp:jar" - ] -} - -task packAllForXcode { - dependsOn = [ - "common:packForXcode", - "fints4k:packForXcode", - "BankFinder:packForXcode", - "EpcQrCodeParser:packForXcode", - "BankingUiCommon:packForXcode", - "fints4kBankingClient:packForXcode", - "BankingUiNativeIntegration:packForXcode" + "fints4k:jvmJar", ] } \ No newline at end of file diff --git a/common/build.gradle b/common/build.gradle index 6b146195..66bb7f9a 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,41 +1,52 @@ plugins { id "org.jetbrains.kotlin.multiplatform" - id "com.android.library" id "maven-publish" } ext.artifactName = "multiplatform-utils" -def frameworkName = "MultiplatformUtils" - kotlin { jvm { - compilations.main.kotlinOptions { - jvmTarget = "1.6" + compilations.all { + kotlinOptions.jvmTarget = '1.8' + } + withJava() + testRuns["test"].executionTask.configure { + useJUnitPlatform() } } - targets { - final def iOSTarget = iOSIsRealDevice ? presets.iosArm64 : presets.iosX64 +// js(BOTH) { +// browser { +// commonWebpackConfig { +// cssSupport.enabled = true +// } +// } +// } - fromPreset(iOSTarget, 'ios') { - binaries { - framework { - baseName = frameworkName - - embedBitcode(embedBitcodeValue) - } + ios { + binaries { + framework { + baseName = "MultiplatformUtils" } } } +// def hostOs = System.getProperty("os.name") +// def isMingwX64 = hostOs.startsWith("Windows") +// def nativeTarget +// if (hostOs == "Mac OS X") nativeTarget = macosX64('native') { binaries.executable() } +// else if (hostOs == "Linux") nativeTarget = linuxX64("native") { binaries.executable() } +// else if (isMingwX64) nativeTarget = mingwX64("native") { binaries.executable() } +// else throw new GradleException("Host OS is not supported in Kotlin/Native.") + sourceSets { commonMain { dependencies { - implementation kotlin("stdlib-common") + } } @@ -51,7 +62,6 @@ kotlin { jvmMain { dependencies { - api kotlin("stdlib-jdk7") compileOnly "org.slf4j:slf4j-api:$slf4jVersion" @@ -79,97 +89,9 @@ kotlin { iosMain { dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion" + } } } -} - - -task copyFramework { - def buildType = project.findProperty('kotlin.build.type') ?: 'DEBUG' - def target = project.findProperty('kotlin.target') ?: 'ios' - def framework = kotlin.targets."$target".binaries.getFramework(buildType) - - dependsOn framework.linkTask - - doLast { - def srcFile = framework.outputFile - def targetDir = getProperty('configuration.build.dir') - - copy { - from srcFile.parent - into targetDir - include "${frameworkName}.framework/**" - include "${frameworkName}.framework.dSYM" - } - } -} - -// 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 - } - } \ No newline at end of file diff --git a/common/src/main/AndroidManifest.xml b/common/src/main/AndroidManifest.xml deleted file mode 100644 index d7b5d1bf..00000000 --- a/common/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/fints4k-jvm/build.gradle b/fints4k-jvm/build.gradle deleted file mode 100644 index 377323fc..00000000 --- a/fints4k-jvm/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' - -sourceCompatibility = "1.7" -targetCompatibility = "1.7" - - -dependencies { - api project(":fints4k") -} \ No newline at end of file diff --git a/fints4k/build.gradle b/fints4k/build.gradle index f10bf355..ac9b8aaa 100644 --- a/fints4k/build.gradle +++ b/fints4k/build.gradle @@ -1,96 +1,75 @@ - -buildscript { - repositories { - jcenter() - } -} - plugins { id "org.jetbrains.kotlin.multiplatform" - id "com.android.library" id "maven-publish" } kotlin { - jvm("jvm6") { - compilations.main.kotlinOptions { - jvmTarget = "1.6" + jvm { + compilations.all { + kotlinOptions.jvmTarget = '1.8' + } + withJava() + testRuns["test"].executionTask.configure { + useJUnitPlatform() } } - android() +// js(BOTH) { +// browser { +// commonWebpackConfig { +// cssSupport.enabled = true +// } +// } +// } - targets { - final def iOSTarget = iOSIsRealDevice ? presets.iosArm64 : presets.iosX64 - - fromPreset(iOSTarget, 'ios') { - binaries { - framework { - baseName = "fints4k" - - embedBitcode(embedBitcodeValue) - - export(project(":common")) - } + ios { + binaries { + framework { + baseName = "fints4k" } } } - js() { - - nodejs { - testTask { - enabled = false - } - } - - browser { - testTask { - enabled = false - } - - } - } +// def hostOs = System.getProperty("os.name") +// def isMingwX64 = hostOs.startsWith("Windows") +// def nativeTarget +// if (hostOs == "Mac OS X") nativeTarget = macosX64('native') { binaries.executable() } +// else if (hostOs == "Linux") nativeTarget = linuxX64("native") { binaries.executable() } +// else if (isMingwX64) nativeTarget = mingwX64("native") { binaries.executable() } +// else throw new GradleException("Host OS is not supported in Kotlin/Native.") sourceSets { commonMain { dependencies { - implementation kotlin("stdlib-common") - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlinCoroutinesVersion" - api project(":common") + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" + implementation "io.ktor:ktor-client-core:$ktorVersion" } } commonTest { dependencies { - implementation kotlin("test-common") - implementation kotlin("test-annotations-common") + implementation kotlin("test") implementation "ch.tutteli.atrium:atrium-fluent-en_GB-common:$atriumVersion" } } - jvm6Main { + jvmMain { dependencies { -// implementation "io.ktor:ktor-client-cio:$ktorVersion" implementation "io.ktor:ktor-client-okhttp:$ktorVersion" implementation "org.slf4j:slf4j-api:$slf4jVersion" } } - jvm6Test { + jvmTest { dependencies { - implementation kotlin("test-junit") - - 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" @@ -98,9 +77,9 @@ kotlin { implementation "ch.tutteli.atrium:atrium-fluent-en_GB:$atriumVersion" - implementation project(":BankingUiCommon") - implementation project(":BankFinder") - implementation project(":fints4kBankingClient") +// implementation project(":BankingUiCommon") +// implementation project(":BankFinder") +// implementation project(":fints4kBankingClient") implementation "org.apache.commons:commons-csv:1.8" @@ -111,111 +90,36 @@ kotlin { } - androidMain { - dependsOn jvm6Main +// jsMain { +// dependencies { +// implementation "io.ktor:ktor-client-js:$ktorVersion" +// } +// } +// +// jsTest { +// +// } - dependencies { - implementation "io.ktor:ktor-client-android:$ktorVersion" - } - } iosMain { dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion" + // ktor Native needs "-native-mt" coroutines version. Export it so that referencing applications don't need to import it on their own + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion-native-mt") implementation "io.ktor:ktor-client-ios:$ktorVersion" - implementation "io.ktor:ktor-client-core-native:$ktorVersion" } } - jsMain { - dependsOn commonMain - + nativeMain { dependencies { - api kotlin("stdlib-js") + // ktor Native needs "-native-mt" coroutines version. Export it so that referencing applications don't need to import it on their own + api"org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion-native-mt" - implementation "io.ktor:ktor-client-js:$ktorVersion" - implementation "io.ktor:ktor-client-encoding-js:$ktorVersion" - implementation "io.ktor:ktor-client-js-kotlinMultiplatform:$ktorVersion" - } - } - - jsTest { - dependencies { - implementation kotlin("test-js") - - implementation "ch.tutteli.atrium:atrium-fluent-en_GB-js:$atriumVersion" + // requires cURL to be installed on your system + implementation "io.ktor:ktor-client-curl:$ktorVersion" } } } -} - - -// 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 - } - } \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilderResult.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilderResult.kt index a6497ce6..278c71c4 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilderResult.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilderResult.kt @@ -24,7 +24,7 @@ open class MessageBuilderResult( } open val getHighestAllowedVersion: Int? - get() = allowedVersions.max() + get() = allowedVersions.maxOrNull() open fun isSendEnteredTanMessage(): Boolean { // contains only a ZweiSchrittTanEinreichung segment diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/webclient/KtorWebClient.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/webclient/KtorWebClient.kt index e4e911b6..4137579c 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/webclient/KtorWebClient.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/webclient/KtorWebClient.kt @@ -2,10 +2,10 @@ package net.dankito.banking.fints.webclient import io.ktor.client.HttpClient import io.ktor.client.request.post -import io.ktor.client.statement.HttpResponse -import io.ktor.client.statement.readText -import io.ktor.content.TextContent +import io.ktor.client.request.setBody +import io.ktor.client.statement.bodyAsText import io.ktor.http.ContentType +import io.ktor.http.contentType import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.cancel @@ -38,11 +38,12 @@ open class KtorWebClient : IWebClient { override fun post(url: String, body: String, contentType: String, userAgent: String, callback: (WebClientResponse) -> Unit) { GlobalScope.async { try { - val clientResponse = client.post(url) { - this.body = TextContent(body, contentType = ContentType.Application.OctetStream) + val clientResponse = client.post(url) { + contentType(ContentType.Application.OctetStream) + setBody(body) } - val responseBody = clientResponse.readText() + val responseBody = clientResponse.bodyAsText() callback(WebClientResponse(clientResponse.status.value == 200, clientResponse.status.value, body = responseBody)) } catch (e: Exception) { diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt index fb5708ea..b8413ad5 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt @@ -15,6 +15,7 @@ import net.dankito.banking.fints.util.FinTsUtils import net.dankito.utils.multiplatform.Date import net.dankito.utils.multiplatform.Month import kotlin.test.AfterTest +import kotlin.test.Ignore import kotlin.test.Test @@ -191,6 +192,7 @@ class MessageBuilderTest : FinTsTestBase() { )) } + @Ignore @Test fun createGetTransactionsMessage_WithContinuationIdSet() { diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt index 6c69c463..79a9e288 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt @@ -17,6 +17,7 @@ import net.dankito.banking.fints.extensions.isFalse import net.dankito.banking.fints.extensions.isTrue import net.dankito.banking.fints.model.Amount import net.dankito.utils.multiplatform.Date +import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.fail @@ -460,6 +461,7 @@ class ResponseParserTest : FinTsTestBase() { ?: run { fail("No segment of type UserParameters found in ${result.receivedSegments}") } } + @Ignore @Test fun parseAccountInfo() { @@ -1315,9 +1317,9 @@ class ResponseParserTest : FinTsTestBase() { assertCouldParseSegment(segment, segmentId, segmentNumber, segmentVersion, referenceSegmentNumber) } - private fun assertCouldParseSegment(segment: ReceivedSegment?, segmentId: ISegmentId, segmentNumber: Int, + private fun assertCouldParseSegment(segment: ReceivedSegment?, segmentId: ISegmentId, segmentNumber: Int, segmentVersion: Int, referenceSegmentNumber: Int?) { - + expect(segment).notToBeNull() segment?.let { @@ -1328,10 +1330,10 @@ class ResponseParserTest : FinTsTestBase() { } } - private fun assertCouldParseJobParametersSegment(segment: JobParameters?, segmentId: ISegmentId, segmentNumber: Int, + private fun assertCouldParseJobParametersSegment(segment: JobParameters?, segmentId: ISegmentId, segmentNumber: Int, segmentVersion: Int, referenceSegmentNumber: Int?, jobName: String, maxCountJobs: Int, minimumCountSignatures: Int, securityClass: Int?) { - + assertCouldParseSegment(segment, segmentId, segmentNumber, segmentVersion, referenceSegmentNumber) segment?.let { diff --git a/fints4k/src/jvm6Test/java/net/dankito/banking/fints/JavaShowcase.java b/fints4k/src/jvm6Test/java/net/dankito/banking/fints/JavaShowcase.java deleted file mode 100644 index 6803de47..00000000 --- a/fints4k/src/jvm6Test/java/net/dankito/banking/fints/JavaShowcase.java +++ /dev/null @@ -1,184 +0,0 @@ -package net.dankito.banking.fints; - -import net.dankito.banking.fints.banks.IBankFinder; -import net.dankito.banking.fints.banks.InMemoryBankFinder; -import net.dankito.banking.fints.callback.FinTsClientCallback; -import net.dankito.banking.fints.callback.SimpleFinTsClientCallback; -import net.dankito.banking.fints.model.AccountData; -import net.dankito.banking.fints.model.AccountFeature; -import net.dankito.banking.fints.model.AccountTransaction; -import net.dankito.banking.fints.model.BankData; -import net.dankito.banking.bankfinder.BankInfo; -import net.dankito.banking.fints.model.BankTransferData; -import net.dankito.banking.fints.model.CustomerData; -import net.dankito.banking.fints.model.EnterTanGeneratorAtcResult; -import net.dankito.banking.fints.model.EnterTanResult; -import net.dankito.banking.fints.model.TanChallenge; -import net.dankito.banking.fints.model.TanMethod; -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium; -import net.dankito.banking.fints.model.mapper.BankDataMapper; -import net.dankito.banking.fints.response.client.AddAccountResponse; -import net.dankito.banking.fints.response.client.FinTsClientResponse; -import net.dankito.banking.fints.response.client.GetTransactionsResponse; -import net.dankito.banking.fints.util.Java8Base64Service; - -import java.math.BigDecimal; -import java.util.List; - - -public class JavaShowcase { - - public static void main(String[] args) { - JavaShowcase showcase = new JavaShowcase(); - - showcase.basicShowcase(); - - showcase.advancedShowcase(); - } - - private void basicShowcase() { - // Set your bank code (Bankleitzahl) here. - // BankInfo contains e.g. a bank's FinTS server address, country code and BIC (needed for money transfer) - List foundBanks = new InMemoryBankFinder().findBankByNameBankCodeOrCity(""); - - if (foundBanks.isEmpty() == false) { // please also check if bank supports FinTS 3.0 - BankData bank = new BankDataMapper().mapFromBankInfo(foundBanks.get(0)); - - // set your customer data (customerId = username you use to log in; pin = online banking pin / password) - CustomerData customer = new CustomerData("", ""); - - FinTsClientCallback callback = new SimpleFinTsClientCallback(); // see advanced showcase for configuring callback - - FinTsClient finTsClient = new FinTsClient(callback, new Java8Base64Service()); - - AddAccountResponse addAccountResponse = finTsClient.addAccount(bank, customer); - - if (addAccountResponse.isSuccessful()) { - System.out.println("Successfully added account for " + bank.getBankCode() + " " + customer.getCustomerId()); - - if (addAccountResponse.getBookedTransactions().isEmpty() == false) { - System.out.println("Account transactions of last 90 days:"); - showGetTransactionsResponse(addAccountResponse); - } - } - else { - System.out.println("Could not add account for " + bank.getBankCode() + " " + customer.getCustomerId() + ":"); - showResponseError(addAccountResponse); - } - - // see advanced show case what else you can do with this library, e.g. retrieving all account transactions and transferring money - } - } - - private void advancedShowcase() { - IBankFinder bankFinder = new InMemoryBankFinder(); - - // Set your bank code (Bankleitzahl) here. Or create BankData manually. Required fields are: - // bankCode, bankCountryCode (Germany = 280), finTs3ServerAddress and for bank transfers bic - List foundBanks = bankFinder.findBankByBankCode(""); - - if (foundBanks.isEmpty() == false) { // please also check if bank supports FinTS 3.0 - BankData bank = new BankDataMapper().mapFromBankInfo(foundBanks.get(0)); - - // set your customer data (customerId = Kontonummer in most cases, pin = online banking pin) - CustomerData customer = new CustomerData("", ""); - - FinTsClientCallback callback = new FinTsClientCallback() { - - @Override - public TanMethod askUserForTanMethod(List supportedTanMethods, TanMethod suggestedTanMethod) { - // E.g. show a dialog to ask for user's TAN method. - // In most cases it's senseful to simply return suggestedTanMethod and to let - // user select TAN method when entering TAN is required (see enterTan() below) - return suggestedTanMethod; - } - - @Override - public EnterTanResult enterTan(CustomerData customer, TanChallenge tanChallenge) { - // e.g. show - // - Android: net.dankito.banking.ui.android.dialogs.EnterTanDialog - // - JavaFX: net.dankito.banking.ui.javafx.dialogs.tan.EnterTanDialog - return EnterTanResult.Companion.userDidNotEnterTan(); // user did not enter TAN. aborts operation - } - - @Override - public EnterTanGeneratorAtcResult enterTanGeneratorAtc(CustomerData customer, TanGeneratorTanMedium tanMedium) { - // needed only in rare cases to synchronize TAN generator for chipTAN methods. E.g. show - // - Android: net.dankito.banking.ui.android.dialogs.EnterAtcDialog - return EnterTanGeneratorAtcResult.Companion.userDidNotEnterAtc(); // user did not enter TAN and ATC. aborts operation - } - }; - - - // may also check if FinTsClientForCustomer fits your needs, avoids passing bank and customer to each method - FinTsClient finTsClient = new FinTsClient(callback, new Java8Base64Service()); - - AddAccountResponse addAccountResponse = finTsClient.addAccount(bank, customer); - if (addAccountResponse.isSuccessful() == false) { - System.out.println("Could not add account for " + bank.getBankCode() + " " + customer.getCustomerId() + ":"); - showResponseError(addAccountResponse); - return; - } - - System.out.println("Successfully added account for " + bank.getBankCode() + " " + customer.getCustomerId()); - - - for (AccountData account : customer.getAccounts()) { // accounts are now retrieved - if (account.supportsFeature(AccountFeature.RetrieveAccountTransactions)) { - // Most banks support retrieving account transactions of last 90 without TAN, may also your bank. - // Alternatively call getTransactions() to retrieve all account transactions. But then entering a TAN is required. - GetTransactionsResponse response = finTsClient.tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account); - - showGetTransactionsResponse(response); - } - - if (account.supportsFeature(AccountFeature.TransferMoney)) { - // Transfer 0.01 € to yourself. Transferring money to one self doesn't require a TAN. - BankTransferData data = new BankTransferData(customer.getName(), account.getIban(), bank.getBic(), - new BigDecimal("0.01"), "Give me some money", false); - FinTsClientResponse transferMoneyResponse = finTsClient.doBankTransfer(data, bank, customer, account); - - if (transferMoneyResponse.isSuccessful()) { - System.out.println("Successfully transferred " + data.getAmount() + " to " + data.getCreditorIban()); - } - else { - showResponseError(transferMoneyResponse); - } - } - } - } - } - - private static void showGetTransactionsResponse(GetTransactionsResponse response) { - if (response.isSuccessful()) { - System.out.println("Balance (Saldo) = " + response.getBalance()); - - System.out.println("Account transactions (Umsätze):"); - for (AccountTransaction transaction : response.getBookedTransactions()) { - System.out.println(transaction.toString()); - } - } - else { - if (response.isStrongAuthenticationRequired()) { - System.out.println("Sorry, your bank doesn't support retrieving account " + - "transactions of last 90 days without TAN"); - } - else { - System.out.println("An error occurred:"); - showResponseError(response); - } - } - } - - private static void showResponseError(FinTsClientResponse response) { - if (response.getException() != null) { // something severe occurred - System.out.println(response.getException().getMessage()); - } - - // error messages retrieved from bank (e.g. PIN is wrong, message contains errors, ...) - for (String retrievedErrorMessage : response.getErrorsToShowToUser()) { - System.out.println(retrievedErrorMessage); - } - } - -} diff --git a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/FinTsClientTestBase.kt b/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/FinTsClientTestBase.kt deleted file mode 100644 index a47a82d4..00000000 --- a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/FinTsClientTestBase.kt +++ /dev/null @@ -1,269 +0,0 @@ -package net.dankito.banking.fints - -import ch.tutteli.atrium.api.fluent.en_GB.* -import ch.tutteli.atrium.api.verbs.expect -import net.dankito.banking.bankfinder.InMemoryBankFinder -import net.dankito.banking.fints.callback.FinTsClientCallback -import net.dankito.banking.fints.extensions.isTrue -import net.dankito.banking.fints.extensions.isFalse -import net.dankito.banking.fints.messages.datenelemente.implementierte.Dialogsprache -import net.dankito.banking.fints.messages.datenelemente.implementierte.KundensystemStatus -import net.dankito.banking.fints.messages.datenelemente.implementierte.KundensystemStatusWerte -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanEinsatzOption -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedienArtVersion -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumKlasse -import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId -import net.dankito.banking.fints.model.* -import net.dankito.banking.fints.response.client.* -import net.dankito.utils.multiplatform.Date -import net.dankito.utils.multiplatform.DateFormatter -import net.dankito.utils.multiplatform.UUID -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicReference -import kotlin.test.DefaultAsserter.fail -import kotlin.test.Ignore -import kotlin.test.Test - - -@Ignore // not an automatic test, supply your settings below -open class FinTsClientTestBase { - - companion object { - - // TODO: add your settings here: - val BankCode = "" - - val CustomerId = "" - - val Password = "" - - - val DateTimeFormatForUniqueBankTransferReference = DateFormatter("yyyy-MM-dd'T'HH:mm:ss.SSS") - } - - - private var didAskUserForTanMethod = false - - private var didAskUserToEnterTan = false - - - private val callback = object : FinTsClientCallback { - - override fun askUserForTanMethod(supportedTanMethods: List, suggestedTanMethod: TanMethod?, callback: (TanMethod?) -> Unit) { - didAskUserForTanMethod = true - callback(suggestedTanMethod) // simply return suggestedTanMethod as in most cases it's the best fitting one - } - - override fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) { - didAskUserToEnterTan = true - - callback(EnterTanResult.userDidNotEnterTan()) - } - - override fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) { - fail("Bank asks you to synchronize your TAN generator for card ${tanMedium.cardNumber} " + - "(card sequence number ${tanMedium.cardSequenceNumber}). Please do this via your online banking portal or Banking UI.") - } - - } - - - private val underTest = FinTsClient(callback) - - - private val BankDataAnonymous = BankData.anonymous("10070000", "https://fints.deutsche-bank.de/", "DEUTDEBBXXX") - - private val bankInfo = InMemoryBankFinder().findBankByBankCode(BankCode).first() - private val Bank = BankData(bankInfo.bankCode, CustomerId, Password, bankInfo.pinTanAddress ?: "", bankInfo.bic, bankInfo.name) - - - - @Test - fun getAnonymousBankInfo() { - - // when - underTest.getAnonymousBankInfo(BankDataAnonymous) { result -> - - // then - expect(result.successful).isTrue() - expect(BankDataAnonymous.supportedHbciVersions).isNotEmpty() - expect(BankDataAnonymous.tanMethodsSupportedByBank).isNotEmpty() - expect(BankDataAnonymous.supportedJobs).isNotEmpty() - expect(BankDataAnonymous.supportedLanguages).isNotEmpty() - expect(BankDataAnonymous.bankName).isNotEmpty() - } - } - - - @Test - fun addAccount() { - - // given - val response = AtomicReference() - val countDownLatch = CountDownLatch(1) - - - // when - underTest.addAccountAsync(Bank.toAddAccountParameter()) { - response.set(it) - countDownLatch.countDown() - } - - - // then - countDownLatch.await(30, TimeUnit.SECONDS) - val result = response.get() - - expect(result.successful).isTrue() - - expect(didAskUserForTanMethod).isFalse() - - expect(Bank.bankName).isNotEmpty() - expect(Bank.supportedJobs).isNotEmpty() // supported jobs are now known - expect(Bank.tanMethodsSupportedByBank).isNotEmpty() // supported tan methods are now known - expect(Bank.supportedHbciVersions).isNotEmpty() // supported HBIC versions are now known - expect(Bank.supportedLanguages).isNotEmpty() // supported languages are now known - - expect(Bank.customerName).isNotEmpty() - expect(Bank.tanMethodsAvailableForUser).isNotEmpty() - expect(Bank.selectedLanguage).notToBe(Dialogsprache.Default) // language is set now - expect(Bank.customerSystemId).notToBe(KundensystemStatus.SynchronizingCustomerSystemId.code) // customer system id is now set - expect(Bank.customerSystemStatus).toBe(KundensystemStatusWerte.Benoetigt) // customerSystemStatus is set now - expect(Bank.accounts).isNotEmpty() // accounts are now known - expect(Bank.accounts.first().allowedJobs).isNotEmpty() // allowed jobs are now known - } - - - @ExperimentalWithOptions - @Test - fun getTransactions() { - - // given - val response = AtomicReference() - val countDownLatch = CountDownLatch(1) - - underTest.addAccountAsync(Bank.toAddAccountParameter(false)) { // retrieve basic data, e.g. accounts - val account = Bank.accounts.firstOrNull { it.supportsFeature(AccountFeature.RetrieveAccountTransactions) } - expect(account).withRepresentation("We need at least one account that supports retrieving account transactions (${CustomerSegmentId.AccountTransactionsMt940.id})").notToBeNull() - - // when - - // some banks support retrieving account transactions of last 90 days without TAN - underTest.tryGetAccountTransactionsOfLast90DaysWithoutTan(Bank, account!!) { - response.set(it) - countDownLatch.countDown() - } - } - - - // then - countDownLatch.await(30, TimeUnit.SECONDS) - val result = response.get() - - expect(result.successful).isTrue() - expect(result.retrievedData.map { it.bookedTransactions }).isNotEmpty() - } - - - @Test - fun getTanMediaList() { - - // this test is only senseful for accounts using chipTAN / TAN generator as TAN method - - // given - val response = AtomicReference() - val countDownLatch = CountDownLatch(1) - val anonymousBankInfoCountDownLatch = CountDownLatch(1) - - - underTest.getAnonymousBankInfo(Bank) { - anonymousBankInfoCountDownLatch.countDown() - } - anonymousBankInfoCountDownLatch.await(30, TimeUnit.SECONDS) - - - val supportsRetrievingTanMedia = Bank.supportedJobs.firstOrNull { it.jobName == "HKTAB" } != null - - if (supportsRetrievingTanMedia == false) { // accounts with appTAN, pushTAN, smsTAN, ... would fail here -> simply return - println("Bank ${Bank.bankName} does not support retrieving TAN media. Therefore cannot execute test getTanMediaList()") - return - } - - expect(Bank.tanMedia).isEmpty() - - - // when - underTest.getTanMediaList(Bank, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) { result -> - response.set(result) - countDownLatch.countDown() - } - - // then - countDownLatch.await(30, TimeUnit.SECONDS) - val result = response.get() - - expect(result.successful).isTrue() - - expect(result.tanMediaList).notToBeNull() - expect(result.tanMediaList!!.usageOption).toBe(TanEinsatzOption.KundeKannGenauEinMediumZuEinerZeitNutzen) // TODO: may adjust to your value - expect(result.tanMediaList!!.tanMedia).isNotEmpty() - - expect(Bank.tanMedia).isNotEmpty() - } - - @Ignore // only works with banks that don't support HKTAB version 5 - @Test - fun getTanMediaList_UnsupportedTanMediumClass() { - - // when - expect { - underTest.getTanMediaList(Bank, TanMedienArtVersion.Alle, TanMediumKlasse.BilateralVereinbart) { } - }.toThrow() - - - // then - // exception gets thrown - } - - - @ExperimentalWithOptions - @Test - fun testBankTransfer() { - - // given - val response = AtomicReference() - val countDownLatch = CountDownLatch(1) - - underTest.addAccountAsync(Bank.toAddAccountParameter(false)) { // retrieve basic data, e.g. accounts - // we need at least one account that supports cash transfer - val account = Bank.accounts.firstOrNull { it.supportsFeature(AccountFeature.TransferMoney) } - expect(account).withRepresentation("We need at least one account that supports cash transfer (${CustomerSegmentId.SepaBankTransfer.id})").notToBeNull() - - // IBAN should be set - expect(account?.iban).withRepresentation("Account IBAN must be set").notToBeNull() - - // transfer 1 cent to yourself. Transferring money to oneself also doesn't require to enter a TAN according to PSD2 - val BankTransferData = BankTransferData(Bank.customerName, account?.iban!!, Bank.bic, Money(Amount("0,01"), "EUR"), - "${DateTimeFormatForUniqueBankTransferReference.format(Date())} Test transaction ${UUID.random()}") - - - // when - underTest.doBankTransferAsync(BankTransferData, Bank, account) { result -> - response.set(result) - countDownLatch.countDown() - } - - } - - - // then - countDownLatch.await(30, TimeUnit.SECONDS) - val result = response.get() - - expect(result.successful).isTrue() - - } - -} \ No newline at end of file diff --git a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt b/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt deleted file mode 100644 index 8cb41eb7..00000000 --- a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt +++ /dev/null @@ -1,317 +0,0 @@ -package net.dankito.banking.fints.bankdetails - -import kotlinx.coroutines.runBlocking -import net.dankito.banking.bankfinder.InMemoryBankFinder -import net.dankito.banking.fints.callback.NoOpFinTsClientCallback -import net.dankito.banking.fints.messages.MessageBuilder -import net.dankito.banking.fints.messages.MessageBuilderResult -import net.dankito.banking.fints.messages.Separators -import net.dankito.banking.fints.messages.datenelemente.implementierte.Dialogsprache -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AuftraggeberkontoErforderlich -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.BezeichnungDesTanMediumsErforderlich -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.SmsAbbuchungskontoErforderlich -import net.dankito.banking.fints.model.* -import net.dankito.banking.bankfinder.BankInfo -import net.dankito.banking.fints.FinTsJobExecutor -import net.dankito.banking.fints.callback.SimpleFinTsClientCallback -import net.dankito.banking.fints.model.mapper.ModelMapper -import net.dankito.banking.fints.response.BankResponse -import net.dankito.banking.fints.response.segments.SepaAccountInfoParameters -import net.dankito.banking.fints.response.segments.TanInfo -import net.dankito.banking.fints.response.segments.TanMethodParameters -import net.dankito.banking.fints.util.* -import org.apache.commons.csv.CSVFormat -import org.apache.commons.csv.CSVPrinter -import org.junit.Ignore -import kotlin.test.Test -import org.slf4j.LoggerFactory -import java.io.File -import java.io.FileWriter -import java.text.SimpleDateFormat -import java.util.* -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicReference - - -@Ignore // not a real test, run manually to retrieve FinTS information from all banks -class BanksFinTsDetailsRetriever { - - companion object { - private val OutputFolderDateFormat = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss") - - private val log = LoggerFactory.getLogger(BanksFinTsDetailsRetriever::class.java) - } - - - private val bankFinder = InMemoryBankFinder() - - private val product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0") // TODO: get version dynamically - - private val messageBuilder = MessageBuilder() - - private val modelMapper = object : ModelMapper(messageBuilder) { - - fun mapToTanMethodTypePublic(parameters: TanMethodParameters): TanMethodType? { - return super.mapToTanMethodType(parameters) - } - - } - - private val jobExecutor = object : FinTsJobExecutor(modelMapper = modelMapper) { - - fun getAndHandleResponseForMessagePublic(context: JobContext, message: MessageBuilderResult, callback: (BankResponse) -> Unit) { - getAndHandleResponseForMessage(context, message, callback) - } - } - - - private val requestNotSuccessful = mutableListOf() - - private val tanMethodParameter = mutableMapOf>() - private val tanMethodTypes = mutableMapOf>() - - private val tanMethodParameterTechnicalIdentification = mutableSetOf() - private val tanMethodParameterVersionDkTanMethod = mutableSetOf() - - private val requiresSmsAbbuchungskonto = mutableListOf() - private val requiresAuftraggeberkonto = mutableListOf() - private val requiresChallengeClass = mutableListOf() - private val signatureStructured = mutableListOf() - private val requiresNameOfTanMedia = mutableListOf() - private val requiresHhdUcResponse = mutableListOf() - - private val doesNotSupportHKTAN6 = mutableListOf() - private val doesNotSupportHKSAL5or7 = mutableListOf() - private val doesNotSupportHKKAZ5to7 = mutableListOf() - private val doesNotSupportHKCCS1 = mutableListOf() - - private val supportsHhd13 = mutableListOf() - private val supportsHhd14 = mutableListOf() - - private val doesNotSupportPain_001_001_or_003_03 = mutableListOf() - - - @Test - fun retrieveAllBanksFinTsDetails() { - - val allBanks = bankFinder.getBankList() - val banksSupportingFinTs = allBanks.filter { it.supportsFinTs3_0 } - - val outputFolder = File("bankData", OutputFolderDateFormat.format(Date())) - val responsesFolder = File(outputFolder, "responses") - responsesFolder.mkdirs() - - val csvFile = FileWriter(File(outputFolder, "bank_details.csv")) - val csvPrinter = CSVPrinter(csvFile, CSVFormat.DEFAULT.withHeader( - "BLZ", "Name", "Ort", "BPD", "Tanverfahren", "Technische Tanverfahrennamen", "HHD 1.3?", "HHD 1.4?", - "HKTAN 6?", "HKTAN", "HKSAL 5?", "HKSAL", "HKKAZ 5-7?", "HKKAZ", "HKCAZ", "HKCCS 1?", "HKCCS", - "pain.001.001.03?", "SEPA Formate", "Sprachen", "Untersstützte Geschäftsvorfälle" - )) - - - val uniqueBanks = banksSupportingFinTs.associateBy { "${it.bankCode}_${it.name}" } - var bankIndex = 0 - - uniqueBanks.forEach { bankName, bankInfo -> - log.info("[${++bankIndex}] Getting details for $bankName ...") - - getAndSaveBankDetails(bankInfo, responsesFolder, csvPrinter) - } - - printStatistics() - - csvPrinter.close() - csvFile.close() - } - - - private fun getAnonymousBankInfo(bank: BankData): BankResponse { - val context = JobContext(JobContextType.AnonymousBankInfo, SimpleFinTsClientCallback(), product, bank) - context.startNewDialog() - - val requestBody = messageBuilder.createAnonymousDialogInitMessage(context) - - val anonymousBankInfoResponse = AtomicReference() - val countDownLatch = CountDownLatch(1) - - jobExecutor.getAndHandleResponseForMessagePublic(context, requestBody) { - anonymousBankInfoResponse.set(it) - countDownLatch.countDown() - } - - countDownLatch.await(30, TimeUnit.SECONDS) - - modelMapper.updateBankData(bank, anonymousBankInfoResponse.get()) - - return anonymousBankInfoResponse.get() - } - - private fun getAndSaveBankDetails(bankInfo: BankInfo, responsesFolder: File, csvPrinter: CSVPrinter) = runBlocking { - val bank = BankData.anonymous(bankInfo.bankCode, bankInfo.pinTanAddress ?: "", bankInfo.bic) - bank.bankName = bankInfo.name - - val anonymousBankInfoResponse = getAnonymousBankInfo(bank) - - File(responsesFolder, "${bankInfo.bankCode}_${bankInfo.name.replace('/', '-')}").writeText( - anonymousBankInfoResponse.receivedSegments.joinToString(System.lineSeparator()) { it.segmentString + Separators.SegmentSeparator }) - - if (anonymousBankInfoResponse.successful == false) { - requestNotSuccessful.add(bankInfo) - log.warn("Did not receive response from bank $bankInfo: ${anonymousBankInfoResponse.receivedSegments.joinToString(System.lineSeparator()) { it.segmentString + Separators.SegmentSeparator }}") - - return@runBlocking - } - - - val supportsHKTAN6 = supportsJobInVersion(bank, "HKTAN", 6) - val supportsHKSAL5or7 = supportsJobInVersion(bank, "HKSAL", listOf(5, 7)) - val supportsHKKAZ5to7 = supportsJobInVersion(bank, "HKKAZ", listOf(5, 6, 7)) - val supportsHKCCS1 = supportsJobInVersion(bank, "HKCCS", 1) - - val tanInfo = anonymousBankInfoResponse.receivedSegments.filterIsInstance(TanInfo::class.java) - val tanMethodParameters = tanInfo.flatMap { it.tanProcedureParameters.methodParameters } - val supportedTanMethods = tanMethodParameters.map { it.technicalTanMethodIdentification } - val hhd13Supported = supportedTanMethods.firstOrNull { it.startsWith("hhd1.3", true) } != null - val hhd14Supported = supportedTanMethods.firstOrNull { it.startsWith("hhd1.4", true) } != null - - val supportedHKTANVersions = tanInfo.map { it.segmentVersion } - val supportedHKSALVersions = getSupportedVersions(bank, "HKSAL") - val supportedHKKAZVersions = getSupportedVersions(bank, "HKKAZ") - val supportedHKCAZVersions = getSupportedVersions(bank, "HKCAZ") - val supportedHKCCSVersions = getSupportedVersions(bank, "HKCCS") - - val sepaAccountInfoParameters = anonymousBankInfoResponse.receivedSegments.filterIsInstance() - val supportedSepaFormats = sepaAccountInfoParameters.flatMap { it.supportedSepaFormats }.map { it.substring(it.indexOf(":xsd:") + ":xsd:".length) } - val supportsPain_001_001_or_003_03 = supportedSepaFormats.firstOrNull { it.contains("pain.001.001.03") or it.contains("pain.001.003.03") } != null - - csvPrinter.printRecord(bankInfo.bankCode, bankInfo.name, bankInfo.city, - bank.bpdVersion, - bank.tanMethodsSupportedByBank.joinToString(", ") { it.securityFunction.code + ": " + it.displayName + " (" + it.type + ")" }, - supportedTanMethods.joinToString(", "), - hhd13Supported, - hhd14Supported, - supportsHKTAN6, - supportedHKTANVersions.joinToString(", "), - supportsHKSAL5or7, - supportedHKSALVersions.joinToString(", "), - supportsHKKAZ5to7, - supportedHKKAZVersions.joinToString(", "), - supportedHKCAZVersions.joinToString(", "), - supportsHKCCS1, - supportedHKCCSVersions.joinToString(", "), - supportsPain_001_001_or_003_03, - supportedSepaFormats.joinToString(", "), - bank.supportedLanguages.filter { it != Dialogsprache.German }.joinToString(", ") { it.name }, - bank.supportedJobs.joinToString(", ") { it.jobName + " " + it.segmentVersion } - ) - - tanMethodParameters.forEach { methodParameter -> - if (tanMethodParameter.containsKey(methodParameter.methodName) == false) { - tanMethodParameter.put(methodParameter.methodName, mutableSetOf(methodParameter)) - } - else { - tanMethodParameter[methodParameter.methodName]?.add(methodParameter) - } - - val tanMethodType = modelMapper.mapToTanMethodTypePublic(methodParameter) - if (tanMethodTypes.containsKey(tanMethodType) == false) { - tanMethodTypes.put(tanMethodType, mutableSetOf(methodParameter)) - } - else { - tanMethodTypes[tanMethodType]?.add(methodParameter) - } - - tanMethodParameterTechnicalIdentification.add(methodParameter.technicalTanMethodIdentification) - tanMethodParameterVersionDkTanMethod.add(methodParameter.versionDkTanMethod) - - if (methodParameter.smsDebitAccountRequired == SmsAbbuchungskontoErforderlich.SmsAbbuchungskontoMussAngegebenWerden) { - requiresSmsAbbuchungskonto.add(bankInfo) - } - if (methodParameter.initiatorAccountRequired == AuftraggeberkontoErforderlich.AuftraggeberkontoMussAngegebenWerdenWennImGeschaeftsvorfallEnthalten) { - requiresAuftraggeberkonto.add(bankInfo) - } - if (methodParameter.challengeClassRequired) { - requiresChallengeClass.add(bankInfo) - } - if (methodParameter.signatureStructured) { - signatureStructured.add(bankInfo) - } - if (methodParameter.nameOfTanMediumRequired == BezeichnungDesTanMediumsErforderlich.BezeichnungDesTanMediumsMussAngegebenWerden) { - requiresNameOfTanMedia.add(bankInfo) - } - if (methodParameter.hhdUcResponseRequired) { - requiresHhdUcResponse.add(bankInfo) - } - } - - if (supportsHKTAN6 == false) { - doesNotSupportHKTAN6.add(bankInfo) - } - if (supportsHKSAL5or7 == false) { - doesNotSupportHKSAL5or7.add(bankInfo) - } - if (supportsHKKAZ5to7 == false) { - doesNotSupportHKKAZ5to7.add(bankInfo) - } - if (supportsHKCCS1 == false) { - doesNotSupportHKCCS1.add(bankInfo) - } - - if (hhd13Supported) { - supportsHhd13.add(bankInfo) - } - if (hhd14Supported) { - supportsHhd14.add(bankInfo) - } - - if (supportsPain_001_001_or_003_03 == false) { - doesNotSupportPain_001_001_or_003_03.add(bankInfo) - } - } - - private fun getSupportedVersions(bank: BankData, jobName: String): List { - return bank.supportedJobs.filter { it.jobName == jobName }.map { it.segmentVersion } - } - - private fun supportsJobInVersion(bank: BankData, jobName: String, version: Int): Boolean { - return supportsJobInVersion(bank, jobName, listOf(version)) - } - - private fun supportsJobInVersion(bank: BankData, jobName: String, versions: List): Boolean { - return bank.supportedJobs.firstOrNull { it.jobName == jobName && versions.contains(it.segmentVersion) } != null - } - - - private fun printStatistics() { - log.info("Did not receive response from Banks ${printBanks(requestNotSuccessful)}") - - log.info("Mapped tanMethodTypes: ${tanMethodTypes.map { System.lineSeparator() + it.key + ": " + it.value.map { it.methodName + " " + it.dkTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") }}\n\n") - log.info("TanMethodParameters:${tanMethodParameter.map { System.lineSeparator() + it.key + ": " + it.value.map { it.securityFunction.code + " " + it.dkTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") } }\n\n") - - log.info("TanMethodParameters TechnicalIdentification:${tanMethodParameterTechnicalIdentification.joinToString(", ") } \n\n") - log.info("TanMethodParameters VersionDkTanMethod:${tanMethodParameterVersionDkTanMethod.joinToString(", ") } \n\n") - - log.info("Requires SmsAbbuchungskonto ${printBanks(requiresSmsAbbuchungskonto)}") // no (only 2) - log.info("Requires Auftraggeberkonto ${printBanks(requiresAuftraggeberkonto)}") // yes, a lot of (12631) - log.info("Requires ChallengeClass ${printBanks(requiresChallengeClass)}") // no - log.info("Has structured signature ${printBanks(signatureStructured)}") // yes, a lot of (12651) - log.info("Requires NameOfTanMedia ${printBanks(requiresNameOfTanMedia)}") // yes, a lot of (912) - log.info("Requires HhdUcResponse ${printBanks(requiresHhdUcResponse)}") // no (only 2) - - log.info("Banks supporting HHD 1.3 (${supportsHhd13.size}):${printBanks(supportsHhd13)}") - log.info("Banks supporting HHD 1.4 (${supportsHhd14.size}):${printBanks(supportsHhd14)}") - - log.info("Banks not supporting HKTAN 6 ${printBanks(doesNotSupportHKTAN6)}") - log.info("Banks not supporting HKSAL 5 or 7 ${printBanks(doesNotSupportHKSAL5or7)}") - log.info("Banks not supporting HKKAZ 5-7 ${printBanks(doesNotSupportHKKAZ5to7)}") - log.info("Banks not supporting HKCCS 1 ${printBanks(doesNotSupportHKCCS1)}") - - log.info("Banks not supporting pain.001.001.03 or pain.001.003.03 ${printBanks(doesNotSupportPain_001_001_or_003_03)}") - } - - private fun printBanks(banks: List): String { - return "(${banks.size}):${ banks.joinToString { System.lineSeparator() + it } }\n\n\n" - } - -} \ No newline at end of file diff --git a/fints4k/src/jvm6Main/kotlin/net/dankito/banking/fints/util/JavaThreadPool.kt b/fints4k/src/jvmMain/kotlin/net/dankito/banking/fints/util/JavaThreadPool.kt similarity index 100% rename from fints4k/src/jvm6Main/kotlin/net/dankito/banking/fints/util/JavaThreadPool.kt rename to fints4k/src/jvmMain/kotlin/net/dankito/banking/fints/util/JavaThreadPool.kt diff --git a/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/FinTsClientTestBase.kt b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/FinTsClientTestBase.kt new file mode 100644 index 00000000..90b08afd --- /dev/null +++ b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/FinTsClientTestBase.kt @@ -0,0 +1,270 @@ +//package net.dankito.banking.fints +// +//import ch.tutteli.atrium.api.fluent.en_GB.* +//import ch.tutteli.atrium.api.verbs.expect +//import jdk.nashorn.internal.ir.annotations.Ignore +//import net.dankito.banking.bankfinder.InMemoryBankFinder +//import net.dankito.banking.fints.callback.FinTsClientCallback +//import net.dankito.banking.fints.extensions.isTrue +//import net.dankito.banking.fints.extensions.isFalse +//import net.dankito.banking.fints.messages.datenelemente.implementierte.Dialogsprache +//import net.dankito.banking.fints.messages.datenelemente.implementierte.KundensystemStatus +//import net.dankito.banking.fints.messages.datenelemente.implementierte.KundensystemStatusWerte +//import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanEinsatzOption +//import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium +//import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedienArtVersion +//import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumKlasse +//import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId +//import net.dankito.banking.fints.model.* +//import net.dankito.banking.fints.response.client.* +//import net.dankito.utils.multiplatform.Date +//import net.dankito.utils.multiplatform.DateFormatter +//import net.dankito.utils.multiplatform.UUID +//import org.assertj.core.api.Assertions.assertThat +//import org.junit.jupiter.api.Test +//import org.junit.jupiter.api.fail +//import java.util.concurrent.CountDownLatch +//import java.util.concurrent.TimeUnit +//import java.util.concurrent.atomic.AtomicReference +// +// +//@Ignore // not an automatic test, supply your settings below +//open class FinTsClientTestBase { +// +// companion object { +// +// // TODO: add your settings here: +// val BankCode = "" +// +// val CustomerId = "" +// +// val Password = "" +// +// +// val DateTimeFormatForUniqueBankTransferReference = DateFormatter("yyyy-MM-dd'T'HH:mm:ss.SSS") +// } +// +// +// private var didAskUserForTanMethod = false +// +// private var didAskUserToEnterTan = false +// +// +// private val callback = object : FinTsClientCallback { +// +// override fun askUserForTanMethod(supportedTanMethods: List, suggestedTanMethod: TanMethod?, callback: (TanMethod?) -> Unit) { +// didAskUserForTanMethod = true +// callback(suggestedTanMethod) // simply return suggestedTanMethod as in most cases it's the best fitting one +// } +// +// override fun enterTan(bank: BankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) { +// didAskUserToEnterTan = true +// +// callback(EnterTanResult.userDidNotEnterTan()) +// } +// +// override fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) { +// fail("Bank asks you to synchronize your TAN generator for card ${tanMedium.cardNumber} " + +// "(card sequence number ${tanMedium.cardSequenceNumber}). Please do this via your online banking portal or Banking UI.") +// } +// +// } +// +// +// private val underTest = FinTsClient(callback) +// +// +// private val BankDataAnonymous = BankData.anonymous("10070000", "https://fints.deutsche-bank.de/", "DEUTDEBBXXX") +// +// private val bankInfo = InMemoryBankFinder().findBankByBankCode(BankCode).first() +// private val Bank = BankData(bankInfo.bankCode, CustomerId, Password, bankInfo.pinTanAddress ?: "", bankInfo.bic, bankInfo.name) +// +// +// +// @Test +// fun getAnonymousBankInfo() { +// +// // when +// underTest.getAnonymousBankInfo(BankDataAnonymous) { result -> +// +// // then +// expect(result.successful).isTrue() +// expect(BankDataAnonymous.supportedHbciVersions).isNotEmpty() +// expect(BankDataAnonymous.tanMethodsSupportedByBank).isNotEmpty() +// expect(BankDataAnonymous.supportedJobs).isNotEmpty() +// expect(BankDataAnonymous.supportedLanguages).isNotEmpty() +// expect(BankDataAnonymous.bankName).isNotEmpty() +// } +// } +// +// +// @Test +// fun addAccount() { +// +// // given +// val response = AtomicReference() +// val countDownLatch = CountDownLatch(1) +// +// +// // when +// underTest.addAccountAsync(Bank.toAddAccountParameter()) { +// response.set(it) +// countDownLatch.countDown() +// } +// +// +// // then +// countDownLatch.await(30, TimeUnit.SECONDS) +// val result = response.get() +// +// expect(result.successful).isTrue() +// +// expect(didAskUserForTanMethod).isFalse() +// +// expect(Bank.bankName).isNotEmpty() +// expect(Bank.supportedJobs).isNotEmpty() // supported jobs are now known +// expect(Bank.tanMethodsSupportedByBank).isNotEmpty() // supported tan methods are now known +// expect(Bank.supportedHbciVersions).isNotEmpty() // supported HBIC versions are now known +// expect(Bank.supportedLanguages).isNotEmpty() // supported languages are now known +// +// expect(Bank.customerName).isNotEmpty() +// expect(Bank.tanMethodsAvailableForUser).isNotEmpty() +// expect(Bank.selectedLanguage).notToBe(Dialogsprache.Default) // language is set now +// expect(Bank.customerSystemId).notToBe(KundensystemStatus.SynchronizingCustomerSystemId.code) // customer system id is now set +// expect(Bank.customerSystemStatus).toBe(KundensystemStatusWerte.Benoetigt) // customerSystemStatus is set now +// expect(Bank.accounts).isNotEmpty() // accounts are now known +// expect(Bank.accounts.first().allowedJobs).isNotEmpty() // allowed jobs are now known +// } +// +// +// @ExperimentalWithOptions +// @Test +// fun getTransactions() { +// +// // given +// val response = AtomicReference() +// val countDownLatch = CountDownLatch(1) +// +// underTest.addAccountAsync(Bank.toAddAccountParameter(false)) { // retrieve basic data, e.g. accounts +// val account = Bank.accounts.firstOrNull { it.supportsFeature(AccountFeature.RetrieveAccountTransactions) } +// expect(account).withRepresentation("We need at least one account that supports retrieving account transactions (${CustomerSegmentId.AccountTransactionsMt940.id})").notToBeNull() +// +// // when +// +// // some banks support retrieving account transactions of last 90 days without TAN +// underTest.tryGetAccountTransactionsOfLast90DaysWithoutTan(Bank, account!!) { +// response.set(it) +// countDownLatch.countDown() +// } +// } +// +// +// // then +// countDownLatch.await(30, TimeUnit.SECONDS) +// val result = response.get() +// +// assertThat(result.successful).isTrue() +// assertThat(result.retrievedData).isNotNull() +// } +// +// +// @Test +// fun getTanMediaList() { +// +// // this test is only senseful for accounts using chipTAN / TAN generator as TAN method +// +// // given +// val response = AtomicReference() +// val countDownLatch = CountDownLatch(1) +// val anonymousBankInfoCountDownLatch = CountDownLatch(1) +// +// +// underTest.getAnonymousBankInfo(Bank) { +// anonymousBankInfoCountDownLatch.countDown() +// } +// anonymousBankInfoCountDownLatch.await(30, TimeUnit.SECONDS) +// +// +// val supportsRetrievingTanMedia = Bank.supportedJobs.firstOrNull { it.jobName == "HKTAB" } != null +// +// if (supportsRetrievingTanMedia == false) { // accounts with appTAN, pushTAN, smsTAN, ... would fail here -> simply return +// println("Bank ${Bank.bankName} does not support retrieving TAN media. Therefore cannot execute test getTanMediaList()") +// return +// } +// +// expect(Bank.tanMedia).isEmpty() +// +// +// // when +// underTest.getTanMediaList(Bank, TanMedienArtVersion.Alle, TanMediumKlasse.AlleMedien) { result -> +// response.set(result) +// countDownLatch.countDown() +// } +// +// // then +// countDownLatch.await(30, TimeUnit.SECONDS) +// val result = response.get() +// +// expect(result.successful).isTrue() +// +// expect(result.tanMediaList).notToBeNull() +// expect(result.tanMediaList!!.usageOption).toBe(TanEinsatzOption.KundeKannGenauEinMediumZuEinerZeitNutzen) // TODO: may adjust to your value +// expect(result.tanMediaList!!.tanMedia).isNotEmpty() +// +// expect(Bank.tanMedia).isNotEmpty() +// } +// +// @Ignore // only works with banks that don't support HKTAB version 5 +// @Test +// fun getTanMediaList_UnsupportedTanMediumClass() { +// +// // when +// expect { +// underTest.getTanMediaList(Bank, TanMedienArtVersion.Alle, TanMediumKlasse.BilateralVereinbart) { } +// }.toThrow() +// +// +// // then +// // exception gets thrown +// } +// +// +// @ExperimentalWithOptions +// @Test +// fun testBankTransfer() { +// +// // given +// val response = AtomicReference() +// val countDownLatch = CountDownLatch(1) +// +// underTest.addAccountAsync(Bank.toAddAccountParameter(false)) { // retrieve basic data, e.g. accounts +// // we need at least one account that supports cash transfer +// val account = Bank.accounts.firstOrNull { it.supportsFeature(AccountFeature.TransferMoney) } +// expect(account).withRepresentation("We need at least one account that supports cash transfer (${CustomerSegmentId.SepaBankTransfer.id})").notToBeNull() +// +// // IBAN should be set +// expect(account?.iban).withRepresentation("Account IBAN must be set").notToBeNull() +// +// // transfer 1 cent to yourself. Transferring money to oneself also doesn't require to enter a TAN according to PSD2 +// val BankTransferData = BankTransferData(Bank.customerName, account?.iban!!, Bank.bic, Money(Amount("0,01"), "EUR"), +// "${DateTimeFormatForUniqueBankTransferReference.format(Date())} Test transaction ${UUID.random()}") +// +// +// // when +// underTest.doBankTransferAsync(BankTransferData, Bank, account) { result -> +// response.set(result) +// countDownLatch.countDown() +// } +// +// } +// +// +// // then +// countDownLatch.await(30, TimeUnit.SECONDS) +// val result = response.get() +// +// expect(result.successful).isTrue() +// +// } +// +//} \ No newline at end of file diff --git a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/FinTsTestBaseJvm.kt b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/FinTsTestBaseJvm.kt similarity index 100% rename from fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/FinTsTestBaseJvm.kt rename to fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/FinTsTestBaseJvm.kt diff --git a/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt new file mode 100644 index 00000000..3f226962 --- /dev/null +++ b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/bankdetails/BanksFinTsDetailsRetriever.kt @@ -0,0 +1,317 @@ +//package net.dankito.banking.fints.bankdetails +// +//import kotlinx.coroutines.runBlocking +//import net.dankito.banking.bankfinder.InMemoryBankFinder +//import net.dankito.banking.fints.callback.NoOpFinTsClientCallback +//import net.dankito.banking.fints.messages.MessageBuilder +//import net.dankito.banking.fints.messages.MessageBuilderResult +//import net.dankito.banking.fints.messages.Separators +//import net.dankito.banking.fints.messages.datenelemente.implementierte.Dialogsprache +//import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AuftraggeberkontoErforderlich +//import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.BezeichnungDesTanMediumsErforderlich +//import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.SmsAbbuchungskontoErforderlich +//import net.dankito.banking.fints.model.* +//import net.dankito.banking.bankfinder.BankInfo +//import net.dankito.banking.fints.FinTsJobExecutor +//import net.dankito.banking.fints.callback.SimpleFinTsClientCallback +//import net.dankito.banking.fints.model.mapper.ModelMapper +//import net.dankito.banking.fints.response.BankResponse +//import net.dankito.banking.fints.response.segments.SepaAccountInfoParameters +//import net.dankito.banking.fints.response.segments.TanInfo +//import net.dankito.banking.fints.response.segments.TanMethodParameters +//import net.dankito.banking.fints.util.* +//import org.apache.commons.csv.CSVFormat +//import org.apache.commons.csv.CSVPrinter +//import org.junit.Ignore +//import kotlin.test.Test +//import org.slf4j.LoggerFactory +//import java.io.File +//import java.io.FileWriter +//import java.text.SimpleDateFormat +//import java.util.* +//import java.util.concurrent.CountDownLatch +//import java.util.concurrent.TimeUnit +//import java.util.concurrent.atomic.AtomicReference +// +// +//@Ignore // not a real test, run manually to retrieve FinTS information from all banks +//class BanksFinTsDetailsRetriever { +// +// companion object { +// private val OutputFolderDateFormat = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss") +// +// private val log = LoggerFactory.getLogger(BanksFinTsDetailsRetriever::class.java) +// } +// +// +// private val bankFinder = InMemoryBankFinder() +// +// private val product: ProductData = ProductData("15E53C26816138699C7B6A3E8", "1.0.0") // TODO: get version dynamically +// +// private val messageBuilder = MessageBuilder() +// +// private val modelMapper = object : ModelMapper(messageBuilder) { +// +// fun mapToTanMethodTypePublic(parameters: TanMethodParameters): TanMethodType? { +// return super.mapToTanMethodType(parameters) +// } +// +// } +// +// private val jobExecutor = object : FinTsJobExecutor(modelMapper = modelMapper) { +// +// fun getAndHandleResponseForMessagePublic(context: JobContext, message: MessageBuilderResult, callback: (BankResponse) -> Unit) { +// getAndHandleResponseForMessage(context, message, callback) +// } +// } +// +// +// private val requestNotSuccessful = mutableListOf() +// +// private val tanMethodParameter = mutableMapOf>() +// private val tanMethodTypes = mutableMapOf>() +// +// private val tanMethodParameterTechnicalIdentification = mutableSetOf() +// private val tanMethodParameterVersionDkTanMethod = mutableSetOf() +// +// private val requiresSmsAbbuchungskonto = mutableListOf() +// private val requiresAuftraggeberkonto = mutableListOf() +// private val requiresChallengeClass = mutableListOf() +// private val signatureStructured = mutableListOf() +// private val requiresNameOfTanMedia = mutableListOf() +// private val requiresHhdUcResponse = mutableListOf() +// +// private val doesNotSupportHKTAN6 = mutableListOf() +// private val doesNotSupportHKSAL5or7 = mutableListOf() +// private val doesNotSupportHKKAZ5to7 = mutableListOf() +// private val doesNotSupportHKCCS1 = mutableListOf() +// +// private val supportsHhd13 = mutableListOf() +// private val supportsHhd14 = mutableListOf() +// +// private val doesNotSupportPain_001_001_or_003_03 = mutableListOf() +// +// +// @Test +// fun retrieveAllBanksFinTsDetails() { +// +// val allBanks = bankFinder.getBankList() +// val banksSupportingFinTs = allBanks.filter { it.supportsFinTs3_0 } +// +// val outputFolder = File("bankData", OutputFolderDateFormat.format(Date())) +// val responsesFolder = File(outputFolder, "responses") +// responsesFolder.mkdirs() +// +// val csvFile = FileWriter(File(outputFolder, "bank_details.csv")) +// val csvPrinter = CSVPrinter(csvFile, CSVFormat.DEFAULT.withHeader( +// "BLZ", "Name", "Ort", "BPD", "Tanverfahren", "Technische Tanverfahrennamen", "HHD 1.3?", "HHD 1.4?", +// "HKTAN 6?", "HKTAN", "HKSAL 5?", "HKSAL", "HKKAZ 5-7?", "HKKAZ", "HKCAZ", "HKCCS 1?", "HKCCS", +// "pain.001.001.03?", "SEPA Formate", "Sprachen", "Untersstützte Geschäftsvorfälle" +// )) +// +// +// val uniqueBanks = banksSupportingFinTs.associateBy { "${it.bankCode}_${it.name}" } +// var bankIndex = 0 +// +// uniqueBanks.forEach { bankName, bankInfo -> +// log.info("[${++bankIndex}] Getting details for $bankName ...") +// +// getAndSaveBankDetails(bankInfo, responsesFolder, csvPrinter) +// } +// +// printStatistics() +// +// csvPrinter.close() +// csvFile.close() +// } +// +// +// private fun getAnonymousBankInfo(bank: BankData): BankResponse { +// val context = JobContext(JobContextType.AnonymousBankInfo, SimpleFinTsClientCallback(), product, bank) +// context.startNewDialog() +// +// val requestBody = messageBuilder.createAnonymousDialogInitMessage(context) +// +// val anonymousBankInfoResponse = AtomicReference() +// val countDownLatch = CountDownLatch(1) +// +// jobExecutor.getAndHandleResponseForMessagePublic(context, requestBody) { +// anonymousBankInfoResponse.set(it) +// countDownLatch.countDown() +// } +// +// countDownLatch.await(30, TimeUnit.SECONDS) +// +// modelMapper.updateBankData(bank, anonymousBankInfoResponse.get()) +// +// return anonymousBankInfoResponse.get() +// } +// +// private fun getAndSaveBankDetails(bankInfo: BankInfo, responsesFolder: File, csvPrinter: CSVPrinter) = runBlocking { +// val bank = BankData.anonymous(bankInfo.bankCode, bankInfo.pinTanAddress ?: "", bankInfo.bic) +// bank.bankName = bankInfo.name +// +// val anonymousBankInfoResponse = getAnonymousBankInfo(bank) +// +// File(responsesFolder, "${bankInfo.bankCode}_${bankInfo.name.replace('/', '-')}").writeText( +// anonymousBankInfoResponse.receivedSegments.joinToString(System.lineSeparator()) { it.segmentString + Separators.SegmentSeparator }) +// +// if (anonymousBankInfoResponse.successful == false) { +// requestNotSuccessful.add(bankInfo) +// log.warn("Did not receive response from bank $bankInfo: ${anonymousBankInfoResponse.receivedSegments.joinToString(System.lineSeparator()) { it.segmentString + Separators.SegmentSeparator }}") +// +// return@runBlocking +// } +// +// +// val supportsHKTAN6 = supportsJobInVersion(bank, "HKTAN", 6) +// val supportsHKSAL5or7 = supportsJobInVersion(bank, "HKSAL", listOf(5, 7)) +// val supportsHKKAZ5to7 = supportsJobInVersion(bank, "HKKAZ", listOf(5, 6, 7)) +// val supportsHKCCS1 = supportsJobInVersion(bank, "HKCCS", 1) +// +// val tanInfo = anonymousBankInfoResponse.receivedSegments.filterIsInstance(TanInfo::class.java) +// val tanMethodParameters = tanInfo.flatMap { it.tanProcedureParameters.methodParameters } +// val supportedTanMethods = tanMethodParameters.map { it.technicalTanMethodIdentification } +// val hhd13Supported = supportedTanMethods.firstOrNull { it.startsWith("hhd1.3", true) } != null +// val hhd14Supported = supportedTanMethods.firstOrNull { it.startsWith("hhd1.4", true) } != null +// +// val supportedHKTANVersions = tanInfo.map { it.segmentVersion } +// val supportedHKSALVersions = getSupportedVersions(bank, "HKSAL") +// val supportedHKKAZVersions = getSupportedVersions(bank, "HKKAZ") +// val supportedHKCAZVersions = getSupportedVersions(bank, "HKCAZ") +// val supportedHKCCSVersions = getSupportedVersions(bank, "HKCCS") +// +// val sepaAccountInfoParameters = anonymousBankInfoResponse.receivedSegments.filterIsInstance() +// val supportedSepaFormats = sepaAccountInfoParameters.flatMap { it.supportedSepaFormats }.map { it.substring(it.indexOf(":xsd:") + ":xsd:".length) } +// val supportsPain_001_001_or_003_03 = supportedSepaFormats.firstOrNull { it.contains("pain.001.001.03") or it.contains("pain.001.003.03") } != null +// +// csvPrinter.printRecord(bankInfo.bankCode, bankInfo.name, bankInfo.city, +// bank.bpdVersion, +// bank.tanMethodsSupportedByBank.joinToString(", ") { it.securityFunction.code + ": " + it.displayName + " (" + it.type + ")" }, +// supportedTanMethods.joinToString(", "), +// hhd13Supported, +// hhd14Supported, +// supportsHKTAN6, +// supportedHKTANVersions.joinToString(", "), +// supportsHKSAL5or7, +// supportedHKSALVersions.joinToString(", "), +// supportsHKKAZ5to7, +// supportedHKKAZVersions.joinToString(", "), +// supportedHKCAZVersions.joinToString(", "), +// supportsHKCCS1, +// supportedHKCCSVersions.joinToString(", "), +// supportsPain_001_001_or_003_03, +// supportedSepaFormats.joinToString(", "), +// bank.supportedLanguages.filter { it != Dialogsprache.German }.joinToString(", ") { it.name }, +// bank.supportedJobs.joinToString(", ") { it.jobName + " " + it.segmentVersion } +// ) +// +// tanMethodParameters.forEach { methodParameter -> +// if (tanMethodParameter.containsKey(methodParameter.methodName) == false) { +// tanMethodParameter.put(methodParameter.methodName, mutableSetOf(methodParameter)) +// } +// else { +// tanMethodParameter[methodParameter.methodName]?.add(methodParameter) +// } +// +// val tanMethodType = modelMapper.mapToTanMethodTypePublic(methodParameter) +// if (tanMethodTypes.containsKey(tanMethodType) == false) { +// tanMethodTypes.put(tanMethodType, mutableSetOf(methodParameter)) +// } +// else { +// tanMethodTypes[tanMethodType]?.add(methodParameter) +// } +// +// tanMethodParameterTechnicalIdentification.add(methodParameter.technicalTanMethodIdentification) +// tanMethodParameterVersionDkTanMethod.add(methodParameter.versionDkTanMethod) +// +// if (methodParameter.smsDebitAccountRequired == SmsAbbuchungskontoErforderlich.SmsAbbuchungskontoMussAngegebenWerden) { +// requiresSmsAbbuchungskonto.add(bankInfo) +// } +// if (methodParameter.initiatorAccountRequired == AuftraggeberkontoErforderlich.AuftraggeberkontoMussAngegebenWerdenWennImGeschaeftsvorfallEnthalten) { +// requiresAuftraggeberkonto.add(bankInfo) +// } +// if (methodParameter.challengeClassRequired) { +// requiresChallengeClass.add(bankInfo) +// } +// if (methodParameter.signatureStructured) { +// signatureStructured.add(bankInfo) +// } +// if (methodParameter.nameOfTanMediumRequired == BezeichnungDesTanMediumsErforderlich.BezeichnungDesTanMediumsMussAngegebenWerden) { +// requiresNameOfTanMedia.add(bankInfo) +// } +// if (methodParameter.hhdUcResponseRequired) { +// requiresHhdUcResponse.add(bankInfo) +// } +// } +// +// if (supportsHKTAN6 == false) { +// doesNotSupportHKTAN6.add(bankInfo) +// } +// if (supportsHKSAL5or7 == false) { +// doesNotSupportHKSAL5or7.add(bankInfo) +// } +// if (supportsHKKAZ5to7 == false) { +// doesNotSupportHKKAZ5to7.add(bankInfo) +// } +// if (supportsHKCCS1 == false) { +// doesNotSupportHKCCS1.add(bankInfo) +// } +// +// if (hhd13Supported) { +// supportsHhd13.add(bankInfo) +// } +// if (hhd14Supported) { +// supportsHhd14.add(bankInfo) +// } +// +// if (supportsPain_001_001_or_003_03 == false) { +// doesNotSupportPain_001_001_or_003_03.add(bankInfo) +// } +// } +// +// private fun getSupportedVersions(bank: BankData, jobName: String): List { +// return bank.supportedJobs.filter { it.jobName == jobName }.map { it.segmentVersion } +// } +// +// private fun supportsJobInVersion(bank: BankData, jobName: String, version: Int): Boolean { +// return supportsJobInVersion(bank, jobName, listOf(version)) +// } +// +// private fun supportsJobInVersion(bank: BankData, jobName: String, versions: List): Boolean { +// return bank.supportedJobs.firstOrNull { it.jobName == jobName && versions.contains(it.segmentVersion) } != null +// } +// +// +// private fun printStatistics() { +// log.info("Did not receive response from Banks ${printBanks(requestNotSuccessful)}") +// +// log.info("Mapped tanMethodTypes: ${tanMethodTypes.map { System.lineSeparator() + it.key + ": " + it.value.map { it.methodName + " " + it.dkTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") }}\n\n") +// log.info("TanMethodParameters:${tanMethodParameter.map { System.lineSeparator() + it.key + ": " + it.value.map { it.securityFunction.code + " " + it.dkTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") } }\n\n") +// +// log.info("TanMethodParameters TechnicalIdentification:${tanMethodParameterTechnicalIdentification.joinToString(", ") } \n\n") +// log.info("TanMethodParameters VersionDkTanMethod:${tanMethodParameterVersionDkTanMethod.joinToString(", ") } \n\n") +// +// log.info("Requires SmsAbbuchungskonto ${printBanks(requiresSmsAbbuchungskonto)}") // no (only 2) +// log.info("Requires Auftraggeberkonto ${printBanks(requiresAuftraggeberkonto)}") // yes, a lot of (12631) +// log.info("Requires ChallengeClass ${printBanks(requiresChallengeClass)}") // no +// log.info("Has structured signature ${printBanks(signatureStructured)}") // yes, a lot of (12651) +// log.info("Requires NameOfTanMedia ${printBanks(requiresNameOfTanMedia)}") // yes, a lot of (912) +// log.info("Requires HhdUcResponse ${printBanks(requiresHhdUcResponse)}") // no (only 2) +// +// log.info("Banks supporting HHD 1.3 (${supportsHhd13.size}):${printBanks(supportsHhd13)}") +// log.info("Banks supporting HHD 1.4 (${supportsHhd14.size}):${printBanks(supportsHhd14)}") +// +// log.info("Banks not supporting HKTAN 6 ${printBanks(doesNotSupportHKTAN6)}") +// log.info("Banks not supporting HKSAL 5 or 7 ${printBanks(doesNotSupportHKSAL5or7)}") +// log.info("Banks not supporting HKKAZ 5-7 ${printBanks(doesNotSupportHKKAZ5to7)}") +// log.info("Banks not supporting HKCCS 1 ${printBanks(doesNotSupportHKCCS1)}") +// +// log.info("Banks not supporting pain.001.001.03 or pain.001.003.03 ${printBanks(doesNotSupportPain_001_001_or_003_03)}") +// } +// +// private fun printBanks(banks: List): String { +// return "(${banks.size}):${ banks.joinToString { System.lineSeparator() + it } }\n\n\n" +// } +// +//} \ No newline at end of file diff --git a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/response/ResponseParserTestJvm.kt b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/response/ResponseParserTestJvm.kt similarity index 96% rename from fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/response/ResponseParserTestJvm.kt rename to fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/response/ResponseParserTestJvm.kt index e4a191be..0eaeb0bd 100644 --- a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/response/ResponseParserTestJvm.kt +++ b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/response/ResponseParserTestJvm.kt @@ -4,7 +4,7 @@ import net.dankito.banking.fints.FinTsTestBaseJvm import net.dankito.banking.fints.messages.HbciCharset import net.dankito.banking.fints.tan.TanImageDecoder import org.assertj.core.api.Assertions.assertThat -import org.junit.Test +import org.junit.jupiter.api.Test import java.nio.charset.Charset diff --git a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/transactions/Mt940AccountTransactionsParserTest.kt b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/transactions/Mt940AccountTransactionsParserTest.kt similarity index 95% rename from fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/transactions/Mt940AccountTransactionsParserTest.kt rename to fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/transactions/Mt940AccountTransactionsParserTest.kt index 5586cbe0..22cd68c1 100644 --- a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/transactions/Mt940AccountTransactionsParserTest.kt +++ b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/transactions/Mt940AccountTransactionsParserTest.kt @@ -4,7 +4,7 @@ import net.dankito.banking.fints.FinTsTestBaseJvm import net.dankito.banking.fints.model.AccountData import net.dankito.banking.fints.model.BankData import org.assertj.core.api.Assertions.assertThat -import org.junit.Test +import org.junit.jupiter.api.Test class Mt940AccountTransactionsParserTest : FinTsTestBaseJvm() { diff --git a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTestJvm.kt b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTestJvm.kt similarity index 94% rename from fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTestJvm.kt rename to fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTestJvm.kt index 38b1b399..dbec106f 100644 --- a/fints4k/src/jvm6Test/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTestJvm.kt +++ b/fints4k/src/jvmTest/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTestJvm.kt @@ -3,7 +3,7 @@ package net.dankito.banking.fints.transactions import net.dankito.banking.fints.FinTsTestBaseJvm import net.dankito.banking.fints.transactions.mt940.Mt940Parser import org.assertj.core.api.Assertions.assertThat -import org.junit.Test +import org.junit.jupiter.api.Test class Mt940ParserTestJvm : FinTsTestBaseJvm() { diff --git a/fints4k/src/jvm6Test/resources/logback-test.xml b/fints4k/src/jvmTest/resources/logback-test.xml similarity index 100% rename from fints4k/src/jvm6Test/resources/logback-test.xml rename to fints4k/src/jvmTest/resources/logback-test.xml diff --git a/fints4k/src/jvm6Test/resources/test_files/Decode_TanChallengeHhdUc_WithMaskedCharacter.txt b/fints4k/src/jvmTest/resources/test_files/Decode_TanChallengeHhdUc_WithMaskedCharacter.txt similarity index 100% rename from fints4k/src/jvm6Test/resources/test_files/Decode_TanChallengeHhdUc_WithMaskedCharacter.txt rename to fints4k/src/jvmTest/resources/test_files/Decode_TanChallengeHhdUc_WithMaskedCharacter.txt diff --git a/fints4k/src/jvm6Test/resources/test_files/TransactionsMt940.txt b/fints4k/src/jvmTest/resources/test_files/TransactionsMt940.txt similarity index 100% rename from fints4k/src/jvm6Test/resources/test_files/TransactionsMt940.txt rename to fints4k/src/jvmTest/resources/test_files/TransactionsMt940.txt diff --git a/fints4k/src/jvm6Test/resources/test_files/TransactionsMt940_2.txt b/fints4k/src/jvmTest/resources/test_files/TransactionsMt940_2.txt similarity index 100% rename from fints4k/src/jvm6Test/resources/test_files/TransactionsMt940_2.txt rename to fints4k/src/jvmTest/resources/test_files/TransactionsMt940_2.txt diff --git a/fints4k/src/main/AndroidManifest.xml b/fints4k/src/main/AndroidManifest.xml deleted file mode 100644 index aa7335e3..00000000 --- a/fints4k/src/main/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 763395f0..155302ba 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,3 @@ -android.enableJetifier=true -android.useAndroidX=true kotlin.code.style=official # Specifies the JVM arguments used for the daemon process. @@ -7,6 +5,7 @@ kotlin.code.style=official org.gradle.jvmargs=-Xmx3072m -quarkusVersion=1.11.0.Final +kotlinVersion=1.6.0 -#kotlin.js.compiler=ir \ No newline at end of file +coroutinesVersion=1.6.0 +ktorVersion=2.0.0-beta-1 \ No newline at end of file diff --git a/persistence/LuceneBankingPersistence/build.gradle b/persistence/LuceneBankingPersistence/build.gradle deleted file mode 100644 index fe208776..00000000 --- a/persistence/LuceneBankingPersistence/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' - - -sourceCompatibility = "1.7" -targetCompatibility = "1.7" - -compileKotlin { - kotlinOptions.jvmTarget = "1.6" -} -compileTestKotlin { - kotlinOptions.jvmTarget = "1.6" -} - - -dependencies { - implementation project(":BankingUiCommon") - - api project(":BankingPersistenceJson") - - implementation "net.dankito.search:lucene-4-utils:$luceneUtilsVersion" - - - testImplementation "junit:junit:$junitVersion" - testImplementation "org.assertj:assertj-core:$assertJVersion" - - testImplementation "org.mockito:mockito-core:$mockitoVersion" - - testImplementation "org.slf4j:slf4j-simple:$slf4jVersion" -} \ No newline at end of file diff --git a/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/LuceneConfig.kt b/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/LuceneConfig.kt deleted file mode 100644 index d97f0f8f..00000000 --- a/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/LuceneConfig.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.dankito.banking - -import java.io.File - - -class LuceneConfig { - - companion object { - const val BankAccountIdFieldName = "bank_account_id" - - const val IdFieldName = "id" - - const val OtherPartyNameFieldName = "other_party_name" - - const val OtherPartyBankCodeFieldName = "other_party_bank_code" - - const val OtherPartyAccountIdFieldName = "other_party_account_id" - - const val BookingDateFieldName = "booking_date" - const val DateSortFieldName = "value_date_sort" - - const val ReferenceFieldName = "reference" - - const val BookingTextFieldName = "booking_text" - - const val AmountFieldName = "amount" - - const val CurrencyFieldName = "currency" - - const val BalanceFieldName = "balance" - - - fun getAccountTransactionsIndexFolder(indexFolder: File): File { - return File(indexFolder, "account_transactions") - } - - } - -} \ No newline at end of file diff --git a/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/persistence/LuceneBankingPersistence.kt b/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/persistence/LuceneBankingPersistence.kt deleted file mode 100644 index 3063331b..00000000 --- a/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/persistence/LuceneBankingPersistence.kt +++ /dev/null @@ -1,109 +0,0 @@ -package net.dankito.banking.persistence - -import net.dankito.utils.multiplatform.File -import net.dankito.banking.LuceneConfig -import net.dankito.banking.LuceneConfig.Companion.AmountFieldName -import net.dankito.banking.LuceneConfig.Companion.BalanceFieldName -import net.dankito.banking.LuceneConfig.Companion.BankAccountIdFieldName -import net.dankito.banking.LuceneConfig.Companion.BookingDateFieldName -import net.dankito.banking.LuceneConfig.Companion.DateSortFieldName -import net.dankito.banking.LuceneConfig.Companion.BookingTextFieldName -import net.dankito.banking.LuceneConfig.Companion.CurrencyFieldName -import net.dankito.banking.LuceneConfig.Companion.IdFieldName -import net.dankito.banking.LuceneConfig.Companion.OtherPartyAccountIdFieldName -import net.dankito.banking.LuceneConfig.Companion.OtherPartyBankCodeFieldName -import net.dankito.banking.LuceneConfig.Companion.OtherPartyNameFieldName -import net.dankito.banking.LuceneConfig.Companion.ReferenceFieldName -import net.dankito.banking.ui.model.* -import net.dankito.banking.util.ISerializer -import net.dankito.banking.util.JacksonJsonSerializer -import net.dankito.utils.lucene.index.DocumentsWriter -import net.dankito.utils.lucene.index.FieldBuilder -import org.apache.lucene.index.IndexableField -import org.slf4j.LoggerFactory - - -open class LuceneBankingPersistence( - protected val indexFolder: File, - databaseFolder: File, - serializer: ISerializer = JacksonJsonSerializer() -) : BankingPersistenceJson(databaseFolder, serializer), IBankingPersistence { - - companion object { - - // i really hate this solution, but could find no other way to avoid app crashes when - // Android app gets restored as previous IndexWriter is not not destroyed yet and holds - // write lock and a new IndexWriter instance in DocumentsWriter gets instantiated - protected var documentsWriter: DocumentsWriter? = null - - - private val log = LoggerFactory.getLogger(LuceneBankingPersistence::class.java) - - } - - - protected val fields = FieldBuilder() - - - override fun saveOrUpdateAccountTransactions(account: TypedBankAccount, transactions: List) { - val writer = getWriter() - - transactions.forEach { transaction -> - writer.updateDocumentForNonNullFields( - IdFieldName, transaction.technicalId, - *createFieldsForAccountTransaction(account, transaction).toTypedArray() - ) - } - - writer.flushChangesToDisk() - } - - protected open fun createFieldsForAccountTransaction(account: TypedBankAccount, transaction: IAccountTransaction): List { - return listOf( - fields.keywordField(BankAccountIdFieldName, account.technicalId), - fields.nullableFullTextSearchField(OtherPartyNameFieldName, transaction.otherPartyName, true), - fields.fullTextSearchField(ReferenceFieldName, transaction.reference, true), - fields.nullableFullTextSearchField(BookingTextFieldName, transaction.bookingText, true), - - fields.nullableStoredField(OtherPartyBankCodeFieldName, transaction.otherPartyBankCode), - fields.nullableStoredField(OtherPartyAccountIdFieldName, transaction.otherPartyAccountId), - fields.storedField(BookingDateFieldName, transaction.bookingDate), - fields.storedField(AmountFieldName, transaction.amount), - fields.storedField(CurrencyFieldName, transaction.currency), - fields.nullableStoredField(BalanceFieldName, transaction.closingBalance), // TODO: remove - - fields.sortField(DateSortFieldName, transaction.valueDate) - ) - } - - - override fun deleteBank(bank: TypedBankData, allBanks: List) { - try { - deleteAccountTransactions(bank.accounts) - } catch (e: Exception) { - log.error("Could not delete account transactions of account $bank", e) - } - - super.deleteBank(bank, allBanks) - } - - protected open fun deleteAccountTransactions(accounts: List) { - val writer = getWriter() - - val accountIds = accounts.map { it.technicalId } - writer.deleteDocumentsAndFlushChangesToDisk(BankAccountIdFieldName, *accountIds.toTypedArray()) - } - - - @Synchronized - protected open fun getWriter(): DocumentsWriter { - documentsWriter?.let { return it } - - val writer = DocumentsWriter(LuceneConfig.getAccountTransactionsIndexFolder(indexFolder)) - - documentsWriter = writer - - return writer - } - -} \ No newline at end of file diff --git a/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/search/LuceneTransactionPartySearcher.kt b/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/search/LuceneTransactionPartySearcher.kt deleted file mode 100644 index 6b5105a1..00000000 --- a/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/search/LuceneTransactionPartySearcher.kt +++ /dev/null @@ -1,45 +0,0 @@ -package net.dankito.banking.search - -import net.dankito.banking.LuceneConfig -import net.dankito.banking.LuceneConfig.Companion.OtherPartyAccountIdFieldName -import net.dankito.banking.LuceneConfig.Companion.OtherPartyBankCodeFieldName -import net.dankito.banking.LuceneConfig.Companion.OtherPartyNameFieldName -import net.dankito.utils.lucene.mapper.PropertyDescription -import net.dankito.utils.lucene.mapper.PropertyType -import net.dankito.utils.lucene.search.MappedSearchConfig -import net.dankito.utils.lucene.search.QueryBuilder -import net.dankito.utils.lucene.search.Searcher -import java.io.File - - -open class LuceneTransactionPartySearcher(indexFolder: File) : ITransactionPartySearcher { - - companion object { - - private val properties = listOf( - PropertyDescription(PropertyType.NullableString, OtherPartyNameFieldName, TransactionParty::name), - PropertyDescription(PropertyType.NullableString, OtherPartyBankCodeFieldName, TransactionParty::bic), - PropertyDescription(PropertyType.NullableString, OtherPartyAccountIdFieldName, TransactionParty::iban) - ) - - } - - - protected val queries = QueryBuilder() - - protected val searcher = Searcher(LuceneConfig.getAccountTransactionsIndexFolder(indexFolder)) - - - override fun findTransactionParty(query: String): List { - val luceneQuery = queries.createQueriesForSingleTerms(query.toLowerCase()) { singleTerm -> - listOf( - queries.fulltextQuery(OtherPartyNameFieldName, singleTerm) - ) - } - - return searcher.searchAndMap(MappedSearchConfig(luceneQuery, TransactionParty::class.java, properties)) - .toSet() // don't display same transaction party multiple times - .filterNot { it.iban.isNullOrBlank() || it.bic.isNullOrBlank() } // e.g. comdirect doesn't supply other party's IBAN and BIC -> filter these as they have no value for auto-entering a transaction party's IBAN and BIC - } - -} \ No newline at end of file diff --git a/persistence/LuceneBankingPersistence/src/test/kotlin/net/dankito/banking/search/LuceneTransactionPartySearcherTest.kt b/persistence/LuceneBankingPersistence/src/test/kotlin/net/dankito/banking/search/LuceneTransactionPartySearcherTest.kt deleted file mode 100644 index 1c714b98..00000000 --- a/persistence/LuceneBankingPersistence/src/test/kotlin/net/dankito/banking/search/LuceneTransactionPartySearcherTest.kt +++ /dev/null @@ -1,235 +0,0 @@ -package net.dankito.banking.search - -import net.dankito.banking.persistence.LuceneBankingPersistence -import net.dankito.banking.ui.model.BankData -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount -import net.dankito.utils.io.FileUtils -import net.dankito.utils.multiplatform.File -import net.dankito.utils.multiplatform.toBigDecimal -import net.dankito.utils.multiplatform.toDate -import org.assertj.core.api.Assertions.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.mockito.Mockito.mock -import java.math.BigDecimal -import java.text.SimpleDateFormat -import java.util.* -import java.util.concurrent.ThreadLocalRandom - - -class LuceneTransactionPartySearcherTest { - - companion object { - - private val dataFolder = File("testData") - - private val databaseFolder = File(dataFolder, "db") - - private val indexFolder = File(dataFolder, "index") - - - private val BookingDate = "27.03.2020" - private val OtherPartyName = "Mahatma Gandhi" - private val OtherPartyBankCode = "12345678" - private val OtherPartyAccountId = "0987654321" - private val Amount = BigDecimal.valueOf(123.45) - - - private val bankAccountMock = BankAccount(mock(BankData::class.java), "", "", null, null, "") - - - private val dateFormat = SimpleDateFormat("dd.MM.yyyy") - - } - - - private val fileUtils = FileUtils() - - private val bankingPersistence = LuceneBankingPersistence(indexFolder, databaseFolder) - - private val underTest = LuceneTransactionPartySearcher(indexFolder) - - - @Before - fun setUp() { - clearDataFolder() - } - - @After - fun tearDown() { - clearDataFolder() - } - - private fun clearDataFolder() { - fileUtils.deleteFolderRecursively(dataFolder) - } - - - @Test - fun findTransactionParty_ByFullName() { - - // given - val query = OtherPartyName - - val before = underTest.findTransactionParty(query) - assertThat(before).isEmpty() - - bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( - createTransaction(bankAccountMock, BookingDate, Amount, OtherPartyName, OtherPartyBankCode, OtherPartyAccountId), - createTransaction(), - createTransaction() - )) - - - // when - val result = underTest.findTransactionParty(query) - - - // then - assertThat(result).hasSize(1) - assertThat(result.first().name).isEqualTo(OtherPartyName) - assertThat(result.first().bic).isEqualTo(OtherPartyBankCode) - assertThat(result.first().iban).isEqualTo(OtherPartyAccountId) - } - - @Test - fun findTransactionParty_ByPartialName() { - - // given - val query = "gand" - - val before = underTest.findTransactionParty(query) - assertThat(before).isEmpty() - - bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( - createTransaction(bankAccountMock, BookingDate, Amount, OtherPartyName, OtherPartyBankCode, OtherPartyAccountId), - createTransaction(), - createTransaction() - )) - - - // when - val result = underTest.findTransactionParty(query) - - - // then - assertThat(result).hasSize(1) - assertThat(result.first().name).isEqualTo(OtherPartyName) - assertThat(result.first().bic).isEqualTo(OtherPartyBankCode) - assertThat(result.first().iban).isEqualTo(OtherPartyAccountId) - } - - @Test - fun findTransactionParty_SimilarNames() { - - // given - val query = "gand" - val secondOtherPartyName = "Gandalf" - - val before = underTest.findTransactionParty(query) - assertThat(before).isEmpty() - - bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( - createTransaction(bankAccountMock, BookingDate, Amount, OtherPartyName, OtherPartyBankCode, OtherPartyAccountId), - createTransaction(otherPartyName = secondOtherPartyName), - createTransaction() - )) - - - // when - val result = underTest.findTransactionParty(query) - - - // then - assertThat(result).hasSize(2) - assertThat(result.map { it.name }).containsExactlyInAnyOrder(OtherPartyName, secondOtherPartyName) - } - - @Test - fun findTransactionParty_DuplicateEntries() { - - // given - val query = OtherPartyName - - val before = underTest.findTransactionParty(query) - assertThat(before).isEmpty() - - bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( - createTransaction(bankAccountMock, BookingDate, Amount, OtherPartyName, OtherPartyBankCode, OtherPartyAccountId), - createTransaction(bankAccountMock, "01.02.2020", Amount, OtherPartyName, OtherPartyBankCode, OtherPartyAccountId), - createTransaction(bankAccountMock, "03.04.2020", Amount, OtherPartyName, OtherPartyBankCode, OtherPartyAccountId), - createTransaction(), - createTransaction() - )) - - - // when - val result = underTest.findTransactionParty(query) - - - // then - assertThat(result).hasSize(1) - assertThat(result.first().name).isEqualTo(OtherPartyName) - assertThat(result.first().bic).isEqualTo(OtherPartyBankCode) - assertThat(result.first().iban).isEqualTo(OtherPartyAccountId) - } - - @Test - fun findTransactionParty_OtherName() { - - // given - val query = "Mandela" - - val before = underTest.findTransactionParty(query) - assertThat(before).isEmpty() - - bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( - createTransaction(bankAccountMock, BookingDate, Amount, OtherPartyName, OtherPartyBankCode, OtherPartyAccountId), - createTransaction(), - createTransaction() - )) - - - // when - val result = underTest.findTransactionParty(query) - - - // then - assertThat(result).isEmpty() - } - - - private fun createTransaction(bankAccount: BankAccount = bankAccountMock, bookingDate: String, amount: BigDecimal = randomBigDecimal(), - otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(), - otherPartyAccountId: String = randomString(), reference: String = randomString()): AccountTransaction { - - return createTransaction(bankAccount, dateFormat.parse(bookingDate), amount, otherPartyName, - otherPartyBankCode, otherPartyAccountId, reference) - } - - private fun createTransaction(bankAccount: BankAccount = bankAccountMock, bookingDate: Date = randomDate(), amount: BigDecimal = randomBigDecimal(), - otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(), - otherPartyAccountId: String = randomString(), reference: String = randomString()): AccountTransaction { - - return AccountTransaction(bankAccount, amount.toBigDecimal(), "EUR", reference, bookingDate.toDate(), otherPartyName, otherPartyBankCode, otherPartyAccountId, null, bookingDate.toDate()) - } - - private fun randomString(): String { - return UUID.randomUUID().toString() - } - - private fun randomDate(): Date { - val pseudoRandomLong = ThreadLocalRandom.current().nextLong(0, Date().time) - - return Date(pseudoRandomLong) - } - - private fun randomBigDecimal(): BigDecimal { - val pseudoRandomDouble = ThreadLocalRandom.current().nextDouble(-5-000.0, 12_000.0) - - return BigDecimal.valueOf(pseudoRandomDouble) - } - -} \ No newline at end of file diff --git a/persistence/database/RoomBankingPersistence/build.gradle b/persistence/database/RoomBankingPersistence/build.gradle deleted file mode 100644 index a4c9db2b..00000000 --- a/persistence/database/RoomBankingPersistence/build.gradle +++ /dev/null @@ -1,39 +0,0 @@ -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" - - implementation "net.zetetic:android-database-sqlcipher:$sqlCipherVersion" - -} \ No newline at end of file diff --git a/persistence/database/RoomBankingPersistence/consumer-rules.pro b/persistence/database/RoomBankingPersistence/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/persistence/database/RoomBankingPersistence/proguard-rules.pro b/persistence/database/RoomBankingPersistence/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/persistence/database/RoomBankingPersistence/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 deleted file mode 100644 index 622eda74..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - / - \ 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 deleted file mode 100644 index 6045d5f5..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/BankingDatabase.kt +++ /dev/null @@ -1,33 +0,0 @@ -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, - TanMethod::class, TanMedium::class, - AppSettings::class, TanMethodSettings::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 tanMethodDao(): TanMethodDao - - abstract fun tanMediumDao(): TanMediumDao - - - abstract fun appSettingsDao(): AppSettingsDao - - abstract fun tanMethodSettingsDao(): TanMethodSettingsDao - -} \ 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 deleted file mode 100644 index e061c6e1..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/RoomBankingPersistence.kt +++ /dev/null @@ -1,286 +0,0 @@ -package net.dankito.banking.persistence - -import android.content.Context -import androidx.room.Room -import net.dankito.banking.persistence.dao.BaseDao -import net.dankito.banking.persistence.dao.saveOrUpdate -import net.dankito.banking.persistence.model.* -import net.dankito.banking.search.ITransactionPartySearcher -import net.dankito.banking.search.TransactionParty -import net.dankito.banking.ui.model.IAccountTransaction -import net.dankito.banking.ui.model.TypedBankAccount -import net.dankito.banking.ui.model.TypedBankData -import net.dankito.banking.ui.model.settings.AppSettings -import net.dankito.banking.ui.model.tan.MobilePhoneTanMedium -import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium -import net.dankito.banking.util.persistence.downloadIcon -import net.dankito.utils.multiplatform.asString -import net.sqlcipher.database.SQLiteDatabase -import net.sqlcipher.database.SupportFactory -import org.slf4j.LoggerFactory -import java.util.concurrent.CopyOnWriteArraySet - - -open class RoomBankingPersistence(protected open val applicationContext: Context) : IBankingPersistence, ITransactionPartySearcher { - - companion object { - const val DatabaseName = "banking-database" - - const val AppSettingsId = 1 - - const val FlickerCodeTanMethodSettingsId = 1 - const val QrCodeTanMethodSettingsId = 2 - const val PhotoTanTanMethodSettingsId = 3 - - private val log = LoggerFactory.getLogger(RoomBankingPersistence::class.java) - } - - - protected lateinit var database: BankingDatabase - - protected open var isInitialized = false - - protected open val initializedListeners = CopyOnWriteArraySet<() -> Unit>() - - - override fun decryptData(password: CharArray): Boolean { - val result = openDatabase(password) - - if (result) { - callInitializedListeners() - } - - return result - } - - override fun changePassword(newPassword: CharArray): Boolean { - if (this::database.isInitialized) { - val cursor = database.query("PRAGMA rekey = '${newPassword.asString()}';", emptyArray()) - - return cursor.count == 1 // TODO: also check if first column content is 'ok' ? - } - else { // database hasn't been opened yet, that means we're on the first app run - return openDatabase(newPassword) - } - } - - protected open fun openDatabase(password: CharArray): Boolean { - try { - val passphrase = SQLiteDatabase.getBytes(password) - val factory = SupportFactory(passphrase) - - database = Room.databaseBuilder(applicationContext, BankingDatabase::class.java, DatabaseName) - .openHelperFactory(factory) - .build() - - return true - } catch (e: Exception) { - log.error("Could not open database", e) - } - - return false - } - - - override fun saveOrUpdateBank(bank: TypedBankData, allBanks: List) { - (bank as? Bank)?.let { bank -> - bank.selectedTanMethodId = bank.selectedTanMethod?.technicalId - - database.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 } - database.bankAccountDao().saveOrUpdate(accounts) - - // TODO: in this way removed TAN methods won't be deleted from DB and therefore still be visible to user - val tanMethods = bank.supportedTanMethods.filterIsInstance() - tanMethods.forEach { tantanMethod -> - if (tantanMethod.bankId == BaseDao.ObjectNotInsertedId) { - tantanMethod.bankId = bank.id - database.tanMethodDao().insert(tantanMethod) - } - else { - database.tanMethodDao().update(tantanMethod) - } - } - - // TODO: in this way removed TAN media won't be deleted from DB and therefore still be visible to user - val tanMedia = bank.tanMedia.map { tanMedium -> - bank.tanMediumEntities.firstOrNull { it.id == tanMedium.technicalId } ?: map(bank, tanMedium) - } - database.tanMediumDao().saveOrUpdate(tanMedia) - bank.tanMediumEntities = tanMedia - } - } - - override fun deleteBank(bank: TypedBankData, allBanks: List) { - (bank as? Bank)?.let { bank -> - database.accountTransactionDao().delete(bank.accounts.flatMap { it.bookedTransactions }.filterIsInstance()) - - database.bankAccountDao().delete(bank.accounts.filterIsInstance()) - - database.tanMethodDao().delete(bank.supportedTanMethods.filterIsInstance()) - database.tanMediumDao().delete(bank.tanMedia.filterIsInstance()) - - database.bankDao().delete(bank) - } - } - - override fun readPersistedBanks(): List { - val banks = database.bankDao().getAll() - - val accounts = database.bankAccountDao().getAll() - - val transactions = database.accountTransactionDao().getAll() - - val tanMethods = database.tanMethodDao().getAll() - - val tanMedia = database.tanMediumDao().getAll() - - banks.forEach { bank -> - bank.accounts = accounts.filter { it.bankId == bank.id } - - bank.accounts.filterIsInstance().forEach { account -> - account.bank = bank - - account.bookedTransactions = transactions.filter { it.accountId == account.id } - - account.bookedTransactions.filterIsInstance().forEach { transaction -> - transaction.account = account - } - } - - bank.supportedTanMethods = tanMethods.filter { it.bankId == bank.id } - bank.selectedTanMethod = bank.supportedTanMethods.firstOrNull { it.technicalId == bank.selectedTanMethodId } - - bank.tanMediumEntities = tanMedia.filter { it.bankId == bank.id } - bank.tanMedia = bank.tanMediumEntities.map { map(it) } - } - - return banks - } - - override fun saveOrUpdateAccountTransactions(account: TypedBankAccount, transactions: List) { - val accountId = (account as? BankAccount)?.id ?: account.technicalId.toLong() - - val mappedTransactions = transactions.filterIsInstance() - - mappedTransactions.forEach { it.accountId = accountId } - - database.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 saveOrUpdateAppSettings(appSettings: AppSettings) { - val mapped = net.dankito.banking.persistence.model.AppSettings(appSettings.automaticallyUpdateAccountsAfterMinutes, - appSettings.lockAppAfterMinutes, appSettings.screenshotsAllowed, appSettings.lastSelectedOpenPdfFolder, appSettings.lastSelectedImportFolder, appSettings.lastSelectedExportFolder) - database.appSettingsDao().saveOrUpdate(mapped) - - saveOrUpdateTanMethodSettings(appSettings.flickerCodeSettings, FlickerCodeTanMethodSettingsId) - saveOrUpdateTanMethodSettings(appSettings.qrCodeSettings, QrCodeTanMethodSettingsId) - saveOrUpdateTanMethodSettings(appSettings.photoTanSettings, PhotoTanTanMethodSettingsId) - } - - protected open fun saveOrUpdateTanMethodSettings(settings: net.dankito.banking.ui.model.settings.TanMethodSettings?, id: Int) { - settings?.let { - val settingsEntity = TanMethodSettings(id, it.width, it.height, it.space, it.frequency) - - database.tanMethodSettingsDao().saveOrUpdate(settingsEntity) - } - } - - override fun readPersistedAppSettings(): AppSettings? { - val tanMethodSettings = database.tanMethodSettingsDao().getAll() - - val settings = AppSettings() - - database.appSettingsDao().getAll().firstOrNull { it.id == AppSettingsId }?.let { persistedSettings -> - settings.automaticallyUpdateAccountsAfterMinutes = persistedSettings.automaticallyUpdateAccountsAfterMinutes - settings.lockAppAfterMinutes = persistedSettings.lockAppAfterMinutes - settings.screenshotsAllowed = persistedSettings.screenshotsAllowed - settings.lastSelectedOpenPdfFolder = persistedSettings.lastSelectedOpenPdfFolder - settings.lastSelectedImportFolder = persistedSettings.lastSelectedImportFolder - settings.lastSelectedExportFolder = persistedSettings.lastSelectedExportFolder - } - - settings.flickerCodeSettings = findTanMethodSettings(FlickerCodeTanMethodSettingsId, tanMethodSettings) - settings.qrCodeSettings = findTanMethodSettings(QrCodeTanMethodSettingsId, tanMethodSettings) - settings.photoTanSettings = findTanMethodSettings(PhotoTanTanMethodSettingsId, tanMethodSettings) - - return settings - } - - protected open fun findTanMethodSettings(id: Int, settings: List): TanMethodSettings? { - return settings.firstOrNull { it.id == id } - } - - - override fun saveBankIcon(bank: TypedBankData, iconUrl: String, fileExtension: String?) { - val iconData = downloadIcon(iconUrl) - bank.iconData = iconData - - (bank as? Bank)?.let { - database.bankDao().saveOrUpdate(it) - } - } - - - override fun findTransactionParty(query: String): List { - return database.accountTransactionDao().findTransactionParty(query) - .toSet() // don't display same transaction party multiple times - .filterNot { it.bankCode.isNullOrBlank() || it.accountId.isNullOrBlank() } - .map { TransactionParty(it.name, it.accountId, it.bankCode) } - } - - - override fun addInitializedListener(listener: () -> Unit) { - if (isInitialized) { - listener() - } else { - initializedListeners.add(listener) - } - } - - protected open fun callInitializedListeners() { - isInitialized = true - val copy = ArrayList(initializedListeners) - initializedListeners.clear() - - copy.forEach { listener -> { - try { - listener() - } catch (e: Exception) { - log.error("Could not call listener $listener", e) - } - } } - } - -} \ 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 deleted file mode 100644 index b067a05c..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/TypeConverters.kt +++ /dev/null @@ -1,91 +0,0 @@ -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.TanMethodType -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 fromTanMethodType(value: TanMethodType): Int { - return value.ordinal - } - - @TypeConverter - fun toTanMethodType(value: Int): TanMethodType { - return TanMethodType.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 deleted file mode 100644 index bbb39265..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/AccountTransactionDao.kt +++ /dev/null @@ -1,18 +0,0 @@ -package net.dankito.banking.persistence.dao - -import androidx.room.Dao -import androidx.room.Query -import net.dankito.banking.persistence.model.AccountTransaction -import net.dankito.banking.persistence.model.TransactionParty - - -@Dao -interface AccountTransactionDao : BaseDao { - - @Query("SELECT * FROM AccountTransaction") - fun getAll(): List - - @Query("SELECT otherPartyName, otherPartyBankCode, otherPartyAccountId FROM AccountTransaction WHERE otherPartyName LIKE '%' || :query || '%'") - fun findTransactionParty(query: String): List - -} \ No newline at end of file diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/AppSettingsDao.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/AppSettingsDao.kt deleted file mode 100644 index 200db798..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/AppSettingsDao.kt +++ /dev/null @@ -1,13 +0,0 @@ -package net.dankito.banking.persistence.dao - -import androidx.room.* -import net.dankito.banking.persistence.model.AppSettings - - -@Dao -interface AppSettingsDao : BaseDao { - - @Query("SELECT * FROM AppSettings") - 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 deleted file mode 100644 index 3a4e2af6..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BankAccountDao.kt +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index ed23ede9..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BankDao.kt +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 794362cb..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BaseDao.kt +++ /dev/null @@ -1,35 +0,0 @@ -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 deleted file mode 100644 index 28dc3c97..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/BaseDaoExtensions.kt +++ /dev/null @@ -1,58 +0,0 @@ -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 deleted file mode 100644 index 676c48b0..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanMediumDao.kt +++ /dev/null @@ -1,14 +0,0 @@ -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/TanMethodDao.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanMethodDao.kt deleted file mode 100644 index cae83b1a..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanMethodDao.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.dankito.banking.persistence.dao - -import androidx.room.Dao -import androidx.room.Query -import net.dankito.banking.persistence.model.TanMethod - - -@Dao -interface TanMethodDao : BaseDao { - - @Query("SELECT * FROM TanMethod") - fun getAll(): List - -} \ No newline at end of file diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanMethodSettingsDao.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanMethodSettingsDao.kt deleted file mode 100644 index 1aaca077..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/dao/TanMethodSettingsDao.kt +++ /dev/null @@ -1,13 +0,0 @@ -package net.dankito.banking.persistence.dao - -import androidx.room.* -import net.dankito.banking.persistence.model.TanMethodSettings - - -@Dao -interface TanMethodSettingsDao : BaseDao { - - @Query("SELECT * FROM TanMethodSettings") - 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 deleted file mode 100644 index 261df062..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/AccountTransaction.kt +++ /dev/null @@ -1,94 +0,0 @@ -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 account: BankAccount, - - override var amount: BigDecimal, - override var currency: String, - override var unparsedReference: 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 sepaReference: String?, - override var deviantOriginator: String?, - override var deviantRecipient: String?, - override var referenceWithNoSpecialType: 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(account: BankAccount, otherPartyName: String?, unparsedReference: String, amount: BigDecimal, valueDate: Date, bookingText: String?) - : this(account, amount, "EUR", unparsedReference, valueDate, - otherPartyName, null, null, bookingText, valueDate) - - - constructor(account: BankAccount, amount: BigDecimal, currency: String, unparsedReference: String, bookingDate: Date, - otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, - bookingText: String?, valueDate: Date) - : this(account, amount, currency, unparsedReference, 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 accountId: 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/AppSettings.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/AppSettings.kt deleted file mode 100644 index 64fd8d0c..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/AppSettings.kt +++ /dev/null @@ -1,24 +0,0 @@ -package net.dankito.banking.persistence.model - -import androidx.room.Entity -import androidx.room.PrimaryKey -import net.dankito.banking.persistence.RoomBankingPersistence -import net.dankito.banking.ui.model.settings.AppSettings - - -@Entity -open class AppSettings( - open var automaticallyUpdateAccountsAfterMinutes: Int? = AppSettings.DefaultAutomaticallyUpdateAccountsAfterMinutes, - open var lockAppAfterMinutes: Int? = null, - open var screenshotsAllowed: Boolean = false, - open var lastSelectedOpenPdfFolder: String? = null, - open var lastSelectedImportFolder: String? = null, - open var lastSelectedExportFolder: String? = null -) { - - internal constructor() : this(AppSettings.DefaultAutomaticallyUpdateAccountsAfterMinutes, null, false) - - @PrimaryKey - open var id: Int = RoomBankingPersistence.AppSettingsId - -} \ 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 deleted file mode 100644 index 468ce9e3..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/Bank.kt +++ /dev/null @@ -1,63 +0,0 @@ -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.TypedBankData -import net.dankito.banking.ui.model.tan.TanMedium -import net.dankito.banking.ui.model.tan.TanMethod - - -@Entity -open class Bank( - override var bankCode: String, - override var userName: 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 = userName, - override var iconData: ByteArray? = null, - - @Ignore - override var accounts: List = listOf(), - - @Ignore - override var supportedTanMethods: List = listOf(), - @Ignore - override var selectedTanMethod: TanMethod? = null, - @Ignore - override var tanMedia: List = listOf(), - - - @PrimaryKey(autoGenerate = true) - open var id: Long = BaseDao.IdNotSet, - - override var technicalId: String = id.toString(), - - override var wrongCredentialsEntered: Boolean = false, - - override var savePassword: Boolean = true, - - override var userSetDisplayName: String? = null, - override var displayIndex: Int = 0 -) : TypedBankData { - - internal constructor() : this("", "", "", "", "", "", "") // for object deserializers - - - open var selectedTanMethodId: String? = null - - @Ignore - open var tanMediumEntities = listOf() - - - 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 deleted file mode 100644 index fadc019c..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/BankAccount.kt +++ /dev/null @@ -1,82 +0,0 @@ -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 bank: TypedBankData, - override var identifier: String, - override var accountHolderName: String, - override var iban: String?, - override var subAccountNumber: String?, - override var balance: BigDecimal = BigDecimal.Zero, - override var currency: String = "EUR", - override var type: BankAccountType = BankAccountType.CheckingAccount, - override var productName: String? = null, - override var accountLimit: String? = null, - override var retrievedTransactionsFromOn: Date? = null, - override var retrievedTransactionsUpTo: Date? = null, - - override var supportsRetrievingAccountTransactions: Boolean = false, - override var supportsRetrievingBalance: Boolean = false, - override var supportsTransferringMoney: Boolean = false, - override var supportsRealTimeTransfer: 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(bank: TypedBankData, productName: String?, identifier: String) : this(bank, productName, identifier, BankAccountType.CheckingAccount) - - constructor(bank: TypedBankData, productName: String?, identifier: String, type: BankAccountType = BankAccountType.CheckingAccount, balance: BigDecimal = BigDecimal.Zero) - : this(bank, 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 haveAllTransactionsBeenRetrieved: Boolean = false - - override var isAccountTypeSupportedByApplication: Boolean = true - - override var countDaysForWhichTransactionsAreKept: Int? = null - - - override var userSetDisplayName: String? = null - - override var displayIndex: Int = 0 - - - override var hideAccount = false - - override var includeInAutomaticAccountsUpdate = true - - override var doNotShowStrikingFetchAllTransactionsView = false - - - 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 deleted file mode 100644 index d61eba78..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/RoomModelCreator.kt +++ /dev/null @@ -1,75 +0,0 @@ -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.TypedBankData -import net.dankito.banking.ui.model.mapper.IModelCreator -import net.dankito.banking.ui.model.tan.AllowedTanFormat -import net.dankito.banking.ui.model.tan.TanMethodType -import net.dankito.utils.multiplatform.BigDecimal -import net.dankito.utils.multiplatform.Date - - -open class RoomModelCreator : IModelCreator { - - override fun createBank( - bankCode: String, userName: String, password: String, finTsServerAddress: String, bankName: String, - bic: String, customerName: String, userId: String, iconData: ByteArray? - ): TypedBankData { - - return Bank(bankCode, userName, password, finTsServerAddress, bankName, bic, customerName, userId, iconData) - } - - override fun createAccount(bank: TypedBankData, productName: String?, identifier: String): TypedBankAccount { - return BankAccount(bank, productName, identifier) - } - - override fun createTransaction( - account: TypedBankAccount, - amount: BigDecimal, - currency: String, - unparsedReference: 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?, - sepaReference: String?, - deviantOriginator: String?, - deviantRecipient: String?, - referenceWithNoSpecialType: String?, - primaNotaNumber: String?, - textKeySupplement: String?, - currencyType: String?, - bookingKey: String, - referenceForTheAccountOwner: String, - referenceOfTheAccountServicingInstitution: String?, - supplementaryDetails: String?, - transactionReferenceNumber: String, - relatedReferenceNumber: String? - ): IAccountTransaction { - return AccountTransaction(account as BankAccount, amount, currency, unparsedReference, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, - bookingText, valueDate, statementNumber, sequenceNumber, openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, - creditorIdentifier, originatorsIdentificationCode, compensationAmount, originalAmount, sepaReference, deviantOriginator, deviantRecipient, - referenceWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner, - referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber) - } - - - override fun createTanMethod(displayName: String, type: TanMethodType, bankInternalMethodCode: String, maxTanInputLength: Int?, allowedTanFormat: AllowedTanFormat): net.dankito.banking.ui.model.tan.TanMethod { - return TanMethod(displayName, type, bankInternalMethodCode, 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 deleted file mode 100644 index 9be6843b..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMedium.kt +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index 719273a4..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMediumType.kt +++ /dev/null @@ -1,12 +0,0 @@ -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/TanMethod.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMethod.kt deleted file mode 100644 index a8edd57f..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMethod.kt +++ /dev/null @@ -1,29 +0,0 @@ -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.TanMethod -import net.dankito.banking.ui.model.tan.TanMethodType - - -@Entity -open class TanMethod( - override var displayName: String, - override var type: TanMethodType, - override var bankInternalMethodCode: String, - override var maxTanInputLength: Int? = null, - override var allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric -) : TanMethod(displayName, type, bankInternalMethodCode, maxTanInputLength, allowedTanFormat) { - - internal constructor() : this("", TanMethodType.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/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMethodSettings.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMethodSettings.kt deleted file mode 100644 index 40188863..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TanMethodSettings.kt +++ /dev/null @@ -1,21 +0,0 @@ -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.settings.TanMethodSettings - - -@Entity -open class TanMethodSettings( - @PrimaryKey - open var id: Int, - width: Int, - height: Int, - space: Int = -1, - frequency: Int = -1 -) : TanMethodSettings(width, height, space, frequency) { - - internal constructor() : this(BaseDao.IdNotSet.toInt(), 0, 0) // for object deserializers - -} \ No newline at end of file diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TransactionParty.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TransactionParty.kt deleted file mode 100644 index 7d346bbc..00000000 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/TransactionParty.kt +++ /dev/null @@ -1,12 +0,0 @@ -package net.dankito.banking.persistence.model - -import androidx.room.ColumnInfo - - -data class TransactionParty( - @ColumnInfo(name = "otherPartyName") val name: String, - - @ColumnInfo(name = "otherPartyBankCode") val bankCode: String?, - - @ColumnInfo(name = "otherPartyAccountId") val accountId: String? -) \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/README.md b/persistence/json/BankingPersistenceJson/README.md deleted file mode 100644 index 2e62b22f..00000000 --- a/persistence/json/BankingPersistenceJson/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# BankingPersistenceJson - -A simple IBankingPersistence implementation based on Json. - -Not meant to be a real or useful implementation. Just there to get you up and running fast. - -Preferably use another IBankingPersistence implementation, e.g. a JPA based one. \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/build.gradle b/persistence/json/BankingPersistenceJson/build.gradle deleted file mode 100644 index 5177cf94..00000000 --- a/persistence/json/BankingPersistenceJson/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'kotlin-kapt' - - -sourceCompatibility = "1.7" -targetCompatibility = "1.7" - -compileKotlin { - kotlinOptions.jvmTarget = "1.6" -} -compileTestKotlin { - kotlinOptions.jvmTarget = "1.6" -} - - -dependencies { - implementation project(':BankingUiCommon') - - - testImplementation "junit:junit:$junitVersion" - - testImplementation "org.assertj:assertj-core:$assertJVersion" - testImplementation "org.mockito:mockito-core:$mockitoVersion" - - testImplementation "org.slf4j:slf4j-simple:$slf4jVersion" -} \ 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 deleted file mode 100644 index eea7e825..00000000 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt +++ /dev/null @@ -1,101 +0,0 @@ -package net.dankito.banking.persistence - -import net.dankito.banking.persistence.model.BankDataEntity -import net.dankito.banking.ui.model.* -import net.dankito.banking.ui.model.settings.AppSettings -import net.dankito.utils.multiplatform.File -import net.dankito.banking.util.ISerializer -import net.dankito.banking.util.persistence.downloadIcon - - -open class BankingPersistenceJson( - protected val databaseFolder: File, - protected val serializer: ISerializer -) : IBankingPersistence { - - companion object { - const val BanksJsonFileName = "accounts.json" - - const val AppSettingsJsonFileName = "app_settings.json" - } - - - protected val banksJsonFile: File - - protected val appSettingsJsonFile: File - - protected var allBanks: List? = null - - - init { - databaseFolder.mkdirs() - - banksJsonFile = File(databaseFolder, BanksJsonFileName) - appSettingsJsonFile = File(databaseFolder, AppSettingsJsonFileName) - } - - - override fun decryptData(password: CharArray): Boolean { - // TODO: may implement data decryption. But then we have to store password to be able to encrypt data - return true - } - - override fun changePassword(newPassword: CharArray): Boolean { - // TODO: may implement data decryption. But then we have to store newPassword to be able to encrypt data - return true - } - - - override fun saveOrUpdateBank(bank: TypedBankData, allBanks: List) { - saveAllBanks(allBanks) - } - - override fun deleteBank(bank: TypedBankData, allBanks: List) { - saveAllBanks(allBanks) - } - - override fun readPersistedBanks(): List { - val banks = serializer.deserializeListOr(banksJsonFile, BankDataEntity::class).map { it as TypedBankData } - - this.allBanks = banks - - return banks - } - - - override fun saveOrUpdateAccountTransactions(account: TypedBankAccount, transactions: List) { - // done when called saveOrUpdateAccount() - // TODO: or also call saveAllBanks()? - } - - - protected open fun saveAllBanks(allBanks: List) { - this.allBanks = allBanks - - serializer.serializeObject(allBanks, banksJsonFile) - } - - - override fun saveOrUpdateAppSettings(appSettings: AppSettings) { - serializer.serializeObject(appSettings, appSettingsJsonFile) - } - - override fun readPersistedAppSettings(): AppSettings? { - return serializer.deserializeObject(appSettingsJsonFile, AppSettings::class) - } - - - override fun saveBankIcon(bank: TypedBankData, iconUrl: String, fileExtension: String?) { - bank.iconData = downloadIcon(iconUrl) - - allBanks?.let { - saveOrUpdateBank(bank, it) - } - } - - - override fun addInitializedListener(listener: () -> Unit) { - listener() - } - -} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt deleted file mode 100644 index cc010de6..00000000 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt +++ /dev/null @@ -1,74 +0,0 @@ -package net.dankito.banking.persistence.mapper - -import net.dankito.banking.persistence.model.AccountTransactionEntity -import net.dankito.banking.persistence.model.BankAccountEntity -import net.dankito.banking.persistence.model.BankDataEntity -import net.dankito.banking.ui.model.IAccountTransaction -import net.dankito.banking.ui.model.TypedBankAccount -import net.dankito.banking.ui.model.TypedBankData -import net.dankito.banking.ui.model.mapper.IModelCreator -import net.dankito.utils.multiplatform.BigDecimal -import net.dankito.utils.multiplatform.Date - - -open class EntitiesModelCreator : IModelCreator { - - override fun createBank( - bankCode: String, userName: String, password: String, finTsServerAddress: String, bankName: String, bic: String, - customerName: String, userId: String, iconData: ByteArray? - ): TypedBankData { - - return BankDataEntity(bankCode, userName, password, finTsServerAddress, bankName, bic, customerName, userId, iconData) as TypedBankData - } - - - override fun createAccount(bank: TypedBankData, productName: String?, identifier: String): TypedBankAccount { - return BankAccountEntity(bank as BankDataEntity, identifier, "", null, null, productName = productName) as TypedBankAccount - } - - override fun createTransaction( - account: TypedBankAccount, - amount: BigDecimal, - currency: String, - unparsedReference: 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?, - sepaReference: String?, - deviantOriginator: String?, - deviantRecipient: String?, - referenceWithNoSpecialType: String?, - primaNotaNumber: String?, - textKeySupplement: String?, - currencyType: String?, - bookingKey: String, - referenceForTheAccountOwner: String, - referenceOfTheAccountServicingInstitution: String?, - supplementaryDetails: String?, - transactionReferenceNumber: String, - relatedReferenceNumber: String? - ) : IAccountTransaction { - - return AccountTransactionEntity(account as BankAccountEntity, amount, currency, unparsedReference, bookingDate, - otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber, - openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier, - originatorsIdentificationCode, compensationAmount, originalAmount, sepaReference, deviantOriginator, deviantRecipient, - referenceWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner, - referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber) - } - -} \ 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 deleted file mode 100644 index f51ca477..00000000 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt +++ /dev/null @@ -1,77 +0,0 @@ -package net.dankito.banking.persistence.model - -import com.fasterxml.jackson.annotation.JsonIdentityInfo -import com.fasterxml.jackson.annotation.ObjectIdGenerators -import net.dankito.banking.ui.model.IAccountTransaction -import net.dankito.utils.multiplatform.BigDecimal -import net.dankito.utils.multiplatform.Date -import net.dankito.utils.multiplatform.UUID - - -@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references -// had to define all properties as 'var' 'cause MapStruct cannot handle vals -open class AccountTransactionEntity( - override var account: BankAccountEntity, - override var amount: BigDecimal, - override var currency: String, - override var unparsedReference: 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 sepaReference: String?, - override var deviantOriginator: String?, - override var deviantRecipient: String?, - override var referenceWithNoSpecialType: 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?, - override var technicalId: String = UUID.random() -) : IAccountTransaction { - - // for object deserializers - internal constructor() : this(BankAccountEntity(), BigDecimal.Zero, "", "", Date(), null, null, null, null, Date(), - -1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, - null, "", null) - - constructor(account: BankAccountEntity, otherPartyName: String?, unparsedReference: String, amount: BigDecimal, valueDate: Date, bookingText: String?) - : this(account, amount, "EUR", unparsedReference, valueDate, otherPartyName, null, null, bookingText, valueDate, 0, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, - null, "", "", null, null, "", null) - - - 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/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 deleted file mode 100644 index 582bf17c..00000000 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt +++ /dev/null @@ -1,51 +0,0 @@ -package net.dankito.banking.persistence.model - -import com.fasterxml.jackson.annotation.JsonIdentityInfo -import com.fasterxml.jackson.annotation.ObjectIdGenerators -import net.dankito.banking.ui.model.* -import net.dankito.utils.multiplatform.BigDecimal -import net.dankito.utils.multiplatform.Date -import net.dankito.utils.multiplatform.UUID - - -@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references -// had to define all properties as 'var' 'cause MapStruct cannot handle vals (and cannot use Pozo's mapstruct-kotlin as SerializableBankAccountBuilder would fail with @Context) -open class BankAccountEntity( - override var bank: BankDataEntity, - override var identifier: String, - override var accountHolderName: String, - override var iban: String?, - override var subAccountNumber: String?, - override var balance: BigDecimal = BigDecimal.Zero, - override var currency: String = "EUR", - override var type: BankAccountType = BankAccountType.CheckingAccount, - override var productName: String? = null, - override var accountLimit: String? = null, - override var retrievedTransactionsFromOn: Date? = null, - override var retrievedTransactionsUpTo: Date? = null, - override var supportsRetrievingAccountTransactions: Boolean = false, - override var supportsRetrievingBalance: Boolean = false, - override var supportsTransferringMoney: Boolean = false, - override var supportsRealTimeTransfer: Boolean = false, - override var bookedTransactions: List = listOf(), - override var unbookedTransactions: List = listOf(), - override var technicalId: String = UUID.random(), - override var userSetDisplayName: String? = null, - override var haveAllTransactionsBeenRetrieved: Boolean = false, - override var isAccountTypeSupportedByApplication: Boolean = true, - override var countDaysForWhichTransactionsAreKept: Int? = null, - override var displayIndex: Int = 0, - override var hideAccount: Boolean = false, - override var includeInAutomaticAccountsUpdate: Boolean = true, - override var doNotShowStrikingFetchAllTransactionsView: Boolean = false - -) : IBankAccount { - - internal constructor() : this(BankDataEntity(), "", "", 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/BankDataEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankDataEntity.kt deleted file mode 100644 index 224add0b..00000000 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankDataEntity.kt +++ /dev/null @@ -1,40 +0,0 @@ -package net.dankito.banking.persistence.model - -import com.fasterxml.jackson.annotation.* -import net.dankito.banking.ui.model.IBankData -import net.dankito.banking.ui.model.tan.TanMedium -import net.dankito.banking.ui.model.tan.TanMethod -import java.util.* - - -@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references -// had to define all properties as 'var' 'cause MapStruct cannot handle vals (and cannot use Pozo's mapstruct-kotlin as SerializableCustomerBuilder would fail with @Context) -open class BankDataEntity( - override var bankCode: String, - override var userName: 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 = userName, - override var iconData: ByteArray? = null, - override var accounts: List = listOf(), - override var supportedTanMethods: List = listOf(), - override var selectedTanMethod: TanMethod? = null, - override var tanMedia: List = listOf(), - override var technicalId: String = UUID.randomUUID().toString(), - override var wrongCredentialsEntered: Boolean = false, - override var savePassword: Boolean = true, - override var userSetDisplayName: String? = null, - override var displayIndex: Int = 0 -) : IBankData { - - internal constructor() : this("", "", "", "", "", "", "") // for object deserializers - - - override fun toString(): String { - return stringRepresentation - } - -} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt b/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt deleted file mode 100644 index dcb92631..00000000 --- a/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt +++ /dev/null @@ -1,238 +0,0 @@ -package net.dankito.banking.persistence - -import net.dankito.banking.persistence.model.AccountTransactionEntity -import net.dankito.banking.persistence.model.BankAccountEntity -import net.dankito.banking.persistence.model.BankDataEntity -import net.dankito.banking.ui.model.* -import net.dankito.banking.util.JacksonJsonSerializer -import net.dankito.utils.multiplatform.BigDecimal -import net.dankito.utils.multiplatform.Date -import net.dankito.utils.multiplatform.File -import org.assertj.core.api.Assertions.assertThat -import org.junit.Assert -import org.junit.Test -import kotlin.random.Random - - -class BankingPersistenceJsonTest { - - companion object { - - const val BankCode = "12345678" - - const val CustomerId = "0987654321" - - const val Password = "12345" - - const val FinTsServerAddress = "http://i-do-not-exist.fail/givemeyourmoney" - - const val BankName = "Abzock GmbH" - - const val Bic = "ABCDDEBB123" - - const val CustomerName = "Hans Dampf" - - const val UserId = CustomerId - - val NowMillis = System.currentTimeMillis() - - val TwoYearsAgoMillis = NowMillis - (2 * 365 * 24 * 60 * 60 * 1000L) - - - val TestDataFolder = File("testData") - - init { - TestDataFolder.mkdirs() - } - } - - - private val file = File(TestDataFolder, BankingPersistenceJson.BanksJsonFileName) - - private val serializer = JacksonJsonSerializer() - - private val underTest = BankingPersistenceJson(TestDataFolder, serializer) - - - @Test - fun saveOrUpdateBank() { - - // given - val banks = listOf( - createBank(2), - createBank(3) - ) - - - // when - underTest.saveOrUpdateBank(banks.first() as TypedBankData, banks.map { it as TypedBankData }) - - - // then - val result = serializer.deserializeListOr(file, BankDataEntity::class) - - assertBanksEqual(result, banks) - } - - @Test - fun saveOrUpdateBankWithAccountsAndTransactions() { - - // given - val bank = createBank(2) - - - // when - underTest.saveOrUpdateBank(bank as TypedBankData, listOf(bank).map { it as TypedBankData }) - - - // then - val result = serializer.deserializeListOr(file, BankDataEntity::class) - - assertBanksEqual(result, listOf(bank) as List) - } - - - @Test - fun readPersistedBanks() { - - // given - val banks = listOf( - createBank(2), - createBank(3) - ) - - serializer.serializeObject(banks, file) - - - // when - val result = underTest.readPersistedBanks() - - - // then - assertBanksEqual(banks, result as List) - } - - - private fun createBank(countAccounts: Int = 0, customerId: String = CustomerId): BankDataEntity { - val result = BankDataEntity(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, null) - - result.accounts = createAccounts(countAccounts, result) - - return result - } - - private fun createAccounts(count: Int, customer: BankDataEntity): List { - val random = Random(System.nanoTime()) - - return IntRange(1, count).map { accountIndex -> - createAccount("Account_$accountIndex", customer, random.nextInt(2, 50)) - } - } - - private fun createAccount(productName: String, customer: BankDataEntity, countTransactions: Int = 0): BankAccountEntity { - val result = BankAccountEntity(customer, customer.userName, "AccountHolder", "DE00" + customer.bankCode + customer.userName, null, - BigDecimal(84.25), productName = productName) - - result.bookedTransactions = createTransactions(countTransactions, result) - - return result - } - - private fun createTransactions(countTransactions: Int, account: BankAccountEntity): List { - return IntRange(1, countTransactions).map { transactionIndex -> - createTransaction(transactionIndex, account) - } - } - - private fun createTransaction(transactionIndex: Int, account: BankAccountEntity): AccountTransactionEntity { - return AccountTransactionEntity(account, "OtherParty_$transactionIndex", "Reference_$transactionIndex", BigDecimal(transactionIndex.toDouble()), createDate(), null) - } - - private fun createDate(): Date { - return Date(Random(System.nanoTime()).nextLong(TwoYearsAgoMillis, NowMillis)) - } - - - private fun assertBanksEqual(deserializedBanks: List, banks: List) { - assertThat(deserializedBanks.size).isEqualTo(banks.size) - - deserializedBanks.forEach { deserializedBanks -> - val bank = banks.firstOrNull { it.technicalId == deserializedBanks.technicalId } - - if (bank == null) { - Assert.fail("Could not find matching bank for deserialized bank $deserializedBanks. banks = $banks") - } - else { - assertBanksEqual(deserializedBanks, bank) - } - } - } - - private fun assertBanksEqual(deserializedBank: BankDataEntity, bank: BankDataEntity) { - assertThat(deserializedBank.bankCode).isEqualTo(bank.bankCode) - assertThat(deserializedBank.userName).isEqualTo(bank.userName) - assertThat(deserializedBank.password).isEqualTo(bank.password) - assertThat(deserializedBank.finTsServerAddress).isEqualTo(bank.finTsServerAddress) - - assertThat(deserializedBank.bankName).isEqualTo(bank.bankName) - assertThat(deserializedBank.bic).isEqualTo(bank.bic) - assertThat(deserializedBank.customerName).isEqualTo(bank.customerName) - assertThat(deserializedBank.userId).isEqualTo(bank.userId) - assertThat(deserializedBank.iconData).isEqualTo(bank.iconData) - - assertAccountsEqual(deserializedBank.accounts, bank.accounts) - } - - private fun assertAccountsEqual(deserializedAccounts: List, accounts: List) { - assertThat(deserializedAccounts.size).isEqualTo(accounts.size) - - deserializedAccounts.forEach { deserializedAccount -> - val account = accounts.firstOrNull { it.technicalId == deserializedAccount.technicalId } - - if (account == null) { - Assert.fail("Could not find matching account for deserialized account $deserializedAccount. accounts = $accounts") - } - else { - assertAccountsEqual(deserializedAccount, account) - } - } - } - - private fun assertAccountsEqual(deserializedAccount: BankAccountEntity, account: BankAccountEntity) { - // to check if MapStruct created reference correctly - assertThat(deserializedAccount.bank.technicalId).isEqualTo(account.bank.technicalId) - - assertThat(deserializedAccount.identifier).isEqualTo(account.identifier) - assertThat(deserializedAccount.iban).isEqualTo(account.iban) - assertThat(deserializedAccount.balance).isEqualTo(account.balance) - assertThat(deserializedAccount.productName).isEqualTo(account.productName) - - assertTransactionsEqual(deserializedAccount.bookedTransactions, account.bookedTransactions) - } - - private fun assertTransactionsEqual(deserializedTransactions: List, transactions: List) { - assertThat(deserializedTransactions.size).isEqualTo(transactions.size) - - deserializedTransactions.forEach { deserializedTransaction -> - val transaction = transactions.firstOrNull { it.technicalId == deserializedTransaction.technicalId } - - if (transaction == null) { - Assert.fail("Could not find matching transaction for deserialized transaction $deserializedTransaction. transactions = $transactions") - } - else { - assertTransactionsEqual(deserializedTransaction, transaction) - } - } - } - - private fun assertTransactionsEqual(deserializedTransaction: AccountTransactionEntity, transaction: AccountTransactionEntity) { - // to check if MapStruct created reference correctly - assertThat(deserializedTransaction.account.technicalId).isEqualTo(transaction.account.technicalId) - - assertThat(deserializedTransaction.otherPartyName).isEqualTo(transaction.otherPartyName) - assertThat(deserializedTransaction.unparsedReference).isEqualTo(transaction.unparsedReference) - assertThat(deserializedTransaction.amount).isEqualTo(transaction.amount) - assertThat(deserializedTransaction.valueDate).isEqualTo(transaction.valueDate) - } - -} \ No newline at end of file diff --git a/rest/BankFinderRest/.dockerignore b/rest/BankFinderRest/.dockerignore deleted file mode 100644 index 4361d2fb..00000000 --- a/rest/BankFinderRest/.dockerignore +++ /dev/null @@ -1,5 +0,0 @@ -* -!build/*-runner -!build/*-runner.jar -!build/lib/* -!build/quarkus-app/* \ No newline at end of file diff --git a/rest/BankFinderRest/build.gradle b/rest/BankFinderRest/build.gradle deleted file mode 100644 index 6c9b04fc..00000000 --- a/rest/BankFinderRest/build.gradle +++ /dev/null @@ -1,66 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' - id "org.jetbrains.kotlin.plugin.allopen" version "1.3.72" - id 'io.quarkus' -} - -apply plugin: 'java-library' -apply plugin: 'kotlin' - - - -dependencies { - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' - - // TODO: why can't Gradle find BankFinder project? .jars have temporarily to be copied to libs folder - which are not committed to repo of course - till this issue is fixed -// implementation project(path: ":common", configuration: 'jvmDefault') -// implementation project(path: ":BankFinder", configuration: 'jvmDefault') -// implementation "net.dankito.banking:BankFinder-jvm:$version" - - implementation fileTree(dir: 'libs', include: ['*.jar']) - - implementation "net.dankito.utils:java-utils:$javaUtilsVersion" - - implementation enforcedPlatform("io.quarkus:quarkus-bom:$quarkusVersion") - implementation 'io.quarkus:quarkus-kotlin' - implementation 'io.quarkus:quarkus-resteasy' - implementation 'io.quarkus:quarkus-resteasy-jackson' - - testImplementation 'io.quarkus:quarkus-junit5' - testImplementation 'io.rest-assured:kotlin-extensions' -} - - -quarkus { - setOutputDirectory("$projectDir/build/classes/kotlin/main") -} - -quarkusDev { - setSourceDir("$projectDir/src/main/kotlin") -} - -allOpen { - annotation("javax.ws.rs.Path") - annotation("javax.enterprise.context.ApplicationScoped") - annotation("io.quarkus.test.junit.QuarkusTest") -} - - -def javaVersion = JavaVersion.VERSION_11 - -java { - sourceCompatibility = javaVersion - targetCompatibility = javaVersion -} - -compileKotlin { - kotlinOptions.jvmTarget = javaVersion - kotlinOptions.javaParameters = true -} - -compileTestKotlin { - kotlinOptions.jvmTarget = javaVersion -} -test { - systemProperty "java.util.logging.manager", "org.jboss.logmanager.LogManager" -} \ No newline at end of file diff --git a/rest/BankFinderRest/src/main/docker/Dockerfile.fast-jar b/rest/BankFinderRest/src/main/docker/Dockerfile.fast-jar deleted file mode 100644 index 2793e081..00000000 --- a/rest/BankFinderRest/src/main/docker/Dockerfile.fast-jar +++ /dev/null @@ -1,57 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode -# -# Before building the container image run: -# -# mvn package -Dquarkus.package.type=fast-jar -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.fast-jar -t codinux/bank-finder-fast-jar . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 codinux/bank-finder-fast-jar -# -# If you want to include the debug port into your docker image -# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 -# -# Then run the container using : -# -# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" codinux/bank-finder-fast-jar -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 - -ARG JAVA_PACKAGE=java-11-openjdk-headless -ARG RUN_JAVA_VERSION=1.3.8 - -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' - -# Install java and the run-java script -# Also set up permissions for user `1001` -RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ - && microdnf update \ - && microdnf clean all \ - && mkdir /deployments \ - && chown 1001 /deployments \ - && chmod "g+rwX" /deployments \ - && chown 1001:root /deployments \ - && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ - && chown 1001 /deployments/run-java.sh \ - && chmod 540 /deployments/run-java.sh \ - && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security - -# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" - -# We make four distinct layers so if there are application changes the library layers can be re-used -COPY --chown=1001 build/quarkus-app/lib/ /deployments/lib/ -COPY --chown=1001 build/quarkus-app/*.jar /deployments/ -COPY --chown=1001 build/quarkus-app/app/ /deployments/app/ -COPY --chown=1001 build/quarkus-app/quarkus/ /deployments/quarkus/ - -EXPOSE 8080 -USER 1001 - -ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/rest/BankFinderRest/src/main/docker/Dockerfile.jvm b/rest/BankFinderRest/src/main/docker/Dockerfile.jvm deleted file mode 100644 index 771a9ae2..00000000 --- a/rest/BankFinderRest/src/main/docker/Dockerfile.jvm +++ /dev/null @@ -1,54 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode -# -# Before building the container image run: -# -# mvn package -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.jvm -t codinux/bank-finder-jvm . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 codinux/bank-finder-jvm -# -# If you want to include the debug port into your docker image -# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 -# -# Then run the container using : -# -# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" codinux/bank-finder-jvm -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 - -ARG JAVA_PACKAGE=java-11-openjdk-headless -ARG RUN_JAVA_VERSION=1.3.8 - -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' - -# Install java and the run-java script -# Also set up permissions for user `1001` -RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ - && microdnf update \ - && microdnf clean all \ - && mkdir /deployments \ - && chown 1001 /deployments \ - && chmod "g+rwX" /deployments \ - && chown 1001:root /deployments \ - && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ - && chown 1001 /deployments/run-java.sh \ - && chmod 540 /deployments/run-java.sh \ - && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security - -# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" - -COPY build/lib/* /deployments/lib/ -COPY build/*-runner.jar /deployments/app.jar - -EXPOSE 8080 -USER 1001 - -ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/rest/BankFinderRest/src/main/docker/Dockerfile.native b/rest/BankFinderRest/src/main/docker/Dockerfile.native deleted file mode 100644 index 39ba04c6..00000000 --- a/rest/BankFinderRest/src/main/docker/Dockerfile.native +++ /dev/null @@ -1,27 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode -# -# Before building the container image run: -# -# mvn package -Pnative -Dquarkus.native.container-build=true -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.native -t codinux/bank-finder . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 codinux/bank-finder -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 -WORKDIR /work/ -RUN chown 1001 /work \ - && chmod "g+rwX" /work \ - && chown 1001:root /work -COPY --chown=1001:root build/*-runner /work/application - -EXPOSE 8080 -USER 1001 - -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/rest/BankFinderRest/src/main/kotlin/net/dankito/banking/bankfinder/rest/BankFinderResource.kt b/rest/BankFinderRest/src/main/kotlin/net/dankito/banking/bankfinder/rest/BankFinderResource.kt deleted file mode 100644 index 36df7493..00000000 --- a/rest/BankFinderRest/src/main/kotlin/net/dankito/banking/bankfinder/rest/BankFinderResource.kt +++ /dev/null @@ -1,25 +0,0 @@ -package net.dankito.banking.bankfinder.rest - -import net.dankito.banking.bankfinder.BankInfo -import net.dankito.banking.bankfinder.InMemoryBankFinder -import org.jboss.resteasy.annotations.jaxrs.PathParam -import javax.ws.rs.GET -import javax.ws.rs.Path -import javax.ws.rs.Produces -import javax.ws.rs.core.MediaType - - -@Path("/bankfinder") -class BankFinderResource { - - protected var bankFinder = InMemoryBankFinder() - - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("{query}") - fun findBank(@PathParam query: String): List { - return bankFinder.findBankByNameBankCodeOrCity(query) - } - -} \ No newline at end of file diff --git a/rest/BankFinderRest/src/main/resources/application.properties b/rest/BankFinderRest/src/main/resources/application.properties deleted file mode 100644 index 4c6d1e55..00000000 --- a/rest/BankFinderRest/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -quarkus.http.port=5666 \ No newline at end of file diff --git a/rest/BankFinderRest/src/native-test/kotlin/net/dankito/banking/bankfinder/rest/NativeBankFinderResourceIT.kt b/rest/BankFinderRest/src/native-test/kotlin/net/dankito/banking/bankfinder/rest/NativeBankFinderResourceIT.kt deleted file mode 100644 index c3437278..00000000 --- a/rest/BankFinderRest/src/native-test/kotlin/net/dankito/banking/bankfinder/rest/NativeBankFinderResourceIT.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.dankito.banking.bankfinder.rest - -import io.quarkus.test.junit.NativeImageTest - -@NativeImageTest -class NativeBankFinderResourceIT : ExampleResourceTest() \ No newline at end of file diff --git a/rest/BankFinderRest/src/test/kotlin/net/dankito/banking/bankfinder/rest/BankFinderResourceTest.kt b/rest/BankFinderRest/src/test/kotlin/net/dankito/banking/bankfinder/rest/BankFinderResourceTest.kt deleted file mode 100644 index 4bb0ce8a..00000000 --- a/rest/BankFinderRest/src/test/kotlin/net/dankito/banking/bankfinder/rest/BankFinderResourceTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package net.dankito.banking.bankfinder.rest - -import io.quarkus.test.junit.QuarkusTest -import io.restassured.RestAssured.given -import org.hamcrest.CoreMatchers.containsString -import org.junit.jupiter.api.Test - - -@QuarkusTest -class BankFinderResourceTest { - - @Test - fun testSparkasse() { - given() - .`when`().get("/bankfinder/Sparkasse") - .then() - .statusCode(200) - .body(containsString("Berliner Sparkasse")) - .body(containsString("\"bankCode\":\"10050000\"")) - } - -} \ No newline at end of file diff --git a/rest/fints4kRest/.dockerignore b/rest/fints4kRest/.dockerignore deleted file mode 100644 index 4361d2fb..00000000 --- a/rest/fints4kRest/.dockerignore +++ /dev/null @@ -1,5 +0,0 @@ -* -!build/*-runner -!build/*-runner.jar -!build/lib/* -!build/quarkus-app/* \ No newline at end of file diff --git a/rest/fints4kRest/build.gradle b/rest/fints4kRest/build.gradle deleted file mode 100644 index bbdd6852..00000000 --- a/rest/fints4kRest/build.gradle +++ /dev/null @@ -1,65 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' - id "org.jetbrains.kotlin.plugin.allopen" version "1.3.72" - id 'io.quarkus' -} - - -dependencies { - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' - - // TODO: why can't Gradle find fints4k project? .jars have temporarily to be copied to libs folder - which are not committed to repo of course - till this issue is fixed - implementation fileTree(dir: 'libs', include: ['*.jar']) - - implementation "net.dankito.utils:java-utils:$javaUtilsVersion" - - implementation "io.ktor:ktor-client-okhttp:$ktorVersion" - - implementation "org.slf4j:slf4j-api:$slf4jVersion" - - implementation enforcedPlatform("io.quarkus:quarkus-bom:$quarkusVersion") - implementation 'io.quarkus:quarkus-kotlin' - implementation 'io.quarkus:quarkus-resteasy' - implementation 'io.quarkus:quarkus-resteasy-jackson' - - implementation "ch.qos.logback:logback-classic:$logbackVersion" - - - testImplementation 'io.quarkus:quarkus-junit5' - testImplementation 'io.rest-assured:kotlin-extensions' -} - - -quarkus { - setOutputDirectory("$projectDir/build/classes/kotlin/main") -} - -quarkusDev { - setSourceDir("$projectDir/src/main/kotlin") -} - -allOpen { - annotation("javax.ws.rs.Path") - annotation("javax.enterprise.context.ApplicationScoped") - annotation("io.quarkus.test.junit.QuarkusTest") -} - - -def javaVersion = JavaVersion.VERSION_11 - -java { - sourceCompatibility = javaVersion - targetCompatibility = javaVersion -} - -compileKotlin { - kotlinOptions.jvmTarget = javaVersion - kotlinOptions.javaParameters = true -} - -compileTestKotlin { - kotlinOptions.jvmTarget = javaVersion -} -test { - systemProperty "java.util.logging.manager", "org.jboss.logmanager.LogManager" -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/docker/Dockerfile.fast-jar b/rest/fints4kRest/src/main/docker/Dockerfile.fast-jar deleted file mode 100644 index ed4e2dd9..00000000 --- a/rest/fints4kRest/src/main/docker/Dockerfile.fast-jar +++ /dev/null @@ -1,57 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode -# -# Before building the container image run: -# -# mvn package -Dquarkus.package.type=fast-jar -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.fast-jar -t codinux/fints4k-fast-jar . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 codinux/fints4k-fast-jar -# -# If you want to include the debug port into your docker image -# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 -# -# Then run the container using : -# -# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" codinux/fints4k-fast-jar -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 - -ARG JAVA_PACKAGE=java-11-openjdk-headless -ARG RUN_JAVA_VERSION=1.3.8 - -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' - -# Install java and the run-java script -# Also set up permissions for user `1001` -RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ - && microdnf update \ - && microdnf clean all \ - && mkdir /deployments \ - && chown 1001 /deployments \ - && chmod "g+rwX" /deployments \ - && chown 1001:root /deployments \ - && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ - && chown 1001 /deployments/run-java.sh \ - && chmod 540 /deployments/run-java.sh \ - && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security - -# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" - -# We make four distinct layers so if there are application changes the library layers can be re-used -COPY --chown=1001 build/quarkus-app/lib/ /deployments/lib/ -COPY --chown=1001 build/quarkus-app/*.jar /deployments/ -COPY --chown=1001 build/quarkus-app/app/ /deployments/app/ -COPY --chown=1001 build/quarkus-app/quarkus/ /deployments/quarkus/ - -EXPOSE 8080 -USER 1001 - -ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/rest/fints4kRest/src/main/docker/Dockerfile.jvm b/rest/fints4kRest/src/main/docker/Dockerfile.jvm deleted file mode 100644 index 0e76ac92..00000000 --- a/rest/fints4kRest/src/main/docker/Dockerfile.jvm +++ /dev/null @@ -1,54 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode -# -# Before building the container image run: -# -# mvn package -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.jvm -t codinux/fints4k-jvm . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 codinux/fints4k-jvm -# -# If you want to include the debug port into your docker image -# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 -# -# Then run the container using : -# -# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" codinux/fints4k-jvm -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 - -ARG JAVA_PACKAGE=java-11-openjdk-headless -ARG RUN_JAVA_VERSION=1.3.8 - -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' - -# Install java and the run-java script -# Also set up permissions for user `1001` -RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ - && microdnf update \ - && microdnf clean all \ - && mkdir /deployments \ - && chown 1001 /deployments \ - && chmod "g+rwX" /deployments \ - && chown 1001:root /deployments \ - && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ - && chown 1001 /deployments/run-java.sh \ - && chmod 540 /deployments/run-java.sh \ - && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security - -# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" - -COPY build/lib/* /deployments/lib/ -COPY build/*-runner.jar /deployments/app.jar - -EXPOSE 8080 -USER 1001 - -ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/rest/fints4kRest/src/main/docker/Dockerfile.native b/rest/fints4kRest/src/main/docker/Dockerfile.native deleted file mode 100644 index c1522ea4..00000000 --- a/rest/fints4kRest/src/main/docker/Dockerfile.native +++ /dev/null @@ -1,27 +0,0 @@ -#### -# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode -# -# Before building the container image run: -# -# mvn package -Pnative -Dquarkus.native.container-build=true -# -# Then, build the image with: -# -# docker build -f src/main/docker/Dockerfile.native -t codinux/fints4k . -# -# Then run the container using: -# -# docker run -i --rm -p 8080:8080 codinux/fints4k -# -### -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 -WORKDIR /work/ -RUN chown 1001 /work \ - && chmod "g+rwX" /work \ - && chown 1001:root /work -COPY --chown=1001:root build/*-runner /work/application - -EXPOSE 8080 -USER 1001 - -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/AccessControlResponseFilter.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/AccessControlResponseFilter.kt deleted file mode 100644 index 0a450ffd..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/AccessControlResponseFilter.kt +++ /dev/null @@ -1,26 +0,0 @@ -package net.dankito.banking.fints.rest - -import javax.annotation.Priority -import javax.ws.rs.Priorities -import javax.ws.rs.container.ContainerRequestContext -import javax.ws.rs.container.ContainerResponseContext -import javax.ws.rs.container.ContainerResponseFilter -import javax.ws.rs.core.MultivaluedMap -import javax.ws.rs.ext.Provider - - -@Provider -@Priority(Priorities.HEADER_DECORATOR) -open class AccessControlResponseFilter : ContainerResponseFilter { - - - override fun filter(requestContext: ContainerRequestContext, responseContext: ContainerResponseContext) { - val headers: MultivaluedMap = responseContext.headers - - headers.add("Access-Control-Allow-Origin", "*") - headers.add("Access-Control-Allow-Headers", "Authorization, Origin, X-Requested-With, Content-Type") - headers.add("Access-Control-Expose-Headers", "Location, Content-Disposition") - headers.add("Access-Control-Allow-Methods", "POST, PUT, GET, DELETE, HEAD, OPTIONS") - } - -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/LoggingFilter.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/LoggingFilter.kt deleted file mode 100644 index 7e84f18f..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/LoggingFilter.kt +++ /dev/null @@ -1,58 +0,0 @@ -package net.dankito.banking.fints.rest - -import com.fasterxml.jackson.databind.ObjectMapper -import org.slf4j.LoggerFactory -import javax.inject.Inject -import javax.ws.rs.container.ContainerRequestContext -import javax.ws.rs.container.ContainerResponseContext -import javax.ws.rs.container.ContainerResponseFilter -import javax.ws.rs.core.Response -import javax.ws.rs.ext.Provider - - -@Provider -class LoggingFilter : ContainerResponseFilter { - - companion object { - private val log = LoggerFactory.getLogger(LoggingFilter::class.java) - } - - - @Inject - internal lateinit var mapper: ObjectMapper - - - override fun filter(requestContext: ContainerRequestContext, responseContext: ContainerResponseContext) { - if (responseContext.statusInfo.family != Response.Status.Family.SUCCESSFUL) { - log.warn("Request ${geRequestUrl(requestContext)} failed: ${getResponseStatus(responseContext)}" - + System.lineSeparator() + getHeadersAsString(responseContext) - + System.lineSeparator() + getBodyAsString(responseContext)) - } - else if (log.isInfoEnabled) { - log.info("Result of request ${geRequestUrl(requestContext)}: ${getResponseStatus(responseContext)}" - + System.lineSeparator() + getHeadersAsString(responseContext) - + System.lineSeparator() + getBodyAsString(responseContext)) - } - } - - private fun geRequestUrl(requestContext: ContainerRequestContext): String { - return "${requestContext.request.method} ${requestContext.uriInfo.requestUri}" - } - - private fun getResponseStatus(responseContext: ContainerResponseContext): String { - return "${responseContext.status} ${responseContext.statusInfo.reasonPhrase}" - } - - private fun getHeadersAsString(responseContext: ContainerResponseContext): String { - return responseContext.stringHeaders.map { header -> "${header.key}: ${header.value}" }.joinToString("\n", "Headers:\n") - } - - private fun getBodyAsString(responseContext: ContainerResponseContext): String { - if (responseContext.hasEntity()) { - return "Body ${responseContext.entityClass.name}:\n" + mapper.writeValueAsString(responseContext.entity) - } - - return "" - } - -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/fints4kResource.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/fints4kResource.kt deleted file mode 100644 index 05bd958e..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/fints4kResource.kt +++ /dev/null @@ -1,72 +0,0 @@ -package net.dankito.banking.fints.rest - -import net.dankito.banking.fints.response.client.AddAccountResponse -import net.dankito.banking.fints.response.client.GetTransactionsResponse -import net.dankito.banking.fints.rest.model.dto.request.AddAccountRequestDto -import net.dankito.banking.fints.rest.mapper.DtoMapper -import net.dankito.banking.fints.rest.model.dto.request.GetAccountsTransactionsRequestDto -import net.dankito.banking.fints.rest.model.dto.request.TanResponseDto -import net.dankito.banking.fints.rest.model.dto.response.AddAccountResponseDto -import net.dankito.banking.fints.rest.model.dto.response.GetAccountsTransactionsResponseDto -import net.dankito.banking.fints.rest.model.dto.response.RestResponse -import net.dankito.banking.fints.rest.service.fints4kService -import net.dankito.banking.fints.rest.service.model.GetAccountsTransactionsResponse -import org.slf4j.LoggerFactory -import javax.inject.Inject -import javax.ws.rs.* -import javax.ws.rs.core.MediaType - - -@Path("/fints/v1") -@Consumes(MediaType.APPLICATION_JSON) -@Produces(MediaType.APPLICATION_JSON) -class fints4kResource { - - @Inject - protected val service = fints4kService() - - protected val mapper = DtoMapper() - - - @POST - @Path("addaccount") - fun addAccount(request: AddAccountRequestDto): RestResponse { - val response = service.getAddAccountResponse(request) - - return mapper.createRestResponse(response) { successResponse -> mapper.map(successResponse) } - } - - - @POST - @Path("transactions") - fun getAccountTransactions(request: GetAccountsTransactionsRequestDto): GetAccountsTransactionsResponseDto { - val response = service.getAccountTransactions(request) - - return mapper.map(response) - } - - - @POST - @Path("tanresponse") - fun tanResponse(dto: TanResponseDto): RestResponse { - val response = service.handleTanResponse(dto) - - // couldn't make it that compiler access ResponseHolder<*> for mapper.createRestResponse(), resulted in very cryptic "{"arity":0}" response -> handle it manually - response.response?.let { successResponse -> - return RestResponse.success(mapSuccessResponse(successResponse)) - } - - // all other cases map here, the responseMapper callback has no function - return mapper.createRestResponse(response) { it!! } - } - - private fun mapSuccessResponse(successResponse: Any): Any { - return when (successResponse) { - is AddAccountResponse -> mapper.map(successResponse) - is GetAccountsTransactionsResponse -> mapper.map(successResponse) - is GetTransactionsResponse -> mapper.mapTransactions(successResponse) - else -> successResponse // add others / new ones here - } - } - -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/mapper/DtoMapper.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/mapper/DtoMapper.kt deleted file mode 100644 index 50187a4c..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/mapper/DtoMapper.kt +++ /dev/null @@ -1,166 +0,0 @@ -package net.dankito.banking.fints.rest.mapper - -import net.dankito.banking.fints.model.* -import net.dankito.banking.fints.response.client.AddAccountResponse -import net.dankito.banking.fints.response.client.FinTsClientResponse -import net.dankito.banking.fints.response.client.GetTransactionsResponse -import net.dankito.banking.fints.rest.model.ResponseHolder -import net.dankito.banking.fints.rest.model.dto.response.* -import net.dankito.banking.fints.rest.service.model.GetAccountsTransactionsResponse -import java.math.BigDecimal -import javax.ws.rs.InternalServerErrorException - - -open class DtoMapper { - - fun createRestResponse(responseHolder: ResponseHolder, responseMapper: (DomainType) -> DtoType): RestResponse { - responseHolder.response?.let { response -> - return RestResponse.success(responseMapper(response)) - } - - responseHolder.enterTanRequest?.let { enterTanRequest -> - return RestResponse.requiresTan(enterTanRequest) - } - - return RestResponse.error(responseHolder.error ?: "Unknown error") - } - - - open fun map(response: AddAccountResponse): AddAccountResponseDto { - return AddAccountResponseDto( - response.successful, - mapErrorMessage(response), - map(response.bank), - map(response.retrievedData) - ) - } - - - protected open fun map(bank: BankData): BankResponseDto { - return BankResponseDto( - bank.bankCode, - bank.customerId, - bank.finTs3ServerAddress, - bank.bic, - bank.bankName, - bank.userId, - bank.customerName, - mapTanMethods(bank.tanMethodsAvailableForUser), - if (bank.isTanMethodSelected) map(bank.selectedTanMethod) else null, - bank.tanMedia, - bank.supportedHbciVersions.map { it.name.replace("Hbci_", "HBCI ").replace("FinTs_", "FinTS ").replace('_', '.') } - ) - } - - - open fun map(response: GetAccountsTransactionsResponse?): GetAccountsTransactionsResponseDto { - // TODO: is this still the case? - // TODO: if a TAN is required then accountsTransactions contains null value(s) (but why?) -> application crashes - if (response == null) { - throw InternalServerErrorException("Could not fetch account transactions. Either TAN hasn't been entered or developers made a mistake.") - } - - return GetAccountsTransactionsResponseDto( - // TODO: is this correct removing accounts from result for which no transactions have been retrieved? - response.transactionsPerAccount.filter { it.response?.retrievedData?.isNotEmpty() != false } - .map { createRestResponse(it) { transactionsResponse -> mapTransactions(transactionsResponse) } } - ) - } - - open fun mapTransactions(accountTransactions: GetTransactionsResponse): GetAccountTransactionsResponseDto { - val retrievedData = accountTransactions.retrievedData.first() - val balance = mapNullable(retrievedData.balance) - val bookedTransactions = map(retrievedData.bookedTransactions) - - return GetAccountTransactionsResponseDto( - retrievedData.account.accountIdentifier, - retrievedData.account.productName, - accountTransactions.successful, - mapErrorMessage(accountTransactions), - balance, - bookedTransactions, - listOf() - ) - } - - - protected open fun map(accountData: List): List { - return accountData.map { map(it) } - } - - protected open fun map(accountData: RetrievedAccountData): BankAccountResponseDto { - val account = accountData.account - - return BankAccountResponseDto( - account.accountIdentifier, - account.subAccountAttribute, - account.iban, - account.accountType, - account.currency, - account.accountHolderName, - account.productName, - account.supportsRetrievingBalance, - account.supportsRetrievingAccountTransactions, - account.supportsTransferringMoney, - account.supportsRealTimeTransfer, - accountData.successfullyRetrievedData, - mapNullable(accountData.balance), - accountData.retrievedTransactionsFrom, - accountData.retrievedTransactionsTo, - map(accountData.bookedTransactions), - listOf() - ) - } - - - protected open fun map(transactions: Collection): Collection { - return transactions.map { map(it) } - } - - protected open fun map(transaction: AccountTransaction): AccountTransactionResponseDto { - return AccountTransactionResponseDto( - map(transaction.amount), - transaction.amount.currency.code, - transaction.reference, - transaction.bookingDate, - transaction.otherPartyName, - transaction.otherPartyBankCode, - transaction.otherPartyAccountId, - transaction.bookingText, - transaction.valueDate - ) - } - - - protected open fun mapTanMethods(tanMethods: List): List { - return tanMethods.map { map(it) } - } - - protected open fun map(tanMethod: TanMethod): TanMethodResponseDto { - return TanMethodResponseDto( - tanMethod.displayName, - tanMethod.securityFunction.code, - tanMethod.type, - tanMethod.hhdVersion?.name?.replace("HHD_", "")?.replace('_', '.'), - tanMethod.maxTanInputLength, - tanMethod.allowedTanFormat - ) - } - - - protected open fun map(money: Money): BigDecimal { - return money.bigDecimal - } - - protected open fun mapNullable(money: Money?): BigDecimal? { - return money?.let { map(it) } - } - - - protected open fun mapErrorMessage(response: FinTsClientResponse): String? { - // TODO: evaluate fields like isJobAllowed or tanRequiredButWeWereToldToAbortIfSo and set error message accordingly - return response.errorMessage - ?: if (response.errorsToShowToUser.isNotEmpty()) response.errorsToShowToUser.joinToString("\n") else null - } - -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/BankAccessData.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/BankAccessData.kt deleted file mode 100644 index c0506723..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/BankAccessData.kt +++ /dev/null @@ -1,13 +0,0 @@ -package net.dankito.banking.fints.rest.model - - -open class BankAccessData( - open val bankCode: String, - open val loginName: String, - open val password: String, - open val finTsServerAddress: String? = null -) { - - internal constructor() : this("", "", "") // for object deserializers - -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/EnterTanContext.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/EnterTanContext.kt deleted file mode 100644 index 2fe11498..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/EnterTanContext.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.dankito.banking.fints.rest.model - -import net.dankito.banking.fints.model.EnterTanResult -import java.util.* -import java.util.concurrent.CountDownLatch -import java.util.concurrent.atomic.AtomicReference - - -class EnterTanContext( - val enterTanResult: AtomicReference, - val responseHolder: ResponseHolder<*>, - val countDownLatch: CountDownLatch, - val tanRequestedTimeStamp: Date = Date() -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/EnteringTanRequested.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/EnteringTanRequested.kt deleted file mode 100644 index 7adcfdd2..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/EnteringTanRequested.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.dankito.banking.fints.rest.model - -import net.dankito.banking.fints.model.TanChallenge - - -class EnteringTanRequested( - val tanRequestId: String, - val tanChallenge: TanChallenge -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/ResponseHolder.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/ResponseHolder.kt deleted file mode 100644 index 02264988..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/ResponseHolder.kt +++ /dev/null @@ -1,63 +0,0 @@ -package net.dankito.banking.fints.rest.model - -import java.util.concurrent.CountDownLatch - - -class ResponseHolder() { - - private var responseReceivedLatch = CountDownLatch(1) - - constructor(error: String) : this() { - setError(error) - } - - - var response: T? = null - private set - - var error: String? = null - private set - - var enterTanRequest: EnteringTanRequested? = null - private set - - - fun setResponse(response: T) { - this.response = response - - signalResponseReceived() - } - - fun setError(error: String) { - this.error = error - - signalResponseReceived() - } - - fun setEnterTanRequest(enterTanRequest: EnteringTanRequested) { - this.enterTanRequest = enterTanRequest - - signalResponseReceived() - } - - - fun waitForResponse() { - responseReceivedLatch.await() - } - - fun resetAfterEnteringTan() { - this.enterTanRequest = null - - responseReceivedLatch = CountDownLatch(1) - } - - private fun signalResponseReceived() { - responseReceivedLatch.countDown() - } - - - override fun toString(): String { - return "Error: $error, TAN requested: $enterTanRequest, success: $response" - } - -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/AccountRequestDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/AccountRequestDto.kt deleted file mode 100644 index 413695a3..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/AccountRequestDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.request - - -open class AccountRequestDto( - open val identifier: String -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/AddAccountRequestDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/AddAccountRequestDto.kt deleted file mode 100644 index 0eeb34d7..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/AddAccountRequestDto.kt +++ /dev/null @@ -1,4 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.request - - -open class AddAccountRequestDto : BankAccessDataRequestDto() \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/BankAccessDataRequestDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/BankAccessDataRequestDto.kt deleted file mode 100644 index 1533e2dc..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/BankAccessDataRequestDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.request - -import net.dankito.banking.fints.rest.model.BankAccessData - - -open class BankAccessDataRequestDto : BankAccessData() \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/GetAccountsTransactionsRequestDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/GetAccountsTransactionsRequestDto.kt deleted file mode 100644 index fdad36af..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/GetAccountsTransactionsRequestDto.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.request - -import net.dankito.banking.fints.rest.model.BankAccessData -import net.dankito.utils.multiplatform.Date - - -open class GetAccountsTransactionsRequestDto( - open val credentials: BankAccessData, - open val accounts: List, - open val alsoRetrieveBalance: Boolean = true, - open val fromDate: Date? = null, - open val toDate: Date? = null, - open val abortIfTanIsRequired: Boolean = false -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/TanResponseDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/TanResponseDto.kt deleted file mode 100644 index d41f383a..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/request/TanResponseDto.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.request - -import net.dankito.banking.fints.model.EnterTanResult - - -class TanResponseDto( - val tanRequestId: String, - val enterTanResult: EnterTanResult -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/AccountTransactionResponseDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/AccountTransactionResponseDto.kt deleted file mode 100644 index 867b923b..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/AccountTransactionResponseDto.kt +++ /dev/null @@ -1,17 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - -import net.dankito.utils.multiplatform.Date -import java.math.BigDecimal - - -open class AccountTransactionResponseDto( - open val amount: BigDecimal, - open val currency: String, - open val reference: String, - open val bookingDate: Date, - open val otherPartyName: String?, - open val otherPartyBankCode: String?, - open val otherPartyAccountId: String?, - open val bookingText: String?, - open val valueDate: Date -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/AddAccountResponseDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/AddAccountResponseDto.kt deleted file mode 100644 index fab0a71c..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/AddAccountResponseDto.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - - -open class AddAccountResponseDto( - successful: Boolean, - errorMessage: String?, - open val bank: BankResponseDto, - open val accounts: List -) : ResponseDtoBase(successful, errorMessage) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/BankAccountResponseDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/BankAccountResponseDto.kt deleted file mode 100644 index d49b216e..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/BankAccountResponseDto.kt +++ /dev/null @@ -1,26 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - -import net.dankito.banking.fints.response.segments.AccountType -import net.dankito.utils.multiplatform.Date -import java.math.BigDecimal - - -open class BankAccountResponseDto( - open val accountIdentifier: String, - open val subAccountAttribute: String?, - open val iban: String?, - open val accountType: AccountType?, - open val currency: String?, - open val accountHolderName: String, - open val productName: String?, - open val supportsRetrievingBalance: Boolean, - open val supportsRetrievingAccountTransactions: Boolean, - open val supportsTransferringMoney: Boolean, - open val supportsInstantPaymentMoneyTransfer: Boolean, - open val successfullyRetrievedData: Boolean, - open val balance: BigDecimal?, - open val retrievedTransactionsFrom: Date?, - open val retrievedTransactionsTo: Date?, - open var bookedTransactions: Collection, - open var unbookedTransactions: Collection -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/BankResponseDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/BankResponseDto.kt deleted file mode 100644 index 2d1b5492..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/BankResponseDto.kt +++ /dev/null @@ -1,22 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium - - -open class BankResponseDto( - open val bankCode: String, - open val userName: String, - open val finTs3ServerAddress: String, - open val bic: String, - - open val bankName: String, - - open val userId: String, - open val customerName: String, - - open val usersTanMethods: List, - open val selectedTanMethod: TanMethodResponseDto?, - open val tanMedia: List, - - open val supportedHbciVersions: List -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/GetAccountTransactionsResponseDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/GetAccountTransactionsResponseDto.kt deleted file mode 100644 index fa4005d9..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/GetAccountTransactionsResponseDto.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - -import java.math.BigDecimal - - -open class GetAccountTransactionsResponseDto( - open val identifier: String, - open val productName: String?, - successful: Boolean, - errorMessage: String?, - open val balance: BigDecimal?, - open var bookedTransactions: Collection, - open var unbookedTransactions: Collection -) : ResponseDtoBase(successful, errorMessage) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/GetAccountsTransactionsResponseDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/GetAccountsTransactionsResponseDto.kt deleted file mode 100644 index dbda2749..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/GetAccountsTransactionsResponseDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - - -open class GetAccountsTransactionsResponseDto( - open val transactionsPerAccount: List> -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/ResponseDtoBase.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/ResponseDtoBase.kt deleted file mode 100644 index e0747d1a..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/ResponseDtoBase.kt +++ /dev/null @@ -1,7 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - - -open class ResponseDtoBase( - open val successful: Boolean, - open val errorMessage: String? -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/ResponseType.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/ResponseType.kt deleted file mode 100644 index bed09865..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/ResponseType.kt +++ /dev/null @@ -1,12 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - - -enum class ResponseType { - - Success, - - Error, - - TanRequired - -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/RestResponse.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/RestResponse.kt deleted file mode 100644 index 291b5b5c..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/RestResponse.kt +++ /dev/null @@ -1,29 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - -import net.dankito.banking.fints.rest.model.EnteringTanRequested - - -class RestResponse( - val status: ResponseType, - val errorMessage: String?, - val successResponse: T?, - val enteringTanRequested: EnteringTanRequested? = null -) { - - companion object { - - fun success(result: T): RestResponse { - return RestResponse(ResponseType.Success, null, result, null) - } - - fun error(errorMessage: String): RestResponse { - return RestResponse(ResponseType.Error, errorMessage, null, null) - } - - fun requiresTan(enteringTanRequested: EnteringTanRequested): RestResponse { - return RestResponse(ResponseType.TanRequired, null, null, enteringTanRequested) - } - - } - -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/TanMethodResponseDto.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/TanMethodResponseDto.kt deleted file mode 100644 index c08f110b..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/model/dto/response/TanMethodResponseDto.kt +++ /dev/null @@ -1,15 +0,0 @@ -package net.dankito.banking.fints.rest.model.dto.response - -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat -import net.dankito.banking.fints.model.TanMethodType - - -open class TanMethodResponseDto( - open val displayName: String, - open val bankInternalMethodCode: String, - open val type: TanMethodType, - open val hhdVersion: String? = null, - open val maxTanInputLength: Int? = null, - open val allowedTanFormat: AllowedTanFormat? = null, - open val nameOfTanMediumRequired: Boolean = false -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/service/fints4kService.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/service/fints4kService.kt deleted file mode 100644 index db759449..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/service/fints4kService.kt +++ /dev/null @@ -1,202 +0,0 @@ -package net.dankito.banking.fints.rest.service - -import net.dankito.banking.bankfinder.InMemoryBankFinder -import net.dankito.banking.fints.FinTsClientForCustomer -import net.dankito.banking.fints.callback.FinTsClientCallback -import net.dankito.banking.fints.callback.SimpleFinTsClientCallback -import net.dankito.banking.fints.model.* -import net.dankito.banking.fints.response.client.AddAccountResponse -import net.dankito.banking.fints.response.client.GetTransactionsResponse -import net.dankito.banking.fints.rest.model.BankAccessData -import net.dankito.banking.fints.rest.model.EnterTanContext -import net.dankito.banking.fints.rest.model.EnteringTanRequested -import net.dankito.banking.fints.rest.model.ResponseHolder -import net.dankito.banking.fints.rest.model.dto.request.GetAccountsTransactionsRequestDto -import net.dankito.banking.fints.rest.model.dto.request.TanResponseDto -import net.dankito.banking.fints.rest.service.model.GetAccountsTransactionsResponse -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CountDownLatch -import java.util.concurrent.atomic.AtomicReference -import javax.enterprise.context.ApplicationScoped - - -@ApplicationScoped -class fints4kService { - - protected val bankFinder = InMemoryBankFinder() - - protected val clientCache = ConcurrentHashMap() - - // TODO: create clean up job for timed out TAN requests - protected val tanRequests = ConcurrentHashMap() - - - fun getAddAccountResponse(accessData: BankAccessData): ResponseHolder { - val (bank, errorMessage) = mapToBankData(accessData) - - if (errorMessage != null) { - return ResponseHolder(errorMessage) - } - - return getAccountData(bank) - } - - protected fun getAccountData(bank: BankData): ResponseHolder { - return getAsyncResponse(bank) { client, responseRetrieved -> - client.addAccountAsync(AddAccountParameter(bank)) { response -> - responseRetrieved(response) - } - } - } - - - fun getAccountTransactions(dto: GetAccountsTransactionsRequestDto): GetAccountsTransactionsResponse { - val (bank, errorMessage) = mapToBankData(dto.credentials) - - if (errorMessage != null) { - return GetAccountsTransactionsResponse(listOf(ResponseHolder(errorMessage))) - } - - val retrievedAccounts = getAccounts(bank) - - val transactionsPerAccount = dto.accounts.map { accountDto -> - val account = retrievedAccounts?.firstOrNull { it.accountIdentifier == accountDto.identifier } - - return@map if (account != null) { - val parameter = GetTransactionsParameter(account, dto.alsoRetrieveBalance, dto.fromDate, dto.toDate, abortIfTanIsRequired = dto.abortIfTanIsRequired) - getAccountTransactions(bank, parameter) - } - else { - ResponseHolder("Account with identifier '${accountDto.identifier}' not found. Available accounts: " + - "${retrievedAccounts?.joinToString(", ") { it.accountIdentifier }}") - } - } - - return GetAccountsTransactionsResponse(transactionsPerAccount) - } - - fun getAccountTransactions(bank: BankData, parameter: GetTransactionsParameter): ResponseHolder { - return getAsyncResponse(bank) { client, responseRetrieved -> - client.getTransactionsAsync(parameter) { response -> - responseRetrieved(response) - } - } - } - - - fun handleTanResponse(dto: TanResponseDto): ResponseHolder<*> { - tanRequests.remove(dto.tanRequestId)?.let { enterTanContext -> - val responseHolder = enterTanContext.responseHolder - responseHolder.resetAfterEnteringTan() - - enterTanContext.enterTanResult.set(dto.enterTanResult) - enterTanContext.countDownLatch.countDown() - - responseHolder.waitForResponse() - - return responseHolder - } - - return ResponseHolder("No TAN request found for TAN Request ID '${dto.tanRequestId}'") - } - - - protected fun getAsyncResponse(bank: BankData, executeRequest: (FinTsClientForCustomer, ((T) -> Unit)) -> Unit): ResponseHolder { - val responseHolder = ResponseHolder() - - val client = getClient(bank, responseHolder) - - executeRequest(client) { response -> - responseHolder.setResponse(response) - } - - responseHolder.waitForResponse() - - return responseHolder - } - - private fun getClient(bank: BankData, responseHolder: ResponseHolder): FinTsClientForCustomer { - val cacheKey = getCacheKey(bank.bankCode, bank.customerId) - - clientCache[cacheKey]?.let { - // TODO: this will not work for two parallel calls for the same account if both calls require entering a TAN as second one overwrites callback and ResponseHolder of first one -> first one blocks forever - it.setCallback(createFinTsClientCallback(responseHolder)) // we have to newly create callback otherwise ResponseHolder instance of when client was created is used -> its CountDownLatch would never signal - return it - } - - val client = FinTsClientForCustomer(bank, createFinTsClientCallback(responseHolder)) - - clientCache[cacheKey] = client - - return client - } - - private fun createFinTsClientCallback(responseHolder: ResponseHolder): FinTsClientCallback { - return SimpleFinTsClientCallback({ bank, tanChallenge -> handleEnterTan(bank, tanChallenge, responseHolder) }) { supportedTanMethods, suggestedTanMethod -> - suggestedTanMethod - } - } - - protected fun handleEnterTan(bank: BankData, tanChallenge: TanChallenge, responseHolder: ResponseHolder): EnterTanResult { - val enterTanResult = AtomicReference() - val enterTanLatch = CountDownLatch(1) - - val tanRequestId = UUID.randomUUID().toString() - - tanRequests.put(tanRequestId, EnterTanContext(enterTanResult, responseHolder, enterTanLatch)) - - responseHolder.setEnterTanRequest(EnteringTanRequested(tanRequestId, tanChallenge)) - - enterTanLatch.await() - - return enterTanResult.get() - } - - - protected fun mapToBankData(accessData: BankAccessData): Pair { - val bankSearchResult = bankFinder.findBankByBankCode(accessData.bankCode) - val fintsServerAddress = accessData.finTsServerAddress ?: bankSearchResult.firstOrNull { it.pinTanAddress != null }?.pinTanAddress - val potentialBankInfo = bankSearchResult.firstOrNull() - - val bank = BankData(accessData.bankCode, accessData.loginName, accessData.password, fintsServerAddress ?: "", - potentialBankInfo?.bic ?: "", potentialBankInfo?.name ?: "") - - if (fintsServerAddress == null) { - val errorMessage = if (bankSearchResult.isEmpty()) "No bank found for bank code '${accessData.bankCode}'" else "Bank '${bankSearchResult.firstOrNull()?.name} does not support FinTS 3.0" - - return Pair(bank, errorMessage) - } - - return Pair(bank, null) - } - - - protected fun getAccounts(bank: BankData): List? { - getCachedClient(bank)?.bank?.accounts?.let { - return it - } - - val addAccountResponse = getAccountData(bank) - - return addAccountResponse.response?.bank?.accounts - } - - - private fun getCachedClient(bank: BankData): FinTsClientForCustomer? { - val cacheKey = getCacheKey(bank.bankCode, bank.customerId) - - return clientCache[cacheKey] - } - - private fun getCachedClient(credentials: BankAccessData): FinTsClientForCustomer? { - val cacheKey = getCacheKey(credentials.bankCode, credentials.loginName) - - return clientCache[cacheKey] - } - - private fun getCacheKey(bankCode: String, loginName: String): String { - return bankCode + "_" + loginName - } - -} \ No newline at end of file diff --git a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/service/model/GetAccountsTransactionsResponse.kt b/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/service/model/GetAccountsTransactionsResponse.kt deleted file mode 100644 index c686e3e1..00000000 --- a/rest/fints4kRest/src/main/kotlin/net/dankito/banking/fints/rest/service/model/GetAccountsTransactionsResponse.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.dankito.banking.fints.rest.service.model - -import net.dankito.banking.fints.response.client.GetTransactionsResponse -import net.dankito.banking.fints.rest.model.ResponseHolder - - -class GetAccountsTransactionsResponse( - val transactionsPerAccount: List> -) \ No newline at end of file diff --git a/rest/fints4kRest/src/main/resources/application.properties b/rest/fints4kRest/src/main/resources/application.properties deleted file mode 100644 index 0793aed7..00000000 --- a/rest/fints4kRest/src/main/resources/application.properties +++ /dev/null @@ -1,11 +0,0 @@ -quarkus.http.port=5555 - -# enable https support in native builds -quarkus.native.enable-https-url-handler=true -quarkus.native.enable-all-security-services=true - -quarkus.log.level=WARN -# FinTS messages are logged on DEBUG -quarkus.log.category."net.dankito.banking.fints.log.MessageLogCollector".level=INFO -# REST responses are logged on INFO -quarkus.log.category."net.dankito.banking.fints.rest.LoggingFilter".level=WARN \ No newline at end of file diff --git a/rest/fints4kRest/src/main/resources/logback.xml b/rest/fints4kRest/src/main/resources/logback.xml deleted file mode 100755 index 828c47b6..00000000 --- a/rest/fints4kRest/src/main/resources/logback.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - DEBUG - - - - - - - - - data/logs/fints4kRest-${bySecond}.log - - - %-4relative [%thread] %-5level %logger{35} - %msg%n - - - - DEBUG - - - - - - - - \ No newline at end of file diff --git a/rest/fints4kRest/src/native-test/kotlin/net/dankito/banking/fints/rest/NativeFints4kIT.kt b/rest/fints4kRest/src/native-test/kotlin/net/dankito/banking/fints/rest/NativeFints4kIT.kt deleted file mode 100644 index 8f497ac9..00000000 --- a/rest/fints4kRest/src/native-test/kotlin/net/dankito/banking/fints/rest/NativeFints4kIT.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.dankito.banking.fints.rest - -import io.quarkus.test.junit.NativeImageTest - -@NativeImageTest -class NativeFints4kIT : ExampleResourceTest() \ No newline at end of file diff --git a/rest/fints4kRest/src/test/kotlin/net/dankito/banking/fints/rest/fints4kResourceTest.kt b/rest/fints4kRest/src/test/kotlin/net/dankito/banking/fints/rest/fints4kResourceTest.kt deleted file mode 100644 index 338e8f8a..00000000 --- a/rest/fints4kRest/src/test/kotlin/net/dankito/banking/fints/rest/fints4kResourceTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package net.dankito.banking.fints.rest - -import io.quarkus.test.junit.QuarkusTest -import io.restassured.RestAssured.given -import org.hamcrest.CoreMatchers.`is` -import org.junit.jupiter.api.Test - -@QuarkusTest -class fints4kResourceTest { - - @Test - fun testHelloEndpoint() { - given() - .`when`().get("/hello") - .then() - .statusCode(200) - .body(`is`("hello")) - } - -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 540066df..1f0c14cc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,82 +1,13 @@ pluginManagement { repositories { - mavenLocal() mavenCentral() gradlePluginPortal() } - plugins { - id 'io.quarkus' version "$quarkusVersion" - } } rootProject.name = 'fints4kProject' -enableFeaturePreview('GRADLE_METADATA') - include ':fints4k' -include ':fints4k-jvm' include ':common' - - -/* UI */ - -include ':BankingUiCommon' - -include ':fints4kBankingClient' -include ':hbci4jBankingClient' - -include ':BankingPersistenceJson' -include ':LuceneBankingPersistence' -include ':RoomBankingPersistence' - -include ':BankingAndroidApp' - -include ':BankingJavaFxControls' -include ':BankingJavaFxApp' - -include ':BankingUiNativeIntegration' - - -project(':BankingUiCommon').projectDir = "$rootDir/ui/BankingUiCommon/" as File - -project(':fints4kBankingClient').projectDir = "$rootDir/ui/fints4kBankingClient/" as File -project(':hbci4jBankingClient').projectDir = "$rootDir/ui/hbci4jBankingClient/" as File - -project(':BankingAndroidApp').projectDir = "$rootDir/ui/BankingAndroidApp/" as File - -project(':BankingJavaFxControls').projectDir = "$rootDir/ui/BankingJavaFxControls/" as File -project(':BankingJavaFxApp').projectDir = "$rootDir/ui/BankingJavaFxApp/" as File - -project(':BankingUiNativeIntegration').projectDir = "$rootDir/ui/BankingUiNativeIntegration/" as File - -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 - - -/* REST APIs */ - -include ':BankFinderRest' -include ':fints4kRest' - -project(':BankFinderRest').projectDir = "$rootDir/rest/BankFinderRest/" as File -project(':fints4kRest').projectDir = "$rootDir/rest/fints4kRest/" as File - - - -/* Tools */ - -include ':BankFinder' -include ':LuceneBankFinder' -include ':BankListCreator' -include ':CsvAccountTransactionsImporterAndExporter' -include ':EpcQrCodeParser' - - -project(':BankFinder').projectDir = "$rootDir/tools/BankFinder/" as File -project(':LuceneBankFinder').projectDir = "$rootDir/tools/LuceneBankFinder/" as File -project(':BankListCreator').projectDir = "$rootDir/tools/BankListCreator/" as File -project(':CsvAccountTransactionsImporterAndExporter').projectDir = "$rootDir/tools/CsvAccountTransactionsImporterAndExporter/" as File -project(':EpcQrCodeParser').projectDir = "$rootDir/tools/EpcQrCodeParser/" as File diff --git a/tools/BankFinder/build.gradle b/tools/BankFinder/build.gradle index 1dcdba63..1795e4dd 100644 --- a/tools/BankFinder/build.gradle +++ b/tools/BankFinder/build.gradle @@ -1,6 +1,5 @@ plugins { id "org.jetbrains.kotlin.multiplatform" - id "com.android.library" id "maven-publish" } @@ -10,31 +9,43 @@ ext.artifactName = "bank-finder" kotlin { jvm { - compilations.main.kotlinOptions { - jvmTarget = "1.6" + compilations.all { + kotlinOptions.jvmTarget = '1.8' + } + withJava() + testRuns["test"].executionTask.configure { + useJUnitPlatform() } } - targets { - final def iOSTarget = iOSIsRealDevice ? presets.iosArm64 : presets.iosX64 +// js(BOTH) { +// browser { +// commonWebpackConfig { +// cssSupport.enabled = true +// } +// } +// } - fromPreset(iOSTarget, 'ios') { - binaries { - framework { - baseName = "BankFinder" - - embedBitcode(embedBitcodeValue) - } + ios { + binaries { + framework { + baseName = "BankFinder" } } } +// def hostOs = System.getProperty("os.name") +// def isMingwX64 = hostOs.startsWith("Windows") +// def nativeTarget +// if (hostOs == "Mac OS X") nativeTarget = macosX64('native') { binaries.executable() } +// else if (hostOs == "Linux") nativeTarget = linuxX64("native") { binaries.executable() } +// else if (isMingwX64) nativeTarget = mingwX64("native") { binaries.executable() } +// else throw new GradleException("Host OS is not supported in Kotlin/Native.") + sourceSets { commonMain { dependencies { - api kotlin("stdlib-common") - api project(":common") } } @@ -55,10 +66,7 @@ kotlin { jvmTest { dependencies { - implementation kotlin("test-junit") - implementation "org.junit.jupiter:junit-jupiter:$junit5Version" - runtimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit5Version" } } @@ -66,97 +74,14 @@ kotlin { iosMain { dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion" + } } iosTest { dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "org.jetbrains.kotlin:kotlin-test" + } } } -} - - -// 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 - } - -} - - -//task jarTest (type: Jar) { -// from sourceSets.jvmTest.output -// classifier = 'test' -//} -// -//configurations { -// testOutput -//} -// -//artifacts { -// testOutput jarTest -//} \ No newline at end of file +} \ No newline at end of file diff --git a/tools/EpcQrCodeParser/build.gradle b/tools/EpcQrCodeParser/build.gradle index c9b48918..25899cfb 100644 --- a/tools/EpcQrCodeParser/build.gradle +++ b/tools/EpcQrCodeParser/build.gradle @@ -1,55 +1,52 @@ plugins { id 'org.jetbrains.kotlin.multiplatform' -// id 'java-library' - id "com.android.library" // TODO: get rid off, use java-library instead id "maven-publish" } ext.artifactName = "epc-qr-code-parser" -def frameworkName = "EpcQrCodeParser" - kotlin { + jvm { + compilations.all { + kotlinOptions.jvmTarget = '1.8' + } + withJava() + testRuns["test"].executionTask.configure { + useJUnitPlatform() + } + } - targets { - final def iOSTarget = iOSIsRealDevice ? presets.iosArm64 : presets.iosX64 +// js(BOTH) { +// browser { +// commonWebpackConfig { +// cssSupport.enabled = true +// } +// } +// } - fromPreset(iOSTarget, 'ios') { - binaries { - framework { - baseName = frameworkName - - embedBitcode(embedBitcodeValue) - } + ios { + binaries { + framework { + baseName = "EpcQrCodeParser" } } } - jvm() +// def hostOs = System.getProperty("os.name") +// def isMingwX64 = hostOs.startsWith("Windows") +// def nativeTarget +// if (hostOs == "Mac OS X") nativeTarget = macosX64('native') { binaries.executable() } +// else if (hostOs == "Linux") nativeTarget = linuxX64("native") { binaries.executable() } +// else if (isMingwX64) nativeTarget = mingwX64("native") { binaries.executable() } +// else throw new GradleException("Host OS is not supported in Kotlin/Native.") - js { - - nodejs { - testTask { - enabled = false - } - } - - browser { - testTask { - enabled = false - } - - } - - } sourceSets { commonMain { dependencies { - implementation kotlin('stdlib-common') + } } commonTest { @@ -61,25 +58,24 @@ kotlin { jvmMain { dependencies { - implementation kotlin('stdlib-jdk8') + } } jvmTest { dependencies { - implementation kotlin('test') - implementation kotlin('test-junit') + } } jsMain { dependencies { - implementation kotlin('stdlib-js') + } } jsTest { dependencies { - implementation kotlin('test-js') + } } @@ -89,73 +85,4 @@ kotlin { } } -} - - -// 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 - - -// TODO: get rid of this -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 - } - } \ No newline at end of file diff --git a/ui/BankingAndroidApp/build.gradle b/ui/BankingAndroidApp/build.gradle deleted file mode 100644 index 73771dde..00000000 --- a/ui/BankingAndroidApp/build.gradle +++ /dev/null @@ -1,184 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' -apply plugin: 'kotlin-kapt' - - -repositories { - // for security issues fixed version of iText 2 from JasperReports - maven { - url "https://jaspersoft.jfrog.io/jaspersoft/third-party-ce-artifacts" - } -} - - -android { - compileSdkVersion androidCompileSdkVersion - buildToolsVersion androidBuildToolsVersion - - - defaultConfig { - applicationId "net.codinux.banking.android" - - minSdkVersion androidMinSdkVersion - targetSdkVersion androidTargetSdkVersion - - versionName version.replace("-", " ") - versionCode appVersionCode - - multiDexEnabled true - - // enable using vector drawables on pre Lollipop devices - vectorDrawables.useSupportLibrary = true - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - kotlinOptions { - jvmTarget = '1.8' - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - buildTypes { - debug { - applicationIdSuffix = ".develop" - } - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - signingConfigs { - debug { - // so that all machines have the same signing key, no matter if app is installed from developer's machine or from Jenkins - storeFile file('debug-keystore.jks') - storePassword 'find_my_bugs_before_releasing_me' - keyAlias 'DebugKey' - keyPassword 'find_my_bugs_before_releasing_me' - } - } - - packagingOptions { - pickFirst 'META-INF/kotlinx-coroutines-io.kotlin_module' - pickFirst 'META-INF/kotlinx-io.kotlin_module' - pickFirst 'META-INF/kotlinx-serialization-runtime.kotlin_module' - pickFirst 'META-INF/ktor-utils.kotlin_module' - pickFirst 'META-INF/ktor-client-core.kotlin_module' - pickFirst 'META-INF/ktor-http.kotlin_module' - pickFirst 'META-INF/ktor-io.kotlin_module' - pickFirst 'META-INF/ktor-http-cio.kotlin_module' - pickFirst 'META-INF/atomicfu.kotlin_module' - - pickFirst 'META-INF/common.kotlin_module' - pickFirst 'META-INF/fints4k.kotlin_module' - pickFirst 'META-INF/BankFinder.kotlin_module' - pickFirst 'META-INF/BankFinder.kotlin_module' - - pickFirst 'META-INF/DEPENDENCIES' - pickFirst 'META-INF/NOTICE' - pickFirst 'META-INF/LICENSE' - pickFirst 'META-INF/LICENSE.txt' - pickFirst 'META-INF/NOTICE.txt' - - pickFirst 'BankList.json' - exclude 'DetailedBankList.json' - } - - lintOptions { - abortOnError false - } - -} - -dependencies { - implementation project(":fints4k") - - implementation project(':BankingUiCommon') - - implementation project(':fints4kBankingClient') - - implementation project(':LuceneBankFinder') - - implementation project(':BankingPersistenceJson') - implementation project(':LuceneBankingPersistence') - implementation project(':RoomBankingPersistence') - - implementation project(':CsvAccountTransactionsImporterAndExporter') - - - implementation "net.dankito.text.extraction:itext2-text-extractor:$textExtractorVersion" - implementation "net.dankito.text.extraction:pdfbox-android-text-extractor:$textExtractorVersion" + "a" // TODO: version 0.6.0a become only necessary due to a misconfigured Maven upload task. Remove 'a' again on next version - - implementation "com.github.clans:fab:$clansFloatingActionButtonVersion" - implementation 'info.hoang8f:android-segmented:1.0.6' - - implementation "com.otaliastudios:autocomplete:$autocompleteVersion" - - implementation("com.journeyapps:zxing-android-embedded:4.1.0") { transitive = false } // transitive to use older ZXing version as ZXing 3.4.0 requires Android > 23 - implementation "com.google.zxing:core:$zxingVersion" - - implementation "com.yakivmospan:scytale:$scytaleVersion" - - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion" - - implementation "net.dankito.filechooserdialog:filechooserdialog-android:$fileChooserDialogVersion", { - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib' - exclude group: 'com.android.support', module: 'appcompat-v7' - exclude group: 'com.android.support', module: 'design' - exclude group: 'com.android.support.constraint', module: 'constraint-layout' - exclude module: 'recyclerview-v7' - } - - implementation "net.dankito.utils:android-utils:$androidUtilsVersion", { - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7' - exclude group: 'com.android.support', module: 'appcompat-v7' - exclude group: 'com.android.support', module: 'design' - } - - // Android 4 support has been dropped with version 3.13.0 - implementation('com.squareup.okhttp3:okhttp') { - version { strictly '3.12.12' } - } - - implementation "org.slf4j:slf4j-android:$slf4jVersion" - - - implementation "androidx.multidex:multidex:$multiDexVersion" - - implementation "androidx.appcompat:appcompat:$appCompatVersion" - implementation "androidx.recyclerview:recyclerview:$appCompatVersion" - implementation "androidx.annotation:annotation:$appCompatVersion" - implementation "com.google.android.material:material:$materialComponentsVersion" - implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion" - - implementation "androidx.biometric:biometric:$androidXBiometricVersion" - implementation "at.favre.lib:bcrypt:$bcryptVersion" - - implementation "com.mikepenz:fastadapter:$fastAdapterVersion" - implementation "com.mikepenz:fastadapter-extensions-binding:$fastAdapterVersion" - implementation "com.mikepenz:fastadapter-extensions-drag:$fastAdapterVersion" - implementation "com.mikepenz:fastadapter-extensions-swipe:$fastAdapterVersion" - implementation "com.mikepenz:fastadapter-extensions-utils:$fastAdapterVersion" - - // for MaterialDrawer - implementation "com.mikepenz:materialdrawer:$materialDrawerVersion" - implementation "com.mikepenz:materialdrawer-nav:$materialDrawerVersion" - - implementation "com.mikepenz:materialdrawer-iconics:$materialDrawerVersion" - - - kapt "com.google.dagger:dagger-compiler:$daggerVersion" - implementation "com.google.dagger:dagger:$daggerVersion" - - - implementation "androidx.navigation:navigation-fragment:$androidXNavigationVersion" - implementation "androidx.navigation:navigation-ui:$androidXNavigationVersion" - implementation "androidx.navigation:navigation-fragment-ktx:$androidXNavigationVersion" - implementation "androidx.navigation:navigation-ui-ktx:$androidXNavigationVersion" -} diff --git a/ui/BankingAndroidApp/debug-keystore.jks b/ui/BankingAndroidApp/debug-keystore.jks deleted file mode 100644 index 0809a56d..00000000 Binary files a/ui/BankingAndroidApp/debug-keystore.jks and /dev/null differ diff --git a/ui/BankingAndroidApp/proguard-rules.pro b/ui/BankingAndroidApp/proguard-rules.pro deleted file mode 100644 index f1b42451..00000000 --- a/ui/BankingAndroidApp/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 diff --git a/ui/BankingAndroidApp/src/main/AndroidManifest.xml b/ui/BankingAndroidApp/src/main/AndroidManifest.xml deleted file mode 100644 index 0a13382e..00000000 --- a/ui/BankingAndroidApp/src/main/AndroidManifest.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/ic_launcher-playstore.png b/ui/BankingAndroidApp/src/main/ic_launcher-playstore.png deleted file mode 100644 index 8e3d8299..00000000 Binary files a/ui/BankingAndroidApp/src/main/ic_launcher-playstore.png and /dev/null differ diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/BankingApp.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/BankingApp.kt deleted file mode 100644 index 5da1fc3e..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/BankingApp.kt +++ /dev/null @@ -1,25 +0,0 @@ -package net.dankito.banking.ui.android - -import androidx.multidex.MultiDexApplication -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.di.BankingModule -import net.dankito.banking.ui.android.di.DaggerBankingComponent - - -class BankingApp : MultiDexApplication() { - - override fun onCreate() { - super.onCreate() - - setupDependencyInjection() - } - - private fun setupDependencyInjection() { - val component = DaggerBankingComponent.builder() - .bankingModule(BankingModule(this)) - .build() - - BankingComponent.component = component - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/MainActivity.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/MainActivity.kt deleted file mode 100644 index 6f61767c..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/MainActivity.kt +++ /dev/null @@ -1,160 +0,0 @@ -package net.dankito.banking.ui.android - -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.os.Handler -import android.os.Looper -import android.view.Menu -import android.view.MotionEvent -import androidx.appcompat.app.ActionBarDrawerToggle -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.widget.Toolbar -import androidx.core.os.postDelayed -import androidx.drawerlayout.widget.DrawerLayout -import com.github.clans.fab.FloatingActionMenu -import com.google.zxing.integration.android.IntentIntegrator -import com.google.zxing.integration.android.IntentResult -import kotlinx.android.synthetic.main.activity_main.* -import net.codinux.banking.tools.epcqrcode.ParseEpcQrCodeResult -import net.dankito.banking.ui.android.activities.BaseActivity -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.di.BankingModule -import net.dankito.banking.ui.android.views.DrawerView -import net.dankito.banking.ui.android.views.MainActivityFloatingActionMenuButton -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.utils.android.permissions.IPermissionsService -import net.dankito.utils.android.permissions.PermissionsService -import javax.inject.Inject - - -class MainActivity : BaseActivity() { - -// private lateinit var appBarConfiguration: AppBarConfiguration - - private lateinit var drawerToggle: ActionBarDrawerToggle - - private lateinit var drawerView: DrawerView - - private lateinit var floatingActionMenuButton: MainActivityFloatingActionMenuButton - - - @Inject - protected lateinit var presenter: BankingPresenter - - @Inject - protected lateinit var permissionsService: IPermissionsService - - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - BankingModule.mainActivity = this - - BankingComponent.component.inject(this) - - initUi() - } - - private fun initUi() { - setContentView(R.layout.activity_main) - - val toolbar: Toolbar = findViewById(R.id.toolbar) - setSupportActionBar(toolbar) - - val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) -// val navController = findNavController(R.id.nav_host_fragment) - -// // Passing each menu ID as a set of Ids because each -// // menu should be considered as top level destinations. -// appBarConfiguration = AppBarConfiguration( -// setOf( -// R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow, -// R.id.nav_tools, R.id.nav_share, R.id.nav_send -// ), drawerLayout -// ) -// -// setupActionBarWithNavController(navController, appBarConfiguration) -// navigationView.setupWithNavController(navController) - - drawerToggle = ActionBarDrawerToggle( - this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) - drawerLayout.addDrawerListener(drawerToggle) - drawerToggle.syncState() - - slider?.let { slider -> - drawerView = DrawerView(this, slider, presenter) - } - - val floatingActionMenu = findViewById(R.id.floatingActionMenu) - floatingActionMenuButton = MainActivityFloatingActionMenuButton(floatingActionMenu, permissionsService, presenter) - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.menu_main, menu) - - return true - } - - - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - permissionsService.onRequestPermissionsResult(requestCode, permissions, grantResults) - - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - val scanQrCodeResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data) - - if (scanQrCodeResult != null) { - // at this point camera activity is still displayed and not returned yet to MainActivity -> app would crash if we don't wait - Handler(Looper.getMainLooper()).postDelayed(250) { - handleQrCodeScanResult(scanQrCodeResult) - } - } - else { - super.onActivityResult(requestCode, resultCode, data) - } - } - - private fun handleQrCodeScanResult(scanQrCodeResult: IntentResult) { - scanQrCodeResult.contents?.let { decodedQrCode -> - val result = presenter.showTransferMoneyDialogWithDataFromQrCode(decodedQrCode) - - if (result.successful == false) { - showParseQrCodeError(result) - } - } - } - - protected fun showParseQrCodeError(result: ParseEpcQrCodeResult) { - // TODO: show localized error message that matches ParseEpcQrCodeResultCode - val errorMessage = getString(R.string.money_transfer_from_scanning_qr_code_error, result.error, result.decodedQrCode) - - AlertDialog.Builder(this) - .setMessage(errorMessage) - .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } - .show() - } - - - override fun dispatchTouchEvent(event: MotionEvent): Boolean { - if(floatingActionMenuButton.handlesTouch(event)) { // close menu when menu is opened and touch is outside floatingActionMenuButton - return true - } - - return super.dispatchTouchEvent(event) - } - - override fun onBackPressed() { - if (floatingActionMenuButton.handlesBackButtonPress()) { // close menu when menu is opened and back button gets pressed - return - } - - super.onBackPressed() - } - -} diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/RouterAndroid.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/RouterAndroid.kt deleted file mode 100644 index 342084d2..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/RouterAndroid.kt +++ /dev/null @@ -1,57 +0,0 @@ -package net.dankito.banking.ui.android - -import net.dankito.banking.ui.android.util.CurrentActivityTracker -import net.dankito.banking.ui.IRouter -import net.dankito.banking.ui.android.dialogs.* -import net.dankito.banking.ui.model.TypedBankData -import net.dankito.banking.ui.model.parameters.TransferMoneyData -import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult -import net.dankito.banking.ui.model.tan.EnterTanResult -import net.dankito.banking.ui.model.tan.TanChallenge -import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium -import net.dankito.banking.ui.presenter.BankingPresenter - - -open class RouterAndroid(protected val activityTracker: CurrentActivityTracker) : IRouter { - - override fun showAddAccountDialog(presenter: BankingPresenter) { - activityTracker.currentOrNextActivity { activity -> - AddAccountDialog().show(activity) - } - } - - override fun getTanFromUserFromNonUiThread(bank: TypedBankData, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) { - activityTracker.currentOrNextActivity { activity -> - activity.runOnUiThread { - EnterTanDialog().show(bank, tanChallenge, activity, false) { result -> - callback(result) - } - } - } - } - - override fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) { - activityTracker.currentOrNextActivity { activity -> - activity.runOnUiThread { - EnterAtcDialog().show(tanMedium, activity, false) { enteredResult -> - callback(enteredResult) - } - } - } - } - - override fun showTransferMoneyDialog(presenter: BankingPresenter, preselectedValues: TransferMoneyData?) { - activityTracker.currentOrNextActivity { activity -> - TransferMoneyDialog().show(activity, preselectedValues) - } - } - - override fun showSendMessageLogDialog(presenter: BankingPresenter) { - activityTracker.currentOrNextActivity { activity -> - activity.runOnUiThread { - SendMessageLogDialog().show(activity) - } - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/ActivityExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/ActivityExtensions.kt deleted file mode 100644 index ded62c06..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/ActivityExtensions.kt +++ /dev/null @@ -1,20 +0,0 @@ -package net.dankito.banking.ui.android.activities - -import android.app.Activity -import android.content.Intent -import android.util.DisplayMetrics - - -fun Activity.navigateToActivity(activityClass: Class) { - val intent = Intent(applicationContext, activityClass) - - startActivity(intent) -} - -val Activity.screenWidth: Int - get() { - val displayMetrics = DisplayMetrics() - windowManager.defaultDisplay.getMetrics(displayMetrics) - - return displayMetrics.widthPixels - } \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/BaseActivity.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/BaseActivity.kt deleted file mode 100644 index 45dae8df..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/BaseActivity.kt +++ /dev/null @@ -1,78 +0,0 @@ -package net.dankito.banking.ui.android.activities - -import android.content.pm.ActivityInfo -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.util.CurrentActivityTracker -import org.slf4j.LoggerFactory -import javax.inject.Inject - - -abstract class BaseActivity : AppCompatActivity() { - - companion object { - private val log = LoggerFactory.getLogger(BaseActivity::class.java) - } - - - @Inject - protected lateinit var currentActivityTracker: CurrentActivityTracker - - - init { - BankingComponent.component.inject(this) - } - - - open val isRunningOnTablet: Boolean - get() = resources.getBoolean(R.bool.isTablet) - - - override fun onCreate(savedInstanceState: Bundle?) { - log.info("Creating Activity $this") - - // a bit bad as it not clearly visible that orientation lock is done here - if (isRunningOnTablet == false) { // lock screen to portrait mode on phones as there app really looks bad and some EditTexts are almost unusable - requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - } - - super.onCreate(savedInstanceState) - } - - override fun onStart() { - super.onStart() - - currentActivityTracker.currentActivity = this - - log.info("Started Activity $this") - } - - override fun onResume() { - super.onResume() - - currentActivityTracker.currentActivity = this - - log.info("Resumed Activity $this") - } - - override fun onPause() { - log.info("Paused Activity $this") - - super.onPause() - } - - override fun onStop() { - log.info("Stopped Activity $this") - - super.onStop() - } - - override fun onDestroy() { - log.info("Destroyed Activity $this") - - super.onDestroy() - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/LandingActivity.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/LandingActivity.kt deleted file mode 100644 index c72b9aa9..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/LandingActivity.kt +++ /dev/null @@ -1,42 +0,0 @@ -package net.dankito.banking.ui.android.activities - -import android.app.Activity -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import net.dankito.banking.ui.android.MainActivity -import net.dankito.banking.ui.android.authentication.AuthenticationService -import net.dankito.banking.ui.android.authentication.AuthenticationType -import net.dankito.banking.ui.android.di.BankingComponent -import javax.inject.Inject - - -open class LandingActivity : AppCompatActivity() { - - @Inject - protected lateinit var authenticationService: AuthenticationService - - - init { - BankingComponent.component.inject(this) - } - - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - if (authenticationService.authenticationType == AuthenticationType.None) { - launchActivity(MainActivity::class.java) - } - else { - launchActivity(LoginActivity::class.java) - } - } - - - protected open fun launchActivity(activityClass: Class) { - navigateToActivity(activityClass) - - finish() - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/LoginActivity.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/LoginActivity.kt deleted file mode 100644 index 2a67a43a..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/activities/LoginActivity.kt +++ /dev/null @@ -1,89 +0,0 @@ -package net.dankito.banking.ui.android.activities - -import android.os.Bundle -import android.view.View -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_login.* -import kotlinx.android.synthetic.main.activity_login.btnBiometricAuthentication -import kotlinx.android.synthetic.main.view_biometric_authentication_button.* -import net.dankito.banking.ui.android.MainActivity -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.authentication.AuthenticationService -import net.dankito.banking.ui.android.authentication.AuthenticationType -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.extensions.addEnterPressedListener -import net.dankito.utils.android.extensions.hide -import javax.inject.Inject - - -open class LoginActivity : BaseActivity() { - - @Inject - protected lateinit var authenticationService: AuthenticationService - - - init { - BankingComponent.component.inject(this) - } - - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - initUi() - } - - - protected open fun initUi() { - setContentView(R.layout.activity_login) - - if (authenticationService.authenticationType == AuthenticationType.Password) { - lytBiometricAuthentication.hide() - - edtxtLoginPassword.actualEditText.addEnterPressedListener { - checkEnteredPasswordAndLogIn() - true - } - - btnLogin.setOnClickListener { checkEnteredPasswordAndLogIn() } - } - else { - lytPasswordAuthentication.hide() - - btnBiometricAuthentication.customButtonClickHandler = { - authenticationService.loginUserWithBiometric { result -> - if (result) { - btnStartBiometricAuthentication.isEnabled = false - - biometricAuthenticationSuccessful() - } - } - } - - btnBiometricAuthentication.showBiometricPrompt() - } - } - - - protected open fun checkEnteredPasswordAndLogIn() { - btnLogin.isEnabled = false - - if (authenticationService.loginUserWithPassword(edtxtLoginPassword.chars)) { - navigateToMainActivity() - } - else { - btnLogin.isEnabled = true - - Toast.makeText(this, R.string.activity_login_incorrect_password_entered, Toast.LENGTH_SHORT).show() - } - } - - protected open fun biometricAuthenticationSuccessful() { - navigateToMainActivity() - } - - protected open fun navigateToMainActivity() { - navigateToActivity(MainActivity::class.java) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt deleted file mode 100644 index fcf92c2c..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt +++ /dev/null @@ -1,81 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import android.view.ContextMenu -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.viewholder.AccountTransactionViewHolder -import net.dankito.banking.ui.android.dialogs.AccountTransactionDetailsDialog -import net.dankito.banking.ui.android.extensions.setIcon -import net.dankito.banking.ui.android.extensions.showAmount -import net.dankito.banking.ui.model.IAccountTransaction -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.utils.android.extensions.asActivity -import net.dankito.utils.android.extensions.hide -import net.dankito.utils.android.extensions.show -import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter - - -open class AccountTransactionAdapter(protected val presenter: BankingPresenter) - : ListRecyclerAdapter() { - - - var selectedTransaction: IAccountTransaction? = null - - - override fun getListItemLayoutId() = R.layout.list_item_account_transaction - - override fun createViewHolder(itemView: View): AccountTransactionViewHolder { - val viewHolder = AccountTransactionViewHolder(itemView) - - itemView.setOnCreateContextMenuListener { menu, view, menuInfo -> createContextMenu(menu, view, menuInfo, viewHolder) } - - return viewHolder - } - - override fun bindItemToView(viewHolder: AccountTransactionViewHolder, item: IAccountTransaction) { - viewHolder.txtvwDate.text = presenter.formatToShortDate(item.valueDate) - - val label = if (item.showOtherPartyName) item.otherPartyName else item.bookingText - viewHolder.txtvwTransactionLabel.text = label ?: item.bookingText ?: "" - - viewHolder.txtvwReference.text = item.reference - - viewHolder.txtvwAmount.showAmount(presenter, item.amount, item.currency) - - if (presenter.areAllAccountSelected) { - viewHolder.imgvwBankIcon.setIcon(item.account.bank) - viewHolder.imgvwBankIcon.show() - } - else { - // TODO: if bank icon isn't set: Show default icon? show at least an empty space to that amount and date don't shift up causing an inconsistent view? - viewHolder.imgvwBankIcon.hide() - } - - viewHolder.itemView.setOnClickListener { - AccountTransactionDetailsDialog().show(item, viewHolder.itemView.context as AppCompatActivity) - } - } - - - protected open fun createContextMenu(menu: ContextMenu, view: View, menuInfo: ContextMenu.ContextMenuInfo?, - viewHolder: AccountTransactionViewHolder) { - - view.context.asActivity()?.menuInflater?.inflate(R.menu.context_menu_account_transactions, menu) - - selectedTransaction = getItem(viewHolder.adapterPosition) - - val canCreateMoneyTransferFrom = selectedTransaction?.canCreateMoneyTransferFrom ?: false - - menu.findItem(R.id.mnitmNewTransferWithSameData)?.isVisible = canCreateMoneyTransferFrom - - menu.findItem(R.id.mnitmNewTransferToSameTransactionParty)?.let { mnitmShowTransferMoneyDialog -> - mnitmShowTransferMoneyDialog.isVisible = canCreateMoneyTransferFrom - - val recipientName = selectedTransaction?.otherPartyName ?: "" - - mnitmShowTransferMoneyDialog.title = view.context.getString(R.string.fragment_home_transfer_money_to, recipientName) - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankDataAdapterItem.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankDataAdapterItem.kt deleted file mode 100644 index 84afc7ed..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankDataAdapterItem.kt +++ /dev/null @@ -1,25 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import android.view.View -import com.mikepenz.fastadapter.drag.IDraggable -import com.mikepenz.fastadapter.items.AbstractItem -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.viewholder.BankDataViewHolder -import net.dankito.banking.ui.model.TypedBankData - - -open class BankDataAdapterItem(open val bank: TypedBankData) : AbstractItem(), IDraggable { - - override var isDraggable = true - - override val type: Int - get() = R.id.bank_data_item_id - - override val layoutRes: Int - get() = R.layout.list_item_bank_data - - override fun getViewHolder(v: View): BankDataViewHolder { - return BankDataViewHolder(v) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankListAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankListAdapter.kt deleted file mode 100644 index 90ca7fc8..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankListAdapter.kt +++ /dev/null @@ -1,43 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import android.view.View -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.viewholder.BankInfoViewHolder -import net.dankito.banking.bankfinder.BankInfo -import net.dankito.utils.android.extensions.setTintColor -import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter - - -open class BankListAdapter(protected val itemClicked: ((BankInfo) -> Unit)? = null) : ListRecyclerAdapter() { - - override fun getListItemLayoutId() = R.layout.list_item_bank_info - - override fun createViewHolder(itemView: View): BankInfoViewHolder { - return BankInfoViewHolder(itemView) - } - - override fun bindItemToView(viewHolder: BankInfoViewHolder, item: BankInfo) { - if (item.supportsFinTs3_0) { - viewHolder.imgSupportsFints30.setImageResource(R.drawable.ic_check_circle_white_48dp) - viewHolder.imgSupportsFints30.setTintColor(R.color.list_item_bank_info_bank_supported) - } - else { - viewHolder.imgSupportsFints30.setImageResource(R.drawable.ic_clear_white_48dp) - viewHolder.imgSupportsFints30.setTintColor(R.color.list_item_bank_info_bank_not_supported) - } - - viewHolder.txtvwBankName.text = item.name - viewHolder.txtvwBankName.isEnabled = item.supportsFinTs3_0 - - viewHolder.txtvwBankCode.text = item.bankCode - viewHolder.txtvwBankCode.isEnabled = item.supportsFinTs3_0 - - viewHolder.txtvwBankAddress.text = item.postalCode + " " + item.city - viewHolder.txtvwBankAddress.isEnabled = item.supportsFinTs3_0 - - viewHolder.itemView.setOnClickListener { - itemClicked?.invoke(item) - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/CheckableValueAdapterItem.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/CheckableValueAdapterItem.kt deleted file mode 100644 index 0d18d1d0..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/CheckableValueAdapterItem.kt +++ /dev/null @@ -1,27 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import android.content.Context -import android.view.View -import androidx.annotation.StringRes -import com.mikepenz.fastadapter.items.AbstractItem -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.viewholder.CheckableValueViewHolder - - -open class CheckableValueAdapterItem(open val isChecked: Boolean, open val text: String) : AbstractItem() { - - constructor(isChecked: Boolean, context: Context, @StringRes textStringResourceId: Int) : this(isChecked, context.getString(textStringResourceId)) - - - override val type: Int - get() = R.id.checkable_value_item_id - - override val layoutRes: Int - get() = R.layout.list_item_checkable_value - - - override fun getViewHolder(v: View): CheckableValueViewHolder { - return CheckableValueViewHolder(v) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/DraggableBankAccountAdapterItem.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/DraggableBankAccountAdapterItem.kt deleted file mode 100644 index 1eb9022e..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/DraggableBankAccountAdapterItem.kt +++ /dev/null @@ -1,25 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import android.view.View -import com.mikepenz.fastadapter.drag.IDraggable -import com.mikepenz.fastadapter.items.AbstractItem -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.viewholder.DraggableBankAccountViewHolder -import net.dankito.banking.ui.model.TypedBankAccount - - -open class DraggableBankAccountAdapterItem(open val account: TypedBankAccount) : AbstractItem(), IDraggable { - - override var isDraggable = true - - override val type: Int - get() = R.id.draggable_bank_account_item_id - - override val layoutRes: Int - get() = R.layout.list_item_draggable_bank_account - - override fun getViewHolder(v: View): DraggableBankAccountViewHolder { - return DraggableBankAccountViewHolder(v) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/FastAdapterRecyclerView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/FastAdapterRecyclerView.kt deleted file mode 100644 index 5351e464..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/FastAdapterRecyclerView.kt +++ /dev/null @@ -1,86 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import androidx.recyclerview.widget.DefaultItemAnimator -import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.mikepenz.fastadapter.FastAdapter -import com.mikepenz.fastadapter.GenericItem -import com.mikepenz.fastadapter.adapters.ItemAdapter -import com.mikepenz.fastadapter.adapters.ItemAdapter.Companion.items -import com.mikepenz.fastadapter.drag.ItemTouchCallback -import com.mikepenz.fastadapter.drag.SimpleDragCallback -import com.mikepenz.fastadapter.select.getSelectExtension -import com.mikepenz.fastadapter.utils.DragDropUtil - - -open class FastAdapterRecyclerView( - recyclerView: RecyclerView, - items: List = listOf(), - enableDragAndDrop: Boolean = false, - open var itemDropped: ((oldPosition: Int, oldItem: Item, newPosition: Int, newItem: Item) -> Unit)? = null, - open var onClickListener: ((Item) -> Unit)? = null -) : ItemTouchCallback { - - - protected val fastAdapter: FastAdapter - protected val itemAdapter: ItemAdapter - - private lateinit var touchCallback: SimpleDragCallback - private lateinit var touchHelper: ItemTouchHelper - - - init { - itemAdapter = items() - - fastAdapter = FastAdapter.with(itemAdapter) - - init(recyclerView, items, enableDragAndDrop) - } - - - protected open fun init(recyclerView: RecyclerView, items: List, enableDragAndDrop: Boolean = true) { - val selectExtension = fastAdapter.getSelectExtension() - selectExtension.isSelectable = true - - fastAdapter.onClickListener = { _, _, item, _ -> - onClickListener?.invoke(item) - false - } - - recyclerView.layoutManager = LinearLayoutManager(recyclerView.context) - recyclerView.itemAnimator = DefaultItemAnimator() - recyclerView.adapter = fastAdapter - - itemAdapter.set(items) - - - if (enableDragAndDrop) { - touchCallback = SimpleDragCallback(this) - touchHelper = ItemTouchHelper(touchCallback) - touchHelper.attachToRecyclerView(recyclerView) - } - } - - - override fun itemTouchStartDrag(viewHolder: RecyclerView.ViewHolder) { - // add visual highlight to dragged item - } - - override fun itemTouchOnMove(oldPosition: Int, newPosition: Int): Boolean { - DragDropUtil.onMove(itemAdapter, oldPosition, newPosition) // change position - return true - } - - override fun itemTouchDropped(oldPosition: Int, newPosition: Int) { - // remove visual highlight to dropped item - - itemDropped?.invoke(oldPosition, itemAdapter.getAdapterItem(oldPosition), newPosition, itemAdapter.getAdapterItem(newPosition)) - } - - - open fun setItems(items: List) { - itemAdapter.set(items) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/IconedBankAccountsAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/IconedBankAccountsAdapter.kt deleted file mode 100644 index 715cbf6e..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/IconedBankAccountsAdapter.kt +++ /dev/null @@ -1,32 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import kotlinx.android.synthetic.main.list_item_iconed_bank_account.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.extensions.setIcon -import net.dankito.banking.ui.model.TypedBankAccount -import net.dankito.utils.android.ui.adapter.ListAdapter - - -open class IconedBankAccountsAdapter(accounts: List) : ListAdapter(accounts) { - - override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? { - - val item = getItem(position) - - val inflater = parent?.context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater - val view = convertView ?: inflater?.inflate(R.layout.list_item_iconed_bank_account, parent, false) - - view?.let { - view.txtBankAccountDisplayName.text = item.displayName - - view.imgBankIcon.setIcon(item.bank) - } - - return view - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/RecipientListAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/RecipientListAdapter.kt deleted file mode 100644 index e46c03c1..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/RecipientListAdapter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import android.view.View -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.viewholder.RecipientViewHolder -import net.dankito.banking.search.TransactionParty -import net.dankito.utils.android.extensions.isGone -import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter - - -open class RecipientListAdapter(protected val itemClicked: ((TransactionParty) -> Unit)? = null) : ListRecyclerAdapter() { - - override fun getListItemLayoutId() = R.layout.list_item_recipient - - override fun createViewHolder(itemView: View): RecipientViewHolder { - return RecipientViewHolder(itemView) - } - - override fun bindItemToView(viewHolder: RecipientViewHolder, item: TransactionParty) { - viewHolder.txtvwRecipientName.text = item.name - - viewHolder.txtvwRecipientBankName.text = item.bankName - viewHolder.txtvwRecipientBankName.isGone = item.bankName.isNullOrBlank() - - viewHolder.txtvwRecipientAccountId.text = item.iban - - viewHolder.txtvwRecipientBankCode.text = item.bic - - viewHolder.itemView.setOnClickListener { - itemClicked?.invoke(item) - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/TanMediumAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/TanMediumAdapter.kt deleted file mode 100644 index de02b008..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/TanMediumAdapter.kt +++ /dev/null @@ -1,25 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.model.tan.TanMedium -import net.dankito.utils.android.extensions.asActivity -import net.dankito.utils.android.ui.adapter.ListAdapter - - -open class TanMediumAdapter : ListAdapter() { - - override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? { - val tanMedium = getItem(position) - - val view = convertView ?: parent?.context?.asActivity()?.layoutInflater?.inflate( - R.layout.list_item_tan_medium, parent, false) - - view?.findViewById(R.id.txtTanMediumDisplayName)?.text = tanMedium.displayName - - return view - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/TanMethodAdapterItem.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/TanMethodAdapterItem.kt deleted file mode 100644 index 49798d3a..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/TanMethodAdapterItem.kt +++ /dev/null @@ -1,7 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import net.dankito.banking.ui.model.tan.TanMethod - - -open class TanMethodAdapterItem(open val tanMethod: TanMethod, isSelectedTanMethod: Boolean) - : CheckableValueAdapterItem(isSelectedTanMethod, tanMethod.displayName) \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/TanMethodsAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/TanMethodsAdapter.kt deleted file mode 100644 index 4be49be7..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/TanMethodsAdapter.kt +++ /dev/null @@ -1,25 +0,0 @@ -package net.dankito.banking.ui.android.adapter - -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.model.tan.TanMethod -import net.dankito.utils.android.extensions.asActivity -import net.dankito.utils.android.ui.adapter.ListAdapter - - -open class TanMethodsAdapter : ListAdapter() { - - override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? { - val method = getItem(position) - - val view = convertView ?: parent?.context?.asActivity()?.layoutInflater?.inflate( - R.layout.list_item_tan_method, parent, false) - - view?.findViewById(R.id.txtTanMethodDisplayName)?.text = method.displayName - - return view - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/BankInfoPresenter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/BankInfoPresenter.kt deleted file mode 100644 index 4fd503b8..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/BankInfoPresenter.kt +++ /dev/null @@ -1,38 +0,0 @@ -package net.dankito.banking.ui.android.adapter.presenter - -import android.content.Context -import androidx.recyclerview.widget.RecyclerView -import com.otaliastudios.autocomplete.RecyclerViewPresenter -import kotlinx.coroutines.* -import net.dankito.banking.ui.android.adapter.BankListAdapter -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.banking.bankfinder.BankInfo -import net.dankito.banking.ui.android.extensions.addHorizontalItemDivider - - -open class BankInfoPresenter(protected val presenter: BankingPresenter, context: Context) : RecyclerViewPresenter(context) { - - protected val adapter = BankListAdapter { dispatchClick(it) } - - protected var lastSearchBanksJob: Job? = null - - - override fun instantiateAdapter(): RecyclerView.Adapter<*> { - recyclerView?.addHorizontalItemDivider() - - return adapter - } - - override fun onQuery(query: CharSequence?) { - lastSearchBanksJob?.cancel() - - lastSearchBanksJob = GlobalScope.launch(Dispatchers.IO) { - val filteredBanks = presenter.findBanksByNameBankCodeOrCity(query?.toString()) - - withContext(Dispatchers.Main) { - adapter.items = filteredBanks - } - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/RecipientPresenter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/RecipientPresenter.kt deleted file mode 100644 index cc71845c..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/presenter/RecipientPresenter.kt +++ /dev/null @@ -1,38 +0,0 @@ -package net.dankito.banking.ui.android.adapter.presenter - -import android.content.Context -import androidx.recyclerview.widget.RecyclerView -import com.otaliastudios.autocomplete.RecyclerViewPresenter -import kotlinx.coroutines.* -import net.dankito.banking.ui.android.adapter.RecipientListAdapter -import net.dankito.banking.search.TransactionParty -import net.dankito.banking.ui.android.extensions.addHorizontalItemDivider -import net.dankito.banking.ui.presenter.BankingPresenter - - -open class RecipientPresenter(protected val bankingPresenter: BankingPresenter, context: Context) : RecyclerViewPresenter(context) { - - protected val adapter = RecipientListAdapter { dispatchClick(it) } - - protected var lastSearchRecipientJob: Job? = null - - - override fun instantiateAdapter(): RecyclerView.Adapter<*> { - recyclerView?.addHorizontalItemDivider() - - return adapter - } - - override fun onQuery(query: CharSequence?) { - lastSearchRecipientJob?.cancel() - - lastSearchRecipientJob = GlobalScope.launch(Dispatchers.IO) { - val potentialRecipients = bankingPresenter.findRecipientsForName(query?.toString() ?: "") - - withContext(Dispatchers.Main) { - adapter.items = potentialRecipients - } - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/AccountTransactionViewHolder.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/AccountTransactionViewHolder.kt deleted file mode 100644 index d05eb757..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/AccountTransactionViewHolder.kt +++ /dev/null @@ -1,22 +0,0 @@ -package net.dankito.banking.ui.android.adapter.viewholder - -import android.view.View -import android.widget.ImageView -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.list_item_account_transaction.view.* - - -open class AccountTransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - - val txtvwDate: TextView = itemView.txtvwDate - - val txtvwTransactionLabel: TextView = itemView.txtvwTransactionLabel - - val txtvwReference: TextView = itemView.txtvwReference - - val txtvwAmount: TextView = itemView.txtvwAmount - - val imgvwBankIcon: ImageView = itemView.imgvwBankIcon - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/BankDataViewHolder.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/BankDataViewHolder.kt deleted file mode 100644 index 8f166ee3..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/BankDataViewHolder.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.dankito.banking.ui.android.adapter.viewholder - -import android.view.View -import android.widget.ImageView -import android.widget.TextView -import com.mikepenz.fastadapter.FastAdapter -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.BankDataAdapterItem -import net.dankito.banking.ui.android.extensions.setIcon - - -open class BankDataViewHolder(view: View) : FastAdapter.ViewHolder(view) { - - protected var bankIcon: ImageView = view.findViewById(R.id.imgBankIcon) - protected var bankDisplayName: TextView = view.findViewById(R.id.txtBankDisplayName) - - - override fun bindView(item: BankDataAdapterItem, payloads: List) { - bankIcon.setIcon(item.bank) - bankDisplayName.text = item.bank.displayName - } - - override fun unbindView(item: BankDataAdapterItem) { - bankDisplayName.text = null - bankIcon.setImageURI(null) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/BankInfoViewHolder.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/BankInfoViewHolder.kt deleted file mode 100644 index b19ad1a3..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/BankInfoViewHolder.kt +++ /dev/null @@ -1,20 +0,0 @@ -package net.dankito.banking.ui.android.adapter.viewholder - -import android.view.View -import android.widget.ImageView -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.list_item_bank_info.view.* - - -open class BankInfoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - - val imgSupportsFints30: ImageView = itemView.imgSupportsFints30 - - val txtvwBankName: TextView = itemView.txtvwBankName - - val txtvwBankCode: TextView = itemView.txtvwBankCode - - val txtvwBankAddress: TextView = itemView.txtvwBankAddress - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/CheckableValueViewHolder.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/CheckableValueViewHolder.kt deleted file mode 100644 index 4d364c69..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/CheckableValueViewHolder.kt +++ /dev/null @@ -1,31 +0,0 @@ -package net.dankito.banking.ui.android.adapter.viewholder - -import android.view.View -import android.widget.ImageView -import android.widget.TextView -import com.mikepenz.fastadapter.FastAdapter -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.CheckableValueAdapterItem -import net.dankito.utils.android.extensions.setVisibleOrInvisible - - -open class CheckableValueViewHolder(view: View) : FastAdapter.ViewHolder(view) { - - protected var imgCheckmark: ImageView = view.findViewById(R.id.imgCheckmark) - - protected var txtValue: TextView = view.findViewById(R.id.txtValue) - - - override fun bindView(item: CheckableValueAdapterItem, payloads: List) { - imgCheckmark.setVisibleOrInvisible(item.isChecked) - - txtValue.text = item.text - } - - override fun unbindView(item: CheckableValueAdapterItem) { - imgCheckmark.setImageURI(null) - - txtValue.text = null - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/DraggableBankAccountViewHolder.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/DraggableBankAccountViewHolder.kt deleted file mode 100644 index 0e33ab52..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/DraggableBankAccountViewHolder.kt +++ /dev/null @@ -1,23 +0,0 @@ -package net.dankito.banking.ui.android.adapter.viewholder - -import android.view.View -import android.widget.TextView -import com.mikepenz.fastadapter.FastAdapter -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.DraggableBankAccountAdapterItem - - -open class DraggableBankAccountViewHolder(view: View) : FastAdapter.ViewHolder(view) { - - protected var accountDisplayName: TextView = view.findViewById(R.id.txtBankDisplayName) - - - override fun bindView(item: DraggableBankAccountAdapterItem, payloads: List) { - accountDisplayName.text = item.account.displayName - } - - override fun unbindView(item: DraggableBankAccountAdapterItem) { - accountDisplayName.text = null - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/RecipientViewHolder.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/RecipientViewHolder.kt deleted file mode 100644 index 80ad493f..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/viewholder/RecipientViewHolder.kt +++ /dev/null @@ -1,19 +0,0 @@ -package net.dankito.banking.ui.android.adapter.viewholder - -import android.view.View -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.list_item_recipient.view.* - - -open class RecipientViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - - val txtvwRecipientName: TextView = itemView.txtvwRecipientName - - val txtvwRecipientBankName: TextView = itemView.txtvwRecipientBankName - - val txtvwRecipientAccountId: TextView = itemView.txtvwRecipientAccountId - - val txtvwRecipientBankCode: TextView = itemView.txtvwRecipientBankCode - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/alerts/AskDeleteAccountAlert.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/alerts/AskDeleteAccountAlert.kt deleted file mode 100644 index 61665151..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/alerts/AskDeleteAccountAlert.kt +++ /dev/null @@ -1,25 +0,0 @@ -package net.dankito.banking.ui.android.alerts - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.model.TypedBankData -import net.dankito.banking.ui.presenter.BankingPresenter - - -open class AskDeleteAccountAlert { - - open fun show(bank: TypedBankData, presenter: BankingPresenter, context: Context, accountDeleted: (() -> Unit)? = null) { - AlertDialog.Builder(context) - .setTitle(context.getString(R.string.alert_ask_delete_account_title)) - .setMessage(context.getString(R.string.alert_ask_delete_account_message, bank.displayName)) - .setPositiveButton(R.string.delete) { dialog, _ -> - presenter.deleteAccount(bank) - dialog.dismiss() - accountDeleted?.invoke() - } - .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() } - .show() - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/alerts/AskDismissChangesAlert.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/alerts/AskDismissChangesAlert.kt deleted file mode 100644 index 6e01711d..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/alerts/AskDismissChangesAlert.kt +++ /dev/null @@ -1,24 +0,0 @@ -package net.dankito.banking.ui.android.alerts - -import androidx.appcompat.app.AlertDialog -import androidx.fragment.app.DialogFragment -import net.dankito.banking.ui.android.R - - -open class AskDismissChangesAlert { - - open fun show(dialog: DialogFragment) { - val context = dialog.requireContext() - - AlertDialog.Builder(context) - .setTitle(context.getString(R.string.alert_ask_discard_changes_title)) - .setMessage(context.getString(R.string.alert_ask_discard_changes_message)) - .setPositiveButton(R.string.discard) { alert, _ -> - alert.dismiss() - dialog.dismiss() - } - .setNegativeButton(R.string.cancel) { alert, _ -> alert.dismiss() } - .show() - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationResult.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationResult.kt deleted file mode 100644 index d6c8111d..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationResult.kt +++ /dev/null @@ -1,18 +0,0 @@ -package net.dankito.banking.ui.android.authentication - - -open class AuthenticationResult( - open val successful: Boolean, - open val error: String? = null -) { - - override fun toString(): String { - return if (successful) { - "Successful" - } - else { - "Error occurred: $error" - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationService.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationService.kt deleted file mode 100644 index a95624f5..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationService.kt +++ /dev/null @@ -1,305 +0,0 @@ -package net.dankito.banking.ui.android.authentication - -import android.content.Context -import android.util.Base64 -import at.favre.lib.crypto.bcrypt.BCrypt -import com.yakivmospan.scytale.Crypto -import com.yakivmospan.scytale.Options -import com.yakivmospan.scytale.Store -import net.dankito.banking.persistence.IBankingPersistence -import net.dankito.banking.ui.android.security.CryptographyManager -import net.dankito.banking.util.ISerializer -import net.dankito.utils.multiplatform.File -import net.dankito.utils.multiplatform.asString -import org.slf4j.LoggerFactory -import javax.crypto.Cipher -import javax.crypto.SecretKey - - -open class AuthenticationService( - protected open val applicationContext: Context, - protected open val biometricAuthenticationService: IBiometricAuthenticationService, - protected open val persistence: IBankingPersistence, - protected open val dataFolder: File, - protected open val serializer: ISerializer, - protected open val cryptographyManager: CryptographyManager = CryptographyManager() -) { - - companion object { - private const val AuthenticationSettingsFilename = "a" - - private const val AuthenticationSettingsFileKey = "AuthenticationSettingsFileKey" - - private val AuthenticationSettingsFileKeyPassword = "AuthenticationSettingsFileKeyAuthenticationSettingsFileKeyPassword".toCharArray() // TODO: store in a secure place - - private const val EncryptionKeyName = "BankingAndroidKey" - - private const val DefaultPasswordEncryptionKey = "AnyData" // TODO: store in a secure place - - private val log = LoggerFactory.getLogger(AuthenticationService::class.java) - } - - - open val isBiometricAuthenticationSupported: Boolean - get() = biometricAuthenticationService.supportsBiometricAuthentication - - open var authenticationType: AuthenticationType = AuthenticationType.None - protected set - - protected open var encryptionCipherForBiometric: Cipher? = null - - - init { - log.info("Initializing AuthenticationService (at the end database must get opened) ...") - - val settings = loadAuthenticationSettings() - - if (settings == null) { // first app run -> create a default password - removeAppProtection() - } - else { - authenticationType = settings.type - - if (settings.type == AuthenticationType.None) { - openDatabase(settings) - } - } - } - - - open fun loginUserWithPassword(enteredPassword: CharArray): Boolean { - if (isCorrectUserPassword(enteredPassword)) { - loadAuthenticationSettings()?.let { settings -> - return openDatabase(settings, enteredPassword) - } - } - - return false - } - - open fun isCorrectUserPassword(enteredPassword: CharArray): Boolean { - loadAuthenticationSettings()?.let { settings -> - val result = BCrypt.verifyer().verify(enteredPassword, settings.hashedUserPassword) - - return result.verified - } - - return false - } - - open fun authenticateUserWithBiometricToSetAsNewAuthenticationMethod(authenticationResult: (AuthenticationResult) -> Unit) { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) { - authenticationResult(AuthenticationResult(false, "Biometric authentication is only supported on Android 6 and above")) - return - } - - val cipher = cryptographyManager.getInitializedCipherForEncryption(EncryptionKeyName) - - biometricAuthenticationService.authenticate(cipher) { result -> - if (result.successful) { - this.encryptionCipherForBiometric = cipher - } - - authenticationResult(result) - } - } - - open fun loginUserWithBiometric(result: (Boolean) -> Unit) { - // Biometric authentication is only supported on Android 6 and above - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) { - result(false) - return - } - - loadAuthenticationSettings()?.let { settings -> - val iv = decodeFromBase64(settings.initializationVector ?: "") - - val cipher = cryptographyManager.getInitializedCipherForDecryption(EncryptionKeyName, iv) - biometricAuthenticationService.authenticate(cipher) { authenticationResult -> - if (authenticationResult.successful) { - settings.defaultPassword?.let { - val encryptedUserPassword = decodeFromBase64(it) - val decrypted = cryptographyManager.decryptData(encryptedUserPassword, cipher) - - result(openDatabase(decrypted)) - } - } - else { - result(false) - } - } - } - ?: run { result(false) } - } - - protected open fun openDatabase(settings: AuthenticationSettings, userPassword: CharArray? = null): Boolean { - settings.defaultPassword?.let { encryptedPassword -> - settings.initializationVector?.let { iv -> - settings.salt?.let { salt -> - val defaultPassword = cryptographyManager.decryptDataWithPbe(decodeFromBase64(encryptedPassword), DefaultPasswordEncryptionKey, - decodeFromBase64(iv), decodeFromBase64(salt)) - - if (userPassword != null) { - return openDatabase(concatPasswords(userPassword, defaultPassword)) - } - else { - return openDatabase(defaultPassword) - } - } - } - } - - return false - } - - protected open fun openDatabase(password: CharArray): Boolean { - val result = persistence.decryptData(password) - - log.info("Did decrypting data / opening database succeed? $result") - - return result - } - - - open fun setAuthenticationMethodToBiometric() { - saveNewAuthenticationMethod(AuthenticationType.Biometric, null) - } - - open fun setAuthenticationMethodToPassword(newPassword: CharArray) { - saveNewAuthenticationMethod(AuthenticationType.Password, newPassword) - } - - open fun removeAppProtection() { - saveNewAuthenticationMethod(AuthenticationType.None, null) - } - - - protected open fun saveNewAuthenticationMethod(type: AuthenticationType, newUserPassword: CharArray?): Boolean { - val settings = loadOrCreateDefaultAuthenticationSettings() - val newDefaultPassword = generateRandomPassword() - var newDatabasePassword = newDefaultPassword - - settings.type = type - settings.hashedUserPassword = null - settings.initializationVector = null - settings.salt = null - - if (type == AuthenticationType.Biometric) { - encryptionCipherForBiometric?.let { encryptionCipher -> - val encryptedPassword = cryptographyManager.encryptData(newDefaultPassword, encryptionCipher) - settings.defaultPassword = encodeToBase64(encryptedPassword) - settings.initializationVector = encodeToBase64(encryptionCipher.iv) - } - } - else { - val salt = cryptographyManager.generateRandomBytes(8) - val (encryptedPassword, iv) = cryptographyManager.encryptDataWithPbe(newDefaultPassword, DefaultPasswordEncryptionKey, salt) - settings.defaultPassword = encodeToBase64(encryptedPassword) - settings.initializationVector = encodeToBase64(iv) - settings.salt = encodeToBase64(salt) - - if (newUserPassword != null) { - settings.hashedUserPassword = BCrypt.withDefaults().hashToString(6, newUserPassword) - newDatabasePassword = concatPasswords(newUserPassword, newDefaultPassword) - } - } - - if (persistence.changePassword(newDatabasePassword)) { - if (saveAuthenticationSettings(settings)) { - this.authenticationType = type - this.encryptionCipherForBiometric = null - - return true - } - } - - return false - } - - protected open fun concatPasswords(userPassword: CharArray, defaultPassword: CharArray): CharArray { - val concatenated = StringBuilder(userPassword.size + defaultPassword.size + 1) - - concatenated.append(userPassword) - concatenated.append("_") - concatenated.append(defaultPassword) - - return concatenated.toList().toCharArray() - } - - protected open fun loadOrCreateDefaultAuthenticationSettings(): AuthenticationSettings { - return loadAuthenticationSettings() ?: AuthenticationSettings(AuthenticationType.None) - } - - protected open fun loadAuthenticationSettings(): AuthenticationSettings? { - try { - val file = File(dataFolder, AuthenticationSettingsFilename) - - if (file.exists()) { - val (key, crypto) = getAuthenticationSettingsFileKey() - val encryptedJson = file.readText() - - val json = crypto.decrypt(encryptedJson, key) - - return serializer.deserializeObject(json, AuthenticationSettings::class) - } - } catch (e: Exception) { - log.error("Could not load AuthenticationSettings", e) - } - - return null - } - - protected open fun saveAuthenticationSettings(settings: AuthenticationSettings): Boolean { - try { - serializer.serializeObjectToString(settings)?.let { json -> - val (key, crypto) = getAuthenticationSettingsFileKey() - val encryptedJson = crypto.encrypt(json, key) - - val file = File(dataFolder, AuthenticationSettingsFilename) - - file.writeText(encryptedJson) - - return true - } - } catch (e: Exception) { - log.error("Could not save AuthenticationSettings", e) - } - - return false - } - - protected open fun getAuthenticationSettingsFileKey(): Pair { - val store = Store(applicationContext) - - val key = if (store.hasKey(AuthenticationSettingsFileKey)) store.getSymmetricKey(AuthenticationSettingsFileKey, AuthenticationSettingsFileKeyPassword) - else store.generateSymmetricKey(AuthenticationSettingsFileKey, AuthenticationSettingsFileKeyPassword) - - return Pair(key, Crypto(Options.TRANSFORMATION_SYMMETRIC)) - } - - - open fun generateRandomPassword(): CharArray { - return generateRandomPassword(30) - } - - open fun generateRandomPassword(passwordLength: Int): CharArray { - val dictionary = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789§±!@#$%^&*-_=+;:|/?.>,<" - - val password = CharArray(passwordLength) - IntRange(0, passwordLength - 1).forEach { index -> - password[index] = dictionary.random() - } - - return password - } - - - open fun encodeToBase64(data: ByteArray): String { - return Base64.encodeToString(data, Base64.DEFAULT) - } - - open fun decodeFromBase64(data: String): ByteArray { - return Base64.decode(data, Base64.DEFAULT) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationSettings.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationSettings.kt deleted file mode 100644 index 1b490ad6..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationSettings.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.dankito.banking.ui.android.authentication - - -open class AuthenticationSettings( - open var type: AuthenticationType, - open var hashedUserPassword: String? = null, - open var defaultPassword: String? = null, - open var initializationVector: String? = null, - open var salt: String? = null -) { - - internal constructor() : this(AuthenticationType.None) // for object deserializers - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationType.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationType.kt deleted file mode 100644 index 578613a1..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/AuthenticationType.kt +++ /dev/null @@ -1,12 +0,0 @@ -package net.dankito.banking.ui.android.authentication - - -enum class AuthenticationType(internal val rawValue: Int) { - - None(3), - - Password(7), - - Biometric(9) - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/BiometricAuthenticationService.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/BiometricAuthenticationService.kt deleted file mode 100644 index 740f7d28..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/BiometricAuthenticationService.kt +++ /dev/null @@ -1,60 +0,0 @@ -package net.dankito.banking.ui.android.authentication - -import android.content.Context -import androidx.biometric.BiometricManager -import androidx.biometric.BiometricPrompt -import androidx.core.content.ContextCompat -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.util.CurrentActivityTracker -import javax.crypto.Cipher - - -open class BiometricAuthenticationService( - protected open val context: Context, - protected open val activityTracker: CurrentActivityTracker, - protected open val biometricManager: BiometricManager = BiometricManager.from(context) -) : IBiometricAuthenticationService { - - override val supportsBiometricAuthentication: Boolean - get() = biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS - - - override fun authenticate(cipher: Cipher?, authenticationResult: (AuthenticationResult) -> Unit) { - activityTracker.currentOrNextActivity { activity -> - val executor = ContextCompat.getMainExecutor(context) - - val biometricPrompt = BiometricPrompt(activity, executor, - object : BiometricPrompt.AuthenticationCallback() { - - override fun onAuthenticationError(errorCode: Int, errorString: CharSequence) { - super.onAuthenticationError(errorCode, errorString) - authenticationResult(AuthenticationResult(false, errorString.toString())) - } - - override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { - super.onAuthenticationSucceeded(result) - - authenticationResult(AuthenticationResult(true)) - } - - override fun onAuthenticationFailed() { - super.onAuthenticationFailed() - authenticationResult(AuthenticationResult(false)) - } - }) - - val promptInfo = BiometricPrompt.PromptInfo.Builder() - .setTitle(context.getString(R.string.activity_login_authenticate_with_biometrics_prompt)) - .setNegativeButtonText(context.getString(android.R.string.cancel)) - .build() - - if (cipher == null) { - biometricPrompt.authenticate(promptInfo) - } - else { - biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher)) - } - } - - } -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/IBiometricAuthenticationService.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/IBiometricAuthenticationService.kt deleted file mode 100644 index 1b890988..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/authentication/IBiometricAuthenticationService.kt +++ /dev/null @@ -1,13 +0,0 @@ -package net.dankito.banking.ui.android.authentication - -import javax.crypto.Cipher - - -interface IBiometricAuthenticationService { - - val supportsBiometricAuthentication: Boolean - - - fun authenticate(cipher: Cipher? = null, authenticationResult: (AuthenticationResult) -> Unit) - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingComponent.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingComponent.kt deleted file mode 100644 index b0c88668..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingComponent.kt +++ /dev/null @@ -1,55 +0,0 @@ -package net.dankito.banking.ui.android.di - -import dagger.Component -import net.dankito.banking.ui.android.MainActivity -import net.dankito.banking.ui.android.activities.BaseActivity -import net.dankito.banking.ui.android.activities.LandingActivity -import net.dankito.banking.ui.android.activities.LoginActivity -import net.dankito.banking.ui.android.dialogs.AddAccountDialog -import net.dankito.banking.ui.android.dialogs.EnterTanDialog -import net.dankito.banking.ui.android.dialogs.SendMessageLogDialog -import net.dankito.banking.ui.android.dialogs.TransferMoneyDialog -import net.dankito.banking.ui.android.dialogs.settings.ProtectAppSettingsDialog -import net.dankito.banking.ui.android.dialogs.settings.SettingsDialog -import net.dankito.banking.ui.android.dialogs.settings.SettingsDialogBase -import net.dankito.banking.ui.android.home.HomeFragment -import net.dankito.banking.ui.android.views.BiometricAuthenticationButton -import javax.inject.Singleton - - -@Singleton -@Component(modules = arrayOf(BankingModule::class)) -interface BankingComponent { - - companion object { - lateinit var component: BankingComponent - } - - - fun inject(baseActivity: BaseActivity) - - fun inject(landingActivity: LandingActivity) - - fun inject(loginActivity: LoginActivity) - - fun inject(mainActivity: MainActivity) - - fun inject(homeFragment: HomeFragment) - - fun inject(addAccountDialog: AddAccountDialog) - - fun inject(enterTanDialog: EnterTanDialog) - - fun inject(transferMoneyDialog: TransferMoneyDialog) - - fun inject(settingsDialogBase: SettingsDialogBase) - - fun inject(settingsDialog: SettingsDialog) - - fun inject(protectAppSettingsDialog: ProtectAppSettingsDialog) - - fun inject(biometricAuthenticationButton: BiometricAuthenticationButton) - - fun inject(sendMessageLogDialog: SendMessageLogDialog) - -} \ No newline at end of file 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 deleted file mode 100644 index 277d91ed..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingModule.kt +++ /dev/null @@ -1,209 +0,0 @@ -package net.dankito.banking.ui.android.di - -import android.content.Context -import androidx.appcompat.app.AppCompatActivity -import dagger.Module -import dagger.Provides -import net.dankito.utils.multiplatform.File -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.search.ITransactionPartySearcher -import net.dankito.banking.ui.IBankingClientCreator -import net.dankito.banking.ui.IRouter -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.banking.bankfinder.IBankFinder -import net.dankito.banking.bankfinder.InMemoryBankFinder -import net.dankito.banking.bankfinder.LuceneBankFinder -import net.dankito.banking.persistence.RoomBankingPersistence -import net.dankito.banking.persistence.model.RoomModelCreator -import net.dankito.banking.ui.android.authentication.AuthenticationService -import net.dankito.banking.ui.android.authentication.BiometricAuthenticationService -import net.dankito.banking.ui.android.authentication.IBiometricAuthenticationService -import net.dankito.banking.ui.model.mapper.IModelCreator -import net.dankito.banking.ui.util.CurrencyInfoProvider -import net.dankito.utils.multiplatform.toFile -import net.dankito.banking.util.* -import net.dankito.banking.util.extraction.* -import net.dankito.text.extraction.TextExtractorRegistry -import net.dankito.text.extraction.pdf.PdfBoxAndroidPdfTextExtractor -import net.dankito.text.extraction.pdf.iText2PdfTextExtractor -import net.dankito.utils.ThreadPool -import net.dankito.utils.android.permissions.IPermissionsService -import net.dankito.utils.android.permissions.PermissionsService -import net.dankito.utils.web.client.IWebClient -import net.dankito.utils.web.client.OkHttpWebClient -import javax.inject.Named -import javax.inject.Singleton - - -@Module -class BankingModule(private val applicationContext: Context) { - - companion object { - - const val DataFolderKey = "data.folder" - - const val DatabaseFolderKey = "database.folder" - - const val IndexFolderKey = "index.folder" - - // TODO: implement activity listener to always get latest activity - lateinit var mainActivity: AppCompatActivity - } - - - @Provides - @Singleton - fun provideApplicationContext() : Context { - return applicationContext - } - - @Provides - @Singleton - @Named(DataFolderKey) - fun provideDataFolder(applicationContext: Context) : File { - return ensureFolderExists(applicationContext.filesDir.toFile(), "data") - } - - @Provides - @Singleton - @Named(DatabaseFolderKey) - fun provideDatabaseFolder(@Named(DataFolderKey) dataFolder: File) : File { - return ensureFolderExists(dataFolder, "db") - } - - @Provides - @Singleton - @Named(IndexFolderKey) - fun provideIndexFolder(@Named(DataFolderKey) dataFolder: File) : File { - return ensureFolderExists(dataFolder, "index") - } - - private fun ensureFolderExists(parentFolder: File, folderName: String): File { - val folder = File(parentFolder, folderName) - - folder.mkdirs() - - return folder - } - - - @Provides - @Singleton - fun providePermissionsService() : IPermissionsService { - return PermissionsService(mainActivity) - } - - - @Provides - @Singleton - fun provideAuthenticationService(biometricAuthenticationService: IBiometricAuthenticationService, persistence: IBankingPersistence, - @Named(DataFolderKey) dataFolder: File, serializer: ISerializer) : AuthenticationService { - return AuthenticationService(applicationContext, biometricAuthenticationService, persistence, dataFolder, serializer) - } - - @Provides - @Singleton - fun provideBiometricAuthenticationService(context: Context, currentActivityTracker: CurrentActivityTracker) : IBiometricAuthenticationService { - return BiometricAuthenticationService(context, currentActivityTracker) - } - - - @Provides - @Singleton - fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder, - @Named(DataFolderKey) dataFolder: File, - persister: IBankingPersistence, transactionPartySearcher: ITransactionPartySearcher, bankIconFinder: IBankIconFinder, - textExtractorRegistry: ITextExtractorRegistry, router: IRouter, invoiceDataExtractor: IInvoiceDataExtractor, - modelCreator: IModelCreator, asyncRunner: IAsyncRunner) : BankingPresenter { - return BankingPresenter(bankingClientCreator, bankFinder, dataFolder, persister, router, modelCreator, - transactionPartySearcher, bankIconFinder, textExtractorRegistry, invoiceDataExtractor, CurrencyInfoProvider(), asyncRunner) - } - - @Provides - @Singleton - fun provideBankFinder(@Named(IndexFolderKey) indexFolder: File) : IBankFinder { - //return LuceneBankFinder(indexFolder) // TODO: undo - return InMemoryBankFinder() - } - - @Provides - @Singleton - fun provideBankIconFinder() : IBankIconFinder { - return BankIconFinder() - } - - @Provides - @Singleton - fun provideBankingClientCreator(modelCreator: IModelCreator, serializer: ISerializer) : IBankingClientCreator { - return fints4kBankingClientCreator(modelCreator, serializer) - } - - @Provides - @Singleton - fun provideBankingPersistence() : IBankingPersistence { - return RoomBankingPersistence(applicationContext) - } - - @Provides - @Singleton - fun provideTransactionPartySearcher(bankingPersistence: IBankingPersistence) : ITransactionPartySearcher { - return bankingPersistence as RoomBankingPersistence - } - - @Provides - @Singleton - fun provideCurrentActivityTracker() : CurrentActivityTracker { - return CurrentActivityTracker() - } - - @Provides - @Singleton - fun provideRouter(currentActivityTracker: CurrentActivityTracker) : IRouter { - return RouterAndroid(currentActivityTracker) - } - - - @Provides - @Singleton - fun provideTextExtractorRegistry(applicationContext: Context) : ITextExtractorRegistry { - // TODO: add PdfTypeDetector - return JavaTextExtractorRegistry(TextExtractorRegistry(listOf( - iText2PdfTextExtractor(), PdfBoxAndroidPdfTextExtractor(applicationContext) - ))) - } - - @Provides - @Singleton - fun provideInvoiceDataExtractor() : IInvoiceDataExtractor { - return JavaInvoiceDataExtractor() - } - - - @Provides - @Singleton - fun provideWebClient() : IWebClient { - return OkHttpWebClient() - } - - @Provides - @Singleton - fun provideSerializer() : ISerializer { - return JacksonJsonSerializer() - } - - @Provides - @Singleton - fun provideModelCreator() : IModelCreator { - return RoomModelCreator() - } - - @Provides - @Singleton - fun provideAsyncRunner() : IAsyncRunner { - return ThreadPoolAsyncRunner(ThreadPool()) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/AccountTransactionDetailsDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/AccountTransactionDetailsDialog.kt deleted file mode 100644 index 4edff1b1..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/AccountTransactionDetailsDialog.kt +++ /dev/null @@ -1,80 +0,0 @@ -package net.dankito.banking.ui.android.dialogs - -import android.os.Bundle -import android.view.* -import androidx.fragment.app.FragmentActivity -import kotlinx.android.synthetic.main.dialog_account_transaction_details.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.dialogs.settings.SettingsDialogBase -import net.dankito.banking.ui.android.views.FormLabelledValue -import net.dankito.banking.ui.model.IAccountTransaction -import net.dankito.utils.multiplatform.BigDecimal - - -open class AccountTransactionDetailsDialog : SettingsDialogBase() { - - companion object { - const val DialogTag = "AccountTransactionDetailsDialog" - } - - - protected lateinit var transaction: IAccountTransaction - - - - fun show(transaction: IAccountTransaction, activity: FragmentActivity) { - this.transaction = transaction - - show(activity, DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val rootView = inflater.inflate(R.layout.dialog_account_transaction_details, container, false) - - setupUI(rootView) - - return rootView - } - - protected open fun setupUI(rootView: View) { - rootView.apply { - toolbar.apply { - setupToolbar(this, context.getString(R.string.dialog_account_transaction_details_title), false) - } - - sender_or_recipient_section_title.setTitle(if (transaction.amount.isPositive) R.string.dialog_account_transaction_details_sender else R.string.dialog_account_transaction_details_recipient) - lvlOtherPartyName.value = transaction.otherPartyName ?: "" - lvlOtherPartyAccountId.value = transaction.otherPartyAccountId ?: "" - lvlOtherPartyBankCode.value = transaction.otherPartyBankCode ?: "" - - lvlAmount.showAmount(presenter, transaction.amount, transaction.currency) - lvlReference.value = transaction.reference - - lvlBookingText.value = transaction.bookingText ?: "" - lvlBookingDate.value = presenter.formatToMediumDate(transaction.bookingDate) - lvlValueDate.value = presenter.formatToMediumDate(transaction.valueDate) - - showAmountIfSet(lvlOpeningBalance, transaction.openingBalance, transaction.account.currency) - showAmountIfSet(lvlClosingBalance, transaction.closingBalance, transaction.account.currency) - } - } - - protected open fun showAmountIfSet(labelledValue: FormLabelledValue, amount: BigDecimal?, currencyCode: String) { - if (amount != null) { - labelledValue.showAmount(presenter, amount, currencyCode) - } - else { - labelledValue.visibility = View.GONE - } - } - - - override val hasUnsavedChanges: Boolean - get() = false - - override fun saveChanges() { - - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/AddAccountDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/AddAccountDialog.kt deleted file mode 100644 index 39792b0d..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/AddAccountDialog.kt +++ /dev/null @@ -1,222 +0,0 @@ -package net.dankito.banking.ui.android.dialogs - -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import android.text.Editable -import android.text.TextWatcher -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.EditText -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.DialogFragment -import com.otaliastudios.autocomplete.Autocomplete -import kotlinx.android.synthetic.main.dialog_add_account.* -import kotlinx.android.synthetic.main.dialog_add_account.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.adapter.presenter.BankInfoPresenter -import net.dankito.banking.ui.android.extensions.addEnterPressedListener -import net.dankito.banking.ui.android.extensions.closePopupOnBackButtonPress -import net.dankito.banking.ui.android.util.StandardAutocompleteCallback -import net.dankito.banking.ui.model.responses.AddAccountResponse -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.banking.bankfinder.BankInfo -import net.dankito.banking.ui.android.util.StandardTextWatcher -import net.dankito.utils.android.extensions.asActivity -import net.dankito.utils.android.extensions.hide -import net.dankito.utils.android.extensions.show -import net.dankito.utils.android.extensions.showKeyboardDelayed -import javax.inject.Inject - - -open class AddAccountDialog : DialogFragment() { - - companion object { - const val DialogTag = "AddAccountDialog" - } - - - protected var selectedBank: BankInfo? = null - - protected var justDidSelectBank: Boolean = false - - - @Inject - protected lateinit var presenter: BankingPresenter - - - init { - BankingComponent.component.inject(this) - } - - - fun show(activity: AppCompatActivity, fullscreen: Boolean = true) { - val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog - setStyle(STYLE_NORMAL, style) - - show(activity.supportFragmentManager, DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val rootView = inflater.inflate(R.layout.dialog_add_account, container, false) - - rootView?.let { - setupUI(rootView) - } - - return rootView - } - - protected open fun setupUI(rootView: View) { - rootView.apply { - initBankListAutocompletion(edtxtBank.actualEditText) - - edtxtUserName.actualEditText.addTextChangedListener(otherEditTextChangedWatcher) - bankCredentialsPassword.passwordBox.addTextChangedListener(otherEditTextChangedWatcher) - - addAccountIfEnterPressed(edtxtBank.actualEditText) - addAccountIfEnterPressed(edtxtUserName.actualEditText) - addAccountIfEnterPressed(bankCredentialsPassword.passwordBox) - - btnAddAccount.setOnClickListener { addAccount() } - btnCancel.setOnClickListener { dismiss() } - - edtxtBank.actualEditText.showKeyboardDelayed(250) - } - } - - private fun initBankListAutocompletion(edtxtBank: EditText) { - edtxtBank.addTextChangedListener(StandardTextWatcher { - mayClearSelectedBank() - }) - - val autocompleteCallback = StandardAutocompleteCallback { _, item -> - bankSelected(item) - true - } - - Autocomplete.on(edtxtBank) - .with(6f) - .with(ColorDrawable(Color.WHITE)) - .with(autocompleteCallback) - .with(BankInfoPresenter(presenter, edtxtBank.context)) - .build() - .closePopupOnBackButtonPress(dialog) - } - - private fun addAccountIfEnterPressed(editText: EditText) { - editText.addEnterPressedListener { - if (btnAddAccount.isEnabled) { // required data has been entered - addAccount() - - return@addEnterPressedListener true - } - - false - } - } - - - protected open fun addAccount() { - selectedBank?.let { selectedBank -> // should always be non-null at this stage - val userName = edtxtUserName.text - val password = bankCredentialsPassword.password - - btnAddAccount.isEnabled = false - pgrbrAddAccount.show() - - presenter.addAccountAsync(selectedBank, userName, password) { response -> - context?.asActivity()?.runOnUiThread { - btnAddAccount.isEnabled = true - pgrbrAddAccount.hide() - - handleAccountCheckResponseOnUiThread(response) - } - } - } - } - - protected open fun handleAccountCheckResponseOnUiThread(response: AddAccountResponse) { - context?.let { context -> - if (response.successful) { - this.dismiss() - } - else { - AlertDialog.Builder(context) - .setMessage(context.getString(R.string.dialog_add_account_message_could_not_add_account, response.errorToShowToUser)) - .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } - .show() - } - } - } - - - protected val otherEditTextChangedWatcher = object : TextWatcher { - - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } - - override fun onTextChanged(enteredText: CharSequence?, start: Int, before: Int, count: Int) { - checkIfRequiredDataEnteredOnUiThread() - } - - override fun afterTextChanged(s: Editable?) { } - - } - - protected open fun bankSelected(bank: BankInfo) { - val didSelectSupportedBank = bank.supportsFinTs3_0 - - selectedBank = if (didSelectSupportedBank) bank else null - - justDidSelectBank = true - - if (didSelectSupportedBank) { - edtxtBank.text = bank.bankCode + " " + bank.name - } - - justDidSelectBank = false - - checkIfRequiredDataEnteredOnUiThread() - - if (didSelectSupportedBank) { - edtxtUserName.requestFocus() - } - else { - showBankDoesNotSupportFinTs30ErrorMessage(bank) - edtxtBank.actualEditText.selectAll() - } - } - - protected open fun mayClearSelectedBank() { - if (justDidSelectBank == false) { - selectedBank = null - } - - checkIfRequiredDataEnteredOnUiThread() - } - - private fun showBankDoesNotSupportFinTs30ErrorMessage(bank: BankInfo) { - activity?.let { context -> - val errorMessage = context.getString(R.string.dialog_add_account_bank_does_not_support_fints_3_error_message, bank.name) - - AlertDialog.Builder(context) - .setMessage(errorMessage) - .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } - .show() - } - } - - protected open fun checkIfRequiredDataEnteredOnUiThread() { - val requiredDataEntered = selectedBank != null - && selectedBank?.supportsFinTs3_0 == true - && edtxtUserName.text.isNotEmpty() - && bankCredentialsPassword.password.isNotEmpty() - - btnAddAccount.isEnabled = requiredDataEntered - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterAtcDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterAtcDialog.kt deleted file mode 100644 index ba1ea367..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterAtcDialog.kt +++ /dev/null @@ -1,91 +0,0 @@ -package net.dankito.banking.ui.android.dialogs - -import android.os.Bundle -import android.text.Html -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.DialogFragment -import kotlinx.android.synthetic.main.dialog_enter_atc.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult -import net.dankito.banking.ui.model.tan.TanMedium -import net.dankito.utils.android.extensions.getSpannedFromHtml - - -open class EnterAtcDialog : DialogFragment() { - - companion object { - const val DialogTag = "EnterAtcDialog" - } - - - protected lateinit var tanMedium: TanMedium - - protected lateinit var atcEnteredCallback: (EnterTanGeneratorAtcResult) -> Unit - - - open fun show(tanMedium: TanMedium, activity: AppCompatActivity, - fullscreen: Boolean = false, atcEnteredCallback: (EnterTanGeneratorAtcResult) -> Unit) { - - this.tanMedium = tanMedium - this.atcEnteredCallback = atcEnteredCallback - - val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog - setStyle(STYLE_NORMAL, style) - - show(activity.supportFragmentManager, DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val rootView = inflater.inflate(R.layout.dialog_enter_atc, container, false) - - setupUI(rootView) - - return rootView - } - - protected open fun setupUI(rootView: View) { - val explanationHtml = rootView.context.getString(R.string.dialog_enter_atc_explanation, tanMedium.displayName) - rootView.txtAtcExplanationToShowToUser.text = explanationHtml.getSpannedFromHtml() - - rootView.btnCancel.setOnClickListener { enteringAtcDone(null, null) } - - rootView.btnEnteringAtcDone.setOnClickListener { enteringAtcDone(rootView.edtxtEnteredTan.text.toString(), rootView.edtxtEnteredAtc.text.toString()) } - } - - - protected open fun enteringAtcDone(enteredTan: String?, enteredAtcString: String?) { - var enteredAtc: Int? = null - - if (enteredAtcString != null) { - try { - enteredAtc = enteredAtcString.toInt() - } catch (e: Exception) { - showEnteredAtcIsNotANumberError(enteredAtcString) - - return - } - } - - val result = if (enteredTan == null || enteredAtc == null) EnterTanGeneratorAtcResult.userDidNotEnterAtc() - else EnterTanGeneratorAtcResult.userEnteredAtc(enteredTan, enteredAtc) - - atcEnteredCallback(result) - - dismiss() - } - - protected open fun showEnteredAtcIsNotANumberError(enteredAtcString: String) { - activity?.let { context -> - AlertDialog.Builder(context) - .setMessage(context.getString(R.string.dialog_enter_atc_error_entered_atc_is_not_a_number, enteredAtcString)) - .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } - .show() - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterTanDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterTanDialog.kt deleted file mode 100644 index 9685bc36..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/EnterTanDialog.kt +++ /dev/null @@ -1,272 +0,0 @@ -package net.dankito.banking.ui.android.dialogs - -import android.content.Context -import android.os.Bundle -import android.os.Handler -import android.text.InputFilter -import android.text.InputType -import android.view.KeyEvent -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Spinner -import androidx.appcompat.app.AlertDialog -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.FragmentActivity -import kotlinx.android.synthetic.main.dialog_enter_tan.* -import kotlinx.android.synthetic.main.dialog_enter_tan.view.* -import kotlinx.android.synthetic.main.view_collapsible_text.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.TanMediumAdapter -import net.dankito.banking.ui.android.adapter.TanMethodsAdapter -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.listener.ListItemSelectedListener -import net.dankito.banking.ui.model.TypedBankData -import net.dankito.banking.ui.model.responses.BankingClientResponse -import net.dankito.banking.ui.model.tan.* -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.utils.android.extensions.getSpannedFromHtml -import net.dankito.utils.android.extensions.show -import javax.inject.Inject - - -open class EnterTanDialog : DialogFragment() { - - companion object { - const val DialogTag = "EnterTanDialog" - } - - - protected lateinit var bank: TypedBankData - - protected lateinit var tanChallenge: TanChallenge - - protected lateinit var tanEnteredCallback: (EnterTanResult) -> Unit - - protected val tanMediumAdapter = TanMediumAdapter() - - - @Inject - protected lateinit var presenter: BankingPresenter - - - init { - BankingComponent.component.inject(this) - } - - - open fun show(bank: TypedBankData, tanChallenge: TanChallenge, activity: FragmentActivity, - fullscreen: Boolean = false, tanEnteredCallback: (EnterTanResult) -> Unit) { - - this.bank = bank - this.tanChallenge = tanChallenge - this.tanEnteredCallback = tanEnteredCallback - - val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog - setStyle(STYLE_NORMAL, style) - - show(activity.supportFragmentManager, DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val rootView = inflater.inflate(R.layout.dialog_enter_tan, container, false) - - setupUI(rootView) - - return rootView - } - - protected open fun setupUI(rootView: View) { - setupSelectTanMethodView(rootView) - - setupTanView(rootView) - - setupEnteringTan(rootView) - - rootView.txtvwCollapsibleText.text = tanChallenge.messageToShowToUser.getSpannedFromHtml() - - rootView.btnCancel.setOnClickListener { enteringTanDone(null) } - - rootView.btnEnteringTanDone.setOnClickListener { enteringTanDone(rootView.edtxtEnteredTan.text.toString()) } - } - - protected open fun setupSelectTanMethodView(rootView: View) { - val adapter = TanMethodsAdapter() - val tanMethodsWithoutUnsupported = bank.supportedTanMethods.filterNot { it.type == TanMethodType.ChipTanUsb } // USB tan generators are not supported on Android - adapter.setItems(tanMethodsWithoutUnsupported) - - rootView.findViewById(R.id.spnTanMethods)?.let { spinner -> - spinner.adapter = adapter - - val selectedTanMethod = bank.selectedTanMethod - ?: tanMethodsWithoutUnsupported.firstOrNull { it.type != TanMethodType.ChipTanManuell && it.type != TanMethodType.ChipTanUsb } - ?: tanMethodsWithoutUnsupported.firstOrNull() - selectedTanMethod?.let { spinner.setSelection(adapter.getItems().indexOf(selectedTanMethod)) } - - spinner.onItemSelectedListener = ListItemSelectedListener(adapter) { newSelectedTanMethod -> - if (newSelectedTanMethod != selectedTanMethod) { - tanEnteredCallback(EnterTanResult.userAsksToChangeTanMethod(newSelectedTanMethod)) - // TODO: find a way to update account.selectedTanMethod afterwards - - dismiss() - } - } - } - } - - protected open fun setupSelectTanMediumView(rootView: View) { - val tanMediaForTanMethod = presenter.getTanMediaForTanMethod(bank, tanChallenge.tanMethod) - - if (tanMediaForTanMethod.size > 1) { - rootView.lytTanMedium.show() - - tanMediumAdapter.setItems(bank.tanMediaSorted) - - rootView.spnTanMedium.adapter = tanMediumAdapter - rootView.spnTanMedium.onItemSelectedListener = ListItemSelectedListener(tanMediumAdapter) { selectedTanMedium -> - // TODO: implement logic to change a mobile phone as TAN medium - if (selectedTanMedium.status != TanMediumStatus.Used) { - (selectedTanMedium as? TanGeneratorTanMedium)?.let { tanGeneratorTanMedium -> - tanEnteredCallback(EnterTanResult.userAsksToChangeTanMedium(tanGeneratorTanMedium) { response -> - handleChangeTanMediumResponse(selectedTanMedium, response) - }) - // TODO: find a way to update account.tanMedia afterwards - } - - // TODO: ensure that only TanGeneratorTanMedium instances get added to spinner? - } - } - } - } - - protected open fun setupTanView(rootView: View) { - if (presenter.isOpticalTanMethod(tanChallenge.tanMethod)) { - setupSelectTanMediumView(rootView) - - if (tanChallenge is FlickerCodeTanChallenge) { - setupFlickerCodeTanView(rootView) - } - else if (tanChallenge is ImageTanChallenge) { - setupImageTanView(rootView) - } - } - } - - protected open fun setupEnteringTan(rootView: View) { - if (tanChallenge.tanMethod.isNumericTan) { - rootView.edtxtEnteredTan.inputType = InputType.TYPE_CLASS_NUMBER - } - - tanChallenge.tanMethod.maxTanInputLength?.let { maxInputLength -> - rootView.edtxtEnteredTan.filters = arrayOf(InputFilter.LengthFilter(maxInputLength)) - } - - rootView.edtxtEnteredTan.setOnKeyListener { _, keyCode, _ -> - if (keyCode == KeyEvent.KEYCODE_ENTER) { - enteringTanDone(rootView.edtxtEnteredTan.text.toString()) - return@setOnKeyListener true - } - false - } - } - - protected open fun setupFlickerCodeTanView(rootView: View) { - val flickerCodeView = rootView.flickerCodeView - flickerCodeView.show() - - val flickerCode = (tanChallenge as FlickerCodeTanChallenge).flickerCode - if (flickerCode.decodingSuccessful) { - flickerCodeView.setCode(flickerCode, presenter.appSettings.flickerCodeSettings) - } - else { - showDecodingTanChallengeFailedErrorDelayed(flickerCode.decodingError) - } - } - - protected open fun setupImageTanView(rootView: View) { - rootView.tanImageView.show() - - val decodedImage = (tanChallenge as ImageTanChallenge).image - if (decodedImage.decodingSuccessful) { - rootView.tanImageView.setImage(tanChallenge as ImageTanChallenge, if (isQrTan(tanChallenge)) presenter.appSettings.qrCodeSettings else presenter.appSettings.photoTanSettings) - } - else { - showDecodingTanChallengeFailedErrorDelayed(decodedImage.decodingError) - } - } - - - /** - * This method gets called right on start up before dialog is shown -> Alert would get displayed before dialog and - * therefore covered by dialog -> delay displaying alert. - */ - protected open fun showDecodingTanChallengeFailedErrorDelayed(error: Exception?) { - val handler = Handler() - - handler.postDelayed({ showDecodingTanChallengeFailedError(error) }, 500) - } - - protected open fun showDecodingTanChallengeFailedError(error: Exception?) { - activity?.let { context -> - AlertDialog.Builder(context) - .setMessage(context.getString(R.string.dialog_enter_tan_error_could_not_decode_tan_image, error?.localizedMessage)) - .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } - .show() - } - } - - - protected open fun handleChangeTanMediumResponse(newUsedTanMedium: TanMedium, response: BankingClientResponse) { - activity?.let { activity -> - activity.runOnUiThread { - handleChangeTanMediumResponseOnUiThread(activity, newUsedTanMedium, response) - } - } - } - - protected open fun handleChangeTanMediumResponseOnUiThread(context: Context, newUsedTanMedium: TanMedium, response: BankingClientResponse) { - if (response.successful) { - AlertDialog.Builder(context) - .setMessage(context.getString(R.string.dialog_enter_tan_tan_medium_successfully_changed, newUsedTanMedium.displayName)) - .setPositiveButton(android.R.string.ok) { dialog, _ -> - dialog.dismiss() - dismiss() - } - .show() - } - else if (response.userCancelledAction == false) { - AlertDialog.Builder(context) - .setMessage(context.getString(R.string.dialog_enter_tan_error_changing_tan_medium, newUsedTanMedium.displayName, response.errorToShowToUser)) - .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } - .show() - } - } - - - protected open fun enteringTanDone(enteredTan: String?) { - val result = if (enteredTan != null) EnterTanResult.userEnteredTan(enteredTan) else EnterTanResult.userDidNotEnterTan() - - tanEnteredCallback(result) - - checkIfAppSettingsChanged() - - dismiss() - } - - - protected open fun checkIfAppSettingsChanged() { - if (flickerCodeView.didTanMethodSettingsChange) { - presenter.updateTanMethodSettings(tanChallenge.tanMethod, flickerCodeView.tanMethodSettings) - } - - if (tanImageView.didTanMethodSettingsChange) { - presenter.updateTanMethodSettings(tanChallenge.tanMethod, tanImageView.tanMethodSettings) - } - } - - protected open fun isQrTan(tanChallenge: TanChallenge): Boolean { - return presenter.isQrTanMethod(tanChallenge.tanMethod) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/SendMessageLogDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/SendMessageLogDialog.kt deleted file mode 100644 index fad45448..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/SendMessageLogDialog.kt +++ /dev/null @@ -1,165 +0,0 @@ -package net.dankito.banking.ui.android.dialogs - -import android.content.ActivityNotFoundException -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.DialogFragment -import kotlinx.android.synthetic.main.dialog_send_message_log.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.utils.android.extensions.hide -import net.dankito.utils.android.extensions.show -import net.dankito.utils.multiplatform.getInnerExceptionMessage -import net.dankito.utils.multiplatform.os.DeviceInfo -import net.dankito.banking.ui.model.issues.CreateTicketRequestDto -import net.dankito.banking.ui.model.issues.IssueDescriptionFormat -import net.dankito.utils.android.extensions.asActivity -import net.dankito.utils.serialization.JacksonJsonSerializer -import net.dankito.utils.web.client.OkHttpWebClient -import net.dankito.utils.web.client.RequestParameters -import net.dankito.utils.web.client.WebClientResponse -import javax.inject.Inject - - -open class SendMessageLogDialog : DialogFragment() { - - companion object { - const val DialogTag = "SendMessageLogDialog" - } - - - @Inject - protected lateinit var presenter: BankingPresenter - - - init { - BankingComponent.component.inject(this) - } - - - fun show(activity: AppCompatActivity, fullscreen: Boolean = true) { - val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog - setStyle(STYLE_NORMAL, style) - - show(activity.supportFragmentManager, DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val rootView = inflater.inflate(R.layout.dialog_send_message_log, container, false) - - rootView?.let { - setupUI(rootView) - } - - return rootView - } - - protected open fun setupUI(rootView: View) { - val messageLog = presenter.getFormattedMessageLogForAccounts(presenter.allBanksSortedByDisplayIndex) - - if (messageLog.isBlank()) { - rootView.txtvwInfoNoMessageLogEntriesYet.show() - rootView.lytMessageLog.hide() - - rootView.btnSendMessageLogDirectly.isEnabled = false - rootView.btnSendMessageLogViaEMail.isEnabled = false - } - else { - rootView.edtxtMessageLog.setText(context?.getString(R.string.dialog_send_message_courteously_add_error_description, getDeviceInfoLine() + "\n\n" + messageLog)) - } - - rootView.btnSendMessageLogDirectly.setOnClickListener { sendMessageLogDirectly(rootView.edtxtMessageLog.text.toString()) } - rootView.btnSendMessageLogViaEMail.setOnClickListener { sendMessageLogViaEMail(rootView.edtxtMessageLog.text.toString()) } - - rootView.btnCancel.setOnClickListener { dismiss() } - } - - protected open fun sendMessageLogViaEMail(messageLog: String) { - // TODO: check if message log exceeds 120.000 characters - - val sendMailActivity = Intent(Intent.ACTION_SEND) - sendMailActivity.type = "message/rfc822" - sendMailActivity.putExtra(Intent.EXTRA_EMAIL, arrayOf("issues@bankmeister.de")) - sendMailActivity.putExtra(Intent.EXTRA_SUBJECT, context?.getString(R.string.dialog_send_message_log_mail_subject)) - sendMailActivity.putExtra(Intent.EXTRA_TEXT, messageLog) - - try { - startActivity(Intent.createChooser(sendMailActivity, context?.getString(R.string.dialog_send_message_log_action_send_chooser_title))) - - showSuccessfullySentMessageLog() - } catch (e: ActivityNotFoundException) { - Toast.makeText(context, context?.getString(R.string.dialog_send_message_log_error_no_app_to_send_message_found), Toast.LENGTH_LONG).show() - } - } - - protected open fun sendMessageLogDirectly(messageLog: String) { - val deviceInfo = getDeviceInfo() - - // TODO: sending with Ktor did not work - //presenter.sendMessageLogDirectly(messageLog, deviceInfo) - - val requestBodyDto = CreateTicketRequestDto(messageLog, "Bankmeister", IssueDescriptionFormat.PlainText, - deviceInfo.osName, deviceInfo.osVersion, deviceInfo.manufacturer, deviceInfo.deviceModel) - val requestBody = JacksonJsonSerializer().serializeObject(requestBodyDto) - - OkHttpWebClient().postAsync(RequestParameters("https://codinux.uber.space/issues", requestBody, "application/json")) { response -> - context?.asActivity()?.runOnUiThread { - handleSendMessageLogDirectlyResponseOnUiThread(response) - } - } - } - - protected open fun getDeviceInfoLine(): String { - val deviceInfo = getDeviceInfo() - - var line = deviceInfo.manufacturer ?: "" - - deviceInfo.deviceModel?.let { model -> - line += (if (line.isEmpty()) "" else " ") + model - } - - deviceInfo.osName?.let { osName -> - line += (if (line.isEmpty()) "" else " - ") + osName - } - - deviceInfo.osVersion?.let { osVersion -> - line += (if (line.isEmpty()) "" else " ") + osVersion - } - - deviceInfo.osArch?.let { osArch -> - line += (if (line.isEmpty()) "" else " ") + osArch - } - - return line - - } - - protected open fun getDeviceInfo(): DeviceInfo { - return DeviceInfo(Build.MANUFACTURER.capitalize(), Build.MODEL, "Android", Build.VERSION.RELEASE, System.getProperty("os.arch") ?: "") - } - - protected open fun handleSendMessageLogDirectlyResponseOnUiThread(response: WebClientResponse) { - if (response.isSuccessResponse) { - showSuccessfullySentMessageLog() - } - else { - Toast.makeText(context, context?.getString(R.string.dialog_send_message_log_error_could_not_sent_message_log, response.error?.getInnerExceptionMessage()), - Toast.LENGTH_LONG).show() - } - } - - protected open fun showSuccessfullySentMessageLog() { - Toast.makeText(context, context?.getString(R.string.dialog_send_message_log_thanks_for_helping_making_app_better), Toast.LENGTH_LONG).show() - - dismiss() - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt deleted file mode 100644 index cf979ad3..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt +++ /dev/null @@ -1,485 +0,0 @@ -package net.dankito.banking.ui.android.dialogs - -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import android.text.TextWatcher -import android.text.method.DigitsKeyListener -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.* -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.DialogFragment -import com.google.android.material.textfield.TextInputLayout -import com.otaliastudios.autocomplete.Autocomplete -import kotlinx.android.synthetic.main.dialog_transfer_money.* -import kotlinx.android.synthetic.main.dialog_transfer_money.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.adapter.IconedBankAccountsAdapter -import net.dankito.banking.ui.android.adapter.presenter.RecipientPresenter -import net.dankito.banking.ui.android.extensions.addEnterPressedListener -import net.dankito.banking.ui.android.extensions.closePopupOnBackButtonPress -import net.dankito.banking.ui.android.listener.ListItemSelectedListener -import net.dankito.banking.ui.android.util.StandardAutocompleteCallback -import net.dankito.banking.ui.android.util.StandardTextWatcher -import net.dankito.banking.search.TransactionParty -import net.dankito.banking.ui.model.TypedBankAccount -import net.dankito.banking.ui.model.parameters.TransferMoneyData -import net.dankito.banking.ui.model.responses.BankingClientResponse -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.banking.util.InputValidator -import net.dankito.banking.bankfinder.BankInfo -import net.dankito.banking.ui.android.extensions.isEllipsized -import net.dankito.banking.ui.android.views.InfoPopupWindow -import net.dankito.banking.util.ValidationResult -import net.dankito.utils.android.extensions.* -import net.dankito.utils.multiplatform.toBigDecimal -import java.math.BigDecimal -import java.text.DecimalFormatSymbols -import java.util.* -import javax.inject.Inject -import kotlin.concurrent.schedule - - -open class TransferMoneyDialog : DialogFragment() { - - companion object { - const val DialogTag = "TransferMoneyDialog" - } - - - protected lateinit var account: TypedBankAccount - - protected var preselectedValues: TransferMoneyData? = null - - protected val inputValidator = InputValidator() // TODO: move to presenter - - - protected var recipientBic: String? = null - - - protected var validRecipientNameEntered = false - - protected var validRecipientIbanEntered = false - - protected var validRecipientBicEntered = false - - protected var validReferenceEntered = true - - protected var validAmountEntered = false - - protected var didJustCorrectInput = mutableMapOf() - - - @Inject - protected lateinit var presenter: BankingPresenter - - - init { - BankingComponent.component.inject(this) - } - - open fun show(activity: AppCompatActivity, fullscreen: Boolean = true) { - show(activity, null, fullscreen) - } - - open fun show(activity: AppCompatActivity, preselectedValues: TransferMoneyData?, fullscreen: Boolean = true) { - this.preselectedValues = preselectedValues - - val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog - setStyle(STYLE_NORMAL, style) - - show(activity.supportFragmentManager, DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_transfer_money, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - setupUI(view) - } - - protected open fun setupUI(rootView: View) { - val accountsSupportingTransferringMoney = presenter.accountsSupportingTransferringMoneySortedByDisplayIndex - account = preselectedValues?.account ?: accountsSupportingTransferringMoney.first() - - if (accountsSupportingTransferringMoney.size > 1) { - rootView.lytSelectBankAccount.show() - - val adapter = IconedBankAccountsAdapter(accountsSupportingTransferringMoney) - rootView.spnBankAccounts.adapter = adapter - rootView.spnBankAccounts.onItemSelectedListener = ListItemSelectedListener(adapter) { selectedBankAccount -> - this.account = selectedBankAccount - setRealTimeTransferControlsVisibility(rootView) - } - preselectedValues?.account?.let { rootView.spnBankAccounts.setSelection(adapter.getItems().indexOf(it)) } - } - - initRecipientAutocompletion(rootView.edtxtRecipientName) - - rootView.edtxtRecipientName.addTextChangedListener(checkRequiredDataWatcher { - checkIfEnteredRecipientNameIsValidWhileUserIsTyping() - }) - - rootView.edtxtRecipientIban.addTextChangedListener(StandardTextWatcher { - checkIfEnteredRecipientIbanIsValidWhileUserIsTyping() - tryToGetBicFromIban(it) - }) - - rootView.edtxtAmount.addTextChangedListener(checkRequiredDataWatcher { - checkIfEnteredAmountIsValid() - }) - rootView.edtxtReference.addTextChangedListener(checkRequiredDataWatcher { - checkIfEnteredReferenceTextIsValid() - }) - - rootView.edtxtRecipientName.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRecipientNameIsValidAfterFocusLost() } - rootView.edtxtRecipientIban.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRecipientIbanIsValidAfterFocusLost() } - rootView.edtxtAmount.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredAmountIsValid() } - rootView.edtxtReference.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredReferenceTextIsValid() } - - transferMoneyIfEnterPressed(rootView.edtxtRecipientName) - transferMoneyIfEnterPressed(rootView.edtxtRecipientIban) - transferMoneyIfEnterPressed(rootView.edtxtAmount) - transferMoneyIfEnterPressed(rootView.edtxtReference) - - rootView.btnShowRealTimeTransferInfo.setOnClickListener { showRealTimeTransferInfo(rootView.btnShowRealTimeTransferInfo) } - - setRealTimeTransferControlsVisibility(rootView) - - rootView.btnCancel.setOnClickListener { dismiss() } - - rootView.btnTransferMoney.setOnClickListener { transferMoney() } - - adjustCheckBoxRealTimeTransferWidth() - } - - - protected open fun adjustCheckBoxRealTimeTransferWidth() { - // wait some time till CheckBox is layout and lineCount is set - val timer = Timer() - timer.schedule(10) { activity?.runOnUiThread { adjustCheckBoxRealTimeTransferWidthOnUiThread() }} - timer.schedule(2500) { activity?.runOnUiThread { adjustCheckBoxRealTimeTransferWidthOnUiThread() }} - } - - protected open fun adjustCheckBoxRealTimeTransferWidthOnUiThread() { - if (chkbxRealTimeTransfer.isEllipsized == false) { - // by default chkbxRealTimeTransfer uses full width, even though if its text doesn't need this space -> there - chkbxRealTimeTransfer.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 0f) - chkbxRealTimeTransfer.requestLayout() - } - } - - protected open fun setRealTimeTransferControlsVisibility(rootView: View) { - rootView.lytRealTimeTransfer.setVisibility(account.supportsRealTimeTransfer) - } - - protected open fun showRealTimeTransferInfo(btnShowRealTimeTransferInfo: ImageButton) { - activity?.let { activity -> - InfoPopupWindow(activity, R.string.dialog_transfer_money_real_time_transfer_info) - .show(btnShowRealTimeTransferInfo) - } - } - - private fun transferMoneyIfEnterPressed(editText: EditText) { - editText.addEnterPressedListener { - if (isRequiredDataEntered()) { - transferMoney() - - return@addEnterPressedListener true - } - - false - } - } - - private fun isRequiredDataEntered() = btnTransferMoney.isEnabled - - private fun initRecipientAutocompletion(edtxtRecipientName: EditText) { - val autocompleteCallback = StandardAutocompleteCallback { _, item -> - recipientSelected(item) - true - } - - Autocomplete.on(edtxtRecipientName) - .with(6f) - .with(ColorDrawable(Color.WHITE)) - .with(autocompleteCallback) - .with(RecipientPresenter(presenter, edtxtRecipientName.context)) - .build() - .closePopupOnBackButtonPress(dialog) - } - - - override fun onStart() { - super.onStart() - - setPreselectedValues() - - if (recipientBic != null) { - tryToGetBicFromIban(edtxtRecipientIban.text.toString()) - } - } - - - protected open fun setPreselectedValues() { - preselectedValues?.let { data -> - preselectedValues = null - - edtxtRecipientName.setText(data.recipientName) - - if (data.recipientAccountId.isNotBlank()) { // set only if recipientAccountId has a value as otherwise recipientBankCode would be overridden by empty search result - edtxtRecipientIban.setText(data.recipientAccountId) - } - - // a little bit inconsistent as if IBAN is not set bank's name won't be displayed even though it can be retrieved by BIC - recipientBic = data.recipientBankCode - - if (data.amount > BigDecimal.ZERO) { - edtxtAmount.setText(data.amount.toString()) - } - - edtxtReference.setText(data.reference) - - focusEditTextAccordingToPreselectedValues() - } - } - - protected open fun focusEditTextAccordingToPreselectedValues() { - when { - edtxtRecipientName.text.toString().isBlank() -> edtxtRecipientName.requestFocus() - edtxtRecipientIban.text.toString().isBlank() -> edtxtRecipientIban.requestFocus() - edtxtAmount.text.toString().isBlank() -> edtxtAmount.requestFocus() - edtxtReference.text.toString().isBlank() -> edtxtReference.requestFocus() - else -> edtxtReference.requestFocus() - } - } - - - protected open fun recipientSelected(item: TransactionParty) { - edtxtRecipientName.setText(item.name) - edtxtRecipientIban.setText(item.iban) - recipientBic = item.bic - - focusEditTextAccordingToPreselectedValues() - } - - protected open fun transferMoney() { - getEnteredAmount()?.let { amount -> // should only come at this stage when a valid amount has been entered - val data = TransferMoneyData( - account, - inputValidator.convertToAllowedSepaCharacters(edtxtRecipientName.text.toString()), - edtxtRecipientIban.text.toString().replace(" ", ""), - recipientBic?.replace(" ", "") ?: "", // should always be != null at this point - amount.toBigDecimal(), - inputValidator.convertToAllowedSepaCharacters(edtxtReference.text.toString()), - chkbxRealTimeTransfer.isChecked - ) - - presenter.transferMoneyAsync(data) { - context?.asActivity()?.runOnUiThread { - handleTransferMoneyResultOnUiThread(data, it) - } - } - } - } - - protected open fun handleTransferMoneyResultOnUiThread(transferData: TransferMoneyData, response: BankingClientResponse) { - context?.let { context -> - if (response.userCancelledAction == false) { - val message = if (response.successful) { - context.getString(R.string.dialog_transfer_money_message_transfer_successful, - String.format("%.02f", transferData.amount), "€", transferData.recipientName) // TODO: where to get currency from? - } - else { - context.getString(R.string.dialog_transfer_money_message_transfer_failed, - String.format("%.02f", transferData.amount), "€", transferData.recipientName, // TODO: where to get currency from? - response.errorToShowToUser - ) - } - - AlertDialog.Builder(context) - .setMessage(message) - .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } - .show() - } - - if (response.successful || response.userCancelledAction) { // do not close dialog if an error occurred - this.dismiss() - } - } - } - - - protected fun checkRequiredDataWatcher(additionalCheck: (() -> Unit)? = null): TextWatcher { - return StandardTextWatcher { - additionalCheck?.invoke() - - checkIfRequiredDataEnteredOnUiThread() - } - } - - protected open fun tryToGetBicFromIban(enteredIban: CharSequence) { - presenter.findUniqueBankForIbanAsync(enteredIban.toString()) { foundBank -> - context?.asActivity()?.runOnUiThread { - showValuesForFoundBankOnUiThread(enteredIban, foundBank) - } - } - } - - private fun showValuesForFoundBankOnUiThread(enteredIban: CharSequence, foundBank: BankInfo?) { - validRecipientBicEntered = foundBank != null - recipientBic = foundBank?.bic - - if (foundBank != null) { - txtRecipientBankInfo.text = getString(R.string.dialog_transfer_money_bic_detected_from_iban, foundBank.bic, foundBank.name) - txtRecipientBankInfo.show() - setIbanValidationErrorVisibility() - } - else if (enteredIban.length >= InputValidator.MinimumLengthToDetermineBicFromIban) { - txtRecipientBankInfo.text = getString(R.string.dialog_transfer_money_could_not_determine_bic_from_iban, enteredIban.substring(4, InputValidator.MinimumLengthToDetermineBicFromIban)) - txtRecipientBankInfo.show() - setIbanValidationErrorVisibility() - } - else { - txtRecipientBankInfo.hide() - } - - checkIfRequiredDataEnteredOnUiThread() - } - - protected open fun setIbanValidationErrorVisibility() { - getIbanTextInputErrorView()?.let { textInputError -> - val displaysErrorOrHint = lytRecipientIban.error != null || lytRecipientIban.helperText != null - (textInputError.parent?.parent as? ViewGroup)?.setVisibility(displaysErrorOrHint) - } - } - - protected open fun getIbanTextInputErrorView(): TextView? { - requireContext().getResourceIdentifier("textinput_error", "id")?.let { textInputErrorId -> - return lytRecipientIban.findViewById(textInputErrorId) - } - - return null - } - - protected open fun checkIfRequiredDataEnteredOnUiThread() { - btnTransferMoney.isEnabled = validRecipientNameEntered && validRecipientIbanEntered - && validRecipientBicEntered - && validAmountEntered && validReferenceEntered - } - - protected open fun checkIfEnteredRecipientNameIsValidWhileUserIsTyping() { - val enteredRecipientName = edtxtRecipientName.text.toString() - val validationResult = inputValidator.validateRecipientNameWhileTyping(enteredRecipientName) - - this.validRecipientNameEntered = validationResult.validationSuccessfulOrCouldCorrectString - - showValidationResult(lytRecipientName, validationResult) - } - - protected open fun checkIfEnteredRecipientNameIsValidAfterFocusLost() { - val enteredRecipientName = edtxtRecipientName.text.toString() - val validationResult = inputValidator.validateRecipientName(enteredRecipientName) - - this.validRecipientNameEntered = validationResult.validationSuccessfulOrCouldCorrectString - - if (validationResult.validationSuccessful == false) { // only update hint / error if validation fails, don't hide previous hint / error otherwise - showValidationResult(lytRecipientName, validationResult) - } - } - - protected open fun checkIfEnteredRecipientIbanIsValidWhileUserIsTyping() { - val enteredIban = edtxtRecipientIban.text.toString() - val validationResult = inputValidator.validateIbanWhileTyping(enteredIban) - - this.validRecipientIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString - - showValidationResult(lytRecipientIban, validationResult) - setIbanValidationErrorVisibility() - } - - protected open fun checkIfEnteredRecipientIbanIsValidAfterFocusLost() { - val validationResult = inputValidator.validateIban(edtxtRecipientIban.text.toString()) - - this.validRecipientIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString - - if (validationResult.validationSuccessful == false) { // only update hint / error if validation fails, don't hide previous hint / error otherwise - showValidationResult(lytRecipientIban, validationResult) - setIbanValidationErrorVisibility() - } - } - - protected open fun checkIfEnteredAmountIsValid() { - val validationResult = inputValidator.validateAmount(edtxtAmount.text.toString()) - - this.validAmountEntered = validationResult.validationSuccessfulOrCouldCorrectString - - showValidationResult(lytAmount, validationResult) - } - - protected open fun getEnteredAmount(): BigDecimal? { - try { - val amountString = edtxtAmount.text.toString().replace(',', '.') - - return amountString.toBigDecimal() - } catch (ignored: Exception) { } - - return null - } - - protected open fun checkIfEnteredReferenceTextIsValid() { - val validationResult = inputValidator.validateReference(edtxtReference.text.toString()) - - this.validReferenceEntered = validationResult.validationSuccessfulOrCouldCorrectString - - showValidationResult(lytReference, validationResult) - } - - protected open fun showValidationResult(textInputLayout: TextInputLayout, validationResult: ValidationResult) { - if (didJustCorrectInput.containsKey(textInputLayout)) { // we have just auto corrected TextInputLayout's EditText's text below, don't overwrite its displayed hints and error - return - } - - if (validationResult.didCorrectString) { - textInputLayout.editText?.let { editText -> - val selectionStart = editText.selectionStart - val selectionEnd = editText.selectionEnd - val lengthDiff = validationResult.correctedInputString.length - validationResult.inputString.length - - didJustCorrectInput.put(textInputLayout, true) - - editText.setText(validationResult.correctedInputString) - - if (validationResult.correctedInputString.isNotEmpty()) { - editText.setSelection(selectionStart + lengthDiff, selectionEnd + lengthDiff) - } - - didJustCorrectInput.remove(textInputLayout) - } - } - - val showHintOrError = validationResult.validationError != null || validationResult.validationHint != null - - val textInputErrorLayout = textInputLayout.getResourceIdentifier("textinput_error", "id")?.let { textInputErrorId -> textInputLayout.findViewById(textInputErrorId)?.parent?.parent as? View } - textInputErrorLayout?.setVisibility(showHintOrError) - - textInputLayout.error = validationResult.validationError - if (validationResult.validationError == null) { // don't overwrite error text - textInputLayout.helperText = validationResult.validationHint - } - - (textInputLayout.layoutParams as? ViewGroup.MarginLayoutParams)?.let { params -> - params.bottomMargin = if (showHintOrError == false || textInputLayout == lytReference) 0 - else context!!.getDimension(R.dimen.dialog_transfer_money_input_fields_bottom_margin_when_displaying_validation_label) - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankAccountSettingsDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankAccountSettingsDialog.kt deleted file mode 100644 index 669eefb5..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankAccountSettingsDialog.kt +++ /dev/null @@ -1,125 +0,0 @@ -package net.dankito.banking.ui.android.dialogs.settings - -import android.content.Intent -import android.os.Bundle -import android.view.* -import androidx.fragment.app.FragmentActivity -import kotlinx.android.synthetic.main.dialog_bank_account_settings.* -import kotlinx.android.synthetic.main.dialog_bank_account_settings.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.CheckableValueAdapterItem -import net.dankito.banking.ui.android.adapter.FastAdapterRecyclerView -import net.dankito.banking.ui.model.BankAccountType -import net.dankito.banking.ui.model.TypedBankAccount - - -open class BankAccountSettingsDialog : SettingsDialogBase() { - - companion object { - const val DialogTag = "BankAccountSettingsDialog" - } - - - protected lateinit var account: TypedBankAccount - - - - fun show(account: TypedBankAccount, activity: FragmentActivity) { - this.account = account - - show(activity, DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_bank_account_settings, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.apply { - setupToolbar(this, account.displayName) - } - - edtxtBankAccountName.text = account.displayName - - swtchHideAccount.setOnCheckedChangeListener { _, hideAccount -> swtchIncludeInAutomaticAccountsUpdate.isEnabled = hideAccount == false } - - swtchHideAccount.isChecked = account.hideAccount - swtchIncludeInAutomaticAccountsUpdate.isChecked = account.includeInAutomaticAccountsUpdate - - btnShareAccountData.setOnClickListener { shareAccountData() } - - lvlAccountHolderName.value = account.accountHolderName - lvlAccountIdentifier.value = account.identifier - lvlSubAccountNumber.setValueAndVisibilityIfValueIsSet(account.subAccountNumber) - lvlIban.setValueAndVisibilityIfValueIsSet(account.iban) - lvlAccountType.value = getString(getBankAccountTypeResId(account.type)) - - val context = view.context - val accountFeaturesItems = listOf( - CheckableValueAdapterItem(account.supportsRetrievingBalance, context, R.string.dialog_bank_account_settings_account_features_supports_retrieving_balance), - CheckableValueAdapterItem(account.supportsRetrievingAccountTransactions, context, R.string.dialog_bank_account_settings_account_features_supports_retrieving_account_transactions), - CheckableValueAdapterItem(account.supportsTransferringMoney, context, R.string.dialog_bank_account_settings_account_features_supports_money_transfer), - CheckableValueAdapterItem(account.supportsRealTimeTransfer, context, R.string.dialog_bank_account_settings_account_features_supports_real_time_transfer) - ) - FastAdapterRecyclerView(view.rcyAccountFeatures, accountFeaturesItems) - } - - protected open fun getBankAccountTypeResId(type: BankAccountType): Int { - return when (type) { - BankAccountType.CheckingAccount -> R.string.checking_account - BankAccountType.SavingsAccount -> R.string.savings_account - BankAccountType.FixedTermDepositAccount -> R.string.fixed_term_deposit_account - BankAccountType.SecuritiesAccount -> R.string.securities_account - BankAccountType.LoanAccount -> R.string.loan_account - BankAccountType.CreditCardAccount -> R.string.credit_card_account - BankAccountType.FundDeposit -> R.string.fund_deposit - BankAccountType.BuildingLoanContract -> R.string.building_loan_contract - BankAccountType.InsuranceContract -> R.string.insurance_contract - else -> R.string.other - } - } - - - protected open fun shareAccountData() { - val accountData = StringBuilder(account.accountHolderName + "\n" + account.bank.bankName) - - account.iban?.let { iban -> - accountData.append("\n" + getString(R.string.share_account_data_iban, iban)) - } - - accountData.append("\n" + getString(R.string.share_account_data_bic, account.bank.bic)) - accountData.append("\n" + getString(R.string.share_account_data_bank_code, account.bank.bankCode)) - accountData.append("\n" + getString(R.string.share_account_data_account_number, account.identifier)) - - - val sendIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_TEXT, accountData.toString()) - type = "text/plain" - } - - val shareIntent = Intent.createChooser(sendIntent, null) - startActivity(shareIntent) - - } - - - override val hasUnsavedChanges: Boolean - get() = didChange(edtxtBankAccountName, account.displayName) - || swtchHideAccount.isChecked != account.hideAccount - || swtchIncludeInAutomaticAccountsUpdate.isChecked != account.includeInAutomaticAccountsUpdate - - override fun saveChanges() { - account.userSetDisplayName = edtxtBankAccountName.text - - val didHideAccountChange = account.hideAccount != swtchHideAccount.isChecked - account.hideAccount = swtchHideAccount.isChecked - account.includeInAutomaticAccountsUpdate = swtchIncludeInAutomaticAccountsUpdate.isChecked - - presenter.accountUpdated(account, didHideAccountChange) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankSettingsDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankSettingsDialog.kt deleted file mode 100644 index 5009c749..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankSettingsDialog.kt +++ /dev/null @@ -1,143 +0,0 @@ -package net.dankito.banking.ui.android.dialogs.settings - -import android.os.Bundle -import android.view.* -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.FragmentActivity -import kotlinx.android.synthetic.main.dialog_bank_settings.* -import kotlinx.android.synthetic.main.dialog_bank_settings.view.* -import kotlinx.android.synthetic.main.dialog_bank_settings.view.toolbar -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.DraggableBankAccountAdapterItem -import net.dankito.banking.ui.android.adapter.FastAdapterRecyclerView -import net.dankito.banking.ui.android.adapter.TanMethodAdapterItem -import net.dankito.banking.ui.android.alerts.AskDeleteAccountAlert -import net.dankito.banking.ui.model.TypedBankAccount -import net.dankito.banking.ui.model.TypedBankData -import net.dankito.banking.ui.model.tan.TanMethod - - -open class BankSettingsDialog : SettingsDialogBase() { - - companion object { - const val DialogTag = "BankSettingsDialog" - } - - - protected lateinit var bank: TypedBankData - - protected var selectedTanMethod: TanMethod? = null - - protected lateinit var bankAccountsAdapter: FastAdapterRecyclerView - - protected var banksChangedListener = { _: List -> - updateBankAccountsAdapterItems() - } - - - - fun show(bank: TypedBankData, activity: FragmentActivity) { - this.bank = bank - this.selectedTanMethod = bank.selectedTanMethod - - show(activity, DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val rootView = inflater.inflate(R.layout.dialog_bank_settings, container, false) - - setupUI(rootView) - - presenter.addBanksChangedListener(banksChangedListener) - - return rootView - } - - protected open fun setupUI(rootView: View) { - rootView.apply { - toolbar.apply { - setupToolbar(this, bank.displayName) - } - - edtxtBankName.text = bank.displayName - edtxtUserName.text = bank.userName - bankCredentialsPassword.password = bank.password - - val tanMethodItems = createTanMethodItems() - val tanMethodsAdapter = FastAdapterRecyclerView(rootView.rcyTanMethods, tanMethodItems) - tanMethodsAdapter.onClickListener = { - selectedTanMethod = it.tanMethod - tanMethodsAdapter.setItems(createTanMethodItems()) - } - - lvlBankCode.value = bank.bankCode - lvlBic.value = bank.bic - lvlCustomerName.value = bank.customerName - lvlFinTsServerAddress.value = bank.finTsServerAddress - - val items = createBankAccountsAdapterItems() - bankAccountsAdapter = FastAdapterRecyclerView(rootView.rcyBankAccounts, items, true) - bankAccountsAdapter.onClickListener = { navigationToBankAccountSettingsDialog(it.account) } - bankAccountsAdapter.itemDropped = { oldPosition, oldItem, newPosition, newItem -> reorderedBankAccounts(oldPosition, oldItem.account, newPosition, newItem.account) } - - btnDeleteAccount.setOnClickListener { askUserToDeleteAccount() } - } - } - - - override fun onDestroy() { - presenter.removeBanksChangedListener(banksChangedListener) - - super.onDestroy() - } - - - protected open fun createTanMethodItems(): List { - return bank.supportedTanMethods.map { TanMethodAdapterItem(it, it == selectedTanMethod) } - } - - - protected open fun createBankAccountsAdapterItems(): List { - return bank.accountsSorted.map { DraggableBankAccountAdapterItem(it) } - } - - protected open fun updateBankAccountsAdapterItems() { - bankAccountsAdapter.setItems(createBankAccountsAdapterItems()) - } - - - protected open fun navigationToBankAccountSettingsDialog(account: TypedBankAccount) { - (activity as? AppCompatActivity)?.let { activity -> - BankAccountSettingsDialog().show(account, requireActivity() as AppCompatActivity) - } - } - - protected open fun reorderedBankAccounts(oldPosition: Int, oldItem: TypedBankAccount, newPosition: Int, newItem: TypedBankAccount) { - oldItem.displayIndex = oldPosition - newItem.displayIndex = newPosition - - presenter.accountUpdated(oldItem, false) - presenter.accountUpdated(newItem, false) - } - - - override val hasUnsavedChanges: Boolean - get() = didChange(edtxtBankName, bank.displayName) - || didChange(edtxtUserName, bank.userName) - || bankCredentialsPassword.password != bank.password - || bank.selectedTanMethod != selectedTanMethod - - override fun saveChanges() { - bank.userSetDisplayName = edtxtBankName.text - - presenter.bankUpdated(bank, edtxtUserName.text, bankCredentialsPassword.password, selectedTanMethod) - } - - protected open fun askUserToDeleteAccount() { - AskDeleteAccountAlert().show(bank, presenter, requireContext()) { - closeDialog() - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/ProtectAppSettingsDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/ProtectAppSettingsDialog.kt deleted file mode 100644 index 404152d3..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/ProtectAppSettingsDialog.kt +++ /dev/null @@ -1,183 +0,0 @@ -package net.dankito.banking.ui.android.dialogs.settings - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.doOnNextLayout -import androidx.fragment.app.FragmentActivity -import kotlinx.android.synthetic.main.dialog_protect_app_settings.* -import kotlinx.android.synthetic.main.dialog_protect_app_settings.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.authentication.AuthenticationService -import net.dankito.banking.ui.android.authentication.AuthenticationType -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.extensions.addEnterPressedListener -import net.dankito.banking.ui.android.util.StandardTextWatcher -import net.dankito.utils.android.extensions.hideKeyboardDelayed -import net.dankito.utils.android.extensions.hide -import net.dankito.utils.android.extensions.setVisibility -import net.dankito.utils.android.extensions.show -import org.slf4j.LoggerFactory -import javax.inject.Inject - - -open class ProtectAppSettingsDialog : SettingsDialogBase() { - - companion object { - const val DialogTag = "ProtectAppSettingsDialog" - - private val log = LoggerFactory.getLogger(ProtectAppSettingsDialog::class.java) - } - - - @Inject - protected lateinit var authenticationService: AuthenticationService - - - init { - BankingComponent.component.inject(this) - } - - - fun show(activity: FragmentActivity) { - show(activity, SettingsDialog.DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_protect_app_settings, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - setupUI(view) - } - - protected open fun setupUI(rootView: View) { - rootView.apply { - toolbar.apply { - setupToolbar(this, context.getString(R.string.settings), false) - } - - val authenticationType = authenticationService.authenticationType - val isBiometricAuthenticationSupported = authenticationService.isBiometricAuthenticationSupported - - val showAuthenticationMethods = isBiometricAuthenticationSupported || authenticationType != AuthenticationType.None // hide select authentication method if password is the only option to choose - segmentedGroup.setVisibility(showAuthenticationMethods) - segmentedGroup.doOnNextLayout { - val segmentedControlButtonWidth = segmentedGroup.measuredWidth / 3 - btnShowBiometricAuthenticationSection.layoutParams.width = segmentedControlButtonWidth - btnShowPasswordAuthenticationSection.layoutParams.width = segmentedControlButtonWidth - btnShowRemoveAppProtectionSection.layoutParams.width = segmentedControlButtonWidth - } - - btnShowBiometricAuthenticationSection.setVisibility(isBiometricAuthenticationSupported) - btnShowBiometricAuthenticationSection.setOnCheckedChangeListener { _, isChecked -> - if (isChecked) { - showAuthenticationLayout(rootView, lytBiometricAuthentication) - } - } - - btnShowPasswordAuthenticationSection.setOnCheckedChangeListener { _, isChecked -> - if (isChecked) { - showAuthenticationLayout(rootView, lytPasswordAuthentication) - checkIfEnteredPasswordsMatch() - } - } - - btnShowRemoveAppProtectionSection.setOnCheckedChangeListener { _, isChecked -> - if (isChecked) { - showAuthenticationLayout(rootView, lytRemoveAppProtection) - } - } - - btnBiometricAuthentication.customButtonClickHandler = { - authenticationService.authenticateUserWithBiometricToSetAsNewAuthenticationMethod { result -> - btnSetAuthenticationMethod.isEnabled = result.successful - } - } - - edtxtPassword.actualEditText.addTextChangedListener(StandardTextWatcher { checkIfEnteredPasswordsMatch() } ) - edtxtPassword.actualEditText.addEnterPressedListener { checkIfEnteredPasswordsMatchAndSetAuthenticationMethod() } - edtxtPasswordConfirmation.actualEditText.addTextChangedListener(StandardTextWatcher { checkIfEnteredPasswordsMatch() } ) - edtxtPasswordConfirmation.actualEditText.addEnterPressedListener { checkIfEnteredPasswordsMatchAndSetAuthenticationMethod() } - - btnSetAuthenticationMethod.setOnClickListener { setAuthenticationMethod() } - - if (isBiometricAuthenticationSupported && authenticationType == AuthenticationType.Biometric) { - btnShowBiometricAuthenticationSection.isChecked = true - } - else { - btnShowPasswordAuthenticationSection.isChecked = true - } - - } - } - - protected open fun showAuthenticationLayout(rootView: View, authenticationLayoutToShow: ViewGroup) { - lytBiometricAuthentication.hide() - lytPasswordAuthentication.hide() - lytRemoveAppProtection.hide() - - authenticationLayoutToShow.show() - - if (authenticationLayoutToShow == lytRemoveAppProtection) { - btnSetAuthenticationMethod.setText(R.string.dialog_protect_app_settings_button_remove_app_protection_title) - btnSetAuthenticationMethod.setBackgroundResource(R.color.destructiveColor) - btnSetAuthenticationMethod.isEnabled = true - } - else { - btnSetAuthenticationMethod.setText(R.string.dialog_protect_app_settings_button_set_new_authentication_method_title) - btnSetAuthenticationMethod.setBackgroundResource(R.drawable.conditionally_disabled_view_background) - btnSetAuthenticationMethod.isEnabled = false - } - - authenticationLayoutToShow.hideKeyboardDelayed(10) - } - - - protected open fun checkIfEnteredPasswordsMatchAndSetAuthenticationMethod(): Boolean { - if (checkIfEnteredPasswordsMatch()) { - setAuthenticationMethod() - - return true - } - - return false - } - - protected open fun checkIfEnteredPasswordsMatch(): Boolean { - val enteredPassword = edtxtPassword.text - - if (enteredPassword.isNotBlank() && enteredPassword == edtxtPasswordConfirmation.text) { - btnSetAuthenticationMethod.isEnabled = true - return true - } - else { - btnSetAuthenticationMethod.isEnabled = false - return false - } - } - - protected open fun setAuthenticationMethod() { - when { - btnShowPasswordAuthenticationSection.isChecked -> authenticationService.setAuthenticationMethodToPassword(edtxtPassword.chars) - btnShowBiometricAuthenticationSection.isChecked -> authenticationService.setAuthenticationMethodToBiometric() - btnShowRemoveAppProtectionSection.isChecked -> authenticationService.removeAppProtection() - else -> log.error("This should never occur! Has there a new authentication method been added?") - } - - closeDialog() - } - - - override val hasUnsavedChanges: Boolean - get() = false - - override fun saveChanges() { - - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialog.kt deleted file mode 100644 index 2c02c78b..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialog.kt +++ /dev/null @@ -1,200 +0,0 @@ -package net.dankito.banking.ui.android.dialogs.settings - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.appcompat.content.res.AppCompatResources -import androidx.fragment.app.FragmentActivity -import kotlinx.android.synthetic.main.dialog_settings.* -import kotlinx.android.synthetic.main.dialog_settings.view.* -import net.codinux.banking.tools.importerexporter.CsvAccountTransactionsExporter -import net.codinux.banking.tools.importerexporter.model.AccountTransaction -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.BankDataAdapterItem -import net.dankito.banking.ui.android.adapter.FastAdapterRecyclerView -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.model.IAccountTransaction -import net.dankito.banking.ui.model.TypedBankData -import net.dankito.filechooserdialog.FileChooserDialog -import net.dankito.filechooserdialog.model.FileChooserDialogConfig -import net.dankito.utils.android.permissions.IPermissionsService -import net.dankito.utils.multiplatform.toFile -import java.io.File -import java.text.SimpleDateFormat -import javax.inject.Inject - - -open class SettingsDialog : SettingsDialogBase() { - - companion object { - val ExportTransactionsDateFormat = SimpleDateFormat("yyyyMMdd") - - const val DialogTag = "SettingsDialog" - } - - - @Inject - protected lateinit var permissionsService: IPermissionsService - - protected lateinit var banksAdapter: FastAdapterRecyclerView - - protected var banksChangedListener = { _: List -> - updateBanksAdapterItems() - } - - - init { - BankingComponent.component.inject(this) - } - - - fun show(activity: FragmentActivity) { - show(activity, DialogTag) - } - - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val rootView = inflater.inflate(R.layout.dialog_settings, container, false) - - setupUI(rootView) - - presenter.addBanksChangedListener(banksChangedListener) - - return rootView - } - - protected open fun setupUI(rootView: View) { - rootView.apply { - toolbar.apply { - setupToolbar(this, rootView.context.getString(R.string.settings)) - } - - val items = createBanksAdapterItems() - banksAdapter = FastAdapterRecyclerView(rootView.rcyBankCredentials, items, true) - banksAdapter.onClickListener = { navigationToBankSettingsDialog(it.bank) } - banksAdapter.itemDropped = { oldPosition, oldItem, newPosition, newItem -> reorderedBanks(oldPosition, oldItem.bank, newPosition, newItem.bank) } - - btnAddAccount.setOnClickListener { presenter.showAddAccountDialog() } - - selectUpdateAccountsAfter.periodInMinutes = presenter.appSettings.automaticallyUpdateAccountsAfterMinutes - - btnSetAppProtection.setOnClickListener { navigateToProtectAppSettingsDialog() } - selectLockAppAfter.periodInMinutes = presenter.appSettings.lockAppAfterMinutes - - btnExportAccountTransactions.setOnClickListener { exportAccountTransactions() } - - // on Pre Lollipop devices setting vector drawables in xml is not supported -> set left drawable here - val sendIcon = AppCompatResources.getDrawable(context, R.drawable.ic_baseline_send_24) - btnShowSendMessageLogDialog.setCompoundDrawablesWithIntrinsicBounds(sendIcon, null, null, null) - btnShowSendMessageLogDialog.setOnClickListener { presenter.showSendMessageLogDialog() } - } - } - - - override fun onDestroy() { - presenter.removeBanksChangedListener(banksChangedListener) - - super.onDestroy() - } - - - protected open fun createBanksAdapterItems(): List { - return presenter.allBanksSortedByDisplayIndex.map { BankDataAdapterItem(it) } - } - - protected open fun updateBanksAdapterItems() { - activity?.runOnUiThread { - banksAdapter.setItems(createBanksAdapterItems()) - } - } - - - protected open fun navigationToBankSettingsDialog(bank: TypedBankData) { - activity?.let { activity -> - BankSettingsDialog().show(bank, activity) - } - } - - protected open fun navigateToProtectAppSettingsDialog() { - activity?.let { activity -> - ProtectAppSettingsDialog().show(activity) - } - } - - - protected open fun exportAccountTransactions() { - val initialDirectory = presenter.appSettings.lastSelectedExportFolder.toFile() - val suggestedFilename = getExportCsvSuggestedFilename() - -// val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) -// intent.addCategory(Intent.CATEGORY_OPENABLE) -// intent.type = "text/csv" -// -// intent.putExtra(Intent.EXTRA_TITLE, suggestedFilename) -// -// startActivityForResult(intent, 1) - - activity?.let { activity -> - val config = FileChooserDialogConfig(initialDirectory = initialDirectory, suggestedFilenameForSaveFileDialog = suggestedFilename) - FileChooserDialog().showSaveFileInFullscreenDialog(activity, permissionsService, config) { _, selectedFile -> - selectedFile?.let { - val transactions = presenter.allTransactionsSorted.map { mapTransaction(it) } - - CsvAccountTransactionsExporter().export(selectedFile, transactions) - - presenter.appSettings.lastSelectedExportFolder = selectedFile.parentFile.absolutePath - presenter.appSettingsChanged() - } - } - } - } - - // TODO: this is almost the same code as in JavaFX MainMenuBar.getExportCsvSuggestedFilename() -> merge - protected open fun getExportCsvSuggestedFilename(): String? { - val transactions = presenter.allTransactions - val transactionsDates = transactions.map { it.valueDate } - val transactionsStartDate = transactionsDates.min() - val transactionsEndDate = transactionsDates.max() - - return context?.getString(R.string.dialog_settings_export_account_transactions_suggested_file_name, - transactionsStartDate?.let { ExportTransactionsDateFormat.format(it) } ?: "", transactionsEndDate?.let { ExportTransactionsDateFormat.format(it) } ?: "") - } - - // TODO: this is exactly the same code as in JavaFX MainMenuBar.mapTransaction() -> merge - protected open fun mapTransaction(transaction: IAccountTransaction): AccountTransaction { - return AccountTransaction( - transaction.account.iban ?: transaction.account.identifier, - transaction.amount, - transaction.currency, - transaction.reference, - transaction.bookingDate, - transaction.valueDate, - transaction.otherPartyName, - transaction.otherPartyBankCode, - transaction.otherPartyAccountId, - transaction.bookingText - ) - } - - - protected open fun reorderedBanks(oldPosition: Int, oldItem: TypedBankData, newPosition: Int, newItem: TypedBankData) { - oldItem.displayIndex = oldPosition - newItem.displayIndex = newPosition - - presenter.bankDisplayIndexUpdated(oldItem) - presenter.bankDisplayIndexUpdated(newItem) - } - - - override val hasUnsavedChanges: Boolean - get() = presenter.appSettings.automaticallyUpdateAccountsAfterMinutes != selectUpdateAccountsAfter.periodInMinutes - || presenter.appSettings.lockAppAfterMinutes != selectLockAppAfter.periodInMinutes - - override fun saveChanges() { - presenter.appSettings.automaticallyUpdateAccountsAfterMinutes = selectUpdateAccountsAfter.periodInMinutes - presenter.appSettings.lockAppAfterMinutes = selectLockAppAfter.periodInMinutes - presenter.appSettingsChanged() - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialogBase.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialogBase.kt deleted file mode 100644 index a21f6ffd..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialogBase.kt +++ /dev/null @@ -1,149 +0,0 @@ -package net.dankito.banking.ui.android.dialogs.settings - -import android.app.Dialog -import android.content.DialogInterface -import android.os.Bundle -import android.view.* -import androidx.appcompat.widget.Toolbar -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.FragmentActivity -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.alerts.AskDismissChangesAlert -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.views.FormEditText -import net.dankito.banking.ui.presenter.BankingPresenter -import org.slf4j.LoggerFactory -import javax.inject.Inject - - -abstract class SettingsDialogBase : DialogFragment() { - - companion object { - private val log = LoggerFactory.getLogger(SettingsDialogBase::class.java) - } - - - protected abstract val hasUnsavedChanges: Boolean - - protected abstract fun saveChanges() - - - @Inject - protected lateinit var presenter: BankingPresenter - - - init { - BankingComponent.component.inject(this) - } - - - - fun show(activity: FragmentActivity, dialogTag: String, fullscreen: Boolean = true) { - val style = if (fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog - setStyle(STYLE_NORMAL, style) - - show(activity.supportFragmentManager, dialogTag) - } - - - override fun setupDialog(dialog: Dialog, style: Int) { - super.setupDialog(dialog, style) - - dialog.setOnKeyListener { _, keyCode, event -> - if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) { - return@setOnKeyListener handleBackButtonPress() - } - - false - } - } - - protected open fun handleBackButtonPress(): Boolean { - askToDismissChanges() - - return hasUnsavedChanges - } - - - protected open fun setupToolbar(toolbar: Toolbar, dialogTitle: String, showSaveButton: Boolean = true) { - toolbar.apply { - title = dialogTitle - - inflateMenu(R.menu.menu_settings_dialog) - menu.findItem(R.id.mnitmSaveChanges).isVisible = showSaveButton - - setOnMenuItemClickListener { item -> onOptionsItemSelected(item) } - - setNavigationOnClickListener { askToDismissChanges() } - } - } - - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.mnitmSaveChanges -> saveChangesAndCloseDialog() - else -> super.onOptionsItemSelected(item) - } - } - - - protected open fun didChange(editedValue: FormEditText, originalValue: String): Boolean { - return editedValue.text != originalValue - } - - - protected open fun saveChangesAndCloseDialog(): Boolean { - if (hasUnsavedChanges) { - saveChanges() - } - - closeDialog() - - return true - } - - protected open fun askToDismissChanges() { - if (hasUnsavedChanges) { - AskDismissChangesAlert().show(this) - } - else { - closeDialog() - } - } - - protected open fun closeDialog() { - dismiss() - } - - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - log.info("Creating Fragment $this") - } - - override fun onDismiss(dialog: DialogInterface) { - log.info("Dismissing Fragment $this") - - super.onDismiss(dialog) - } - - override fun onPause() { - log.info("Pausing Fragment $this") - - super.onPause() - } - - override fun onStop() { - log.info("Stopping Fragment $this") - - super.onStop() - } - - override fun onDestroy() { - log.info("Destroying Fragment $this") - - super.onDestroy() - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/AutocompleteExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/AutocompleteExtensions.kt deleted file mode 100644 index 9b764549..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/AutocompleteExtensions.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.dankito.banking.ui.android.extensions - -import android.app.Dialog -import android.view.KeyEvent -import com.otaliastudios.autocomplete.Autocomplete - - -/** - * This will not work if Dialog is null! - * - * Making the Dialog parameter nullable is just for convienience to be able to call - * Autocomplete - * .on<>(view) - * .build() - * .closePopupOnBackButtonPress(dialog) - */ -fun Autocomplete<*>.closePopupOnBackButtonPress(dialog: Dialog?) { - dialog?.setOnKeyListener { _, keyCode, _ -> - if (keyCode == KeyEvent.KEYCODE_BACK) { - if (this.isPopupShowing) { // close autocomplete popup on back button press - this.dismissPopup() - return@setOnKeyListener true - } - } - - false - } -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ByteArrayExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ByteArrayExtensions.kt deleted file mode 100644 index 8692542b..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ByteArrayExtensions.kt +++ /dev/null @@ -1,16 +0,0 @@ -package net.dankito.banking.ui.android.extensions - -import android.content.res.Resources -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.Drawable - - -fun ByteArray.toBitmap(): Bitmap { - return BitmapFactory.decodeByteArray(this, 0, this.size) -} - -fun ByteArray.toDrawable(resources: Resources): Drawable { - return BitmapDrawable(resources, this.toBitmap()) -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ContextExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ContextExtensions.kt deleted file mode 100644 index b562f6b6..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ContextExtensions.kt +++ /dev/null @@ -1,16 +0,0 @@ -package net.dankito.banking.ui.android.extensions - -import android.content.Context -import android.os.IBinder -import android.view.View -import android.view.inputmethod.InputMethodManager - - -fun Context.hideKeyboard(anyViewInHierarchy: View, flags: Int = 0) { - hideKeyboard(anyViewInHierarchy.windowToken, flags) -} - -fun Context.hideKeyboard(windowToken: IBinder, flags: Int = 0) { - val keyboard = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - keyboard.hideSoftInputFromWindow(windowToken, flags) -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/EditTextExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/EditTextExtensions.kt deleted file mode 100644 index 5bceda8e..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/EditTextExtensions.kt +++ /dev/null @@ -1,19 +0,0 @@ -package net.dankito.banking.ui.android.extensions - -import android.view.KeyEvent -import android.widget.EditText - - -val EditText.textString: String - get() = this.text.toString() - - -fun EditText.addEnterPressedListener(enterPressed: () -> Boolean) { - this.setOnKeyListener { _, keyCode, event -> - if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_UP) { - return@setOnKeyListener enterPressed() - } - - false - } -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/IDrawerItemExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/IDrawerItemExtensions.kt deleted file mode 100644 index 53e9106e..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/IDrawerItemExtensions.kt +++ /dev/null @@ -1,20 +0,0 @@ -package net.dankito.banking.ui.android.extensions - -import android.content.Context -import com.mikepenz.iconics.typeface.IIcon -import com.mikepenz.materialdrawer.iconics.withIcon -import com.mikepenz.materialdrawer.model.BaseDescribeableDrawerItem -import com.mikepenz.materialdrawer.model.BaseViewHolder -import com.mikepenz.materialdrawer.model.interfaces.withIconColor -import net.dankito.utils.android.extensions.createColorStateList - - -fun BaseDescribeableDrawerItem.withIcon(context: Context, icon: IIcon, iconColorId: Int) - : BaseDescribeableDrawerItem { - - withIcon(icon) - - withIconColor(context.createColorStateList(iconColorId)) - - return this -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt deleted file mode 100644 index 2dd1b018..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.dankito.banking.ui.android.extensions - -import android.widget.ImageView -import net.dankito.banking.ui.model.IBankData -import net.dankito.utils.android.extensions.hide -import net.dankito.utils.android.extensions.show - - -fun ImageView.setIcon(bank: IBankData<*, *>) { - try { - val iconData = bank.iconData - - if (iconData != null) { - this.show() - this.setImageFromBytes(iconData) - } - else { - this.hide() - this.setImageURI(null) - } - } catch (e: Exception) { - this.hide() - } -} - -fun ImageView.setImageFromBytes(data: ByteArray) { - this.setImageBitmap(data.toBitmap()) -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/RecyclerViewExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/RecyclerViewExtensions.kt deleted file mode 100644 index 9f86f958..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/RecyclerViewExtensions.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.dankito.banking.ui.android.extensions - -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.RecyclerView - - -fun RecyclerView.addHorizontalItemDivider() { - this.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/TextViewExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/TextViewExtensions.kt deleted file mode 100644 index 8c399563..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/TextViewExtensions.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.dankito.banking.ui.android.extensions - -import android.widget.TextView -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.utils.android.extensions.setTextColorToColorResource -import net.dankito.utils.multiplatform.BigDecimal - - -fun TextView.showAmount(presenter: BankingPresenter, amount: BigDecimal, currencyIsoCode: String? = null) { - text = presenter.formatAmount(amount, currencyIsoCode) - setTextColorForAmount(amount) -} - -fun TextView.setTextColorForAmount(amount: BigDecimal) { - setTextColorToColorResource(if (amount >= java.math.BigDecimal.ZERO) R.color.positiveAmount else R.color.negativeAmount) -} - -val TextView.isEllipsized: Boolean - get() { - this.layout?.let { layout -> - for (i in 0..layout.lineCount) { - if (layout.getEllipsisCount(i) > 0) { - return true - } - } - } - - return false - } \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/home/HomeFragment.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/home/HomeFragment.kt deleted file mode 100644 index 7d0d6e2c..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/home/HomeFragment.kt +++ /dev/null @@ -1,363 +0,0 @@ -package net.dankito.banking.ui.android.home - -import android.app.SearchManager -import android.content.Context -import android.os.Bundle -import android.view.* -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.ImageButton -import androidx.appcompat.widget.Toolbar -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.widget.SearchView -import androidx.core.content.ContextCompat -import androidx.core.graphics.drawable.DrawableCompat -import androidx.fragment.app.Fragment -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.fragment_home.* -import kotlinx.android.synthetic.main.fragment_home.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.adapter.AccountTransactionAdapter -import net.dankito.banking.ui.android.di.BankingComponent -import net.dankito.banking.ui.android.extensions.addHorizontalItemDivider -import net.dankito.banking.ui.android.extensions.showAmount -import net.dankito.banking.ui.android.views.InfoPopupWindow -import net.dankito.banking.ui.model.SelectedAccountType -import net.dankito.banking.ui.model.TransactionsRetrievalState -import net.dankito.banking.ui.model.TypedBankAccount -import net.dankito.banking.ui.model.parameters.TransferMoneyData -import net.dankito.banking.ui.model.responses.GetTransactionsResponse -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.utils.android.extensions.* -import net.dankito.utils.multiplatform.sum -import javax.inject.Inject - - -class HomeFragment : Fragment() { - - companion object { - - val TransactionsCannotBeRetrievedStates = listOf(TransactionsRetrievalState.AccountTypeNotSupported, TransactionsRetrievalState.AccountDoesNotSupportFetchingTransactions) - - } - - - private lateinit var mnitmSearchTransactions: MenuItem - - private lateinit var mnitmUpdateTransactions: MenuItem - - - private var accountsForWhichNotAllTransactionsHaveBeenFetched = listOf() - - private var showTopFetchAllTransactionsView = true // TODO: read from db - - - private val transactionAdapter: AccountTransactionAdapter - - protected var appliedTransactionsFilter = "" - - - @Inject - protected lateinit var presenter: BankingPresenter - - - init { - BankingComponent.component.inject(this) - - transactionAdapter = AccountTransactionAdapter(presenter) - } - - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val rootView = inflater.inflate(R.layout.fragment_home, container, false) - - val rcyvwAccountTransactions: RecyclerView = rootView.findViewById(R.id.rcyvwAccountTransactions) - rcyvwAccountTransactions.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) - rcyvwAccountTransactions.adapter = transactionAdapter - rcyvwAccountTransactions.addHorizontalItemDivider() - rcyvwAccountTransactions.isNestedScrollingEnabled = false - - registerForContextMenu(rcyvwAccountTransactions) // this is actually bad, splits code as context menu is created in AccountTransactionAdapter - - rootView.btnTopFetchAllTransactions.setOnClickListener { - fetchAllTransactions() - } - - rootView.btnBottomFetchAllTransactions.setOnClickListener { - fetchAllTransactions() - } - - rootView.btnShowFetchAllTransactionsInfo.setOnClickListener { showFetchAllTransactionsInfo(rootView.btnShowFetchAllTransactionsInfo) } - - rootView.btnHideTopFetchAllTransactionsView.setOnClickListener { - hideTopFetchAllTransactionsView() - } - - rootView.btnRetrieveTransactions.setOnClickListener { fetchTransactions() } - rootView.btnAddAccount.setOnClickListener { presenter.showAddAccountDialog() } - - return rootView - } - - override fun onResume() { - super.onResume() - - if (this::mnitmSearchTransactions.isInitialized) { // restore displayed transactions after onStop() - updateMenuItemsStateAndTransactionsToDisplay() - } - } - - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - super.onCreateOptionsMenu(menu, inflater) - - mnitmSearchTransactions = menu.findItem(R.id.mnitmSearchTransactions) - mnitmUpdateTransactions = menu.findItem(R.id.mnitmUpdateTransactions) - - initSearchView() - - initLogicAfterUiInitialized() - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.mnitmUpdateTransactions -> { - updateAccountsTransactions() - return true - } - } - - return super.onOptionsItemSelected(item) - } - - private fun initSearchView() { - context?.asActivity()?.let { context -> - (context.getSystemService(Context.SEARCH_SERVICE) as? SearchManager)?.let { searchManager -> - (mnitmSearchTransactions.actionView as? SearchView)?.let { searchView -> - searchView.setSearchableInfo(searchManager.getSearchableInfo(context.componentName)) - - // if imeOptions aren't set like this searchView would take whole remaining screen when focused in landscape mode (see https://stackoverflow.com/questions/15296129/searchview-and-keyboard) - val searchInput = - searchView.findViewById(androidx.appcompat.R.id.search_src_text) as? EditText - searchInput?.imeOptions = EditorInfo.IME_ACTION_SEARCH or EditorInfo.IME_FLAG_NO_EXTRACT_UI - - searchView.setOnQueryTextListener(searchAccountTransactionsTextListener) - } - } - } - } - - - override fun onContextItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.mnitmNewTransferToSameTransactionParty -> { - newTransferToSameTransactionParty() - return true - } - R.id.mnitmNewTransferWithSameData -> { - newTransferWithSameData() - return true - } - } - - return super.onContextItemSelected(item) - } - - - private fun initLogicAfterUiInitialized() { - presenter.addBanksChangedListener { updateMenuItemsStateAndTransactionsToDisplay() } // on account addition or deletion may menu items' state changes - presenter.addSelectedAccountsChangedListener { updateMenuItemsStateAndTransactionsToDisplay() } - - presenter.addRetrievedAccountTransactionsResponseListener { response -> - handleGetTransactionsResponseOffUiThread(response) - } - - updateMenuItemsStateAndTransactionsToDisplay() - } - - - private fun updateMenuItemsStateAndTransactionsToDisplay() { - context?.asActivity()?.runOnUiThread { - mnitmSearchTransactions.isVisible = presenter.doSelectedAccountsSupportRetrievingTransactions - mnitmUpdateTransactions.isVisible = presenter.doSelectedAccountsSupportRetrievingTransactions - - updateTransactionsToDisplayOnUiThread() - } - } - - private fun updateAccountsTransactions() { - mnitmUpdateTransactions.isEnabled = false - - val icon = mnitmUpdateTransactions.icon?.let { DrawableCompat.wrap(it) } - icon?.let { DrawableCompat.setTint(it, ContextCompat.getColor(context!!, R.color.disabledColor)) } - - presenter.updateSelectedAccountsTransactionsAsync { - context?.asActivity()?.runOnUiThread { - mnitmUpdateTransactions.isEnabled = true - icon?.let { DrawableCompat.setTintList(it, null) } - } - } - } - - private fun handleGetTransactionsResponseOffUiThread(response: GetTransactionsResponse) { - context?.asActivity()?.let { activity -> - activity.runOnUiThread { - handleGetTransactionsResponseOnUiThread(activity, response) - } - } - } - - private fun handleGetTransactionsResponseOnUiThread(context: Context, response: GetTransactionsResponse) { - response.retrievedData.forEach { retrievedData -> - if (retrievedData.successfullyRetrievedData) { - updateTransactionsToDisplayOnUiThread() - } - else if (response.userCancelledAction == false) { // if user cancelled entering TAN then don't show a error message - AlertDialog.Builder(context) - .setMessage(context.getString(R.string.fragment_home_could_not_retrieve_account_transactions, - retrievedData.account.displayName, response.errorToShowToUser)) - .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } - .show() - } - } - } - - - private fun newTransferToSameTransactionParty() { - transactionAdapter.selectedTransaction?.let { selectedTransaction -> - presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndReference(selectedTransaction)) - } - } - - private fun newTransferWithSameData() { - transactionAdapter.selectedTransaction?.let { selectedTransaction -> - presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransaction(selectedTransaction)) - } - } - - - private val searchAccountTransactionsTextListener: SearchView.OnQueryTextListener = object : SearchView.OnQueryTextListener { - override fun onQueryTextChange(query: String): Boolean { - appliedTransactionsFilter = query - - updateTransactionsToDisplayOnUiThread() - - return true - } - - override fun onQueryTextSubmit(query: String): Boolean { - return true - } - } - - - private fun updateTransactionsToDisplayOnUiThread() { - setToolbarTitle() - - transactionAdapter.items = presenter.searchSelectedAccountTransactions(appliedTransactionsFilter) - - lytTransactionsSummary.setVisibility(presenter.doSelectedAccountsSupportRetrievingBalance) - - txtCountTransactions.text = context?.getString(R.string.fragment_home_count_transactions, transactionAdapter.items.size) - - val sumOfDisplayedTransactions = if (appliedTransactionsFilter.isBlank()) presenter.balanceOfSelectedAccounts - else transactionAdapter.items.map { it.amount }.sum() - txtTransactionsBalance.showAmount(presenter, sumOfDisplayedTransactions) - - setRecyclerViewAndNoTransactionsFetchedView() - - setFetchAllTransactionsView() - } - - private fun setToolbarTitle() { - // TODO: also set selected account's icon - - if (presenter.allBanks.isNotEmpty()) { - activity?.findViewById(R.id.toolbar)?.let { toolbar -> - toolbar.title = when (presenter.selectedAccountType) { - SelectedAccountType.AllAccounts -> context?.getString(R.string.drawer_menu_all_bank_accounts_title) - SelectedAccountType.SingleBank -> presenter.getSingleSelectedBank()?.displayName - SelectedAccountType.SingleAccount -> presenter.getSingleSelectedAccount()?.displayName - } - } - } - } - - private fun setRecyclerViewAndNoTransactionsFetchedView() { - val transactionsRetrievalState = presenter.selectedAccountsTransactionRetrievalState - val haveTransactionsBeenRetrieved = transactionsRetrievalState == TransactionsRetrievalState.RetrievedTransactions - val noAccountsAddedYet = presenter.allBanks.isEmpty() - - lytTransactionsTopBar.setVisibility(haveTransactionsBeenRetrieved) - rcyvwAccountTransactions.setVisibility(haveTransactionsBeenRetrieved) - lytNoTransactionsFetched.isGone = haveTransactionsBeenRetrieved || noAccountsAddedYet - btnRetrieveTransactions.isGone = TransactionsCannotBeRetrievedStates.contains(transactionsRetrievalState) - btnAddAccount.setVisibility(noAccountsAddedYet) - - val messageArgs = mutableListOf() - val transactionsRetrievalStateMessageId = when (transactionsRetrievalState) { - TransactionsRetrievalState.AccountTypeNotSupported -> R.string.fragment_home_transactions_retrieval_state_account_type_not_supported - TransactionsRetrievalState.AccountDoesNotSupportFetchingTransactions -> R.string.fragment_home_transactions_retrieval_state_account_does_not_support_retrieving_transactions - TransactionsRetrievalState.NoTransactionsInRetrievedPeriod -> { - val account = presenter.selectedAccounts.first() - account.retrievedTransactionsFromOn?.let { messageArgs.add(presenter.formatToMediumDate(it)) } - account.retrievedTransactionsUpTo?.let { messageArgs.add(presenter.formatToMediumDate(it)) } - R.string.fragment_home_transactions_retrieval_state_no_transactions_in_retrieved_period - } - TransactionsRetrievalState.NeverRetrievedTransactions -> R.string.fragment_home_transactions_retrieval_state_never_retrieved_transactions - else -> null - } - txtNoTransactionsFetchedMessage.text = transactionsRetrievalStateMessageId?.let { requireContext().getString(transactionsRetrievalStateMessageId, *messageArgs.toTypedArray()) } ?: "" - } - - private fun setFetchAllTransactionsView() { - accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedAccountsForWhichNotAllTransactionsHaveBeenFetched - showTopFetchAllTransactionsView = presenter.showStrikingFetchAllTransactionsViewForSelectedAccounts - val showFetchAllTransactionsView = presenter.showFetchAllTransactionsViewForSelectedAccounts - - lytTopFetchAllTransactions.setVisibility(showFetchAllTransactionsView && showTopFetchAllTransactionsView) - - if (showFetchAllTransactionsView && showTopFetchAllTransactionsView == false) { - // TODO: implement CoordinatorLayout to show lytBottomFetchAllTransactions below rcyvwAccountTransactions -// lytBottomFetchAllTransactions.visibility = View.VISIBLE - } - else { - lytBottomFetchAllTransactions.hide() - } - } - - private fun hideTopFetchAllTransactionsView() { - presenter.doNotShowStrikingFetchAllTransactionsViewAnymore(accountsForWhichNotAllTransactionsHaveBeenFetched) - - setFetchAllTransactionsView() - } - - private fun showFetchAllTransactionsInfo(btnShowFetchAllTransactionsInfo: ImageButton) { - activity?.let { activity -> - val account = presenter.selectedAccountsForWhichNotAllTransactionsHaveBeenFetched.first() - - val dateOfFirstRetrievedTransaction = account.retrievedTransactionsFromOn?.let { presenter.formatToMediumDate(it) } ?: "" - val info = activity.getString(R.string.popup_fetch_all_transactions_info, dateOfFirstRetrievedTransaction, - account.countDaysForWhichTransactionsAreKept, presenter.formatToMediumDate(presenter.getDayOfFirstTransactionStoredOnBankServer(account))) - - InfoPopupWindow(activity, info).show(btnShowFetchAllTransactionsInfo, Gravity.BOTTOM) - } - } - - - private fun fetchTransactions() { - presenter.fetchTransactionsOfSelectedAccounts() - } - - private fun fetchAllTransactions() { - presenter.fetchAllTransactionsOfSelectedAccounts() - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/listener/ItemSelectedListener.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/listener/ItemSelectedListener.kt deleted file mode 100644 index f61d348f..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/listener/ItemSelectedListener.kt +++ /dev/null @@ -1,15 +0,0 @@ -package net.dankito.banking.ui.android.listener - -import android.view.View -import android.widget.AdapterView - - -open class ItemSelectedListener(val itemSelected: (position: Int) -> Unit) : AdapterView.OnItemSelectedListener { - - override fun onNothingSelected(parent: AdapterView<*>?) { } - - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - itemSelected(position) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/listener/ListItemSelectedListener.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/listener/ListItemSelectedListener.kt deleted file mode 100644 index 60db4616..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/listener/ListItemSelectedListener.kt +++ /dev/null @@ -1,16 +0,0 @@ -package net.dankito.banking.ui.android.listener - -import android.view.View -import android.widget.AdapterView -import net.dankito.utils.android.ui.adapter.ListAdapter - - -open class ListItemSelectedListener(val adapter: ListAdapter, val itemSelected: (item: T) -> Unit) : AdapterView.OnItemSelectedListener { - - override fun onNothingSelected(parent: AdapterView<*>?) { } - - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - itemSelected(adapter.getItem(position)) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/security/CryptographyManager.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/security/CryptographyManager.kt deleted file mode 100644 index 4b71cd5e..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/security/CryptographyManager.kt +++ /dev/null @@ -1,181 +0,0 @@ -package net.dankito.banking.ui.android.security - -import android.os.Build -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyProperties -import androidx.annotation.RequiresApi -import java.security.KeyStore -import java.security.SecureRandom -import java.security.Security -import javax.crypto.Cipher -import javax.crypto.KeyGenerator -import javax.crypto.SecretKey -import javax.crypto.SecretKeyFactory -import javax.crypto.spec.GCMParameterSpec -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.PBEKeySpec -import javax.crypto.spec.SecretKeySpec - - -open class CryptographyManager { - - companion object { - - const val AndroidKeyStore = "AndroidKeyStore" - - private const val KeySize: Int = 256 - private const val IterationCount = 4096 - private const val PbeCipher = "AES/GCM/NoPadding" - private const val EncryptionBlockMode = KeyProperties.BLOCK_MODE_GCM - private const val EncryptionPadding = KeyProperties.ENCRYPTION_PADDING_NONE - private const val EncryptionAlgorithm = KeyProperties.KEY_ALGORITHM_AES - - private const val CipherTransformation = "$EncryptionAlgorithm/$EncryptionBlockMode/$EncryptionPadding" - - } - - - @RequiresApi(Build.VERSION_CODES.M) - open fun getInitializedCipherForEncryption(keyName: String): Cipher { - return getInitializedCipher(keyName, Cipher.ENCRYPT_MODE) - } - - @RequiresApi(Build.VERSION_CODES.M) - open fun getInitializedCipherForDecryption(keyName: String, initializationVector: ByteArray): Cipher { - return getInitializedCipher(keyName, Cipher.DECRYPT_MODE, initializationVector) - } - - @RequiresApi(Build.VERSION_CODES.M) - protected open fun getInitializedCipher(keyName: String, cipherMode: Int, initializationVector: ByteArray? = null): Cipher { - val cipher = Cipher.getInstance(CipherTransformation) - val secretKey = getOrCreateSecretKey(keyName) - - cipher.init(cipherMode, secretKey, initializationVector?.let { GCMParameterSpec(128, initializationVector) }) - - return cipher - } - - @RequiresApi(Build.VERSION_CODES.M) - protected open fun getOrCreateSecretKey(keyName: String): SecretKey { - val keyStore = KeyStore.getInstance(AndroidKeyStore) - keyStore.load(null) - keyStore.getKey(keyName, null)?.let { return it as SecretKey } - - val paramsBuilder = KeyGenParameterSpec.Builder(keyName, - KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) - paramsBuilder.apply { - setBlockModes(EncryptionBlockMode) - setEncryptionPaddings(EncryptionPadding) - setKeySize(KeySize) - setUserAuthenticationRequired(true) - } - - val keyGenParams = paramsBuilder.build() - val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, - AndroidKeyStore) - keyGenerator.init(keyGenParams) - return keyGenerator.generateKey() - } - - - open fun encryptData(plaintext: CharArray, cipher: Cipher): ByteArray { - return cipher.doFinal(mapToBytes(plaintext)) - } - - open fun decryptData(cipherText: ByteArray, cipher: Cipher): CharArray { - val plainTextBytes = cipher.doFinal(cipherText) - return mapToChars(plainTextBytes) - } - - - open fun encryptDataWithPbe(plaintext: CharArray, password: String, salt: ByteArray): Pair { - val secret: SecretKey = generatePbeSecretKey(password, salt) - - val cipher: Cipher = Cipher.getInstance(PbeCipher) - cipher.init(Cipher.ENCRYPT_MODE, secret) - val initializationVector = cipher.iv - - return Pair(cipher.doFinal(mapToBytes(plaintext)), initializationVector) - } - - open fun decryptDataWithPbe(cipherText: ByteArray, password: String, initializationVector: ByteArray, salt: ByteArray): CharArray { - val secret: SecretKey = generatePbeSecretKey(password, salt) - val cipher = Cipher.getInstance(PbeCipher) - cipher.init(Cipher.DECRYPT_MODE, secret, IvParameterSpec(initializationVector)) - - val plainTextBytes = cipher.doFinal(cipherText) - return mapToChars(plainTextBytes) - } - - protected open fun generatePbeSecretKey(userPassword: String, salt: ByteArray): SecretKey { - // Initialize PBE with password - val factory = SecretKeyFactory.getInstance(findBestPbeAlgorithm()!!) - val spec = PBEKeySpec(userPassword.toCharArray(), salt, IterationCount, KeySize) - val key = factory.generateSecret(spec) - - return SecretKeySpec(key.encoded, "AES") - } - - open fun generateRandomBytes(countBytes: Int): ByteArray { - return ByteArray(countBytes).apply { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - SecureRandom.getInstanceStrong().nextBytes(this) - } else { - SecureRandom().nextBytes(this) - } - } - } - - - protected open fun mapToBytes(chars: CharArray): ByteArray { - return chars.map { it.toByte() }.toByteArray() - } - - protected open fun mapToChars(plainTextBytes: ByteArray): CharArray { - return plainTextBytes.map { mapToChar(it) }.toCharArray() - } - - protected open fun mapToChar(byte: Byte): Char { - // Byte is signed but Char isn't -> convert negative byte values to positive ones - if (byte.toInt() < 0) { - return (256 + byte.toInt()).toChar() - } - else { - return byte.toChar() - } - } - - - open fun findBestPbeAlgorithm(): String? { - return findBestMatchingAlgorithm(SecurityProviderServiceType.SecretKeyFactory, "PBKDF2","PBKDF2WithHmacSHA256") - ?: findBestMatchingAlgorithm(SecurityProviderServiceType.SecretKeyFactory, "PBE") - } - - open fun findBestMatchingAlgorithm(type: SecurityProviderServiceType, nameStartsWith: String, vararg preferredAlgorithms: String): String? { - val supportedAlgorithms = listServiceTypeAlgorithmsWithName(type, nameStartsWith) - - val bestMatchingAlgorithm = preferredAlgorithms.firstOrNull { supportedAlgorithms.contains(it) } - ?: supportedAlgorithms.maxBy { it.length } - - return bestMatchingAlgorithm - } - - open fun listServiceTypeAlgorithmsWithName(type: SecurityProviderServiceType, nameStartsWith: String): List { - return listServiceTypeAlgorithms(type) - .filter { it.startsWith(nameStartsWith, true) } - } - - open fun listServiceTypeAlgorithms(type: SecurityProviderServiceType): List { - val algorithms = mutableListOf() - - Security.getProviders().forEach { provider -> - algorithms.addAll(provider.services - .filter { it.type == type.type } - .map { it.algorithm } - ) - } - - return algorithms - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/security/SecurityProviderServiceType.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/security/SecurityProviderServiceType.kt deleted file mode 100644 index 34c75a7d..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/security/SecurityProviderServiceType.kt +++ /dev/null @@ -1,44 +0,0 @@ -package net.dankito.banking.ui.android.security - - -enum class SecurityProviderServiceType(val type: String) { - - AlgorithmParameterGenerator("AlgorithmParameterGenerator"), - - AlgorithmParameters("AlgorithmParameters"), - - CertPathBuilder("CertPathBuilder"), - - CertPathValidator("CertPathValidator"), - - CertStore("CertStore"), - - CertificateFactory("CertificateFactory"), - - Cipher("Cipher"), - - KeyAgreement("KeyAgreement"), - - KeyFactory("KeyFactory"), - - KeyGenerator("KeyGenerator"), - - KeyManagerFactory("KeyManagerFactory"), - - KeyPairGenerator("KeyPairGenerator"), - - KeyStore("KeyStore"), - - Mac("Mac"), - - MessageDigest("MessageDigest"), - - SSLContext("SSLContext"), - - SecretKeyFactory("SecretKeyFactory"), - - SecureRandom("SecureRandom"), - - Signature("Signature"), - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/util/CurrentActivityTracker.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/util/CurrentActivityTracker.kt deleted file mode 100644 index 916a28d6..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/util/CurrentActivityTracker.kt +++ /dev/null @@ -1,53 +0,0 @@ -package net.dankito.banking.ui.android.util - -import net.dankito.banking.ui.android.activities.BaseActivity -import java.util.* -import java.util.concurrent.CopyOnWriteArrayList -import kotlin.concurrent.schedule - - -open class CurrentActivityTracker { - - protected val nextActivitySetListeners = CopyOnWriteArrayList<(BaseActivity) -> Unit>() - - - var currentActivity: BaseActivity? = null - set(value) { - field = value // TODO: check field != value - - if (value != null) { - callAndClearNextActivitySetListeners(value) - } - } - - - open fun currentOrNextActivity(activity: (BaseActivity) -> Unit) { - currentActivity?.let { - activity(it) - } - ?: addNextActivitySetListener { - activity(it) - } - } - - open fun addNextActivitySetListener(listener: (BaseActivity) -> Unit) { - synchronized(nextActivitySetListeners) { - nextActivitySetListeners.add(listener) - } - } - - protected open fun callAndClearNextActivitySetListeners(activity: BaseActivity) { - synchronized(nextActivitySetListeners) { - val listenersCopy = ArrayList(nextActivitySetListeners) - - nextActivitySetListeners.clear() - - Timer().schedule(500) { // wait some time till activity is initialized - activity.runOnUiThread { - listenersCopy.forEach { it(activity) } - } - } - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/util/StandardAutocompleteCallback.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/util/StandardAutocompleteCallback.kt deleted file mode 100644 index 99a38a69..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/util/StandardAutocompleteCallback.kt +++ /dev/null @@ -1,20 +0,0 @@ -package net.dankito.banking.ui.android.util - -import android.text.Editable -import com.otaliastudios.autocomplete.AutocompleteCallback - - -open class StandardAutocompleteCallback( - protected val onPopupVisibilityChanged: ((shown: Boolean) -> Unit)? = null, - protected val onPopupItemClicked: ((editable: Editable?, item: T) -> Boolean)? = null -) : AutocompleteCallback { - - override fun onPopupItemClicked(editable: Editable?, item: T): Boolean { - return onPopupItemClicked?.invoke(editable, item) ?: false - } - - override fun onPopupVisibilityChanged(shown: Boolean) { - onPopupVisibilityChanged?.invoke(shown) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/util/StandardTextWatcher.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/util/StandardTextWatcher.kt deleted file mode 100644 index 83c69886..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/util/StandardTextWatcher.kt +++ /dev/null @@ -1,27 +0,0 @@ -package net.dankito.banking.ui.android.util - -import android.text.Editable -import android.text.TextWatcher - - -open class StandardTextWatcher( - protected val beforeTextChanged: ((string: CharSequence?, start: Int, count: Int, after: Int) -> Unit)? = null, - protected val onFullParameterizedTextChanged: ((string: CharSequence?, start: Int, before: Int, count: Int) -> Unit)? = null, - protected val afterTextChanged: ((editable: Editable?) -> Unit)? = null, - protected val onTextChanged: ((string: CharSequence) -> Unit)? = null -) : TextWatcher { - - override fun beforeTextChanged(string: CharSequence?, start: Int, count: Int, after: Int) { - beforeTextChanged?.invoke(string, start, count, after) - } - - override fun onTextChanged(string: CharSequence?, start: Int, before: Int, count: Int) { - onTextChanged?.invoke(string ?: "") // can string parameter ever be null? - onFullParameterizedTextChanged?.invoke(string, start, before, count) - } - - override fun afterTextChanged(editable: Editable?) { - afterTextChanged?.invoke(editable) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/AccountDrawerItem.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/AccountDrawerItem.kt deleted file mode 100644 index 2fb24a08..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/AccountDrawerItem.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.dankito.banking.ui.android.views - - -class AccountDrawerItem : SecondaryIconDrawerItem() { - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/BankCredentialsPasswordView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/BankCredentialsPasswordView.kt deleted file mode 100644 index b394ab05..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/BankCredentialsPasswordView.kt +++ /dev/null @@ -1,41 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.LinearLayout -import com.google.android.material.textfield.TextInputEditText -import com.google.android.material.textfield.TextInputLayout -import kotlinx.android.synthetic.main.view_bank_credentials_password.view.* -import kotlinx.android.synthetic.main.view_form_edit_text.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.extensions.textString - - -open class BankCredentialsPasswordView @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr) { - - - init { - setupUi(context, attrs) - } - - private fun setupUi(context: Context, attrs: AttributeSet?) { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - inflater.inflate(R.layout.view_bank_credentials_password, this, true) - } - - - open var password: String - get() = edtxtPassword.text - set(value) { - edtxtPassword.text = value - } - - open val passwordBox: TextInputEditText - get() = textInputEditText - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/BiometricAuthenticationButton.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/BiometricAuthenticationButton.kt deleted file mode 100644 index d06e27f9..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/BiometricAuthenticationButton.kt +++ /dev/null @@ -1,57 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.LinearLayout -import kotlinx.android.synthetic.main.view_biometric_authentication_button.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.authentication.IBiometricAuthenticationService -import net.dankito.banking.ui.android.di.BankingComponent -import javax.inject.Inject - - -open class BiometricAuthenticationButton @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr) { - - @Inject - protected lateinit var biometricAuthenticationService: IBiometricAuthenticationService - - - open var authenticationSuccessful: (() -> Unit)? = null - - open var customButtonClickHandler: (() -> Unit)? = null - - - init { - BankingComponent.component.inject(this) - - setupUi(context) - } - - private fun setupUi(context: Context) { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - val rootView = inflater.inflate(R.layout.view_biometric_authentication_button, this, true) - - rootView.apply { - btnStartBiometricAuthentication.setOnClickListener { - customButtonClickHandler?.invoke() ?: doBiometricAuthenticationAndLogIn() - } - } - } - - protected open fun doBiometricAuthenticationAndLogIn() { - biometricAuthenticationService.authenticate { result -> - if (result.successful) { - authenticationSuccessful?.invoke() - } - } - } - - - open fun showBiometricPrompt() { - btnStartBiometricAuthentication.performClick() - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeStripeView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeStripeView.kt deleted file mode 100644 index a84b3fe1..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeStripeView.kt +++ /dev/null @@ -1,60 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.content.Context -import android.graphics.Color -import android.util.AttributeSet -import android.view.View -import net.dankito.banking.ui.util.Bit - - -open class ChipTanFlickerCodeStripeView @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : View(context, attrs, defStyleAttr) { - - - companion object { - val White = Color.WHITE - val Black = Color.BLACK - } - - - protected var currentColor = 0 - - - init { - drawWhite() - } - - - open fun setStripeVisibility(stripe: Bit) { - setStripeVisibility(stripe.isHigh) - } - - open fun setStripeVisibility(showStripe: Boolean) { - if (showStripe) { - drawWhite() - } - else { - drawBlack() - } - } - - open fun drawWhite() { - drawInColor(White) - } - - open fun drawBlack() { - drawInColor(Black) - } - - open fun drawInColor(color: Int) { - if (color != currentColor) { - setBackgroundColor(color) - - currentColor = color - - invalidate() - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeView.kt deleted file mode 100644 index 1c580aec..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/ChipTanFlickerCodeView.kt +++ /dev/null @@ -1,242 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.widget.ImageButton -import android.widget.LinearLayout -import kotlinx.android.synthetic.main.view_flicker_code.view.* -import kotlinx.android.synthetic.main.view_tan_image_size_controls.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.model.tan.FlickerCode -import net.dankito.banking.ui.util.FlickerCodeAnimator -import net.dankito.banking.ui.model.settings.ITanView -import net.dankito.banking.ui.model.settings.TanMethodSettings -import net.dankito.banking.ui.util.Step - - -open class ChipTanFlickerCodeView @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr), ITanView { - - companion object { - const val FrequencyStepSize = 2 - const val MinFrequency = 2 - const val MaxFrequency = 40 - const val DefaultFrequency = 30 - - const val StripesHeightStepSize = 7 - const val StripesWidthStepSize = 2 - const val SpaceBetweenStripesStepSize = 1 - } - - - protected lateinit var stripe1: ChipTanFlickerCodeStripeView - protected lateinit var stripe2: ChipTanFlickerCodeStripeView - protected lateinit var stripe3: ChipTanFlickerCodeStripeView - protected lateinit var stripe4: ChipTanFlickerCodeStripeView - protected lateinit var stripe5: ChipTanFlickerCodeStripeView - - protected lateinit var allStripes: List - - protected lateinit var tanGeneratorLeftMarker: View - protected lateinit var tanGeneratorRightMarker: View - - protected lateinit var btnPauseFlickerCode: ImageButton - - protected val animator = FlickerCodeAnimator() - - - protected var stripesHeight = 360 - protected var stripesWidth = 120 - protected var spaceBetweenStripes = 30 - - protected var currentFrequency = DefaultFrequency - - protected var isFlickerCodePaused = false - - - override var didTanMethodSettingsChange: Boolean = false - protected set - - override var tanMethodSettings: TanMethodSettings? = null - protected set - - - init { - setupUi(context) - } - - private fun setupUi(context: Context) { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - val rootView = inflater.inflate(R.layout.view_flicker_code, this, true) - - rootView.btnDecreaseSize.setOnClickListener { decreaseSize() } - rootView.btnIncreaseSize.setOnClickListener { increaseSize() } - - rootView.btnDecreaseSpeed.setOnClickListener { decreaseFrequency() } - rootView.btnIncreaseSpeed.setOnClickListener { increaseFrequency() } - - btnPauseFlickerCode = rootView.btnPauseFlickerCode - btnPauseFlickerCode.setOnClickListener { togglePauseFlickerCode() } - - stripe1 = rootView.findViewById(R.id.flickerCodeStripe1) - stripe2 = rootView.findViewById(R.id.flickerCodeStripe2) - stripe3 = rootView.findViewById(R.id.flickerCodeStripe3) - stripe4 = rootView.findViewById(R.id.flickerCodeStripe4) - stripe5 = rootView.findViewById(R.id.flickerCodeStripe5) - - allStripes = listOf(stripe1, stripe2, stripe3, stripe4, stripe5) - - stripesHeight = stripe1.layoutParams.height - stripesWidth = stripe1.layoutParams.width - (stripe1.layoutParams as? MarginLayoutParams)?.let { spaceBetweenStripes = it.rightMargin } - - tanGeneratorLeftMarker = rootView.findViewById(R.id.tanGeneratorLeftMarker) - tanGeneratorRightMarker = rootView.findViewById(R.id.tanGeneratorRightMarker) - - setMarkerPositionAfterStripesLayoutSet() - - tanMethodSettings?.let { - setSize(it.width, it.height, it.space) - setFrequency(it.frequency) - } - } - - - override fun onDetachedFromWindow() { - animator.stop() - - super.onDetachedFromWindow() - } - - - - open fun decreaseSize() { - setSize( - stripesWidth - StripesWidthStepSize, - stripesHeight - StripesHeightStepSize, - spaceBetweenStripes - SpaceBetweenStripesStepSize - ) - } - - open fun increaseSize() { - setSize( - stripesWidth + StripesWidthStepSize, - stripesHeight + StripesHeightStepSize, - spaceBetweenStripes + SpaceBetweenStripesStepSize - ) - } - - open fun setSize(width: Int, height: Int, spaceBetweenStripes: Int) { - this.stripesWidth = width - this.stripesHeight = height - this.spaceBetweenStripes = spaceBetweenStripes - - applySize() - } - - protected open fun applySize() { - allStripes.forEach { stripe -> - val params = stripe.layoutParams - params.height = stripesHeight - params.width = stripesWidth - - (params as? MarginLayoutParams)?.let { marginParams -> - if (marginParams.rightMargin > 0) { // don't set a margin right on fifth stripe - marginParams.rightMargin = spaceBetweenStripes - } - } - - stripe.layoutParams = params - } - - requestLayout() - - setMarkerPositionAfterStripesLayoutSet() - - tanMethodSettingsChanged() - } - - protected open fun setMarkerPositionAfterStripesLayoutSet() { - postDelayed({ setMarkerPosition() }, 10L) // we need to wait till layout for stripes is applied before we can set marker positions correctly - } - - protected open fun setMarkerPosition() { - tanGeneratorLeftMarker.x = stripe1.x + (stripe1.width - tanGeneratorLeftMarker.layoutParams.width) / 2 - - tanGeneratorRightMarker.x = stripe5.x + (stripe5.width - tanGeneratorRightMarker.layoutParams.width) / 2 - } - - - open fun decreaseFrequency() { - if (currentFrequency - FrequencyStepSize >= MinFrequency) { - setFrequency(currentFrequency - FrequencyStepSize) - } - } - - open fun increaseFrequency() { - if (currentFrequency + FrequencyStepSize <= MaxFrequency) { - setFrequency(currentFrequency + FrequencyStepSize) - } - } - - open fun setFrequency(frequency: Int) { - currentFrequency = frequency - - animator.setFrequency(frequency) - - tanMethodSettingsChanged() - } - - protected open fun tanMethodSettingsChanged() { - tanMethodSettings = TanMethodSettings(stripesWidth, stripesHeight, spaceBetweenStripes, currentFrequency) - - didTanMethodSettingsChange = true // we don't check if settings really changed, it's not that important - } - - - open fun togglePauseFlickerCode() { - if (isFlickerCodePaused == false) { - animator.pause() - btnPauseFlickerCode.setImageResource(R.drawable.ic_baseline_play_arrow_24) - } - else { - animator.resume() - btnPauseFlickerCode.setImageResource(R.drawable.ic_baseline_pause_24) - } - - isFlickerCodePaused = !!! isFlickerCodePaused - } - - - open fun setCode(flickerCode: FlickerCode, tanMethodSettings: TanMethodSettings?) { - animator.stop() - - tanMethodSettings?.let { - setSize(it.width, it.height, it.space) - setFrequency(it.frequency) - } - ?: run { - setFrequency(DefaultFrequency) - } - - this.tanMethodSettings = tanMethodSettings - this.didTanMethodSettingsChange = false - - animator.animateFlickerCode(flickerCode) { step -> - showStepOnUiThread(step) - } - } - - protected open fun showStepOnUiThread(step: Step) { - - stripe1.setStripeVisibility(step.bit1) - stripe2.setStripeVisibility(step.bit2) - stripe3.setStripeVisibility(step.bit3) - stripe4.setStripeVisibility(step.bit4) - stripe5.setStripeVisibility(step.bit5) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/CollapsibleTextView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/CollapsibleTextView.kt deleted file mode 100644 index 47f988cb..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/CollapsibleTextView.kt +++ /dev/null @@ -1,73 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.widget.ScrollView -import kotlinx.android.synthetic.main.view_collapsible_text.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.util.StandardTextWatcher -import net.dankito.utils.android.extensions.asActivity -import net.dankito.utils.android.extensions.setVisibility -import java.util.* -import kotlin.concurrent.schedule - - -open class CollapsibleTextView @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : ScrollView(context, attrs, defStyleAttr) { - - companion object { - - const val CountDisplayedLinesWhenCollapsed = 3 - - } - - protected var isCollapsed = true - - - init { - setupUi(context) - } - - private fun setupUi(context: Context) { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - val rootView = inflater.inflate(R.layout.view_collapsible_text, this, true) - - rootView.apply { - txtvwCollapsibleText.setOnClickListener { toggleIsCollapsed() } - btnExpandCollapseTextView.setOnClickListener { toggleIsCollapsed() } - - txtvwCollapsibleText.addTextChangedListener(StandardTextWatcher { - checkIfExpandCollapseButtonShouldBeDisplayed(context) - }) - } - } - - - protected open fun toggleIsCollapsed() { - if (isCollapsed) { - txtvwCollapsibleText.maxLines = Int.MAX_VALUE - btnExpandCollapseTextView.setImageResource(R.drawable.ic_baseline_expand_less_24) - - isCollapsed = false - } - else { - txtvwCollapsibleText.maxLines = CountDisplayedLinesWhenCollapsed - btnExpandCollapseTextView.setImageResource(R.drawable.ic_baseline_expand_more_24) - - isCollapsed = true - } - } - - protected open fun checkIfExpandCollapseButtonShouldBeDisplayed(context: Context) { - Timer().schedule(500) { // wait some time till txtvwCollapsibleText is layout and lineCount is set - context.asActivity()?.runOnUiThread { - val showExpandButton = isCollapsed == false || txtvwCollapsibleText.lineCount > CountDisplayedLinesWhenCollapsed - btnExpandCollapseTextView.setVisibility(showExpandButton) - } - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt deleted file mode 100644 index fcf35634..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt +++ /dev/null @@ -1,218 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.graphics.drawable.Drawable -import android.view.View -import android.widget.TextView -import androidx.annotation.ColorRes -import androidx.annotation.DrawableRes -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.content.res.AppCompatResources -import androidx.core.graphics.drawable.DrawableCompat -import androidx.core.view.GravityCompat -import androidx.drawerlayout.widget.DrawerLayout -import com.mikepenz.materialdrawer.model.DividerDrawerItem -import com.mikepenz.materialdrawer.model.PrimaryDrawerItem -import com.mikepenz.materialdrawer.model.SecondaryDrawerItem -import com.mikepenz.materialdrawer.model.SectionDrawerItem -import com.mikepenz.materialdrawer.model.interfaces.* -import com.mikepenz.materialdrawer.util.addItems -import com.mikepenz.materialdrawer.util.addItemsAtPosition -import com.mikepenz.materialdrawer.util.getDrawerItem -import com.mikepenz.materialdrawer.util.removeItemByPosition -import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.dialogs.settings.BankSettingsDialog -import net.dankito.banking.ui.android.dialogs.settings.SettingsDialog -import net.dankito.banking.ui.android.extensions.toDrawable -import net.dankito.banking.ui.model.TypedBankData -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.utils.android.extensions.getColorFromResource -import org.slf4j.LoggerFactory - - -open class DrawerView( - protected val activity: AppCompatActivity, - protected val slider: MaterialDrawerSliderView, - protected val presenter: BankingPresenter -) { - - companion object { - private const val BankLevel = 2 - - private const val AccountLevel = 7 - - private const val AccountsSectionHeaderId = 1000L - private const val AllAccountsId = 1001L - private const val AddAccountId = 1002L - - private const val CountDefaultAccountItems = 5 - private const val CountDefaultAccountItemsAtTop = 3 - - - private val log = LoggerFactory.getLogger(DrawerView::class.java) - } - - - init { - setupDrawerView() - } - - - protected open fun setupDrawerView() { - slider.headerView = activity.layoutInflater.inflate(R.layout.nav_header_main, null) - - showAppVersion(slider.headerView) - - setDefaultDrawerItems() - - presenter.addBanksChangedListener { - activity.runOnUiThread { updateDrawerItems() } - } - - presenter.addSelectedAccountsChangedListener { - activity.runOnUiThread { updateDrawerItems() } - } - - updateDrawerItems() - } - - private fun setDefaultDrawerItems() { - slider.apply { - addItems( - SectionDrawerItem() - .withName(R.string.accounts) - .withIdentifier(AccountsSectionHeaderId) - .withDivider(false) - , - - PrimaryDrawerItem() - .withName(R.string.drawer_menu_all_bank_accounts_title) - .withIdentifier(AllAccountsId) - .withLevel(BankLevel) - .withSelected(true) - .withIcon(getVectorDrawable(R.drawable.ic_accounts, R.color.primaryTextColor_Dark)) - .withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAllAccounts() } } - , - - PrimaryDrawerItem() - .withName(R.string.add_account) - .withIdentifier(AddAccountId) - .withLevel(BankLevel) - .withIcon(getVectorDrawable(R.drawable.ic_baseline_add_24, R.color.primaryTextColor_Dark)) - .withSelectable(false) - .withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.showAddAccountDialog() } } - , - - DividerDrawerItem() - .withSelectable(false) - , - - PrimaryDrawerItem() - .withName(R.string.settings) - .withIcon(getVectorDrawable(R.drawable.ic_baseline_settings_24, R.color.primaryTextColor_Dark)) - .withSelectable(false) - .withOnDrawerItemClickListener { _, _, _ -> itemClicked { SettingsDialog().show(activity) } } - - ) - } - } - - private fun updateDrawerItems() { - // removes previously shown accounts; index 1 = 'Accounts header', 1 = 'All accounts', index 2 = 'Add account', don't remove these - while (slider.itemAdapter.adapterItems.size > CountDefaultAccountItems) { - slider.removeItemByPosition(CountDefaultAccountItemsAtTop) - } - - val accountItems = createAccountsDrawerItems() - - slider.addItemsAtPosition(CountDefaultAccountItemsAtTop, *accountItems.toTypedArray()) - - slider.getDrawerItem(AllAccountsId)?.let { allAccountsItem -> - if (presenter.areAllAccountSelected) slider.selectExtension.select(allAccountsItem, false) - else slider.selectExtension.deselectByItems(setOf(allAccountsItem)) - } - } - - private fun createAccountsDrawerItems(): List> { - return presenter.allBanksSortedByDisplayIndex.map { account -> - val accountItem = createAccountDrawerItem(account) - - val accountsItems = createBankAccountsDrawerItems(account).toMutableList() - accountsItems.add(0, accountItem) - - return@map accountsItems - }.flatten() - } - - private fun createAccountDrawerItem(bank: TypedBankData): IDrawerItem<*> { - - val accountItem = AccountDrawerItem() - .withName(bank.displayName) - .withLevel(BankLevel) - .withSecondaryIcon(getVectorDrawable(R.drawable.ic_baseline_settings_24, R.color.primaryTextColor_Dark)) - .withOnSecondaryIconClickedListener { closeDrawerAndEditAccount(bank) } - .withIcon(bank.iconData?.toDrawable(activity.resources)) - .withSelected(presenter.isSingleSelectedBank(bank)) - .withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedBank(bank) } } - - if (bank.iconData == null) { - accountItem.withIcon(getVectorDrawable(R.drawable.ic_accounts, R.color.primaryTextColor_Dark)) - } - - - return accountItem - } - - private fun createBankAccountsDrawerItems(bank: TypedBankData): List> { - return bank.visibleAccountsSorted.map { account -> - SecondaryDrawerItem() - .withName(account.displayName) - .withLevel(AccountLevel) - .withSelected(presenter.isSingleSelectedAccount(account)) - .withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAccount(account) } } - } - } - - private fun itemClicked(action: () -> Unit): Boolean { - action() - - return false - } - - private fun closeDrawerAndEditAccount(bank: TypedBankData) { - closeDrawer() - - editAccount(bank) - } - - private fun editAccount(bank: TypedBankData) { - BankSettingsDialog().show(bank, activity) - } - - private fun showAppVersion(navigationHeaderView: View?) { - try { - val packageInfo = activity.packageManager.getPackageInfo(activity.packageName, 0) - val version = packageInfo.versionName - (navigationHeaderView?.findViewById(R.id.txtAppVersion) as? TextView)?.text = version - } catch (e: Exception) { - log.error("Could not read application version") - } - } - - private fun closeDrawer() { - val drawerLayout = activity.findViewById(R.id.drawer_layout) - drawerLayout.closeDrawer(GravityCompat.START) - } - - - private fun getVectorDrawable(@DrawableRes drawableResId: Int, @ColorRes tintColorResId: Int? = null): Drawable? { - val drawable = AppCompatResources.getDrawable(activity, drawableResId) - - if (tintColorResId != null && drawable != null) { - DrawableCompat.setTint(drawable, activity.getColorFromResource(tintColorResId)) - } - - return drawable - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FloatingActionMenuButton.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FloatingActionMenuButton.kt deleted file mode 100644 index a80f5a86..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FloatingActionMenuButton.kt +++ /dev/null @@ -1,78 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.os.Bundle -import android.view.MotionEvent -import com.github.clans.fab.FloatingActionMenu -import net.dankito.utils.android.extensions.isTouchInsideView - - -open class FloatingActionMenuButton(protected val floatingActionMenu: FloatingActionMenu) { - - companion object { - private const val IS_OPENED_EXTRA_NAME = "IS_OPENED" - } - - - private var isClosingMenu = false - - - init { - setup() - } - - - protected open fun setup() { - floatingActionMenu.setClosedOnTouchOutside(true) - floatingActionMenu.setOnMenuToggleListener { isClosingMenu = false } - } - - - protected open fun executeAndCloseMenu(action: () -> Unit) { - action() // first execute action and then close menu as when action sets menu items visibility closeMenu() would otherwise overwrite this value - closeMenu() - } - - protected open fun closeMenu() { - isClosingMenu = true // as closing is animated it takes till animation end till floatingActionMenu.isOpened is set to true - floatingActionMenu.close(true) - } - - - open fun handlesBackButtonPress(): Boolean { - if(floatingActionMenu.isOpened) { - closeMenu() - return true - } - - return false - } - - - open fun handlesTouch(event: MotionEvent): Boolean { - if(floatingActionMenu.isOpened) { // if menu is opened and user clicked somewhere else in the view, close menu - if(floatingActionMenu.isTouchInsideView(event) == false) { - closeMenu() - - return true - } - } - - return false - } - - - open fun saveInstanceState(outState: Bundle?) { - outState?.let { - outState.putBoolean(IS_OPENED_EXTRA_NAME, floatingActionMenu.isOpened && isClosingMenu == false) - } - } - - open fun restoreInstanceState(savedInstanceState: Bundle?) { - savedInstanceState?.let { - if(savedInstanceState.getBoolean(IS_OPENED_EXTRA_NAME, false)) { - floatingActionMenu.open(false) - } - } - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormEditText.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormEditText.kt deleted file mode 100644 index 9431dd4b..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormEditText.kt +++ /dev/null @@ -1,62 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.LinearLayout -import com.google.android.material.textfield.TextInputLayout -import kotlinx.android.synthetic.main.view_form_edit_text.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.extensions.textString - - -open class FormEditText @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr) { - - - init { - setupUi(context, attrs) - } - - private fun setupUi(context: Context, attrs: AttributeSet?) { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - val rootView = inflater.inflate(R.layout.view_form_edit_text, this, true) - - rootView.apply { - - context.theme.obtainStyledAttributes( - attrs, - R.styleable.FormEditText, - 0, 0).apply { - - try { - textInputLayout.hint = getString(R.styleable.FormEditText_android_hint) - if (getBoolean(R.styleable.FormEditText_showPasswordToggle, false)) { - textInputLayout.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE - } - - textInputEditText.setText(getString(R.styleable.FormEditText_android_text)) - textInputEditText.inputType = getInt(R.styleable.FormEditText_android_inputType, EditorInfo.TYPE_TEXT_VARIATION_NORMAL) - textInputEditText.setSelectAllOnFocus(getBoolean(R.styleable.FormEditText_android_selectAllOnFocus, false)) - } finally { - recycle() - } - } - } - } - - - open var text: String - get() = textInputEditText.textString - set(value) = textInputEditText.setText(value) - - open val chars: CharArray - get() = actualEditText.text.toList().toCharArray() - - open val actualEditText: EditText - get() = textInputEditText - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormLabelledValue.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormLabelledValue.kt deleted file mode 100644 index c50568e5..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormLabelledValue.kt +++ /dev/null @@ -1,76 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.widget.LinearLayout -import android.widget.TextView -import kotlinx.android.synthetic.main.view_form_labelled_value.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.extensions.setTextColorForAmount -import net.dankito.utils.android.extensions.setVisibility -import net.dankito.banking.ui.presenter.BankingPresenter -import net.dankito.utils.multiplatform.BigDecimal - - -open class FormLabelledValue @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr) { - - protected lateinit var txtLabel: TextView - - protected lateinit var txtValue: TextView - - - init { - setupUi(context, attrs) - } - - private fun setupUi(context: Context, attrs: AttributeSet?) { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - val rootView = inflater.inflate(R.layout.view_form_labelled_value, this, true) - - rootView.apply { - txtLabel = findViewById(R.id.txtLabel) - txtValue = findViewById(R.id.txtValue) - - context.theme.obtainStyledAttributes( - attrs, - R.styleable.FormLabelledValue, - 0, 0).apply { - - try { - txtLabel.text = getString(R.styleable.FormLabelledValue_android_label) - txtValue.text = getString(R.styleable.FormLabelledValue_android_value) - } finally { - recycle() - } - } - } - } - - - open var label: CharSequence - get() = txtLabel.text - set(value) { - txtLabel.text = value - } - - open var value: CharSequence - get() = txtValue.text - set(value) { - txtValue.text = value - } - - open fun setValueAndVisibilityIfValueIsSet(value: CharSequence?) { - this.setVisibility(value != null) - this.value = value ?: "" - } - - open fun showAmount(presenter: BankingPresenter, amount: BigDecimal, currencyIsoCode: String? = null) { - value = presenter.formatAmount(amount, currencyIsoCode) - txtValue.setTextColorForAmount(amount) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormSectionTitle.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormSectionTitle.kt deleted file mode 100644 index 6cc0eb3d..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormSectionTitle.kt +++ /dev/null @@ -1,50 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.LinearLayout -import androidx.annotation.StringRes -import kotlinx.android.synthetic.main.view_form_section_title.view.* -import net.dankito.banking.ui.android.R - - -open class FormSectionTitle @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr) { - - - init { - setupUi(context, attrs) - } - - private fun setupUi(context: Context, attrs: AttributeSet?) { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - val rootView = inflater.inflate(R.layout.view_form_section_title, this, true) - - rootView.apply { - - context.theme.obtainStyledAttributes( - attrs, - R.styleable.FormEditText, - 0, 0).apply { - - try { - txtvwSectionTitle.text = getString(R.styleable.FormEditText_android_text) - } finally { - recycle() - } - } - } - } - - - open var title: CharSequence - get() = txtvwSectionTitle.text - set(value) = txtvwSectionTitle.setText(value) - - open fun setTitle(@StringRes titleResId: Int) { - this.title = context.getString(titleResId) - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormSelectPeriod.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormSelectPeriod.kt deleted file mode 100644 index 1dfc37bf..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/FormSelectPeriod.kt +++ /dev/null @@ -1,102 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.content.Context -import android.os.Build -import android.util.AttributeSet -import android.view.Gravity -import android.view.LayoutInflater -import android.view.View -import android.widget.RelativeLayout -import android.widget.TextView -import net.dankito.banking.ui.android.R - - -open class FormSelectPeriod @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : RelativeLayout(context, attrs, defStyleAttr) { - - protected lateinit var txtLabel: TextView - - protected lateinit var txtValue: TextView - - - init { - setupUi(context, attrs) - } - - private fun setupUi(context: Context, attrs: AttributeSet?) { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - val rootView = inflater.inflate(R.layout.view_form_select_period, this, true) - - rootView.apply { - txtLabel = findViewById(R.id.txtLabel) - txtValue = findViewById(R.id.txtValue) - - txtLabel.gravity = Gravity.CENTER_VERTICAL - txtValue.gravity = Gravity.CENTER_VERTICAL - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - txtLabel.textAlignment = View.TEXT_ALIGNMENT_GRAVITY - txtValue.textAlignment = View.TEXT_ALIGNMENT_GRAVITY - } - - this.isEnabled = false // TODO: undo as soon as selecting periods is implemented - - context.theme.obtainStyledAttributes( - attrs, - R.styleable.FormLabelledValue, - 0, 0).apply { - - try { - txtLabel.text = getString(R.styleable.FormSelectPeriod_android_label) - - val defaultValue = Int.MIN_VALUE - val configuredPeriod = getInteger(R.styleable.FormSelectPeriod_periodInMinutes, defaultValue) - periodInMinutes = if (configuredPeriod == defaultValue) null else configuredPeriod - displaySelectedPeriod() - } finally { - recycle() - } - } - } - } - - - protected open fun displaySelectedPeriod() { - txtValue.text = getDisplayTextForSelectedPeriod() - } - - protected open fun getDisplayTextForSelectedPeriod(): String { - periodInMinutes?.let { periodInMinutes -> - if (periodInMinutes > 0) { - if (periodInMinutes < 60) { - return context.getString(R.string.minutes, periodInMinutes) - } - - return context.getString(R.string.hours, (periodInMinutes / 60)) - } - } - - return context.getString(R.string.never) - } - - - open var label: CharSequence - get() = txtLabel.text - set(value) { - txtLabel.text = value - } - - open var periodInMinutes: Int? = null - set(value) { - field = value - displaySelectedPeriod() - } - - override fun setEnabled(enabled: Boolean) { - super.setEnabled(enabled) - - alpha = if (enabled) 1f else 0.5f - } - -} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/InfoPopupWindow.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/InfoPopupWindow.kt deleted file mode 100644 index cdab7f36..00000000 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/InfoPopupWindow.kt +++ /dev/null @@ -1,38 +0,0 @@ -package net.dankito.banking.ui.android.views - -import android.app.Activity -import android.view.Gravity -import android.view.View -import android.view.ViewGroup -import android.widget.Button -import android.widget.PopupWindow -import androidx.annotation.StringRes -import kotlinx.android.synthetic.main.view_info_popup.view.* -import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.android.extensions.hideKeyboard - - -open class InfoPopupWindow(open val activity: Activity, open val info: String) { - - constructor(activity: Activity, @StringRes infoStringResId: Int) : this(activity, activity.getString(infoStringResId)) - - - open fun show(atLocationOf: View, gravity: Int = Gravity.TOP) { - activity.layoutInflater.inflate(R.layout.view_info_popup, null)?.let { contentView -> - activity.hideKeyboard(atLocationOf) - - contentView.txtInfo.text = info - - val popupWindow = PopupWindow(contentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) - - popupWindow.isFocusable = true - popupWindow.isOutsideTouchable = true - - contentView.findViewById