diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/FiatMoneyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/payments/FiatMoneyUtil.java index 54f38fbb85..4d7e4e715f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/FiatMoneyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/FiatMoneyUtil.java @@ -16,13 +16,21 @@ import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.payments.Money; import java.io.IOException; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.NumberFormat; +import java.util.Currency; import java.util.Locale; -public class FiatMoneyUtil { +public final class FiatMoneyUtil { private static final String TAG = Log.tag(FiatMoneyUtil.class); + private static final char CURRENCY_SYMBOL_PLACE_HOLDER = '\u00A4'; + private static final char NON_BREAKING_WHITESPACE = '\u00A0'; + + private FiatMoneyUtil() {} + public static @NonNull LiveData> getExchange(@NonNull LiveData amount) { return LiveDataUtil.mapAsync(amount, a -> { try { @@ -61,6 +69,38 @@ public class FiatMoneyUtil { return formattedAmount; } + /** + * Prefixes or postfixes the currency symbol based on the formatter for the currency. + * + * @param value String so that you can force trailing zeros. + */ + public static String manualFormat(@NonNull Currency currency, @NonNull String value) { + NumberFormat format = NumberFormat.getCurrencyInstance(); + format.setCurrency(currency); + + DecimalFormat decimalFormat = (DecimalFormat) format; + DecimalFormatSymbols decimalFormatSymbols = decimalFormat.getDecimalFormatSymbols(); + String symbol = decimalFormatSymbols.getCurrencySymbol(); + String localizedPattern = decimalFormat.toLocalizedPattern(); + int currencySymbolIndex = localizedPattern.indexOf(CURRENCY_SYMBOL_PLACE_HOLDER); + boolean prefixSymbol = currencySymbolIndex <= 0; + + if (currencySymbolIndex == 0) { + char cAfterSymbol = localizedPattern.charAt(currencySymbolIndex + 1); + if (Character.isWhitespace(cAfterSymbol) || cAfterSymbol == NON_BREAKING_WHITESPACE) { + symbol = symbol + cAfterSymbol; + } + } else if (currencySymbolIndex > 0) { + char cBeforeSymbol = localizedPattern.charAt(currencySymbolIndex - 1); + if (Character.isWhitespace(cBeforeSymbol) || cBeforeSymbol == NON_BREAKING_WHITESPACE) { + symbol = cBeforeSymbol + symbol; + } + } + + return prefixSymbol ? symbol + value + : value + symbol; + } + public static FormatOptions formatOptions() { return new FormatOptions(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/create/CreatePaymentFragment.java b/app/src/main/java/org/thoughtcrime/securesms/payments/create/CreatePaymentFragment.java index 929f7d1ae0..24d8597f6f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/create/CreatePaymentFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/create/CreatePaymentFragment.java @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.payments.create; import android.app.AlertDialog; import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.TextUtils; @@ -148,12 +147,12 @@ public class CreatePaymentFragment extends LoggingFragment { } private void initializeInfoIcon() { - spacer = Objects.requireNonNull(AppCompatResources.getDrawable(requireContext(), R.drawable.payment_info_pad)); + spacer = Objects.requireNonNull(AppCompatResources.getDrawable(requireContext(), R.drawable.payment_info_pad)); infoIcon = Objects.requireNonNull(AppCompatResources.getDrawable(requireContext(), R.drawable.ic_update_info_16)); DrawableCompat.setTint(infoIcon, exchange.getCurrentTextColor()); - spacer.setBounds(0, 0, ViewUtil.dpToPx(13), ViewUtil.dpToPx(16)); + spacer.setBounds(0, 0, ViewUtil.dpToPx(8), ViewUtil.dpToPx(16)); infoIcon.setBounds(0, 0, ViewUtil.dpToPx(16), ViewUtil.dpToPx(16)); } @@ -182,6 +181,7 @@ public class CreatePaymentFragment extends LoggingFragment { break; case FIAT_MONEY: amount.setMoney(inputState.getMoney(), false, inputState.getExchangeRate().get().getTimestamp()); + amount.append(SpanUtil.buildImageSpan(spacer)); amount.append(SpanUtil.buildImageSpan(infoIcon)); break; } @@ -205,8 +205,7 @@ public class CreatePaymentFragment extends LoggingFragment { break; case FIAT_MONEY: Currency currency = inputState.getFiatMoney().get().getCurrency(); - exchange.setText(new SpannableStringBuilder().append(currency.getSymbol()) - .append(inputState.getFiatAmount())); + exchange.setText(FiatMoneyUtil.manualFormat(currency, inputState.getFiatAmount())); break; } } diff --git a/app/src/test/java/org/thoughtcrime/securesms/payments/FiatMoneyUtil_manualFormat_Test.java b/app/src/test/java/org/thoughtcrime/securesms/payments/FiatMoneyUtil_manualFormat_Test.java new file mode 100644 index 0000000000..f7ce12fdba --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/payments/FiatMoneyUtil_manualFormat_Test.java @@ -0,0 +1,65 @@ +package org.thoughtcrime.securesms.payments; + +import org.junit.Test; + +import java.util.Currency; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; + +public final class FiatMoneyUtil_manualFormat_Test { + + @Test + public void gbp_UK() { + Locale.setDefault(Locale.UK); + + String format = FiatMoneyUtil.manualFormat(Currency.getInstance("GBP"), "1.20"); + + assertEquals("£1.20", format); + } + + @Test + public void eur_France() { + Locale.setDefault(Locale.FRANCE); + + String format = FiatMoneyUtil.manualFormat(Currency.getInstance("EUR"), "2"); + + assertEquals("2 €", format); + } + + @Test + public void aud_France() { + Locale.setDefault(Locale.FRANCE); + + String format = FiatMoneyUtil.manualFormat(Currency.getInstance("AUD"), "1"); + + assertEquals("1 AUD", format); + } + + @Test + public void usd_US() { + Locale.setDefault(Locale.US); + + String format = FiatMoneyUtil.manualFormat(Currency.getInstance("USD"), "4.0"); + + assertEquals("$4.0", format); + } + + @Test + public void cad_US() { + Locale.setDefault(Locale.US); + + String format = FiatMoneyUtil.manualFormat(Currency.getInstance("CAD"), "5.00"); + + assertEquals("CAD5.00", format); + } + + @Test + public void cad_Canada() { + Locale.setDefault(Locale.CANADA); + + String format = FiatMoneyUtil.manualFormat(Currency.getInstance("CAD"), "5.12"); + + assertEquals("$5.12", format); + } +}