zcash-android-wallet-sdk/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/model/ZecString.kt

107 lines
3.3 KiB
Kotlin

package cash.z.ecc.android.sdk.demoapp.model
import android.content.Context
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.ext.convertZecToZatoshi
import cash.z.ecc.android.sdk.model.Zatoshi
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.text.ParseException
import java.util.Locale
object ZecString {
fun allowedCharacters(monetarySeparators: MonetarySeparators) = buildSet<Char> {
add('0')
add('1')
add('2')
add('3')
add('4')
add('5')
add('6')
add('7')
add('8')
add('9')
add(monetarySeparators.decimal)
add(monetarySeparators.grouping)
}
}
data class MonetarySeparators(val grouping: Char, val decimal: Char) {
init {
require(grouping != decimal) { "Grouping and decimal separator cannot be the same character" }
}
companion object {
/**
* @return The current localized monetary separators. Do not cache this value, as it
* can change if the system Locale changes.
*/
fun current(): MonetarySeparators {
val decimalFormatSymbols = DecimalFormatSymbols.getInstance()
return MonetarySeparators(
decimalFormatSymbols.groupingSeparator,
decimalFormatSymbols.monetaryDecimalSeparator
)
}
}
}
private const val DECIMALS = 8
// TODO [#412]: https://github.com/zcash/zcash-android-wallet-sdk/issues/412
// The SDK needs to fix the API for currency conversion
fun Zatoshi.toZecString() = convertZatoshiToZecString(DECIMALS, DECIMALS)
/*
* ZEC is our own currency, so there's not going to be an existing localization that matches it perfectly.
*
* To ensure consistent behavior regardless of user Locale, use US localization except that we swap out the
* separator characters based on the user's current Locale. This should avoid unexpected surprises
* while also localizing the separator format.
*/
/**
* @return [zecString] parsed into Zatoshi or null if parsing failed.
*/
@SuppressWarnings("ReturnCount")
fun Zatoshi.Companion.fromZecString(
context: Context,
zecString: String,
monetarySeparators: MonetarySeparators
): Zatoshi? {
if (!ZecStringExt.filterConfirm(context, monetarySeparators, zecString)) {
return null
}
val symbols = DecimalFormatSymbols.getInstance(Locale.US).apply {
this.groupingSeparator = monetarySeparators.grouping
this.decimalSeparator = monetarySeparators.decimal
}
val localizedPattern = "#${monetarySeparators.grouping}##0${monetarySeparators.decimal}0#"
// TODO [#321]: https://github.com/zcash/secant-android-wallet/issues/321
val decimalFormat = DecimalFormat(localizedPattern, symbols).apply {
isParseBigDecimal = true
roundingMode = RoundingMode.HALF_EVEN // aka Bankers rounding
}
// TODO [#343]: https://github.com/zcash/secant-android-wallet/issues/343
val bigDecimal = try {
decimalFormat.parse(zecString) as BigDecimal
} catch (e: NumberFormatException) {
null
} catch (e: ParseException) {
null
}
@Suppress("SwallowedException")
return try {
bigDecimal.convertZecToZatoshi()
} catch (e: IllegalArgumentException) {
null
}
}