summaryrefslogtreecommitdiff
path: root/taler-kotlin-common
diff options
context:
space:
mode:
Diffstat (limited to 'taler-kotlin-common')
-rw-r--r--taler-kotlin-common/.gitlab-ci.yml6
-rw-r--r--taler-kotlin-common/build.gradle130
-rw-r--r--taler-kotlin-common/consumer-rules.pro0
-rw-r--r--taler-kotlin-common/proguard-rules.pro21
-rw-r--r--taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Amount.kt (renamed from taler-kotlin-common/src/main/java/net/taler/common/Amount.kt)70
-rw-r--r--taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Time.kt81
-rw-r--r--taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Version.kt (renamed from taler-kotlin-common/src/main/java/net/taler/common/Version.kt)0
-rw-r--r--taler-kotlin-common/src/commonTest/kotlin/net/taler/common/AmountTest.kt (renamed from taler-kotlin-common/src/test/java/net/taler/common/AmountTest.kt)147
-rw-r--r--taler-kotlin-common/src/commonTest/kotlin/net/taler/common/TestUtils.kt (renamed from taler-kotlin-common/src/main/java/net/taler/common/SignedAmount.kt)28
-rw-r--r--taler-kotlin-common/src/commonTest/kotlin/net/taler/common/VersionTest.kt (renamed from taler-kotlin-common/src/test/java/net/taler/common/VersionTest.kt)6
-rw-r--r--taler-kotlin-common/src/jsMain/kotlin/net/taler/common/Time.kt23
-rw-r--r--taler-kotlin-common/src/jvmMain/kotlin/net/taler/common/Time.kt21
-rw-r--r--taler-kotlin-common/src/main/AndroidManifest.xml24
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt123
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/ByteArrayUtils.kt53
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/CombinedLiveData.kt51
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt91
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/Event.kt51
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/NfcManager.kt234
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/QrCodeManager.kt42
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt58
-rw-r--r--taler-kotlin-common/src/main/res/drawable/selectable_background.xml21
-rw-r--r--taler-kotlin-common/src/main/res/values-night/colors.xml5
-rw-r--r--taler-kotlin-common/src/main/res/values/colors.xml24
-rw-r--r--taler-kotlin-common/src/main/res/values/strings.xml21
-rw-r--r--taler-kotlin-common/src/nativeMain/kotlin/net/taler/common/Time.kt23
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()
+}