[#223] fromZecString reject bad input.
- Implementing regex solution for validation of input ZecAmount after confirm button is pressed. - Added also unit tests for this new feature. - Move necessary components from sdk-ext-lib to sdk-ext-ui module. - Align implementation with comments form PR. - Fix name of run configuration. - Ensure consistent behavior of tests.
This commit is contained in:
parent
9485a7d4c3
commit
4e5e5c4ab7
|
@ -1,6 +1,6 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="sdk-ext-ui:connectedCheck" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
|
||||
<module name="zcash-android-app.sdk-ext-ui" />
|
||||
<configuration default="false" name="sdk-ext-ui-lib:connectedCheck" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
|
||||
<module name="zcash-android-app.sdk-ext-ui-lib" />
|
||||
<option name="TESTING_TYPE" value="0" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="CLASS_NAME" value="" />
|
|
@ -148,6 +148,7 @@ dependencies {
|
|||
androidTestImplementation(libs.bundles.androidx.test)
|
||||
androidTestImplementation(projects.sdkExtLib)
|
||||
androidTestImplementation(projects.spackleLib)
|
||||
androidTestImplementation(projects.sdkExtUiLib)
|
||||
|
||||
if (isOrchestratorEnabled) {
|
||||
androidTestUtil(libs.androidx.test.orchestrator) {
|
||||
|
|
|
@ -23,9 +23,9 @@ import androidx.test.filters.SmallTest
|
|||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import androidx.test.runner.screenshot.Screenshot
|
||||
import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.fixture.SeedPhraseFixture
|
||||
import cash.z.ecc.sdk.fixture.WalletAddressFixture
|
||||
import cash.z.ecc.sdk.model.MonetarySeparators
|
||||
import co.electriccoin.zcash.app.test.EccScreenCaptureProcessor
|
||||
import co.electriccoin.zcash.app.test.getStringResource
|
||||
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
package cash.z.ecc.sdk.model
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class ZecStringTest {
|
||||
|
||||
companion object {
|
||||
private val EN_US_MONETARY_SEPARATORS = MonetarySeparators(',', '.')
|
||||
}
|
||||
|
||||
@Test
|
||||
fun empty_string() {
|
||||
val actual = Zatoshi.fromZecString("", EN_US_MONETARY_SEPARATORS)
|
||||
val expected = null
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decimal_monetary_separator() {
|
||||
val actual = Zatoshi.fromZecString("1.13", EN_US_MONETARY_SEPARATORS)
|
||||
val expected = Zatoshi(113000000L)
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun comma_grouping_separator() {
|
||||
val actual = Zatoshi.fromZecString("1,130", EN_US_MONETARY_SEPARATORS)
|
||||
val expected = Zatoshi(113000000000L)
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decimal_monetary_and() {
|
||||
val actual = Zatoshi.fromZecString("1,130", EN_US_MONETARY_SEPARATORS)
|
||||
val expected = Zatoshi(113000000000L)
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toZecString() {
|
||||
val expected = "1.13000000"
|
||||
val actual = Zatoshi(113000000).toZecString()
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("https://github.com/zcash/zcash-android-wallet-sdk/issues/412")
|
||||
fun round_trip() {
|
||||
val expected = Zatoshi(113000000L)
|
||||
val actual = Zatoshi.fromZecString(expected.toZecString(), EN_US_MONETARY_SEPARATORS)
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("https://github.com/zcash/secant-android-wallet/issues/223")
|
||||
fun parse_bad_string() {
|
||||
val actual = Zatoshi.fromZecString("asdf", EN_US_MONETARY_SEPARATORS)
|
||||
|
||||
assertNull(actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("https://github.com/zcash/secant-android-wallet/issues/223")
|
||||
fun parse_bad_number() {
|
||||
val actual = Zatoshi.fromZecString("1.2,3,4", EN_US_MONETARY_SEPARATORS)
|
||||
|
||||
assertNull(actual)
|
||||
}
|
||||
}
|
|
@ -1,47 +1,5 @@
|
|||
package cash.z.ecc.sdk.model
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
data class ZecSend(val destination: WalletAddress, val amount: Zatoshi, val memo: Memo) {
|
||||
companion object
|
||||
}
|
||||
|
||||
fun ZecSend.Companion.new(
|
||||
destinationString: String,
|
||||
zecString: String,
|
||||
memoString: String,
|
||||
monetarySeparators: MonetarySeparators
|
||||
): ZecSendValidation {
|
||||
|
||||
// This runBlocking shouldn't have a performance impact, since everything needs to be loaded at this point.
|
||||
// However it would be better to eliminate it entirely.
|
||||
val destination = runBlocking { WalletAddress.Unified.new(destinationString) }
|
||||
val amount = Zatoshi.fromZecString(zecString, monetarySeparators)
|
||||
val memo = Memo(memoString)
|
||||
|
||||
val validationErrors = buildSet {
|
||||
if (null == amount) {
|
||||
add(ZecSendValidation.Invalid.ValidationError.INVALID_AMOUNT)
|
||||
}
|
||||
|
||||
// TODO [#250]: Implement all validation
|
||||
// TODO [#342]: https://github.com/zcash/zcash-android-wallet-sdk/issues/342
|
||||
}
|
||||
|
||||
return if (validationErrors.isEmpty()) {
|
||||
ZecSendValidation.Valid(ZecSend(destination, amount!!, memo))
|
||||
} else {
|
||||
ZecSendValidation.Invalid(validationErrors)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ZecSendValidation {
|
||||
data class Valid(val zecSend: ZecSend) : ZecSendValidation()
|
||||
data class Invalid(val validationErrors: Set<ValidationError>) : ZecSendValidation() {
|
||||
enum class ValidationError {
|
||||
INVALID_ADDRESS,
|
||||
INVALID_AMOUNT,
|
||||
INVALID_MEMO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,15 @@ android {
|
|||
allWarningsAsErrors = project.property("ZCASH_IS_TREAT_WARNINGS_AS_ERRORS").toString().toBoolean()
|
||||
freeCompilerArgs = freeCompilerArgs.plus("-Xopt-in=kotlin.RequiresOptIn")
|
||||
}
|
||||
|
||||
resourcePrefix = "co_electriccoin_zcash_"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.sdkExtLib)
|
||||
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
|
||||
androidTestImplementation(libs.bundles.androidx.test)
|
||||
androidTestImplementation(libs.kotlin.test)
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package cash.z.ecc.sdk.ext.ui.model
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import cash.z.ecc.sdk.model.Zatoshi
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.util.Locale
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class ZecStringTest {
|
||||
|
||||
companion object {
|
||||
private val EN_US_MONETARY_SEPARATORS = MonetarySeparators(',', '.')
|
||||
private val context = run {
|
||||
val applicationContext = ApplicationProvider.getApplicationContext<Context>()
|
||||
val enUsConfiguration = Configuration(applicationContext.resources.configuration).apply {
|
||||
setLocale(Locale.US)
|
||||
}
|
||||
applicationContext.createConfigurationContext(enUsConfiguration)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun empty_string() {
|
||||
val actual = Zatoshi.fromZecString(context, "", EN_US_MONETARY_SEPARATORS)
|
||||
val expected = null
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decimal_monetary_separator() {
|
||||
val actual = Zatoshi.fromZecString(context, "1.13", EN_US_MONETARY_SEPARATORS)
|
||||
val expected = Zatoshi(113000000L)
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun comma_grouping_separator() {
|
||||
val actual = Zatoshi.fromZecString(context, "1,130", EN_US_MONETARY_SEPARATORS)
|
||||
val expected = Zatoshi(113000000000L)
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decimal_monetary_and() {
|
||||
val actual = Zatoshi.fromZecString(context, "1,130", EN_US_MONETARY_SEPARATORS)
|
||||
val expected = Zatoshi(113000000000L)
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("https://github.com/zcash/zcash-android-wallet-sdk/issues/412")
|
||||
fun toZecString() {
|
||||
val expected = "1.13000000"
|
||||
val actual = Zatoshi(113000000).toZecString()
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("https://github.com/zcash/zcash-android-wallet-sdk/issues/412")
|
||||
fun round_trip() {
|
||||
val expected = Zatoshi(113000000L)
|
||||
val actual = Zatoshi.fromZecString(context, expected.toZecString(), EN_US_MONETARY_SEPARATORS)
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_bad_string() {
|
||||
assertNull(Zatoshi.fromZecString(context, "", EN_US_MONETARY_SEPARATORS))
|
||||
assertNull(Zatoshi.fromZecString(context, "+@#$~^&*=", EN_US_MONETARY_SEPARATORS))
|
||||
assertNull(Zatoshi.fromZecString(context, "asdf", EN_US_MONETARY_SEPARATORS))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_invalid_numbers() {
|
||||
assertNull(Zatoshi.fromZecString(context, "", EN_US_MONETARY_SEPARATORS))
|
||||
assertNull(Zatoshi.fromZecString(context, "1,2", EN_US_MONETARY_SEPARATORS))
|
||||
assertNull(Zatoshi.fromZecString(context, "1,23,", EN_US_MONETARY_SEPARATORS))
|
||||
assertNull(Zatoshi.fromZecString(context, "1,234,", EN_US_MONETARY_SEPARATORS))
|
||||
}
|
||||
}
|
|
@ -3,8 +3,8 @@ package cash.z.ecc.sdk.ext.ui.regex
|
|||
import androidx.test.filters.SmallTest
|
||||
import cash.z.ecc.sdk.ext.ui.R
|
||||
import cash.z.ecc.sdk.ext.ui.ZecStringExt
|
||||
import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.ext.ui.test.getStringResourceWithArgs
|
||||
import cash.z.ecc.sdk.model.MonetarySeparators
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotNull
|
||||
|
@ -18,7 +18,17 @@ class ZecStringExtTest {
|
|||
|
||||
private fun getContinuousRegex(): Regex {
|
||||
return getStringResourceWithArgs(
|
||||
R.string.zec_amount_regex_continuous_filter,
|
||||
R.string.co_electriccoin_zcash_zec_amount_regex_continuous_filter,
|
||||
arrayOf(
|
||||
EN_US_SEPARATORS.grouping,
|
||||
EN_US_SEPARATORS.decimal
|
||||
)
|
||||
).toRegex()
|
||||
}
|
||||
|
||||
private fun getConfirmRegex(): Regex {
|
||||
return getStringResourceWithArgs(
|
||||
R.string.co_electriccoin_zcash_zec_amount_regex_confirm_filter,
|
||||
arrayOf(
|
||||
EN_US_SEPARATORS.grouping,
|
||||
EN_US_SEPARATORS.decimal
|
||||
|
@ -28,9 +38,9 @@ class ZecStringExtTest {
|
|||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun check_regex_validity() {
|
||||
fun check_continuous_regex_validity() {
|
||||
val regexString = getStringResourceWithArgs(
|
||||
R.string.zec_amount_regex_continuous_filter,
|
||||
R.string.co_electriccoin_zcash_zec_amount_regex_continuous_filter,
|
||||
arrayOf(
|
||||
EN_US_SEPARATORS.grouping,
|
||||
EN_US_SEPARATORS.decimal
|
||||
|
@ -48,7 +58,27 @@ class ZecStringExtTest {
|
|||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun check_regex_functionality_valid_inputs() {
|
||||
fun check_confirm_regex_validity() {
|
||||
val regexString = getStringResourceWithArgs(
|
||||
R.string.co_electriccoin_zcash_zec_amount_regex_confirm_filter,
|
||||
arrayOf(
|
||||
EN_US_SEPARATORS.grouping,
|
||||
EN_US_SEPARATORS.decimal
|
||||
)
|
||||
)
|
||||
assertNotNull(regexString)
|
||||
|
||||
val regexAmountChecker = regexString.toRegex()
|
||||
|
||||
regexAmountChecker.also {
|
||||
assertNotNull(regexAmountChecker)
|
||||
assertTrue(regexAmountChecker.pattern.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun check_continuous_regex_functionality_valid_inputs() {
|
||||
getContinuousRegex().also {
|
||||
assertTrue(it.matches(""))
|
||||
assertTrue(it.matches("123"))
|
||||
|
@ -66,7 +96,7 @@ class ZecStringExtTest {
|
|||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun check_regex_functionality_invalid_inputs() {
|
||||
fun check_continuous_regex_functionality_invalid_inputs() {
|
||||
getContinuousRegex().also {
|
||||
assertFalse(it.matches("aaa"))
|
||||
assertFalse(it.matches("123aaa"))
|
||||
|
@ -80,6 +110,44 @@ class ZecStringExtTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun check_confirm_regex_functionality_valid_inputs() {
|
||||
getConfirmRegex().also {
|
||||
assertTrue(it.matches("123"))
|
||||
assertTrue(it.matches(".123"))
|
||||
assertTrue(it.matches("1,234"))
|
||||
assertTrue(it.matches("1,234,567,890"))
|
||||
assertTrue(it.matches("1.2"))
|
||||
assertTrue(it.matches("123.4"))
|
||||
assertTrue(it.matches("1.234"))
|
||||
assertTrue(it.matches("1,123."))
|
||||
assertTrue(it.matches("1,234.567"))
|
||||
assertTrue(it.matches("1,234,567.890"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun check_confirm_regex_functionality_invalid_inputs() {
|
||||
getContinuousRegex().also {
|
||||
assertFalse(it.matches("+@#$~^&*="))
|
||||
assertFalse(it.matches("asdf"))
|
||||
assertFalse(it.matches(".."))
|
||||
assertFalse(it.matches(","))
|
||||
assertFalse(it.matches(",,"))
|
||||
assertFalse(it.matches(",."))
|
||||
assertFalse(it.matches(".,"))
|
||||
assertFalse(it.matches(",123"))
|
||||
assertFalse(it.matches("1,2,3"))
|
||||
assertFalse(it.matches("1.2,3,4"))
|
||||
assertFalse(it.matches("123,,456"))
|
||||
assertFalse(it.matches("123..456"))
|
||||
assertFalse(it.matches("1.234,567"))
|
||||
assertFalse(it.matches("1.234,567,890"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun check_digits_between_grouping_separators_valid_test() {
|
|
@ -0,0 +1,54 @@
|
|||
package cash.z.ecc.sdk.ext.ui
|
||||
|
||||
import android.content.Context
|
||||
import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.ext.ui.model.fromZecString
|
||||
import cash.z.ecc.sdk.model.Memo
|
||||
import cash.z.ecc.sdk.model.WalletAddress
|
||||
import cash.z.ecc.sdk.model.Zatoshi
|
||||
import cash.z.ecc.sdk.model.ZecSend
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
object ZecSendExt {
|
||||
|
||||
fun new(
|
||||
context: Context,
|
||||
destinationString: String,
|
||||
zecString: String,
|
||||
memoString: String,
|
||||
monetarySeparators: MonetarySeparators
|
||||
): ZecSendValidation {
|
||||
|
||||
// This runBlocking shouldn't have a performance impact, since everything needs to be loaded at this point.
|
||||
// However it would be better to eliminate it entirely.
|
||||
val destination = runBlocking { WalletAddress.Unified.new(destinationString) }
|
||||
val amount = Zatoshi.fromZecString(context, zecString, monetarySeparators)
|
||||
val memo = Memo(memoString)
|
||||
|
||||
val validationErrors = buildSet {
|
||||
if (null == amount) {
|
||||
add(ZecSendValidation.Invalid.ValidationError.INVALID_AMOUNT)
|
||||
}
|
||||
|
||||
// TODO [#250]: Implement all validation
|
||||
// TODO [#342]: https://github.com/zcash/zcash-android-wallet-sdk/issues/342
|
||||
}
|
||||
|
||||
return if (validationErrors.isEmpty()) {
|
||||
ZecSendValidation.Valid(ZecSend(destination, amount!!, memo))
|
||||
} else {
|
||||
ZecSendValidation.Invalid(validationErrors)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ZecSendValidation {
|
||||
data class Valid(val zecSend: ZecSend) : ZecSendValidation()
|
||||
data class Invalid(val validationErrors: Set<ValidationError>) : ZecSendValidation() {
|
||||
enum class ValidationError {
|
||||
INVALID_ADDRESS,
|
||||
INVALID_AMOUNT,
|
||||
INVALID_MEMO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
package cash.z.ecc.sdk.ext.ui
|
||||
|
||||
import android.content.Context
|
||||
import cash.z.ecc.sdk.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators
|
||||
|
||||
object ZecStringExt {
|
||||
|
||||
private const val DIGITS_BETWEEN_GROUP_SEPARATORS = 3
|
||||
|
||||
/**
|
||||
* Builds filter with current local monetary separators for continuous input checking. Solution
|
||||
* is build upon regex validation and character checking.
|
||||
* Builds filter with current local monetary separators for continuous input checking. The
|
||||
* solution is built upon regex validation and other common string validation checks.
|
||||
*
|
||||
* Regex example: ^([0-9]*([0-9]+([,]$|[,][0-9]+))*([.]$|[.][0-9]+)?)?$
|
||||
* Inputs may differ according to user locale.
|
||||
*
|
||||
* Valid amounts: "" . | .123 | 123, | 123. | 123,456 | 123.456 | 123,456.789 | 123,456,789 | 123,456,789.123 | etc.
|
||||
* Invalid amounts: 123,, | 123,. | 123.. | .123 | ,123 | 123.456.789 | etc.
|
||||
* Invalid amounts: 123,, | 123,. | 123.. | ,123 | 123.456.789 | etc.
|
||||
*
|
||||
* @param context used for loading localized pattern from strings.xml
|
||||
* @param separators which consist of localized monetary separators
|
||||
|
@ -25,7 +25,7 @@ object ZecStringExt {
|
|||
*/
|
||||
fun filterContinuous(context: Context, separators: MonetarySeparators, zecString: String): Boolean {
|
||||
if (!context.getString(
|
||||
R.string.zec_amount_regex_continuous_filter,
|
||||
R.string.co_electriccoin_zcash_zec_amount_regex_continuous_filter,
|
||||
separators.grouping,
|
||||
separators.decimal
|
||||
).toRegex().matches(zecString) || !checkFor3Digits(separators, zecString)
|
||||
|
@ -54,4 +54,38 @@ object ZecStringExt {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds filter with current local monetary separators for validation of entered ZEC amount
|
||||
* after confirm button is pressed. The solution is built upon regex validation and other common
|
||||
* string validation checks.
|
||||
*
|
||||
* Regex example: ^([0-9]{1,3}(?:[,]?[0-9]{3})*)*(?:[0-9]*[.][0-9]*)?$
|
||||
* Inputs may differ according to user locale.
|
||||
*
|
||||
* Valid amounts: 123 | .123 | 123. | 123, | 123.456 | 123,456 | 123,456.789 | 123,456,789 | 123,456,789.123 | etc.
|
||||
* Invalid amounts: "" | , | . | 123,, | 123,. | 123.. | ,123 | 123.456.789 | etc.
|
||||
*
|
||||
* @param context used for loading localized pattern from strings.xml
|
||||
* @param separators which consist of localized monetary separators
|
||||
* @param zecString to be validated
|
||||
*
|
||||
* @return true in case of validation success, false otherwise
|
||||
*/
|
||||
fun filterConfirm(context: Context, separators: MonetarySeparators, zecString: String): Boolean {
|
||||
if (zecString.isBlank() ||
|
||||
zecString == separators.grouping.toString() ||
|
||||
zecString == separators.decimal.toString()
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return (
|
||||
context.getString(
|
||||
R.string.co_electriccoin_zcash_zec_amount_regex_confirm_filter,
|
||||
separators.grouping,
|
||||
separators.decimal
|
||||
).toRegex().matches(zecString) && checkFor3Digits(separators, zecString)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
package cash.z.ecc.sdk.model
|
||||
package cash.z.ecc.sdk.ext.ui.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.sdk.ext.ui.ZecStringExt
|
||||
import cash.z.ecc.sdk.model.Zatoshi
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
|
@ -64,8 +67,12 @@ fun Zatoshi.toZecString() = value.convertZatoshiToZecString(DECIMALS, DECIMALS)
|
|||
/**
|
||||
* @return [zecString] parsed into Zatoshi or null if parsing failed.
|
||||
*/
|
||||
fun Zatoshi.Companion.fromZecString(zecString: String, monetarySeparators: MonetarySeparators): Zatoshi? {
|
||||
if (zecString.isBlank()) {
|
||||
fun Zatoshi.Companion.fromZecString(
|
||||
context: Context,
|
||||
zecString: String,
|
||||
monetarySeparators: MonetarySeparators
|
||||
): Zatoshi? {
|
||||
if (!ZecStringExt.filterConfirm(context, monetarySeparators, zecString)) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -81,6 +88,7 @@ fun Zatoshi.Companion.fromZecString(zecString: String, monetarySeparators: Monet
|
|||
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) {
|
|
@ -0,0 +1,4 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="co_electriccoin_zcash_zec_amount_regex_continuous_filter" formatted="true" tools:ignore="TypographyDashes">^([0-9]*([0-9]+([<xliff:g id="group" example=",">%1$s</xliff:g>]$|[<xliff:g id="group" example=",">%1$s</xliff:g>][0-9]+))*([<xliff:g id="dec" example=".">%2$s</xliff:g>]$|[<xliff:g id="dec" example=".">%2$s</xliff:g>][0-9]+)?)?$</string>
|
||||
<string name="co_electriccoin_zcash_zec_amount_regex_confirm_filter" formatted="true" tools:ignore="TypographyDashes">^([0-9]{1,3}(?:[<xliff:g id="group" example=",">%1$s</xliff:g>]?[0-9]{3})*)*(?:[0-9]*[<xliff:g id="group" example=".">%2$s</xliff:g>][0-9]*)?$</string>
|
||||
</resources>
|
|
@ -1,3 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="zec_amount_regex_continuous_filter" formatted="true" tools:ignore="TypographyDashes">^([0-9]*([0-9]+([<xliff:g id="group" example=",">%1$s</xliff:g>]$|[<xliff:g id="group" example=",">%1$s</xliff:g>][0-9]+))*([<xliff:g id="dec" example=".">%2$s</xliff:g>]$|[<xliff:g id="dec" example=".">%2$s</xliff:g>][0-9]+)?)?$</string>
|
||||
</resources>
|
|
@ -210,7 +210,7 @@ include("build-info-lib")
|
|||
include("preference-api-lib")
|
||||
include("preference-impl-android-lib")
|
||||
include("sdk-ext-lib")
|
||||
include("sdk-ext-ui")
|
||||
include("sdk-ext-ui-lib")
|
||||
include("spackle-lib")
|
||||
include("ui-design-lib")
|
||||
include("ui-lib")
|
||||
|
|
|
@ -63,7 +63,7 @@ dependencies {
|
|||
implementation(projects.preferenceApiLib)
|
||||
implementation(projects.preferenceImplAndroidLib)
|
||||
implementation(projects.sdkExtLib)
|
||||
implementation(projects.sdkExtUi)
|
||||
implementation(projects.sdkExtUiLib)
|
||||
implementation(projects.spackleLib)
|
||||
implementation(projects.uiDesignLib)
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ import androidx.compose.ui.test.performClick
|
|||
import androidx.compose.ui.test.performTextClearance
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.test.filters.MediumTest
|
||||
import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.fixture.WalletAddressFixture
|
||||
import cash.z.ecc.sdk.fixture.ZecRequestFixture
|
||||
import cash.z.ecc.sdk.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.model.Zatoshi
|
||||
import cash.z.ecc.sdk.model.ZecRequest
|
||||
import cash.z.ecc.sdk.model.ZecRequestMessage
|
||||
|
@ -102,8 +102,8 @@ class RequestViewTest {
|
|||
composeTestRule.clickCreateAndSend()
|
||||
assertEquals(1, testSetup.getOnCreateCount())
|
||||
|
||||
// e.g. 123,
|
||||
composeTestRule.setAmount("123${separators.grouping}")
|
||||
// e.g. 123,456
|
||||
composeTestRule.setAmount("123${separators.grouping}456")
|
||||
composeTestRule.clickCreateAndSend()
|
||||
assertEquals(2, testSetup.getOnCreateCount())
|
||||
|
||||
|
|
|
@ -10,12 +10,12 @@ import androidx.compose.ui.test.performClick
|
|||
import androidx.compose.ui.test.performTextClearance
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.test.filters.MediumTest
|
||||
import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.fixture.MemoFixture
|
||||
import cash.z.ecc.sdk.fixture.WalletAddressFixture
|
||||
import cash.z.ecc.sdk.fixture.ZatoshiFixture
|
||||
import cash.z.ecc.sdk.fixture.ZecRequestFixture
|
||||
import cash.z.ecc.sdk.model.Memo
|
||||
import cash.z.ecc.sdk.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.model.Zatoshi
|
||||
import cash.z.ecc.sdk.model.ZecSend
|
||||
import co.electriccoin.zcash.ui.R
|
||||
|
|
|
@ -20,7 +20,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import cash.z.ecc.android.sdk.db.entity.Transaction
|
||||
import cash.z.ecc.sdk.model.toZecString
|
||||
import cash.z.ecc.sdk.ext.ui.model.toZecString
|
||||
import cash.z.ecc.sdk.model.total
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
||||
|
|
|
@ -25,14 +25,14 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cash.z.ecc.sdk.ext.ui.ZecStringExt
|
||||
import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.ext.ui.model.ZecString
|
||||
import cash.z.ecc.sdk.ext.ui.model.fromZecString
|
||||
import cash.z.ecc.sdk.fixture.WalletAddressFixture
|
||||
import cash.z.ecc.sdk.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.model.WalletAddress
|
||||
import cash.z.ecc.sdk.model.Zatoshi
|
||||
import cash.z.ecc.sdk.model.ZecRequest
|
||||
import cash.z.ecc.sdk.model.ZecRequestMessage
|
||||
import cash.z.ecc.sdk.model.ZecString
|
||||
import cash.z.ecc.sdk.model.fromZecString
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||
|
@ -129,7 +129,7 @@ private fun RequestMainContent(
|
|||
|
||||
Spacer(Modifier.fillMaxHeight(MINIMAL_WEIGHT))
|
||||
|
||||
val zatoshi = Zatoshi.fromZecString(amountZecString, monetarySeparators)
|
||||
val zatoshi = Zatoshi.fromZecString(context, amountZecString, monetarySeparators)
|
||||
|
||||
PrimaryButton(
|
||||
onClick = {
|
||||
|
|
|
@ -28,16 +28,15 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cash.z.ecc.sdk.ext.ui.ZecSendExt
|
||||
import cash.z.ecc.sdk.ext.ui.ZecStringExt
|
||||
import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.ext.ui.model.ZecString
|
||||
import cash.z.ecc.sdk.ext.ui.model.toZecString
|
||||
import cash.z.ecc.sdk.fixture.ZatoshiFixture
|
||||
import cash.z.ecc.sdk.model.Memo
|
||||
import cash.z.ecc.sdk.model.MonetarySeparators
|
||||
import cash.z.ecc.sdk.model.Zatoshi
|
||||
import cash.z.ecc.sdk.model.ZecSend
|
||||
import cash.z.ecc.sdk.model.ZecSendValidation
|
||||
import cash.z.ecc.sdk.model.ZecString
|
||||
import cash.z.ecc.sdk.model.new
|
||||
import cash.z.ecc.sdk.model.toZecString
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||
|
@ -157,7 +156,9 @@ private fun SendForm(
|
|||
}
|
||||
var memoString by rememberSaveable { mutableStateOf(previousZecSend?.memo?.value ?: "") }
|
||||
|
||||
var validation by rememberSaveable { mutableStateOf<Set<ZecSendValidation.Invalid.ValidationError>>(emptySet()) }
|
||||
var validation by rememberSaveable {
|
||||
mutableStateOf<Set<ZecSendExt.ZecSendValidation.Invalid.ValidationError>>(emptySet())
|
||||
}
|
||||
|
||||
Column(Modifier.fillMaxHeight()) {
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
|
@ -205,7 +206,8 @@ private fun SendForm(
|
|||
|
||||
PrimaryButton(
|
||||
onClick = {
|
||||
val zecSendValidation = ZecSend.new(
|
||||
val zecSendValidation = ZecSendExt.new(
|
||||
context,
|
||||
recipientAddressString,
|
||||
amountZecString,
|
||||
memoString,
|
||||
|
@ -213,8 +215,8 @@ private fun SendForm(
|
|||
)
|
||||
|
||||
when (zecSendValidation) {
|
||||
is ZecSendValidation.Valid -> onCreateAndSend(zecSendValidation.zecSend)
|
||||
is ZecSendValidation.Invalid -> validation = zecSendValidation.validationErrors
|
||||
is ZecSendExt.ZecSendValidation.Valid -> onCreateAndSend(zecSendValidation.zecSend)
|
||||
is ZecSendExt.ZecSendValidation.Invalid -> validation = zecSendValidation.validationErrors
|
||||
}
|
||||
},
|
||||
text = stringResource(id = R.string.send_create),
|
||||
|
|
Loading…
Reference in New Issue