aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2023-01-03 11:22:42 -0300
committerTorsten Grote <t@grobox.de>2023-01-03 11:22:42 -0300
commit9bb3505277b8132c3fd0be52d9bbbc6078723ff2 (patch)
tree60688f65d464ee02834c4ae1d89b715f4e8f5640
parent3cf3abb25adf040ad8e4e30d0fc8f311b2b1f4ee (diff)
downloadtaler-android-9bb3505277b8132c3fd0be52d9bbbc6078723ff2.tar.gz
taler-android-9bb3505277b8132c3fd0be52d9bbbc6078723ff2.tar.bz2
taler-android-9bb3505277b8132c3fd0be52d9bbbc6078723ff2.zip
[wallet] Don't allow entering invalid amounts
#0007350
-rw-r--r--taler-kotlin-android/build.gradle4
-rw-r--r--taler-kotlin-android/src/main/java/net/taler/common/Amount.kt44
-rw-r--r--wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt4
-rw-r--r--wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt4
4 files changed, 41 insertions, 15 deletions
diff --git a/taler-kotlin-android/build.gradle b/taler-kotlin-android/build.gradle
index efd305f..69b4f4d 100644
--- a/taler-kotlin-android/build.gradle
+++ b/taler-kotlin-android/build.gradle
@@ -21,6 +21,7 @@ plugins {
}
android {
+ namespace 'net.taler.common'
compileSdkVersion 32
buildToolsVersion "$build_tools_version"
@@ -49,9 +50,6 @@ android {
excludes += ['META-INF/*.kotlin_module']
}
}
-
- namespace 'net.taler.common'
-
}
dependencies {
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
index 4a6e4b3..750a1de 100644
--- a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
@@ -25,8 +25,11 @@ import kotlin.math.floor
import kotlin.math.pow
import kotlin.math.roundToInt
-public class AmountParserException(msg: String? = null, cause: Throwable? = null) : Exception(msg, cause)
-public class AmountOverflowException(msg: String? = null, cause: Throwable? = null) : Exception(msg, cause)
+public class AmountParserException(msg: String? = null, cause: Throwable? = null) :
+ Exception(msg, cause)
+
+public class AmountOverflowException(msg: String? = null, cause: Throwable? = null) :
+ Exception(msg, cause)
@Serializable(with = KotlinXAmountSerializer::class)
public data class Amount(
@@ -50,7 +53,7 @@ public data class Amount(
* of the base currency value. For example, a fraction
* of 50_000_000 would correspond to 50 cents.
*/
- val fraction: Int
+ val fraction: Int,
) : Comparable<Amount> {
public companion object {
@@ -63,8 +66,8 @@ public data class Amount(
public const val MAX_FRACTION: Int = 99_999_999
public fun fromDouble(currency: String, value: Double): Amount {
- val intPart = Math.floor(value).toLong()
- val fraPart = Math.floor((value - intPart) * FRACTIONAL_BASE).toInt()
+ val intPart = floor(value).toLong()
+ val fraPart = floor((value - intPart) * FRACTIONAL_BASE).toInt()
return Amount(currency, intPart, fraPart)
}
@@ -87,14 +90,34 @@ public data class Amount(
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)
+ checkFraction(fractionStr.getFraction())
} else 0
return Amount(checkCurrency(currency), value, fraction)
}
+ public fun isValidAmountStr(str: String): Boolean {
+ val split = str.split(".")
+ try {
+ checkValue(split[0].toLongOrNull())
+ } catch (e: AmountParserException) {
+ return false
+ }
+ // also check fraction, if it exists
+ if (split.size > 1) {
+ val fractionStr = split[1]
+ if (fractionStr.length > MAX_FRACTION_LENGTH) return false
+ val fraction = fractionStr.getFraction() ?: return false
+ return fraction <= MAX_FRACTION
+ }
+ return true
+ }
+
+ private fun String.getFraction(): Int? {
+ return "0.$this".toDoubleOrNull()
+ ?.times(FRACTIONAL_BASE)
+ ?.roundToInt()
+ }
+
public fun min(currency: String): Amount = Amount(currency, 0, 1)
public fun max(currency: String): Amount = Amount(currency, MAX_VALUE, MAX_FRACTION)
@@ -132,7 +155,8 @@ public data class Amount(
public operator fun plus(other: Amount): Amount {
check(currency == other.currency) { "Can only subtract from same currency" }
- val resultValue = value + other.value + floor((fraction + other.fraction).toDouble() / FRACTIONAL_BASE).toLong()
+ 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
diff --git a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
index 647512c..8ae96ad 100644
--- a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
@@ -55,6 +55,7 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import net.taler.common.Amount
+import net.taler.common.Amount.Companion.isValidAmountStr
import net.taler.wallet.exchanges.ExchangeItem
class ReceiveFundsFragment : Fragment() {
@@ -136,7 +137,8 @@ private fun ReceiveFundsIntro(
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = Decimal),
onValueChange = { input ->
isError = false
- text = input.filter { it.isDigit() || it == '.' }
+ val filtered = input.filter { it.isDigit() || it == '.' }
+ if (filtered.endsWith('.') || isValidAmountStr(filtered)) text = filtered
},
isError = isError,
label = {
diff --git a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
index 640fbf7..c5348a3 100644
--- a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
@@ -52,6 +52,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import net.taler.common.Amount
+import net.taler.common.Amount.Companion.isValidAmountStr
class SendFundsFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
@@ -126,7 +127,8 @@ private fun SendFundsIntro(
onValueChange = { input ->
isError = false
insufficientBalance = false
- text = input.filter { it.isDigit() || it == '.' }
+ val filtered = input.filter { it.isDigit() || it == '.' }
+ if (filtered.endsWith('.') || isValidAmountStr(filtered)) text = filtered
},
isError = isError || insufficientBalance,
label = {