From 7ae8277a7a5ce0b349becd387b1aec16c7edefe9 Mon Sep 17 00:00:00 2001 From: Vitaly Katz Date: Fri, 29 Jan 2021 12:10:58 +0100 Subject: [PATCH 1/5] Adjust toolbar title for import owner flow (#1233) --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ac410c0aa4..581dc1614e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -312,7 +312,7 @@ Enter private key or seed phrase Seed phrase is not valid - Import Owner Key + Import owner key How does it work? Enter the private key or seed phrase of your owner key controlling your Safe. Your owner key will be imported into this app. You can then confirm proposed transactions on the go. How secure is that? From a1d81330d0939a798961a8e112dd9984ea6fb74e Mon Sep 17 00:00:00 2001 From: Vitaly Katz Date: Fri, 29 Jan 2021 16:41:46 +0100 Subject: [PATCH 2/5] Add callbacks to copy owner address to clipboard & open etherscan on add safe owner screen (#1236) --- .../safe/ui/safe/add/AddSafeOwnerFragment.kt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/gnosis/safe/ui/safe/add/AddSafeOwnerFragment.kt b/app/src/main/java/io/gnosis/safe/ui/safe/add/AddSafeOwnerFragment.kt index ea60a3f7b1..8e3cbcd41f 100644 --- a/app/src/main/java/io/gnosis/safe/ui/safe/add/AddSafeOwnerFragment.kt +++ b/app/src/main/java/io/gnosis/safe/ui/safe/add/AddSafeOwnerFragment.kt @@ -15,6 +15,10 @@ import io.gnosis.safe.ui.assets.AssetsFragmentDirections import io.gnosis.safe.ui.base.fragment.BaseViewBindingFragment import io.gnosis.safe.ui.settings.app.SettingsHandler import io.gnosis.safe.utils.formatEthAddress +import pm.gnosis.crypto.utils.asEthereumAddressChecksumString +import pm.gnosis.svalinn.common.utils.copyToClipboard +import pm.gnosis.svalinn.common.utils.openUrl +import pm.gnosis.svalinn.common.utils.snackbar import pm.gnosis.utils.asEthereumAddress import javax.inject.Inject @@ -23,7 +27,7 @@ class AddSafeOwnerFragment : BaseViewBindingFragment() private val name by lazy { navArgs.safeName } private val address by lazy { navArgs.safeAddress.asEthereumAddress()!! } - + @Inject lateinit var settingsHandler: SettingsHandler @@ -42,6 +46,19 @@ class AddSafeOwnerFragment : BaseViewBindingFragment Date: Fri, 29 Jan 2021 17:46:25 +0100 Subject: [PATCH 3/5] Use defined number formatting for fiat balances (#1235) --- .../io/gnosis/safe/utils/BalanceFormatter.kt | 10 +- .../gnosis/safe/utils/BalanceFormatterTest.kt | 96 +++++++++++++++++-- 2 files changed, 95 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/io/gnosis/safe/utils/BalanceFormatter.kt b/app/src/main/java/io/gnosis/safe/utils/BalanceFormatter.kt index 4e5dc4cd3d..1f22d7d7f9 100644 --- a/app/src/main/java/io/gnosis/safe/utils/BalanceFormatter.kt +++ b/app/src/main/java/io/gnosis/safe/utils/BalanceFormatter.kt @@ -54,11 +54,15 @@ class BalanceFormatter { } } - fun fiatBalanceWithCurrency(amount: BigDecimal, currencyCode: String): String = - NumberFormat.getCurrencyInstance(Locale.getDefault()).apply { + fun fiatBalanceWithCurrency(amount: BigDecimal, currencyCode: String): String { + val numberFormat = NumberFormat.getCurrencyInstance(Locale.getDefault()).apply { maximumFractionDigits = 2 currency = Currency.getInstance(currencyCode) - }.format(amount) + } + val formattedAmount = numberFormat.format(0) + val shortAmount = shortAmount(amount) + return formattedAmount.replace("0${decimalSeparator}00", shortAmount) + } fun shortAmount(value: BigDecimal): String = when { value <= BigDecimal.ZERO -> { diff --git a/app/src/test/java/io/gnosis/safe/utils/BalanceFormatterTest.kt b/app/src/test/java/io/gnosis/safe/utils/BalanceFormatterTest.kt index 5e80dbe0b8..609c75b1b9 100644 --- a/app/src/test/java/io/gnosis/safe/utils/BalanceFormatterTest.kt +++ b/app/src/test/java/io/gnosis/safe/utils/BalanceFormatterTest.kt @@ -122,42 +122,122 @@ class BalanceFormatterTest { } @Test - fun `fiatBalanceWithCurrency (USD 0 with US locale) $0 with 2 decimals`() { + fun `fiatBalanceWithCurrency (USD 0 with US locale) $0`() { val input = BigDecimal.valueOf(0) Locale.setDefault(Locale.US) + val balanceFormatter = BalanceFormatter() val actual = balanceFormatter.fiatBalanceWithCurrency(input, "USD") - val expected = "$0.00" + val expected = "$0" assertEquals(expected, actual) } @Test - fun `fiatBalanceWithCurrency (USD 1000 with US locale) $1,000 with 2 decimals`() { + fun `fiatBalanceWithCurrency (USD 1000 with US locale) $1,000`() { val input = BigDecimal.valueOf(1000) Locale.setDefault(Locale.US) + val balanceFormatter = BalanceFormatter() val actual = balanceFormatter.fiatBalanceWithCurrency(input, "USD") - val expected = "$1,000.00" + val expected = "$1,000" assertEquals(expected, actual) } @Test - fun `fiatBalanceWithCurrency (EUR 1000 with US locale) €1,000 with 2 decimals`() { + fun `fiatBalanceWithCurrency (EUR 1000 with US locale) €1,000`() { val input = BigDecimal.valueOf(1000) Locale.setDefault(Locale.UK) + val balanceFormatter = BalanceFormatter() val actual = balanceFormatter.fiatBalanceWithCurrency(input, "EUR") - val expected = "€1,000.00" + val expected = "€1,000" assertEquals(expected, actual) } @Test - fun `fiatBalanceWithCurrency (EUR 1000 with DE locale) €1,000 with 2 decimals`() { + fun `fiatBalanceWithCurrency (EUR 1000 with DE locale) €1_000`() { val input = BigDecimal.valueOf(1000) Locale.setDefault(Locale.GERMANY) + val balanceFormatter = BalanceFormatter() val actual = balanceFormatter.fiatBalanceWithCurrency(input, "EUR") - val expected = "1.000,00 €" + val expected = "1.000 €" assertEquals(expected, actual) } + + @Test + fun `fiatBalanceWithCurrency (EUR with DE locale) should have correct number of decimals`() { + + Locale.setDefault(Locale.GERMANY) + val balanceFormatter = BalanceFormatter() + + // 5 decimals till 1k + val value1 = BigDecimal.valueOf(0.123456789) + val fiatAmount1 = balanceFormatter.fiatBalanceWithCurrency(value1, "EUR") + assertEquals("0,12345 €", fiatAmount1) + + // 4 decimals till 10k + val value2 = BigDecimal.valueOf(1000.123456789) + val fiatAmount2 = balanceFormatter.fiatBalanceWithCurrency(value2, "EUR") + assertEquals("1.000,1234 €", fiatAmount2) + + // 3 decimals till 100k + val value3 = BigDecimal.valueOf(10_000.123456789) + val fiatAmount3 = balanceFormatter.fiatBalanceWithCurrency(value3, "EUR") + assertEquals("10.000,123 €", fiatAmount3) + + // 2 decimals till 1M + val value4 = BigDecimal.valueOf(100_000.123456789) + val fiatAmount4 = balanceFormatter.fiatBalanceWithCurrency(value4, "EUR") + assertEquals("100.000,12 €", fiatAmount4) + + // 1 decimal till 10M + val value5 = BigDecimal.valueOf(5_000_000.123456789) + val fiatAmount5 = balanceFormatter.fiatBalanceWithCurrency(value5, "EUR") + assertEquals("5.000.000,1 €", fiatAmount5) + + // no decimals after 10M + val value6 = BigDecimal.valueOf(15_000_000.123456789) + val fiatAmount6 = balanceFormatter.fiatBalanceWithCurrency(value6, "EUR") + assertEquals("15.000.000 €", fiatAmount6) + } + + @Test + fun `fiatBalanceWithCurrency (EUR with DE locale) should have correct notation for big numbers`() { + + Locale.setDefault(Locale.GERMANY) + val balanceFormatter = BalanceFormatter() + + val value1 = BigDecimal.valueOf(100_000_000.123456789) + val fiatAmount1 = balanceFormatter.fiatBalanceWithCurrency(value1, "EUR") + assertEquals("100M €", fiatAmount1) + + val value2 = BigDecimal.valueOf(100_100_000.123456789) + val fiatAmount2 = balanceFormatter.fiatBalanceWithCurrency(value2,"EUR") + assertEquals("100,1M €", fiatAmount2) + + val value3 = BigDecimal.valueOf(999_999_999.123456789) + val fiatAmount3 = balanceFormatter.fiatBalanceWithCurrency(value3, "EUR") + assertEquals("999,999M €", fiatAmount3) + + val value4 = BigDecimal.valueOf(1_999_999_999.123456789) + val fiatAmount4 = balanceFormatter.fiatBalanceWithCurrency(value4, "EUR") + assertEquals("1,999B €", fiatAmount4) + + val value5 = BigDecimal.valueOf(1_999_999_999_999.123456789) + val fiatAmount5 = balanceFormatter.fiatBalanceWithCurrency(value5, "EUR") + assertEquals("1,999T €", fiatAmount5) + + val value6 = BigDecimal.valueOf(999_000_000_000_000) + val fiatAmount6 = balanceFormatter.fiatBalanceWithCurrency(value6, "EUR") + assertEquals("999T €", fiatAmount6) + + val value7 = BigDecimal.valueOf(999_999_999_999_999.123456789) + val fiatAmount7 = balanceFormatter.fiatBalanceWithCurrency(value7, "EUR") + assertEquals("999,999T €", fiatAmount7) + + val value8 = BigDecimal.valueOf(1_999_999_999_999_999.123456789) + val fiatAmount8 = balanceFormatter.fiatBalanceWithCurrency(value8, "EUR") + assertEquals("> 999T €", fiatAmount8) + } } From e05c72ef124b18fa1a1dea2ecb4dd2e6e573a1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dirk=20J=C3=A4ckel?= Date: Fri, 29 Jan 2021 18:18:35 +0100 Subject: [PATCH 4/5] Text and margin updated (#1232) --- .../settings/owner/OwnerSeedPhraseFragment.kt | 21 +++++++++++++++++++ .../res/layout/fragment_owner_seed_phrase.xml | 6 +++--- app/src/main/res/values/dimens.xml | 2 ++ app/src/main/res/values/strings.xml | 3 +-- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/gnosis/safe/ui/settings/owner/OwnerSeedPhraseFragment.kt b/app/src/main/java/io/gnosis/safe/ui/settings/owner/OwnerSeedPhraseFragment.kt index 1be380e604..9ca03e8239 100644 --- a/app/src/main/java/io/gnosis/safe/ui/settings/owner/OwnerSeedPhraseFragment.kt +++ b/app/src/main/java/io/gnosis/safe/ui/settings/owner/OwnerSeedPhraseFragment.kt @@ -2,10 +2,14 @@ package io.gnosis.safe.ui.settings.owner import android.os.Bundle import android.text.InputType +import android.text.Spannable +import android.text.SpannableString +import android.text.style.AbsoluteSizeSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo +import androidx.annotation.StringRes import androidx.core.widget.doOnTextChanged import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController @@ -18,6 +22,7 @@ import io.gnosis.safe.ui.base.fragment.BaseViewBindingFragment import pm.gnosis.svalinn.common.utils.hideSoftKeyboard import pm.gnosis.svalinn.common.utils.showKeyboardForView import timber.log.Timber +import java.util.regex.Pattern import javax.inject.Inject class OwnerSeedPhraseFragment : BaseViewBindingFragment() { @@ -33,6 +38,8 @@ class OwnerSeedPhraseFragment : BaseViewBindingFragment + tools:text="@string/enter_seed_phrase_description" + android:gravity="center_horizontal" /> 2dp 2dp 44dp + 8dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 581dc1614e..91ee094d40 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -308,8 +308,7 @@ Tx Notifications Notifications about incoming transfers and processed transactions. - Enter the private key or the seed phrase from your hardware wallet or MetaMask owner wallet.\n\nA seed phrase is typically 12 (sometimes 24) words separated by single spaces.\nA private key is a string of 64 characters. - + Enter the seed phrase from your hardware wallet or MetaMask or the private key from any wallet.\n\nA seed phrase is typically 12 (sometimes 24) words separated by single spaces.\nA private key is a string of 64 characters. Enter private key or seed phrase Seed phrase is not valid Import owner key From aee3497e984b4964c256258d5ec39b56ceda1830 Mon Sep 17 00:00:00 2001 From: Vitaly Katz Date: Fri, 29 Jan 2021 20:55:25 +0100 Subject: [PATCH 5/5] Reload content on opening (#1234) --- .../java/io/gnosis/safe/ui/assets/coins/CoinsFragment.kt | 7 +++++++ .../java/io/gnosis/safe/ui/assets/coins/CoinsViewModel.kt | 4 ++++ .../safe/ui/assets/collectibles/CollectiblesFragment.kt | 7 +++++++ .../safe/ui/assets/collectibles/CollectiblesViewModel.kt | 4 ++++ .../gnosis/safe/ui/settings/safe/SafeSettingsFragment.kt | 8 +++++++- .../gnosis/safe/ui/settings/safe/SafeSettingsViewModel.kt | 5 +++++ .../safe/ui/transactions/TransactionListFragment.kt | 2 +- 7 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/gnosis/safe/ui/assets/coins/CoinsFragment.kt b/app/src/main/java/io/gnosis/safe/ui/assets/coins/CoinsFragment.kt index 4b3831a612..9dd8f2f3d5 100644 --- a/app/src/main/java/io/gnosis/safe/ui/assets/coins/CoinsFragment.kt +++ b/app/src/main/java/io/gnosis/safe/ui/assets/coins/CoinsFragment.kt @@ -86,6 +86,13 @@ class CoinsFragment : BaseViewBindingFragment() { }) } + override fun onResume() { + super.onResume() + if (!viewModel.isLoading()) { + viewModel.load(true) + } + } + private fun hideLoading() { binding.progress.visible(false) binding.refresh.isRefreshing = false diff --git a/app/src/main/java/io/gnosis/safe/ui/assets/coins/CoinsViewModel.kt b/app/src/main/java/io/gnosis/safe/ui/assets/coins/CoinsViewModel.kt index cce3b0cf5c..7ab968c2e8 100644 --- a/app/src/main/java/io/gnosis/safe/ui/assets/coins/CoinsViewModel.kt +++ b/app/src/main/java/io/gnosis/safe/ui/assets/coins/CoinsViewModel.kt @@ -54,6 +54,10 @@ class CoinsViewModel } } + fun isLoading(): Boolean { + return (state.value as CoinsState).loading + } + suspend fun getBalanceViewData(coinBalanceData: CoinBalances, showBanner: Boolean): List { val userCurrencyCode = settingsHandler.userDefaultFiat val result = mutableListOf() diff --git a/app/src/main/java/io/gnosis/safe/ui/assets/collectibles/CollectiblesFragment.kt b/app/src/main/java/io/gnosis/safe/ui/assets/collectibles/CollectiblesFragment.kt index ec7f2149a8..57b315d634 100644 --- a/app/src/main/java/io/gnosis/safe/ui/assets/collectibles/CollectiblesFragment.kt +++ b/app/src/main/java/io/gnosis/safe/ui/assets/collectibles/CollectiblesFragment.kt @@ -77,6 +77,13 @@ class CollectiblesFragment : BaseViewBindingFragment { val collectiblesViewData = mutableListOf() diff --git a/app/src/main/java/io/gnosis/safe/ui/settings/safe/SafeSettingsFragment.kt b/app/src/main/java/io/gnosis/safe/ui/settings/safe/SafeSettingsFragment.kt index d341d48b17..a2957049c4 100644 --- a/app/src/main/java/io/gnosis/safe/ui/settings/safe/SafeSettingsFragment.kt +++ b/app/src/main/java/io/gnosis/safe/ui/settings/safe/SafeSettingsFragment.kt @@ -22,7 +22,6 @@ import io.gnosis.safe.ui.settings.SettingsFragmentDirections import io.gnosis.safe.ui.settings.view.AddressItem import io.gnosis.safe.utils.showConfirmDialog import pm.gnosis.model.Solidity -import pm.gnosis.svalinn.common.utils.snackbar import pm.gnosis.svalinn.common.utils.visible import javax.inject.Inject @@ -86,6 +85,13 @@ class SafeSettingsFragment : BaseViewBindingFragment