diff options
author | Torsten Grote <t@grobox.de> | 2020-07-30 16:40:23 -0300 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2020-07-30 16:40:23 -0300 |
commit | 8815105bf2462787885214a12af927d484226f21 (patch) | |
tree | 597076d6970e336b881d68ca6b48577b007c9730 /taler-kotlin-common | |
parent | e19ba096d57353db6b1f141da4bf170ef2d2d534 (diff) | |
download | taler-android-8815105bf2462787885214a12af927d484226f21.tar.gz taler-android-8815105bf2462787885214a12af927d484226f21.tar.bz2 taler-android-8815105bf2462787885214a12af927d484226f21.zip |
Split out common code into multiplatform Kotlin library
Diffstat (limited to 'taler-kotlin-common')
26 files changed, 275 insertions, 1079 deletions
diff --git a/taler-kotlin-common/.gitlab-ci.yml b/taler-kotlin-common/.gitlab-ci.yml index 49d3e98..c241e31 100644 --- a/taler-kotlin-common/.gitlab-ci.yml +++ b/taler-kotlin-common/.gitlab-ci.yml @@ -4,8 +4,4 @@ taler_kotlin_common_test: changes: - taler-kotlin-common/**/* - build.gradle - script: ./gradlew :taler-kotlin-common:check - artifacts: - paths: - - taler-kotlin-common/build/reports/lint-results.html - expire_in: 1 week + script: ./gradlew :taler-kotlin-common:jvmTest diff --git a/taler-kotlin-common/build.gradle b/taler-kotlin-common/build.gradle index dd083b7..129881d 100644 --- a/taler-kotlin-common/build.gradle +++ b/taler-kotlin-common/build.gradle @@ -1,72 +1,82 @@ -/* - * This file is part of GNU Taler - * (C) 2020 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - plugins { - id 'com.android.library' - id 'kotlin-android' - id 'kotlin-android-extensions' + id 'org.jetbrains.kotlin.multiplatform' id 'kotlinx-serialization' } -android { - compileSdkVersion 29 - //noinspection GradleDependency - buildToolsVersion "$build_tools_version" +group 'net.taler' +version '0.0.1' - defaultConfig { - minSdkVersion 24 - targetSdkVersion 29 - versionCode 1 - versionName "0.1" +apply plugin: 'maven-publish' - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles 'consumer-rules.pro' +kotlin { + jvm() + // This is for iPhone simulator + // Switch here to iosArm64 (or iosArm32) to build library for iPhone device + iosX64("ios") { + binaries { + framework() + } } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + linuxX64("linux") + js { + browser { + } + nodejs { + } + } + sourceSets { + def serialization_version = "0.20.0" + commonMain { + dependencies { + implementation kotlin('stdlib-common') + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + } + } + commonTest { + dependencies { + implementation kotlin('test-common') + implementation kotlin('test-annotations-common') + } + } + jvmMain { + dependencies { + implementation kotlin('stdlib-jdk8') + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" + } + } + jvmTest { + dependencies { + implementation kotlin('test') + implementation kotlin('test-junit') + } + } + jsMain { + dependencies { + implementation kotlin('stdlib-js') + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + } + } + jsTest { + dependencies { + implementation kotlin('test-js') + } + } + nativeMain { + dependsOn commonMain + dependencies { + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version" + } + } + nativeTest { + dependsOn commonTest + } + configure([targets.linux, targets.ios]) { + compilations.main.source(sourceSets.nativeMain) + compilations.test.source(sourceSets.nativeTest) } } - } -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.core:core-ktx:1.3.0' - - // Navigation - implementation "androidx.navigation:navigation-ui-ktx:$nav_version" - implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" - - // ViewModel and LiveData - def lifecycle_version = "2.2.0" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" - - // QR codes - implementation 'com.google.zxing:core:3.4.0' // needs minSdkVersion 24+ - - // JSON parsing and serialization - api "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0" - implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.2" - - lintChecks 'com.github.thirdegg:lint-rules:0.0.4-alpha' - - testImplementation 'junit:junit:4.13' - testImplementation 'org.json:json:20190722' +configurations { + compileClasspath } diff --git a/taler-kotlin-common/consumer-rules.pro b/taler-kotlin-common/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 --- a/taler-kotlin-common/consumer-rules.pro +++ /dev/null diff --git a/taler-kotlin-common/proguard-rules.pro b/taler-kotlin-common/proguard-rules.pro deleted file mode 100644 index f1b4245..0000000 --- a/taler-kotlin-common/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/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Amount.kt index 992f93b..84d10c5 100644 --- a/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt +++ b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Amount.kt @@ -16,23 +16,12 @@ package net.taler.common -import android.annotation.SuppressLint -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonMappingException -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import com.fasterxml.jackson.databind.annotation.JsonSerialize -import com.fasterxml.jackson.databind.deser.std.StdDeserializer -import com.fasterxml.jackson.databind.ser.std.StdSerializer import kotlinx.serialization.Decoder import kotlinx.serialization.Encoder import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.Serializer -import org.json.JSONObject -import java.lang.Math.floorDiv +import kotlin.math.floor import kotlin.math.pow import kotlin.math.roundToInt @@ -40,8 +29,6 @@ class AmountParserException(msg: String? = null, cause: Throwable? = null) : Exc class AmountOverflowException(msg: String? = null, cause: Throwable? = null) : Exception(msg, cause) @Serializable(with = KotlinXAmountSerializer::class) -@JsonSerialize(using = AmountSerializer::class) -@JsonDeserialize(using = AmountDeserializer::class) data class Amount( /** * name of the currency using either a three-character ISO 4217 currency code, @@ -70,29 +57,21 @@ data class Amount( private const val FRACTIONAL_BASE: Int = 100000000 // 1e8 - @Suppress("unused") - private val REGEX = Regex("""^[-_*A-Za-z0-9]{1,12}:([0-9]+)\.?([0-9]+)?$""") private val REGEX_CURRENCY = Regex("""^[-_*A-Za-z0-9]{1,12}$""") - private val MAX_VALUE = 2.0.pow(52) + val MAX_VALUE = 2.0.pow(52).toLong() private const val MAX_FRACTION_LENGTH = 8 - private const val MAX_FRACTION = 99_999_999 + const val MAX_FRACTION = 99_999_999 - @Throws(AmountParserException::class) - @SuppressLint("CheckedExceptions") fun zero(currency: String): Amount { return Amount(checkCurrency(currency), 0, 0) } - @Throws(AmountParserException::class) - @SuppressLint("CheckedExceptions") fun fromJSONString(str: String): Amount { val split = str.split(":") if (split.size != 2) throw AmountParserException("Invalid Amount Format") return fromString(split[0], split[1]) } - @Throws(AmountParserException::class) - @SuppressLint("CheckedExceptions") fun fromString(currency: String, str: String): Amount { // value val valueSplit = str.split(".") @@ -110,31 +89,23 @@ data class Amount( return Amount(checkCurrency(currency), value, fraction) } - @Throws(AmountParserException::class) - @SuppressLint("CheckedExceptions") - fun fromJsonObject(json: JSONObject): Amount { - val currency = checkCurrency(json.optString("currency")) - val value = checkValue(json.optString("value").toLongOrNull()) - val fraction = checkFraction(json.optString("fraction").toIntOrNull()) - return Amount(currency, value, fraction) - } + fun min(currency: String): Amount = Amount(currency, 0, 1) + fun max(currency: String): Amount = Amount(currency, MAX_VALUE, MAX_FRACTION) + - @Throws(AmountParserException::class) - private fun checkCurrency(currency: String): String { + internal fun checkCurrency(currency: String): String { if (!REGEX_CURRENCY.matches(currency)) throw AmountParserException("Invalid currency: $currency") return currency } - @Throws(AmountParserException::class) - private fun checkValue(value: Long?): Long { + internal fun checkValue(value: Long?): Long { if (value == null || value > MAX_VALUE) throw AmountParserException("Value $value greater than $MAX_VALUE") return value } - @Throws(AmountParserException::class) - private fun checkFraction(fraction: Int?): Int { + internal fun checkFraction(fraction: Int?): Int { if (fraction == null || fraction > MAX_FRACTION) throw AmountParserException("Fraction $fraction greater than $MAX_FRACTION") return fraction @@ -153,25 +124,23 @@ data class Amount( "$value.$fractionStr" } - @Throws(AmountOverflowException::class) operator fun plus(other: Amount): Amount { check(currency == other.currency) { "Can only subtract from same currency" } - val resultValue = value + other.value + floorDiv(fraction + other.fraction, FRACTIONAL_BASE) + val resultValue = value + other.value + floor((fraction + other.fraction).toDouble() / FRACTIONAL_BASE).toLong() if (resultValue > MAX_VALUE) throw AmountOverflowException() val resultFraction = (fraction + other.fraction) % FRACTIONAL_BASE return Amount(currency, resultValue, resultFraction) } - @Throws(AmountOverflowException::class) operator fun times(factor: Int): Amount { + // TODO consider replacing with a faster implementation if (factor == 0) return zero(currency) var result = this for (i in 1 until factor) result += this return result } - @Throws(AmountOverflowException::class) operator fun minus(other: Amount): Amount { check(currency == other.currency) { "Can only subtract from same currency" } var resultValue = value @@ -227,20 +196,3 @@ object KotlinXAmountSerializer: KSerializer<Amount> { return Amount.fromJSONString(decoder.decodeString()) } } - -class AmountSerializer : StdSerializer<Amount>(Amount::class.java) { - override fun serialize(value: Amount, gen: JsonGenerator, provider: SerializerProvider) { - gen.writeString(value.toJSONString()) - } -} - -class AmountDeserializer : StdDeserializer<Amount>(Amount::class.java) { - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Amount { - val node = p.codec.readValue(p, String::class.java) - try { - return Amount.fromJSONString(node) - } catch (e: AmountParserException) { - throw JsonMappingException(p, "Error parsing Amount", e) - } - } -} diff --git a/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Time.kt b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Time.kt new file mode 100644 index 0000000..962e004 --- /dev/null +++ b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Time.kt @@ -0,0 +1,81 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.common + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import net.taler.common.Duration.Companion.FOREVER +import kotlin.math.max + +expect fun nowMillis(): Long + +@Serializable +data class Timestamp( + @SerialName("t_ms") + val ms: Long +) : Comparable<Timestamp> { + + companion object { + const val NEVER: Long = -1 // TODO or UINT64_MAX? + fun now(): Timestamp = Timestamp(nowMillis()) + } + + /** + * Returns a copy of this [Timestamp] rounded to seconds. + */ + fun truncateSeconds(): Timestamp { + if (ms == NEVER) return Timestamp(ms) + return Timestamp((ms / 1000L) * 1000L) + } + + operator fun minus(other: Timestamp): Duration = when { + ms == NEVER -> Duration(FOREVER) + other.ms == NEVER -> throw Error("Invalid argument for timestamp comparision") + ms < other.ms -> Duration(0) + else -> Duration(ms - other.ms) + } + + operator fun minus(other: Duration): Timestamp = when { + ms == NEVER -> this + other.ms == FOREVER -> Timestamp(0) + else -> Timestamp(max(0, ms - other.ms)) + } + + override fun compareTo(other: Timestamp): Int { + return if (ms == NEVER) { + if (other.ms == NEVER) 0 + else 1 + } else { + if (other.ms == NEVER) -1 + else ms.compareTo(other.ms) + } + } + +} + +@Serializable +data class Duration( + /** + * Duration in milliseconds. + */ + @SerialName("d_ms") + val ms: Long +) { + companion object { + const val FOREVER: Long = -1 // TODO or UINT64_MAX? + } +} diff --git a/taler-kotlin-common/src/main/java/net/taler/common/Version.kt b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Version.kt index 8774115..8774115 100644 --- a/taler-kotlin-common/src/main/java/net/taler/common/Version.kt +++ b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Version.kt diff --git a/taler-kotlin-common/src/test/java/net/taler/common/AmountTest.kt b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/AmountTest.kt index 97d9667..e184307 100644 --- a/taler-kotlin-common/src/test/java/net/taler/common/AmountTest.kt +++ b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/AmountTest.kt @@ -16,20 +16,26 @@ package net.taler.common -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.KotlinModule -import com.fasterxml.jackson.module.kotlin.readValue -import org.json.JSONObject -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Assert.fail -import org.junit.Test +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue +import kotlin.test.fail class AmountTest { + companion object { + fun getRandomAmount() = getRandomAmount(getRandomString(1, Random.nextInt(1, 12))) + fun getRandomAmount(currency: String): Amount { + val value = Random.nextLong(0, Amount.MAX_VALUE) + val fraction = Random.nextInt(0, Amount.MAX_FRACTION) + return Amount(currency, value, fraction) + } + } + @Test - fun `test fromJSONString() works`() { + fun testFromJSONString() { var str = "TESTKUDOS:23.42" var amount = Amount.fromJSONString(str) assertEquals(str, amount.toJSONString()) @@ -56,7 +62,7 @@ class AmountTest { } @Test - fun `test fromJSONString() accepts max values, rejects above`() { + fun testFromJSONStringAcceptsMaxValuesRejectsAbove() { val maxValue = 4503599627370496 val str = "TESTKUDOS123:$maxValue.99999999" val amount = Amount.fromJSONString(str) @@ -82,35 +88,7 @@ class AmountTest { } @Test - fun `test JSON deserialization()`() { - val mapper = ObjectMapper().registerModule(KotlinModule()) - var str = "TESTKUDOS:23.42" - var amount: Amount = mapper.readValue("\"$str\"") - assertEquals(str, amount.toJSONString()) - assertEquals("TESTKUDOS", amount.currency) - assertEquals(23, amount.value) - assertEquals((0.42 * 1e8).toInt(), amount.fraction) - assertEquals("23.42 TESTKUDOS", amount.toString()) - - str = "EUR:500000000.00000001" - amount = mapper.readValue("\"$str\"") - assertEquals(str, amount.toJSONString()) - assertEquals("EUR", amount.currency) - assertEquals(500000000, amount.value) - assertEquals(1, amount.fraction) - assertEquals("500000000.00000001 EUR", amount.toString()) - - str = "EUR:1500000000.00000003" - amount = mapper.readValue("\"$str\"") - assertEquals(str, amount.toJSONString()) - assertEquals("EUR", amount.currency) - assertEquals(1500000000, amount.value) - assertEquals(3, amount.fraction) - assertEquals("1500000000.00000003 EUR", amount.toString()) - } - - @Test - fun `test fromJSONString() rejections`() { + fun testFromJSONStringRejections() { assertThrows<AmountParserException> { Amount.fromJSONString("TESTKUDOS:0,5") } @@ -132,71 +110,7 @@ class AmountTest { } @Test - fun `test fromJsonObject() works`() { - val map = mapOf( - "currency" to "TESTKUDOS", - "value" to "23", - "fraction" to "42000000" - ) - - val amount = Amount.fromJsonObject(JSONObject(map)) - assertEquals("TESTKUDOS:23.42", amount.toJSONString()) - assertEquals("TESTKUDOS", amount.currency) - assertEquals(23, amount.value) - assertEquals(42000000, amount.fraction) - assertEquals("23.42 TESTKUDOS", amount.toString()) - } - - @Test - fun `test fromJsonObject() accepts max values, rejects above`() { - val maxValue = 4503599627370496 - val maxFraction = 99999999 - var map = mapOf( - "currency" to "TESTKUDOS123", - "value" to "$maxValue", - "fraction" to "$maxFraction" - ) - - val amount = Amount.fromJsonObject(JSONObject(map)) - assertEquals("TESTKUDOS123:$maxValue.$maxFraction", amount.toJSONString()) - assertEquals("TESTKUDOS123", amount.currency) - assertEquals(maxValue, amount.value) - assertEquals(maxFraction, amount.fraction) - assertEquals("$maxValue.$maxFraction TESTKUDOS123", amount.toString()) - - // longer currency not accepted - assertThrows<AmountParserException>("longer currency was accepted") { - map = mapOf( - "currency" to "TESTKUDOS1234", - "value" to "$maxValue", - "fraction" to "$maxFraction" - ) - Amount.fromJsonObject(JSONObject(map)) - } - - // max value + 1 not accepted - assertThrows<AmountParserException>("max value + 1 was accepted") { - map = mapOf( - "currency" to "TESTKUDOS123", - "value" to "${maxValue + 1}", - "fraction" to "$maxFraction" - ) - Amount.fromJsonObject(JSONObject(map)) - } - - // max fraction + 1 not accepted - assertThrows<AmountParserException>("max fraction + 1 was accepted") { - map = mapOf( - "currency" to "TESTKUDOS123", - "value" to "$maxValue", - "fraction" to "${maxFraction + 1}" - ) - Amount.fromJsonObject(JSONObject(map)) - } - } - - @Test - fun `test addition`() { + fun testAddition() { assertEquals( Amount.fromJSONString("EUR:2"), Amount.fromJSONString("EUR:1") + Amount.fromJSONString("EUR:1") @@ -218,7 +132,7 @@ class AmountTest { } @Test - fun `test times`() { + fun testTimes() { assertEquals( Amount.fromJSONString("EUR:2"), Amount.fromJSONString("EUR:2") * 1 @@ -231,6 +145,12 @@ class AmountTest { Amount.fromJSONString("EUR:4.5"), Amount.fromJSONString("EUR:1.5") * 3 ) + assertEquals(Amount.fromJSONString("EUR:0"), Amount.fromJSONString("EUR:1.11") * 0) + assertEquals(Amount.fromJSONString("EUR:1.11"), Amount.fromJSONString("EUR:1.11") * 1) + assertEquals(Amount.fromJSONString("EUR:2.22"), Amount.fromJSONString("EUR:1.11") * 2) + assertEquals(Amount.fromJSONString("EUR:3.33"), Amount.fromJSONString("EUR:1.11") * 3) + assertEquals(Amount.fromJSONString("EUR:4.44"), Amount.fromJSONString("EUR:1.11") * 4) + assertEquals(Amount.fromJSONString("EUR:5.55"), Amount.fromJSONString("EUR:1.11") * 5) assertEquals( Amount.fromJSONString("EUR:1500000000.00000003"), Amount.fromJSONString("EUR:500000000.00000001") * 3 @@ -241,7 +161,7 @@ class AmountTest { } @Test - fun `test subtraction`() { + fun testSubtraction() { assertEquals( Amount.fromJSONString("EUR:0"), Amount.fromJSONString("EUR:1") - Amount.fromJSONString("EUR:1") @@ -263,7 +183,7 @@ class AmountTest { } @Test - fun `test isZero()`() { + fun testIsZero() { assertTrue(Amount.zero("EUR").isZero()) assertTrue(Amount.fromJSONString("EUR:0").isZero()) assertTrue(Amount.fromJSONString("EUR:0.0").isZero()) @@ -276,14 +196,17 @@ class AmountTest { } @Test - fun `test comparision`() { + fun testComparision() { assertTrue(Amount.fromJSONString("EUR:0") <= Amount.fromJSONString("EUR:0")) assertTrue(Amount.fromJSONString("EUR:0") <= Amount.fromJSONString("EUR:0.00000001")) assertTrue(Amount.fromJSONString("EUR:0") < Amount.fromJSONString("EUR:0.00000001")) assertTrue(Amount.fromJSONString("EUR:0") < Amount.fromJSONString("EUR:1")) - assertTrue(Amount.fromJSONString("EUR:0") == Amount.fromJSONString("EUR:0")) - assertTrue(Amount.fromJSONString("EUR:42") == Amount.fromJSONString("EUR:42")) - assertTrue(Amount.fromJSONString("EUR:42.00000001") == Amount.fromJSONString("EUR:42.00000001")) + assertEquals(Amount.fromJSONString("EUR:0"), Amount.fromJSONString("EUR:0")) + assertEquals(Amount.fromJSONString("EUR:42"), Amount.fromJSONString("EUR:42")) + assertEquals( + Amount.fromJSONString("EUR:42.00000001"), + Amount.fromJSONString("EUR:42.00000001") + ) assertTrue(Amount.fromJSONString("EUR:42.00000001") >= Amount.fromJSONString("EUR:42.00000001")) assertTrue(Amount.fromJSONString("EUR:42.00000002") >= Amount.fromJSONString("EUR:42.00000001")) assertTrue(Amount.fromJSONString("EUR:42.00000002") > Amount.fromJSONString("EUR:42.00000001")) diff --git a/taler-kotlin-common/src/main/java/net/taler/common/SignedAmount.kt b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/TestUtils.kt index 03a0d6e..e3a6c17 100644 --- a/taler-kotlin-common/src/main/java/net/taler/common/SignedAmount.kt +++ b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/TestUtils.kt @@ -16,25 +16,11 @@ package net.taler.common -import android.annotation.SuppressLint +import kotlin.random.Random -data class SignedAmount( - val positive: Boolean, - val amount: Amount -) { - - companion object { - @Throws(AmountParserException::class) - @SuppressLint("CheckedExceptions") - fun fromJSONString(str: String): SignedAmount = when (str.substring(0, 1)) { - "-" -> SignedAmount(false, Amount.fromJSONString(str.substring(1))) - "+" -> SignedAmount(true, Amount.fromJSONString(str.substring(1))) - else -> SignedAmount(true, Amount.fromJSONString(str)) - } - } - - override fun toString(): String { - return if (positive) "$amount" else "-$amount" - } - -}
\ No newline at end of file +private val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9') +fun getRandomString(minLength: Int = 1, maxLength: Int = Random.nextInt(0, 1337)) = + (minLength..maxLength) + .map { Random.nextInt(0, charPool.size) } + .map(charPool::get) + .joinToString("") diff --git a/taler-kotlin-common/src/test/java/net/taler/common/VersionTest.kt b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/VersionTest.kt index 70f30eb..f4f17ea 100644 --- a/taler-kotlin-common/src/test/java/net/taler/common/VersionTest.kt +++ b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/VersionTest.kt @@ -16,9 +16,9 @@ package net.taler.common -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull -import org.junit.Test +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull class VersionTest { diff --git a/taler-kotlin-common/src/jsMain/kotlin/net/taler/common/Time.kt b/taler-kotlin-common/src/jsMain/kotlin/net/taler/common/Time.kt new file mode 100644 index 0000000..b114022 --- /dev/null +++ b/taler-kotlin-common/src/jsMain/kotlin/net/taler/common/Time.kt @@ -0,0 +1,23 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.common + +import kotlin.js.Date + +actual fun nowMillis(): Long { + return Date().getMilliseconds().toLong() +} diff --git a/taler-kotlin-common/src/jvmMain/kotlin/net/taler/common/Time.kt b/taler-kotlin-common/src/jvmMain/kotlin/net/taler/common/Time.kt new file mode 100644 index 0000000..6cd9040 --- /dev/null +++ b/taler-kotlin-common/src/jvmMain/kotlin/net/taler/common/Time.kt @@ -0,0 +1,21 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.common + +actual fun nowMillis(): Long { + return System.currentTimeMillis() +} diff --git a/taler-kotlin-common/src/main/AndroidManifest.xml b/taler-kotlin-common/src/main/AndroidManifest.xml deleted file mode 100644 index 902ddc1..0000000 --- a/taler-kotlin-common/src/main/AndroidManifest.xml +++ /dev/null @@ -1,24 +0,0 @@ -<!-- - ~ This file is part of GNU Taler - ~ (C) 2020 Taler Systems S.A. - ~ - ~ GNU Taler is free software; you can redistribute it and/or modify it under the - ~ terms of the GNU General Public License as published by the Free Software - ~ Foundation; either version 3, or (at your option) any later version. - ~ - ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ~ - ~ You should have received a copy of the GNU General Public License along with - ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="net.taler.common"> - - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.NFC" /> - -</manifest> diff --git a/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt b/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt deleted file mode 100644 index b46f306..0000000 --- a/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2020 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -package net.taler.common - -import android.content.Context -import android.content.Context.CONNECTIVITY_SERVICE -import android.content.Intent -import android.content.pm.PackageManager.MATCH_DEFAULT_ONLY -import android.net.ConnectivityManager -import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET -import android.os.Build.VERSION.SDK_INT -import android.os.Looper -import android.text.format.DateUtils.DAY_IN_MILLIS -import android.text.format.DateUtils.FORMAT_ABBREV_ALL -import android.text.format.DateUtils.FORMAT_ABBREV_MONTH -import android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE -import android.text.format.DateUtils.FORMAT_NO_YEAR -import android.text.format.DateUtils.FORMAT_SHOW_DATE -import android.text.format.DateUtils.FORMAT_SHOW_TIME -import android.text.format.DateUtils.FORMAT_SHOW_YEAR -import android.text.format.DateUtils.MINUTE_IN_MILLIS -import android.text.format.DateUtils.formatDateTime -import android.text.format.DateUtils.getRelativeTimeSpanString -import android.view.View -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.inputmethod.InputMethodManager -import androidx.core.content.ContextCompat.getSystemService -import androidx.fragment.app.Fragment -import androidx.navigation.NavDirections -import androidx.navigation.fragment.findNavController - -fun View.fadeIn(endAction: () -> Unit = {}) { - if (visibility == VISIBLE && alpha == 1f) return - alpha = 0f - visibility = VISIBLE - animate().alpha(1f).withEndAction { - if (context != null) endAction.invoke() - }.start() -} - -fun View.fadeOut(endAction: () -> Unit = {}) { - if (visibility == INVISIBLE) return - animate().alpha(0f).withEndAction { - if (context == null) return@withEndAction - visibility = INVISIBLE - alpha = 1f - endAction.invoke() - }.start() -} - -fun View.hideKeyboard() { - getSystemService(context, InputMethodManager::class.java) - ?.hideSoftInputFromWindow(windowToken, 0) -} - -fun assertUiThread() { - check(Looper.getMainLooper().thread == Thread.currentThread()) -} - -/** - * Use this with 'when' expressions when you need it to handle all possibilities/branches. - */ -val <T> T.exhaustive: T - get() = this - -fun Context.isOnline(): Boolean { - val cm = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager - return if (SDK_INT < 29) { - @Suppress("DEPRECATION") - cm.activeNetworkInfo?.isConnected == true - } else { - val capabilities = cm.getNetworkCapabilities(cm.activeNetwork) ?: return false - capabilities.hasCapability(NET_CAPABILITY_INTERNET) - } -} - -fun Intent.isSafe(context: Context): Boolean { - return context.packageManager.queryIntentActivities(this, MATCH_DEFAULT_ONLY).isNotEmpty() -} - -fun Fragment.navigate(directions: NavDirections) = findNavController().navigate(directions) - -fun Long.toRelativeTime(context: Context): CharSequence { - val now = System.currentTimeMillis() - return if (now - this > DAY_IN_MILLIS * 2) { - val flags = FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_ABBREV_MONTH or FORMAT_NO_YEAR - formatDateTime(context, this, flags) - } else getRelativeTimeSpanString(this, now, MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE) -} - -fun Long.toAbsoluteTime(context: Context): CharSequence { - val flags = FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_SHOW_YEAR - return formatDateTime(context, this, flags) -} - -fun Long.toShortDate(context: Context): CharSequence { - val flags = FORMAT_SHOW_DATE or FORMAT_SHOW_YEAR or FORMAT_ABBREV_ALL - return formatDateTime(context, this, flags) -} - -fun Version.getIncompatibleStringOrNull(context: Context, otherVersion: String): String? { - val other = Version.parse(otherVersion) ?: return context.getString(R.string.version_invalid) - val match = compare(other) ?: return context.getString(R.string.version_invalid) - if (match.compatible) return null - if (match.currentCmp < 0) return context.getString(R.string.version_too_old) - if (match.currentCmp > 0) return context.getString(R.string.version_too_new) - throw AssertionError("$this == $other") -} diff --git a/taler-kotlin-common/src/main/java/net/taler/common/ByteArrayUtils.kt b/taler-kotlin-common/src/main/java/net/taler/common/ByteArrayUtils.kt deleted file mode 100644 index fba0d07..0000000 --- a/taler-kotlin-common/src/main/java/net/taler/common/ByteArrayUtils.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2020 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -package net.taler.common - -object ByteArrayUtils { - - private const val HEX_CHARS = "0123456789ABCDEF" - - fun hexStringToByteArray(data: String): ByteArray { - val result = ByteArray(data.length / 2) - - for (i in data.indices step 2) { - val firstIndex = HEX_CHARS.indexOf(data[i]) - val secondIndex = HEX_CHARS.indexOf(data[i + 1]) - - val octet = firstIndex.shl(4).or(secondIndex) - result[i.shr(1)] = octet.toByte() - } - return result - } - - - private val HEX_CHARS_ARRAY = HEX_CHARS.toCharArray() - - @Suppress("unused") - fun toHex(byteArray: ByteArray): String { - val result = StringBuffer() - - byteArray.forEach { - val octet = it.toInt() - val firstIndex = (octet and 0xF0).ushr(4) - val secondIndex = octet and 0x0F - result.append(HEX_CHARS_ARRAY[firstIndex]) - result.append(HEX_CHARS_ARRAY[secondIndex]) - } - return result.toString() - } - -} diff --git a/taler-kotlin-common/src/main/java/net/taler/common/CombinedLiveData.kt b/taler-kotlin-common/src/main/java/net/taler/common/CombinedLiveData.kt deleted file mode 100644 index 4e7016b..0000000 --- a/taler-kotlin-common/src/main/java/net/taler/common/CombinedLiveData.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2020 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -package net.taler.common - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MediatorLiveData -import androidx.lifecycle.Observer - -class CombinedLiveData<T, K, S>( - source1: LiveData<T>, - source2: LiveData<K>, - private val combine: (data1: T?, data2: K?) -> S -) : MediatorLiveData<S>() { - - private var data1: T? = null - private var data2: K? = null - - init { - super.addSource(source1) { t -> - data1 = t - value = combine(data1, data2) - } - super.addSource(source2) { k -> - data2 = k - value = combine(data1, data2) - } - } - - override fun <S : Any?> addSource(source: LiveData<S>, onChanged: Observer<in S>) { - throw UnsupportedOperationException() - } - - override fun <T : Any?> removeSource(toRemote: LiveData<T>) { - throw UnsupportedOperationException() - } - -} diff --git a/taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt b/taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt deleted file mode 100644 index b891ef7..0000000 --- a/taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2020 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -package net.taler.common - -import androidx.annotation.RequiresApi -import com.fasterxml.jackson.annotation.JsonIgnore -import com.fasterxml.jackson.annotation.JsonIgnoreProperties -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY -import com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL -import com.fasterxml.jackson.annotation.JsonProperty -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import net.taler.common.TalerUtils.getLocalizedString - -@Serializable -@JsonIgnoreProperties(ignoreUnknown = true) -data class ContractTerms( - val summary: String, - @SerialName("summary_i18n") - val summaryI18n: Map<String, String>? = null, - val amount: Amount, - @SerialName("fulfillment_url") - val fulfillmentUrl: String, - val products: List<ContractProduct> -) - -@JsonInclude(NON_NULL) -abstract class Product { - @get:JsonProperty("product_id") - abstract val productId: String? - abstract val description: String - - @get:JsonProperty("description_i18n") - abstract val descriptionI18n: Map<String, String>? - abstract val price: Amount - - @get:JsonProperty("delivery_location") - abstract val location: String? - abstract val image: String? - - @get:JsonIgnore - val localizedDescription: String - @RequiresApi(26) - get() = getLocalizedString(descriptionI18n, description) -} - -@Serializable -data class ContractProduct( - @SerialName("product_id") - override val productId: String? = null, - override val description: String, - @SerialName("description_i18n") - override val descriptionI18n: Map<String, String>? = null, - override val price: Amount, - @SerialName("delivery_location") - override val location: String? = null, - override val image: String? = null, - val quantity: Int -) : Product() { - @get:JsonIgnore - val totalPrice: Amount by lazy { - price * quantity - } -} - -data class ContractMerchant( - val name: String -) - -@Serializable -@JsonInclude(NON_EMPTY) -class Timestamp( - @SerialName("t_ms") - @JsonProperty("t_ms") - val ms: Long -) diff --git a/taler-kotlin-common/src/main/java/net/taler/common/Event.kt b/taler-kotlin-common/src/main/java/net/taler/common/Event.kt deleted file mode 100644 index 779247f..0000000 --- a/taler-kotlin-common/src/main/java/net/taler/common/Event.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2020 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -package net.taler.common - -import androidx.lifecycle.LiveData -import androidx.lifecycle.Observer -import java.util.concurrent.atomic.AtomicBoolean - -/** - * Used as a wrapper for data that is exposed via a [LiveData] that represents an one-time event. - */ -open class Event<out T>(private val content: T) { - - private val isConsumed = AtomicBoolean(false) - - /** - * Returns the content and prevents its use again. - */ - fun getIfNotConsumed(): T? { - return if (isConsumed.compareAndSet(false, true)) content else null - } - -} - -fun <T> T.toEvent() = Event(this) - -/** - * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has - * already been consumed. - * - * [onEvent] is *only* called if the [Event]'s contents has not been consumed. - */ -class EventObserver<T>(private val onEvent: (T) -> Unit) : Observer<Event<T>> { - override fun onChanged(event: Event<T>?) { - event?.getIfNotConsumed()?.let { onEvent(it) } - } -} diff --git a/taler-kotlin-common/src/main/java/net/taler/common/NfcManager.kt b/taler-kotlin-common/src/main/java/net/taler/common/NfcManager.kt deleted file mode 100644 index 11e1e1e..0000000 --- a/taler-kotlin-common/src/main/java/net/taler/common/NfcManager.kt +++ /dev/null @@ -1,234 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2020 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -package net.taler.common - -import android.app.Activity -import android.content.Context -import android.nfc.NfcAdapter -import android.nfc.NfcAdapter.FLAG_READER_NFC_A -import android.nfc.NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK -import android.nfc.NfcAdapter.getDefaultAdapter -import android.nfc.Tag -import android.nfc.tech.IsoDep -import android.util.Log -import net.taler.common.ByteArrayUtils.hexStringToByteArray -import org.json.JSONObject -import java.io.ByteArrayOutputStream -import java.net.URL -import javax.net.ssl.HttpsURLConnection - -@Suppress("unused") -private const val TALER_AID = "A0000002471001" - -class NfcManager : NfcAdapter.ReaderCallback { - - companion object { - const val TAG = "taler-merchant" - - /** - * Returns true if NFC is supported and false otherwise. - */ - fun hasNfc(context: Context): Boolean { - return getNfcAdapter(context) != null - } - - /** - * Enables NFC reader mode. Don't forget to call [stop] afterwards. - */ - fun start(activity: Activity, nfcManager: NfcManager) { - getNfcAdapter(activity)?.enableReaderMode(activity, nfcManager, nfcManager.flags, null) - } - - /** - * Disables NFC reader mode. Call after [start]. - */ - fun stop(activity: Activity) { - getNfcAdapter(activity)?.disableReaderMode(activity) - } - - private fun getNfcAdapter(context: Context): NfcAdapter? { - return getDefaultAdapter(context) - } - } - - private val flags = FLAG_READER_NFC_A or FLAG_READER_SKIP_NDEF_CHECK - - private var tagString: String? = null - private var currentTag: IsoDep? = null - - fun setTagString(tagString: String) { - this.tagString = tagString - } - - override fun onTagDiscovered(tag: Tag?) { - - Log.v(TAG, "tag discovered") - - val isoDep = IsoDep.get(tag) - isoDep.connect() - - currentTag = isoDep - - isoDep.transceive(apduSelectFile()) - - val tagString: String? = tagString - if (tagString != null) { - isoDep.transceive(apduPutTalerData(1, tagString.toByteArray())) - } - - // FIXME: use better pattern for sleeps in between requests - // -> start with fast polling, poll more slowly if no requests are coming - - while (true) { - try { - val reqFrame = isoDep.transceive(apduGetData()) - if (reqFrame.size < 2) { - Log.v(TAG, "request frame too small") - break - } - val req = ByteArray(reqFrame.size - 2) - if (req.isEmpty()) { - continue - } - reqFrame.copyInto(req, 0, 0, reqFrame.size - 2) - val jsonReq = JSONObject(req.toString(Charsets.UTF_8)) - val reqId = jsonReq.getInt("id") - Log.v(TAG, "got request $jsonReq") - val jsonInnerReq = jsonReq.getJSONObject("request") - val method = jsonInnerReq.getString("method") - val urlStr = jsonInnerReq.getString("url") - Log.v(TAG, "url '$urlStr'") - Log.v(TAG, "method '$method'") - val url = URL(urlStr) - val conn: HttpsURLConnection = url.openConnection() as HttpsURLConnection - conn.setRequestProperty("Accept", "application/json") - conn.connectTimeout = 5000 - conn.doInput = true - when (method) { - "get" -> { - conn.requestMethod = "GET" - } - "postJson" -> { - conn.requestMethod = "POST" - conn.doOutput = true - conn.setRequestProperty("Content-Type", "application/json; utf-8") - val body = jsonInnerReq.getString("body") - conn.outputStream.write(body.toByteArray(Charsets.UTF_8)) - } - else -> { - throw Exception("method not supported") - } - } - Log.v(TAG, "connecting") - conn.connect() - Log.v(TAG, "connected") - - val statusCode = conn.responseCode - val tunnelResp = JSONObject() - tunnelResp.put("id", reqId) - tunnelResp.put("status", conn.responseCode) - - if (statusCode == 200) { - val stream = conn.inputStream - val httpResp = stream.buffered().readBytes() - tunnelResp.put("responseJson", JSONObject(httpResp.toString(Charsets.UTF_8))) - } - - Log.v(TAG, "sending: $tunnelResp") - - isoDep.transceive(apduPutTalerData(2, tunnelResp.toString().toByteArray())) - } catch (e: Exception) { - Log.v(TAG, "exception during NFC loop: $e") - break - } - } - - isoDep.close() - } - - private fun writeApduLength(stream: ByteArrayOutputStream, size: Int) { - when { - size == 0 -> { - // No size field needed! - } - size <= 255 -> // One byte size field - stream.write(size) - size <= 65535 -> { - stream.write(0) - // FIXME: is this supposed to be little or big endian? - stream.write(size and 0xFF) - stream.write((size ushr 8) and 0xFF) - } - else -> throw Error("payload too big") - } - } - - private fun apduSelectFile(): ByteArray { - return hexStringToByteArray("00A4040007A0000002471001") - } - - private fun apduPutData(payload: ByteArray): ByteArray { - val stream = ByteArrayOutputStream() - - // Class - stream.write(0x00) - - // Instruction 0xDA = put data - stream.write(0xDA) - - // Instruction parameters - // (proprietary encoding) - stream.write(0x01) - stream.write(0x00) - - writeApduLength(stream, payload.size) - - stream.write(payload) - - return stream.toByteArray() - } - - private fun apduPutTalerData(talerInst: Int, payload: ByteArray): ByteArray { - val realPayload = ByteArrayOutputStream() - realPayload.write(talerInst) - realPayload.write(payload) - return apduPutData(realPayload.toByteArray()) - } - - private fun apduGetData(): ByteArray { - val stream = ByteArrayOutputStream() - - // Class - stream.write(0x00) - - // Instruction 0xCA = get data - stream.write(0xCA) - - // Instruction parameters - // (proprietary encoding) - stream.write(0x01) - stream.write(0x00) - - // Max expected response size, two - // zero bytes denotes 65536 - stream.write(0x0) - stream.write(0x0) - - return stream.toByteArray() - } - -} diff --git a/taler-kotlin-common/src/main/java/net/taler/common/QrCodeManager.kt b/taler-kotlin-common/src/main/java/net/taler/common/QrCodeManager.kt deleted file mode 100644 index e2a9a55..0000000 --- a/taler-kotlin-common/src/main/java/net/taler/common/QrCodeManager.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2020 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -package net.taler.common - -import android.graphics.Bitmap -import android.graphics.Bitmap.Config.RGB_565 -import android.graphics.Color.BLACK -import android.graphics.Color.WHITE -import com.google.zxing.BarcodeFormat.QR_CODE -import com.google.zxing.qrcode.QRCodeWriter - -object QrCodeManager { - - fun makeQrCode(text: String, size: Int = 256): Bitmap { - val qrCodeWriter = QRCodeWriter() - val bitMatrix = qrCodeWriter.encode(text, QR_CODE, size, size) - val height = bitMatrix.height - val width = bitMatrix.width - val bmp = Bitmap.createBitmap(width, height, RGB_565) - for (x in 0 until width) { - for (y in 0 until height) { - bmp.setPixel(x, y, if (bitMatrix.get(x, y)) BLACK else WHITE) - } - } - return bmp - } - -} diff --git a/taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt b/taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt deleted file mode 100644 index 444caa4..0000000 --- a/taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2020 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -package net.taler.common - -import androidx.annotation.RequiresApi -import androidx.core.os.LocaleListCompat -import java.util.* -import kotlin.collections.ArrayList - -object TalerUtils { - - @RequiresApi(26) - fun getLocalizedString(map: Map<String, String>?, default: String): String { - // just return the default, if it is the only element - if (map == null) return default - // create a priority list of language ranges from system locales - val locales = LocaleListCompat.getDefault() - val priorityList = ArrayList<Locale.LanguageRange>(locales.size()) - for (i in 0 until locales.size()) { - priorityList.add(Locale.LanguageRange(locales[i].toLanguageTag())) - } - // create a list of locales available in the given map - val availableLocales = map.keys.mapNotNull { - if (it == "_") return@mapNotNull null - val list = it.split("_") - when (list.size) { - 1 -> Locale(list[0]) - 2 -> Locale(list[0], list[1]) - 3 -> Locale(list[0], list[1], list[2]) - else -> null - } - } - val match = Locale.lookup(priorityList, availableLocales) - return match?.toString()?.let { map[it] } ?: default - } - -} - -/** - * Returns the current time in milliseconds epoch rounded to nearest seconds. - */ -fun now(): Long { - return ((System.currentTimeMillis() + 500) / 1000) * 1000 -} diff --git a/taler-kotlin-common/src/main/res/drawable/selectable_background.xml b/taler-kotlin-common/src/main/res/drawable/selectable_background.xml deleted file mode 100644 index 3c383a8..0000000 --- a/taler-kotlin-common/src/main/res/drawable/selectable_background.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ This file is part of GNU Taler - ~ (C) 2020 Taler Systems S.A. - ~ - ~ GNU Taler is free software; you can redistribute it and/or modify it under the - ~ terms of the GNU General Public License as published by the Free Software - ~ Foundation; either version 3, or (at your option) any later version. - ~ - ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ~ - ~ You should have received a copy of the GNU General Public License along with - ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - --> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@color/selectedBackground" android:state_activated="true" /> - <item android:drawable="@android:color/transparent" /> -</selector> diff --git a/taler-kotlin-common/src/main/res/values-night/colors.xml b/taler-kotlin-common/src/main/res/values-night/colors.xml deleted file mode 100644 index 10bdbb9..0000000 --- a/taler-kotlin-common/src/main/res/values-night/colors.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <color name="highlightedBackground">#2E2E2E</color> - <color name="selectedBackground">#363636</color> -</resources> diff --git a/taler-kotlin-common/src/main/res/values/colors.xml b/taler-kotlin-common/src/main/res/values/colors.xml deleted file mode 100644 index c916442..0000000 --- a/taler-kotlin-common/src/main/res/values/colors.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ This file is part of GNU Taler - ~ (C) 2020 Taler Systems S.A. - ~ - ~ GNU Taler is free software; you can redistribute it and/or modify it under the - ~ terms of the GNU General Public License as published by the Free Software - ~ Foundation; either version 3, or (at your option) any later version. - ~ - ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ~ - ~ You should have received a copy of the GNU General Public License along with - ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - --> - -<resources> - <color name="highlightedBackground">#E4E4E4</color> - <color name="selectedBackground">#DADADA</color> - - <color name="green">#388E3C</color> - <color name="red">#C62828</color> -</resources> diff --git a/taler-kotlin-common/src/main/res/values/strings.xml b/taler-kotlin-common/src/main/res/values/strings.xml deleted file mode 100644 index a5b1df1..0000000 --- a/taler-kotlin-common/src/main/res/values/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ This file is part of GNU Taler - ~ (C) 2020 Taler Systems S.A. - ~ - ~ GNU Taler is free software; you can redistribute it and/or modify it under the - ~ terms of the GNU General Public License as published by the Free Software - ~ Foundation; either version 3, or (at your option) any later version. - ~ - ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ~ - ~ You should have received a copy of the GNU General Public License along with - ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - --> - -<resources> - <string name="version_invalid">Invalid version. Please try again later!</string> - <string name="version_too_old">App too old. Please upgrade!</string> - <string name="version_too_new">Service outdated. Please wait until it gets upgraded.</string> -</resources> diff --git a/taler-kotlin-common/src/nativeMain/kotlin/net/taler/common/Time.kt b/taler-kotlin-common/src/nativeMain/kotlin/net/taler/common/Time.kt new file mode 100644 index 0000000..8a4091a --- /dev/null +++ b/taler-kotlin-common/src/nativeMain/kotlin/net/taler/common/Time.kt @@ -0,0 +1,23 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.common + +import kotlin.system.getTimeMillis + +actual fun nowMillis(): Long { + return getTimeMillis() +} |