From 8815105bf2462787885214a12af927d484226f21 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Thu, 30 Jul 2020 16:40:23 -0300 Subject: Split out common code into multiplatform Kotlin library --- .../src/main/java/net/taler/common/Amount.kt | 246 --------------------- .../src/main/java/net/taler/common/AndroidUtils.kt | 123 ----------- .../main/java/net/taler/common/ByteArrayUtils.kt | 53 ----- .../main/java/net/taler/common/CombinedLiveData.kt | 51 ----- .../main/java/net/taler/common/ContractTerms.kt | 91 -------- .../src/main/java/net/taler/common/Event.kt | 51 ----- .../src/main/java/net/taler/common/NfcManager.kt | 234 -------------------- .../main/java/net/taler/common/QrCodeManager.kt | 42 ---- .../src/main/java/net/taler/common/SignedAmount.kt | 40 ---- .../src/main/java/net/taler/common/TalerUtils.kt | 58 ----- .../src/main/java/net/taler/common/Version.kt | 70 ------ 11 files changed, 1059 deletions(-) delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/Amount.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/ByteArrayUtils.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/CombinedLiveData.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/Event.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/NfcManager.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/QrCodeManager.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/SignedAmount.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt delete mode 100644 taler-kotlin-common/src/main/java/net/taler/common/Version.kt (limited to 'taler-kotlin-common/src/main/java') 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 - */ - -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 { - - 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 { - 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::class.java) { - override fun serialize(value: Amount, gen: JsonGenerator, provider: SerializerProvider) { - gen.writeString(value.toJSONString()) - } -} - -class AmountDeserializer : StdDeserializer(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 - */ - -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.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 - */ - -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 - */ - -package net.taler.common - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MediatorLiveData -import androidx.lifecycle.Observer - -class CombinedLiveData( - source1: LiveData, - source2: LiveData, - private val combine: (data1: T?, data2: K?) -> S -) : MediatorLiveData() { - - 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 addSource(source: LiveData, onChanged: Observer) { - throw UnsupportedOperationException() - } - - override fun removeSource(toRemote: LiveData) { - 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 - */ - -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? = null, - val amount: Amount, - @SerialName("fulfillment_url") - val fulfillmentUrl: String, - val products: List -) - -@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? - 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? = 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 - */ - -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(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.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(private val onEvent: (T) -> Unit) : Observer> { - override fun onChanged(event: Event?) { - 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 - */ - -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 - */ - -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 - */ - -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 - */ - -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?, 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(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 - */ - -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 - ) - -} -- cgit v1.2.3