diff --git a/app/src/main/java/com/electricdreams/numo/PaymentRequestActivity.kt b/app/src/main/java/com/electricdreams/numo/PaymentRequestActivity.kt
index b2663ee1..bba95a9e 100644
--- a/app/src/main/java/com/electricdreams/numo/PaymentRequestActivity.kt
+++ b/app/src/main/java/com/electricdreams/numo/PaymentRequestActivity.kt
@@ -318,10 +318,14 @@ class PaymentRequestActivity : AppCompatActivity() {
val entryUnit: String
val enteredAmount: Long
+ // Get current preferred currency to help resolve ambiguous symbols (like "kr" for SEK/NOK)
+ val currentCurrencyCode = CurrencyManager.getInstance(this).getCurrentCurrency()
+ val currentCurrency = Amount.Currency.fromCode(currentCurrencyCode)
+
if (tipAmountSats > 0 && baseAmountSats > 0) {
// Tip is present - use base amounts for accounting
// Parse base formatted amount to get the original entry unit
- val parsedBase = Amount.parse(baseFormattedAmount)
+ val parsedBase = Amount.parse(baseFormattedAmount, currentCurrency)
if (parsedBase != null) {
entryUnit = if (parsedBase.currency == Currency.BTC) "sat" else parsedBase.currency.name
enteredAmount = parsedBase.value
@@ -333,7 +337,7 @@ class PaymentRequestActivity : AppCompatActivity() {
Log.d(TAG, "Creating pending payment with tip: base=$enteredAmount $entryUnit, tip=$tipAmountSats sats, total=$paymentAmount sats")
} else {
// No tip - parse the formatted amount string
- val parsedAmount = Amount.parse(formattedAmountString)
+ val parsedAmount = Amount.parse(formattedAmountString, currentCurrency)
if (parsedAmount != null) {
entryUnit = if (parsedAmount.currency == Currency.BTC) "sat" else parsedAmount.currency.name
enteredAmount = parsedAmount.value
diff --git a/app/src/main/java/com/electricdreams/numo/core/model/Amount.kt b/app/src/main/java/com/electricdreams/numo/core/model/Amount.kt
index 6f23714b..598f17be 100644
--- a/app/src/main/java/com/electricdreams/numo/core/model/Amount.kt
+++ b/app/src/main/java/com/electricdreams/numo/core/model/Amount.kt
@@ -25,7 +25,10 @@ data class Amount(
USD("$"),
EUR("€"),
GBP("£"),
- JPY("¥");
+ JPY("¥"),
+ DKK("kr."),
+ SEK("kr"),
+ NOK("kr");
/**
* Get the appropriate locale for formatting this currency.
@@ -37,6 +40,9 @@ data class Amount(
GBP -> Locale.UK // Period decimal: £4.20
JPY -> Locale.JAPAN // No decimals: ¥420
BTC -> Locale.US // Comma thousand separator: ₿1,000
+ DKK -> Locale("da", "DK") // Comma decimal: DKK 100,00
+ SEK -> Locale("sv", "SE") // Comma decimal: SEK 100,00
+ NOK -> Locale("nb", "NO") // Comma decimal: NOK 100,00
}
companion object {
@@ -115,17 +121,39 @@ data class Amount(
* Handles formats like "$0.25", "€1,50", "₿24", "¥100", etc.
* Accepts both period and comma as decimal separators for input flexibility.
* Returns null if parsing fails.
+ *
+ * @param formatted The formatted string to parse (e.g. "$10.50")
+ * @param defaultCurrency Optional default currency to use if the symbol is ambiguous (e.g. "kr" could be SEK or NOK).
*/
@JvmStatic
- fun parse(formatted: String): Amount? {
+ @JvmOverloads
+ fun parse(formatted: String, defaultCurrency: Currency? = null): Amount? {
if (formatted.isEmpty()) return null
- // Find the currency by the first character (symbol)
- val symbol = formatted.take(1)
- val currency = Currency.fromSymbol(symbol) ?: return null
+ // Find matching currencies by matching the start of the string
+ // We sort by symbol length descending to match longest symbols first (e.g. "kr." vs "kr")
+ val matchingCurrencies = Currency.entries
+ .filter { formatted.startsWith(it.symbol) }
+ .sortedByDescending { it.symbol.length }
+
+ if (matchingCurrencies.isEmpty()) return null
+
+ // If we have multiple matches (e.g. SEK and NOK both use "kr"), try to use the default currency
+ // Otherwise, pick the first one (which is deterministic but might be wrong if ambiguous)
+ // Note: Since we sorted by length, "kr." (DKK) will come before "kr" (SEK/NOK), which is correct behavior.
+ // The ambiguity is mainly between SEK and NOK.
+ val currency = if (matchingCurrencies.size > 1 && defaultCurrency != null) {
+ if (matchingCurrencies.contains(defaultCurrency)) {
+ defaultCurrency
+ } else {
+ matchingCurrencies.first()
+ }
+ } else {
+ matchingCurrencies.first()
+ }
// Extract the numeric part (remove symbol)
- var numericPart = formatted.drop(1).trim()
+ var numericPart = formatted.drop(currency.symbol.length).trim()
// Normalize the input: handle both comma and period as decimal separators
// First, determine if comma is used as thousand separator or decimal separator
diff --git a/app/src/main/java/com/electricdreams/numo/core/util/CurrencyManager.kt b/app/src/main/java/com/electricdreams/numo/core/util/CurrencyManager.kt
index d6cb5208..aba82719 100644
--- a/app/src/main/java/com/electricdreams/numo/core/util/CurrencyManager.kt
+++ b/app/src/main/java/com/electricdreams/numo/core/util/CurrencyManager.kt
@@ -24,6 +24,9 @@ class CurrencyManager private constructor(context: Context) {
const val CURRENCY_EUR = "EUR"
const val CURRENCY_GBP = "GBP"
const val CURRENCY_JPY = "JPY"
+ const val CURRENCY_DKK = "DKK"
+ const val CURRENCY_SEK = "SEK"
+ const val CURRENCY_NOK = "NOK"
// Default currency is USD
private const val DEFAULT_CURRENCY = CURRENCY_USD
@@ -68,6 +71,9 @@ class CurrencyManager private constructor(context: Context) {
CURRENCY_GBP -> "£"
CURRENCY_JPY -> "¥"
CURRENCY_USD -> "$"
+ CURRENCY_DKK -> "kr."
+ CURRENCY_SEK -> "kr"
+ CURRENCY_NOK -> "kr"
else -> "$"
}
@@ -93,7 +99,8 @@ class CurrencyManager private constructor(context: Context) {
/** Check if a currency code is valid and supported. */
fun isValidCurrency(currencyCode: String?): Boolean {
return when (currencyCode) {
- CURRENCY_USD, CURRENCY_EUR, CURRENCY_GBP, CURRENCY_JPY -> true
+ CURRENCY_USD, CURRENCY_EUR, CURRENCY_GBP, CURRENCY_JPY,
+ CURRENCY_DKK, CURRENCY_SEK, CURRENCY_NOK -> true
else -> false
}
}
diff --git a/app/src/main/java/com/electricdreams/numo/core/util/ReceiptPrinter.kt b/app/src/main/java/com/electricdreams/numo/core/util/ReceiptPrinter.kt
index e8ed35af..6d806cd8 100644
--- a/app/src/main/java/com/electricdreams/numo/core/util/ReceiptPrinter.kt
+++ b/app/src/main/java/com/electricdreams/numo/core/util/ReceiptPrinter.kt
@@ -284,8 +284,18 @@ class ReceiptPrinter(private val context: Context) {
// Bitcoin price at time of transaction
data.bitcoinPrice?.let { price ->
- val formattedPrice = String.format(Locale.US, "$%,.2f", price)
- sb.appendLine(leftRight("BTC/USD Rate:", formattedPrice))
+ val priceCurrencyCode = data.basket?.currency ?: data.enteredCurrency
+ val priceCurrency = if (priceCurrencyCode.equals("BTC", ignoreCase = true) || priceCurrencyCode.equals("sat", ignoreCase = true)) {
+ Amount.Currency.USD
+ } else {
+ Amount.Currency.fromCode(priceCurrencyCode)
+ }
+
+ // Format price using Amount class
+ val priceMinorUnits = kotlin.math.round(price * 100).toLong()
+ val formattedPrice = Amount(priceMinorUnits, priceCurrency).toString()
+
+ sb.appendLine(leftRight("BTC/${priceCurrency.name} Rate:", formattedPrice))
}
sb.appendLine()
@@ -590,10 +600,22 @@ class ReceiptPrinter(private val context: Context) {
$tipSectionHtml
- ${data.bitcoinPrice?.let { """
+ ${data.bitcoinPrice?.let { price ->
+ val priceCurrencyCode = data.basket?.currency ?: data.enteredCurrency
+ val priceCurrency = if (priceCurrencyCode.equals("BTC", ignoreCase = true) || priceCurrencyCode.equals("sat", ignoreCase = true)) {
+ Amount.Currency.USD
+ } else {
+ Amount.Currency.fromCode(priceCurrencyCode)
+ }
+
+ // Format price using Amount class
+ val priceMinorUnits = kotlin.math.round(price * 100).toLong()
+ val formattedPrice = Amount(priceMinorUnits, priceCurrency).toString()
+
+ """
- BTC/USD Rate:
- ${String.format(Locale.US, "$%,.2f", it)}
+ BTC/${priceCurrency.name} Rate:
+ $formattedPrice
""" } ?: ""}
diff --git a/app/src/main/java/com/electricdreams/numo/core/worker/BitcoinPriceWorker.kt b/app/src/main/java/com/electricdreams/numo/core/worker/BitcoinPriceWorker.kt
index 76315751..19b1d483 100644
--- a/app/src/main/java/com/electricdreams/numo/core/worker/BitcoinPriceWorker.kt
+++ b/app/src/main/java/com/electricdreams/numo/core/worker/BitcoinPriceWorker.kt
@@ -101,6 +101,9 @@ class BitcoinPriceWorker private constructor(context: Context) {
CurrencyManager.CURRENCY_EUR,
CurrencyManager.CURRENCY_GBP,
CurrencyManager.CURRENCY_JPY,
+ CurrencyManager.CURRENCY_DKK,
+ CurrencyManager.CURRENCY_SEK,
+ CurrencyManager.CURRENCY_NOK,
)
for (currency in supportedCurrencies) {
diff --git a/app/src/main/java/com/electricdreams/numo/feature/history/TransactionDetailActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/history/TransactionDetailActivity.kt
index e9f86d5b..cca2fad2 100644
--- a/app/src/main/java/com/electricdreams/numo/feature/history/TransactionDetailActivity.kt
+++ b/app/src/main/java/com/electricdreams/numo/feature/history/TransactionDetailActivity.kt
@@ -133,40 +133,22 @@ class TransactionDetailActivity : AppCompatActivity() {
val amountSubtitleText: TextView = findViewById(R.id.detail_amount_subtitle)
val amountValueText: TextView = findViewById(R.id.detail_amount_value)
- // Parse basket to determine display mode
- val basket = CheckoutBasket.fromJson(checkoutBasketJson)
- val showSatsAsPrimary = basket?.let {
- it.hasMixedPriceTypes() || it.getFiatItems().isEmpty()
- } ?: (entry.getEntryUnit() == "sat")
-
// Use BASE amount (excluding tip) for display - this is what was sold
val baseAmountSats = entry.getBaseAmountSats()
val baseSatAmount = Amount(baseAmountSats, Amount.Currency.BTC)
-
- if (showSatsAsPrimary) {
- // Primary: Sats (base amount)
- amountText.text = baseSatAmount.toString()
- amountValueText.text = baseSatAmount.toString()
-
- // Secondary: Fiat equivalent
- if (entry.enteredAmount > 0 && entry.getEntryUnit() != "sat") {
- val entryCurrency = Amount.Currency.fromCode(entry.getEntryUnit())
- val fiatAmount = Amount(entry.enteredAmount, entryCurrency)
- amountSubtitleText.text = "≈ $fiatAmount"
- amountSubtitleText.visibility = View.VISIBLE
- } else {
- amountSubtitleText.visibility = View.GONE
- }
- } else {
- // Primary: Fiat (entered amount - which is the base amount)
+
+ // Amount is ALWAYS the settlement one in sats (per user request)
+ amountText.text = baseSatAmount.toString()
+ amountValueText.text = baseSatAmount.toString()
+
+ // Secondary: Fiat equivalent (if applicable)
+ if (entry.enteredAmount > 0 && entry.getEntryUnit() != "sat") {
val entryCurrency = Amount.Currency.fromCode(entry.getEntryUnit())
val fiatAmount = Amount(entry.enteredAmount, entryCurrency)
- amountText.text = fiatAmount.toString()
- amountValueText.text = fiatAmount.toString()
-
- // Secondary: Sats paid (base amount, not total)
- amountSubtitleText.text = baseSatAmount.toString()
+ amountSubtitleText.text = "≈ $fiatAmount"
amountSubtitleText.visibility = View.VISIBLE
+ } else {
+ amountSubtitleText.visibility = View.GONE
}
// Date
@@ -227,7 +209,7 @@ class TransactionDetailActivity : AppCompatActivity() {
if (entry.getEntryUnit() != "sat") {
val entryCurrency = Amount.Currency.fromCode(entry.getEntryUnit())
val enteredAmount = Amount(entry.enteredAmount, entryCurrency)
- enteredAmountText.text = enteredAmount.toString()
+ enteredAmountText.text = enteredAmount.toStringWithoutSymbol()
enteredAmountRow.visibility = View.VISIBLE
enteredAmountDivider.visibility = View.VISIBLE
} else {
@@ -242,7 +224,26 @@ class TransactionDetailActivity : AppCompatActivity() {
val btcPrice = entry.bitcoinPrice
if (btcPrice != null && btcPrice > 0) {
- val formattedPrice = String.format(Locale.US, "$%,.2f", btcPrice)
+ // Determine the correct currency for the Bitcoin price.
+ // 1. If entry unit is a fiat currency, use that.
+ // 2. If available, check the checkout basket currency.
+ // 3. Fallback to USD (default behavior).
+ val currencyCode = if (entry.getEntryUnit() != "sat" && entry.getEntryUnit() != "BTC") {
+ entry.getEntryUnit()
+ } else {
+ val basket = CheckoutBasket.fromJson(checkoutBasketJson)
+ basket?.currency ?: "USD"
+ }
+
+ val currency = Amount.Currency.fromCode(currencyCode)
+ // If we somehow still resolved to BTC (e.g. basket had "SAT"), force USD for price display
+ val priceCurrency = if (currency == Amount.Currency.BTC) Amount.Currency.USD else currency
+
+ // Format price using Amount class to respect locale and currency symbol
+ // Amount expects minor units (cents), so multiply by 100
+ val priceMinorUnits = kotlin.math.round(btcPrice * 100).toLong()
+ val formattedPrice = Amount(priceMinorUnits, priceCurrency).toString()
+
bitcoinPriceText.text = formattedPrice
bitcoinPriceRow.visibility = View.VISIBLE
bitcoinPriceDivider.visibility = View.VISIBLE
diff --git a/app/src/main/java/com/electricdreams/numo/feature/settings/CurrencySettingsActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/settings/CurrencySettingsActivity.kt
index 2e3448ae..60b2c29d 100644
--- a/app/src/main/java/com/electricdreams/numo/feature/settings/CurrencySettingsActivity.kt
+++ b/app/src/main/java/com/electricdreams/numo/feature/settings/CurrencySettingsActivity.kt
@@ -15,6 +15,9 @@ class CurrencySettingsActivity : AppCompatActivity() {
private lateinit var radioEur: RadioButton
private lateinit var radioGbp: RadioButton
private lateinit var radioJpy: RadioButton
+ private lateinit var radioDkk: RadioButton
+ private lateinit var radioSek: RadioButton
+ private lateinit var radioNok: RadioButton
private lateinit var currencyManager: CurrencyManager
override fun onCreate(savedInstanceState: Bundle?) {
@@ -30,6 +33,9 @@ class CurrencySettingsActivity : AppCompatActivity() {
radioEur = findViewById(R.id.radio_eur)
radioGbp = findViewById(R.id.radio_gbp)
radioJpy = findViewById(R.id.radio_jpy)
+ radioDkk = findViewById(R.id.radio_dkk)
+ radioSek = findViewById(R.id.radio_sek)
+ radioNok = findViewById(R.id.radio_nok)
setSelectedCurrency(currencyManager.getCurrentCurrency())
@@ -45,6 +51,9 @@ class CurrencySettingsActivity : AppCompatActivity() {
CurrencyManager.CURRENCY_GBP -> radioGbp.isChecked = true
CurrencyManager.CURRENCY_JPY -> radioJpy.isChecked = true
CurrencyManager.CURRENCY_USD -> radioUsd.isChecked = true
+ CurrencyManager.CURRENCY_DKK -> radioDkk.isChecked = true
+ CurrencyManager.CURRENCY_SEK -> radioSek.isChecked = true
+ CurrencyManager.CURRENCY_NOK -> radioNok.isChecked = true
else -> radioUsd.isChecked = true
}
}
@@ -55,6 +64,9 @@ class CurrencySettingsActivity : AppCompatActivity() {
R.id.radio_eur -> CurrencyManager.CURRENCY_EUR
R.id.radio_gbp -> CurrencyManager.CURRENCY_GBP
R.id.radio_jpy -> CurrencyManager.CURRENCY_JPY
+ R.id.radio_dkk -> CurrencyManager.CURRENCY_DKK
+ R.id.radio_sek -> CurrencyManager.CURRENCY_SEK
+ R.id.radio_nok -> CurrencyManager.CURRENCY_NOK
else -> CurrencyManager.CURRENCY_USD
}
}
diff --git a/app/src/main/java/com/electricdreams/numo/feature/tips/TipSelectionActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/tips/TipSelectionActivity.kt
index 72bf81f3..5e24bfcf 100644
--- a/app/src/main/java/com/electricdreams/numo/feature/tips/TipSelectionActivity.kt
+++ b/app/src/main/java/com/electricdreams/numo/feature/tips/TipSelectionActivity.kt
@@ -132,6 +132,20 @@ class TipSelectionActivity : AppCompatActivity() {
if (entryCurrency != Currency.BTC) {
enteredAmountFiat = parsedAmount.value
}
+ } else {
+ // Fallback parsing with current system currency if implicit parsing fails
+ // (e.g. for ambiguous symbols like "kr")
+ val currencyManager = com.electricdreams.numo.core.util.CurrencyManager.getInstance(this)
+ val currentCurrencyCode = currencyManager.getCurrentCurrency()
+ val currentCurrency = Amount.Currency.fromCode(currentCurrencyCode)
+ val parsedWithContext = Amount.parse(formattedAmount, currentCurrency)
+
+ if (parsedWithContext != null) {
+ entryCurrency = parsedWithContext.currency
+ if (entryCurrency != Currency.BTC) {
+ enteredAmountFiat = parsedWithContext.value
+ }
+ }
}
// Set default for custom input based on entry currency
@@ -838,8 +852,7 @@ class TipSelectionActivity : AppCompatActivity() {
}
val totalFiat = (enteredAmountFiat / 100.0) + tipFiat
// Format as currency
- val symbol = entryCurrency.symbol
- "${symbol}${String.format("%.2f", totalFiat)}"
+ Amount(kotlin.math.round(totalFiat * 100).toLong(), entryCurrency).toString()
} else {
formattedAmount
}
diff --git a/app/src/main/res/layout/activity_currency_settings.xml b/app/src/main/res/layout/activity_currency_settings.xml
index 03191649..f516b26c 100644
--- a/app/src/main/res/layout/activity_currency_settings.xml
+++ b/app/src/main/res/layout/activity_currency_settings.xml
@@ -120,6 +120,45 @@
android:paddingHorizontal="24dp"
android:text="@string/currency_settings_option_jpy"
android:textAppearance="@style/Text.BodyBold" />
+
+
+
+
+
+
diff --git a/app/src/main/res/values-es/strings_settings.xml b/app/src/main/res/values-es/strings_settings.xml
index 5fe28039..9b358b82 100644
--- a/app/src/main/res/values-es/strings_settings.xml
+++ b/app/src/main/res/values-es/strings_settings.xml
@@ -24,6 +24,9 @@
Euro (EUR)
Libra esterlina (GBP)
Yen japonés (JPY)
+ Corona danesa (DKK)
+ Corona sueca (SEK)
+ Corona noruega (NOK)
Mints
diff --git a/app/src/main/res/values/strings_settings.xml b/app/src/main/res/values/strings_settings.xml
index 9aab49fe..f496831d 100644
--- a/app/src/main/res/values/strings_settings.xml
+++ b/app/src/main/res/values/strings_settings.xml
@@ -24,6 +24,9 @@
Euro (EUR)
British Pound (GBP)
Japanese Yen (JPY)
+ Danish Krone (DKK)
+ Swedish Krona (SEK)
+ Norwegian Krone (NOK)
Mints
diff --git a/app/src/test/java/com/electricdreams/numo/core/model/AmountTest.kt b/app/src/test/java/com/electricdreams/numo/core/model/AmountTest.kt
new file mode 100644
index 00000000..b03b3f09
--- /dev/null
+++ b/app/src/test/java/com/electricdreams/numo/core/model/AmountTest.kt
@@ -0,0 +1,103 @@
+package com.electricdreams.numo.core.model
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import java.util.Locale
+
+class AmountTest {
+
+ @Test
+ fun `parse handles single character symbols`() {
+ val usd = Amount.parse("$10.50")
+ assertEquals(Amount.Currency.USD, usd?.currency)
+ assertEquals(1050L, usd?.value)
+
+ val eur = Amount.parse("€10,50")
+ assertEquals(Amount.Currency.EUR, eur?.currency)
+ assertEquals(1050L, eur?.value)
+
+ val gbp = Amount.parse("£10.50")
+ assertEquals(Amount.Currency.GBP, gbp?.currency)
+ assertEquals(1050L, gbp?.value)
+
+ val jpy = Amount.parse("¥100")
+ assertEquals(Amount.Currency.JPY, jpy?.currency)
+ assertEquals(10000L, jpy?.value) // JPY stored as cents (100 * 100)
+ }
+
+ @Test
+ fun `parse handles multi-character symbols`() {
+ val dkk = Amount.parse("kr. 100,50")
+ assertEquals(Amount.Currency.DKK, dkk?.currency)
+ assertEquals(10050L, dkk?.value)
+
+ // DKK "kr." is unique, so it should be found without hint
+ val dkk2 = Amount.parse("kr.100,50")
+ assertEquals(Amount.Currency.DKK, dkk2?.currency)
+ }
+
+ @Test
+ fun `parse handles ambiguous symbols with default currency`() {
+ // "kr" is ambiguous between SEK and NOK
+ // Without default, it might pick the first one (SEK in enum order if sorted by length equal)
+ // Wait, DKK is "kr.", SEK is "kr", NOK is "kr".
+
+ // Test SEK preference
+ val sek = Amount.parse("kr 100,50", Amount.Currency.SEK)
+ assertEquals(Amount.Currency.SEK, sek?.currency)
+ assertEquals(10050L, sek?.value)
+
+ // Test NOK preference
+ val nok = Amount.parse("kr 100,50", Amount.Currency.NOK)
+ assertEquals(Amount.Currency.NOK, nok?.currency)
+ assertEquals(10050L, nok?.value)
+ }
+
+ @Test
+ fun `parse returns first match if ambiguous and no default provided`() {
+ val result = Amount.parse("kr 100,50")
+ // It should match either SEK or NOK.
+ // Logic sorts by length desc. "kr" length 2.
+ // Filter matches SEK and NOK.
+ // Returns first.
+ assert(result?.currency == Amount.Currency.SEK || result?.currency == Amount.Currency.NOK)
+ }
+
+ @Test
+ fun `parse handles multi-character symbols without space`() {
+ val dkk = Amount.parse("kr.100,50")
+ assertEquals(Amount.Currency.DKK, dkk?.currency)
+ assertEquals(10050L, dkk?.value)
+ }
+
+ @Test
+ fun `parse returns null for invalid input`() {
+ assertNull(Amount.parse(""))
+ assertNull(Amount.parse("INVALID"))
+ assertNull(Amount.parse("XYZ 100"))
+ }
+
+ @Test
+ fun `toString formats correctly`() {
+ // Force US locale for consistent testing of USD
+ val usd = Amount(1050, Amount.Currency.USD)
+ // Locale.US uses period
+ assertEquals("$10.50", usd.toString())
+
+ // DKK uses comma
+ val dkk = Amount(1050, Amount.Currency.DKK)
+ // Locale("da", "DK") uses comma for decimal and period for thousands
+ // We expect "kr.10,50" or "kr. 10,50" depending on NumberFormat implementation
+ // Broad check for prefix and suffix
+ val dkkStr = dkk.toString()
+ assert(dkkStr.startsWith("kr."))
+ assert(dkkStr.contains("10,50"))
+
+ // SEK uses comma
+ val sek = Amount(1050, Amount.Currency.SEK)
+ val sekStr = sek.toString()
+ assert(sekStr.startsWith("kr"))
+ assert(sekStr.contains("10,50"))
+ }
+}
diff --git a/app/src/test/java/com/electricdreams/numo/core/util/CurrencyManagerTest.kt b/app/src/test/java/com/electricdreams/numo/core/util/CurrencyManagerTest.kt
index f659eea0..9ec27ca9 100644
--- a/app/src/test/java/com/electricdreams/numo/core/util/CurrencyManagerTest.kt
+++ b/app/src/test/java/com/electricdreams/numo/core/util/CurrencyManagerTest.kt
@@ -109,4 +109,35 @@ class CurrencyManagerTest {
currencyManager.setPreferredCurrency("USD")
assertEquals("https://api.coinbase.com/v2/prices/BTC-USD/spot", currencyManager.getCoinbaseApiUrl())
}
+
+ @Test
+ fun `isValidCurrency supports Nordic currencies`() {
+ assertTrue(currencyManager.isValidCurrency("DKK"))
+ assertTrue(currencyManager.isValidCurrency("SEK"))
+ assertTrue(currencyManager.isValidCurrency("NOK"))
+ }
+
+ @Test
+ fun `getCurrentSymbol returns correct symbols for Nordic currencies`() {
+ currencyManager.setPreferredCurrency("DKK")
+ assertEquals("kr.", currencyManager.getCurrentSymbol())
+
+ currencyManager.setPreferredCurrency("SEK")
+ assertEquals("kr", currencyManager.getCurrentSymbol())
+
+ currencyManager.setPreferredCurrency("NOK")
+ assertEquals("kr", currencyManager.getCurrentSymbol())
+ }
+
+ @Test
+ fun `getCoinbaseApiUrl returns correct URL for Nordic currencies`() {
+ currencyManager.setPreferredCurrency("DKK")
+ assertEquals("https://api.coinbase.com/v2/prices/BTC-DKK/spot", currencyManager.getCoinbaseApiUrl())
+
+ currencyManager.setPreferredCurrency("SEK")
+ assertEquals("https://api.coinbase.com/v2/prices/BTC-SEK/spot", currencyManager.getCoinbaseApiUrl())
+
+ currencyManager.setPreferredCurrency("NOK")
+ assertEquals("https://api.coinbase.com/v2/prices/BTC-NOK/spot", currencyManager.getCoinbaseApiUrl())
+ }
}