summaryrefslogtreecommitdiff
path: root/taler-kotlin-common/src/main/java
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-07-30 16:40:23 -0300
committerTorsten Grote <t@grobox.de>2020-07-30 16:40:23 -0300
commit8815105bf2462787885214a12af927d484226f21 (patch)
tree597076d6970e336b881d68ca6b48577b007c9730 /taler-kotlin-common/src/main/java
parente19ba096d57353db6b1f141da4bf170ef2d2d534 (diff)
downloadtaler-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/src/main/java')
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/Amount.kt246
-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/SignedAmount.kt40
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt58
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/Version.kt70
11 files changed, 0 insertions, 1059 deletions
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt b/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt
deleted file mode 100644
index 992f93b..0000000
--- a/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt
+++ /dev/null
@@ -1,246 +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.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.pow
-import kotlin.math.roundToInt
-
-class AmountParserException(msg: String? = null, cause: Throwable? = null) : Exception(msg, cause)
-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,
- * or a regional currency identifier starting with a "*" followed by at most 10 characters.
- * ISO 4217 exponents in the name are not supported,
- * although the "fraction" is corresponds to an ISO 4217 exponent of 6.
- */
- val currency: String,
-
- /**
- * The integer part may be at most 2^52.
- * Note that "1" here would correspond to 1 EUR or 1 USD, depending on currency, not 1 cent.
- */
- val value: Long,
-
- /**
- * Unsigned 32 bit fractional value to be added to value representing
- * an additional currency fraction, in units of one hundred millionth (1e-8)
- * of the base currency value. For example, a fraction
- * of 50_000_000 would correspond to 50 cents.
- */
- val fraction: Int
-) : Comparable<Amount> {
-
- companion object {
-
- 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)
- private const val MAX_FRACTION_LENGTH = 8
- private 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(".")
- val value = checkValue(valueSplit[0].toLongOrNull())
- // fraction
- val fraction: Int = if (valueSplit.size > 1) {
- val fractionStr = valueSplit[1]
- if (fractionStr.length > MAX_FRACTION_LENGTH)
- throw AmountParserException("Fraction $fractionStr too long")
- val fraction = "0.$fractionStr".toDoubleOrNull()
- ?.times(FRACTIONAL_BASE)
- ?.roundToInt()
- checkFraction(fraction)
- } else 0
- 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)
- }
-
- @Throws(AmountParserException::class)
- private 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 {
- 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 {
- if (fraction == null || fraction > MAX_FRACTION)
- throw AmountParserException("Fraction $fraction greater than $MAX_FRACTION")
- return fraction
- }
-
- }
-
- val amountStr: String
- get() = if (fraction == 0) "$value" else {
- var f = fraction
- var fractionStr = ""
- while (f > 0) {
- fractionStr += f / (FRACTIONAL_BASE / 10)
- f = (f * 10) % FRACTIONAL_BASE
- }
- "$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)
- 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 {
- 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
- var resultFraction = fraction
- if (resultFraction < other.fraction) {
- if (resultValue < 1L)
- throw AmountOverflowException()
- resultValue--
- resultFraction += FRACTIONAL_BASE
- }
- check(resultFraction >= other.fraction)
- resultFraction -= other.fraction
- if (resultValue < other.value)
- throw AmountOverflowException()
- resultValue -= other.value
- return Amount(currency, resultValue, resultFraction)
- }
-
- fun isZero(): Boolean {
- return value == 0L && fraction == 0
- }
-
- fun toJSONString(): String {
- return "$currency:$amountStr"
- }
-
- override fun toString(): String {
- return "$amountStr $currency"
- }
-
- override fun compareTo(other: Amount): Int {
- check(currency == other.currency) { "Can only compare amounts with the same currency" }
- when {
- value == other.value -> {
- if (fraction < other.fraction) return -1
- if (fraction > other.fraction) return 1
- return 0
- }
- value < other.value -> return -1
- else -> return 1
- }
- }
-
-}
-
-@Serializer(forClass = Amount::class)
-object KotlinXAmountSerializer: KSerializer<Amount> {
- override fun serialize(encoder: Encoder, value: Amount) {
- encoder.encodeString(value.toJSONString())
- }
-
- override fun deserialize(decoder: Decoder): 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/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/SignedAmount.kt b/taler-kotlin-common/src/main/java/net/taler/common/SignedAmount.kt
deleted file mode 100644
index 03a0d6e..0000000
--- a/taler-kotlin-common/src/main/java/net/taler/common/SignedAmount.kt
+++ /dev/null
@@ -1,40 +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.annotation.SuppressLint
-
-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
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/java/net/taler/common/Version.kt b/taler-kotlin-common/src/main/java/net/taler/common/Version.kt
deleted file mode 100644
index 8774115..0000000
--- a/taler-kotlin-common/src/main/java/net/taler/common/Version.kt
+++ /dev/null
@@ -1,70 +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 kotlin.math.sign
-
-/**
- * Semantic versioning, but libtool-style.
- * See https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
- */
-data class Version(
- val current: Int,
- val revision: Int,
- val age: Int
-) {
- companion object {
- fun parse(v: String): Version? {
- val elements = v.split(":")
- if (elements.size != 3) return null
- val (currentStr, revisionStr, ageStr) = elements
- val current = currentStr.toIntOrNull()
- val revision = revisionStr.toIntOrNull()
- val age = ageStr.toIntOrNull()
- if (current == null || revision == null || age == null) return null
- return Version(current, revision, age)
- }
- }
-
- /**
- * Compare two libtool-style versions.
- *
- * Returns a [VersionMatchResult] or null if the given version was null.
- */
- fun compare(other: Version?): VersionMatchResult? {
- if (other == null) return null
- val compatible = current - age <= other.current &&
- current >= other.current - other.age
- val currentCmp = sign((current - other.current).toDouble()).toInt()
- return VersionMatchResult(compatible, currentCmp)
- }
-
- /**
- * Result of comparing two libtool versions.
- */
- data class VersionMatchResult(
- /**
- * Is the first version compatible with the second?
- */
- val compatible: Boolean,
- /**
- * Is the first version older (-1), newer (+1) or identical (0)?
- */
- val currentCmp: Int
- )
-
-}