diff --git a/app/src/androidTest/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesPreviewContentTest.kt b/app/src/androidTest/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesPreviewContentTest.kt index 3566df1a3..918ec1d6e 100644 --- a/app/src/androidTest/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesPreviewContentTest.kt +++ b/app/src/androidTest/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesPreviewContentTest.kt @@ -58,7 +58,7 @@ class HeadlinesPreviewContentTest { // Verify settings and preview section composeTestRule.onNodeWithTag("WidgetEdit").assertExists() composeTestRule.onNodeWithTag("headlines_preview_carousel").assertExists() - composeTestRule.onNodeWithTag("headline_card_small").assertExists() + composeTestRule.onNodeWithTag("headline_card_wide").assertExists() // Verify buttons composeTestRule.onNodeWithTag("buttons_row").assertExists() @@ -164,7 +164,7 @@ class HeadlinesPreviewContentTest { composeTestRule.onNodeWithTag("divider").assertExists() composeTestRule.onNodeWithTag("WidgetEdit").assertExists() composeTestRule.onNodeWithTag("headlines_preview_carousel").assertExists() - composeTestRule.onNodeWithTag("headline_card_small").assertExists() + composeTestRule.onNodeWithTag("headline_card_wide").assertExists() composeTestRule.onNodeWithTag("buttons_row").assertExists() composeTestRule.onNodeWithTag("WidgetDelete").assertExists() composeTestRule.onNodeWithTag("WidgetSave").assertExists() diff --git a/app/src/main/java/to/bitkit/appwidget/AppWidgetPreferencesStore.kt b/app/src/main/java/to/bitkit/appwidget/AppWidgetPreferencesStore.kt index 1bcb54cc0..4473a4f45 100644 --- a/app/src/main/java/to/bitkit/appwidget/AppWidgetPreferencesStore.kt +++ b/app/src/main/java/to/bitkit/appwidget/AppWidgetPreferencesStore.kt @@ -19,6 +19,7 @@ import to.bitkit.data.dto.WeatherDTO import to.bitkit.data.dto.price.GraphPeriod import to.bitkit.data.dto.price.PriceDTO import to.bitkit.data.serializers.AppWidgetDataSerializer +import to.bitkit.repositories.CurrencyRepo import javax.inject.Inject import javax.inject.Singleton @@ -32,6 +33,7 @@ private val Context.appWidgetDataStore: DataStore by dataStore( interface AppWidgetEntryPoint { fun appWidgetPreferencesStore(): AppWidgetPreferencesStore fun appWidgetDataRepository(): AppWidgetDataRepository + fun currencyRepo(): CurrencyRepo } @Singleton diff --git a/app/src/main/java/to/bitkit/appwidget/config/AppWidgetConfigActivity.kt b/app/src/main/java/to/bitkit/appwidget/config/AppWidgetConfigActivity.kt index 976a32c9e..2866db9fb 100644 --- a/app/src/main/java/to/bitkit/appwidget/config/AppWidgetConfigActivity.kt +++ b/app/src/main/java/to/bitkit/appwidget/config/AppWidgetConfigActivity.kt @@ -1,6 +1,5 @@ package to.bitkit.appwidget.config -import android.app.Activity import android.appwidget.AppWidgetManager import android.content.Intent import android.os.Bundle @@ -20,6 +19,7 @@ import to.bitkit.appwidget.ui.price.PriceGlanceWidget import to.bitkit.appwidget.ui.weather.WeatherGlanceReceiver import to.bitkit.appwidget.ui.weather.WeatherGlanceWidget import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.utils.enableAppEdgeToEdge import to.bitkit.utils.Logger @AndroidEntryPoint @@ -34,6 +34,7 @@ class AppWidgetConfigActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableAppEdgeToEdge() val appWidgetId = intent?.extras?.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, @@ -58,7 +59,9 @@ class AppWidgetConfigActivity : ComponentActivity() { onConfirm = { when (viewModel.uiState.value.type) { AppWidgetType.PRICE -> PriceGlanceWidget().updateAll(this@AppWidgetConfigActivity) - AppWidgetType.HEADLINES -> HeadlinesGlanceWidget().updateAll(this@AppWidgetConfigActivity) + AppWidgetType.HEADLINES -> HeadlinesGlanceWidget().updateAll( + this@AppWidgetConfigActivity, + ) AppWidgetType.BLOCKS -> BlocksGlanceWidget().updateAll(this@AppWidgetConfigActivity) AppWidgetType.FACTS -> Unit AppWidgetType.WEATHER -> WeatherGlanceWidget().updateAll(this@AppWidgetConfigActivity) @@ -68,7 +71,7 @@ class AppWidgetConfigActivity : ComponentActivity() { AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId, ) - setResult(Activity.RESULT_OK, result) + setResult(RESULT_OK, result) finish() }, onCancel = { finish() }, diff --git a/app/src/main/java/to/bitkit/appwidget/config/AppWidgetConfigViewModel.kt b/app/src/main/java/to/bitkit/appwidget/config/AppWidgetConfigViewModel.kt index 3a29d30f4..0e4b9b38d 100644 --- a/app/src/main/java/to/bitkit/appwidget/config/AppWidgetConfigViewModel.kt +++ b/app/src/main/java/to/bitkit/appwidget/config/AppWidgetConfigViewModel.kt @@ -20,6 +20,7 @@ import to.bitkit.appwidget.model.HomeHeadlinePreferences import to.bitkit.appwidget.model.HomePricePreferences import to.bitkit.appwidget.model.HomeWeatherPreferences import to.bitkit.data.dto.FeeCondition +import to.bitkit.data.dto.WeatherDTO import to.bitkit.data.dto.price.GraphPeriod import to.bitkit.data.dto.price.TradingPair import to.bitkit.models.widget.ArticleModel @@ -29,6 +30,7 @@ import to.bitkit.models.widget.PricePreferences import to.bitkit.models.widget.WeatherDataOption import to.bitkit.models.widget.WeatherPreferences import to.bitkit.models.widget.toArticleModel +import to.bitkit.repositories.CurrencyRepo import to.bitkit.ui.screens.widgets.blocks.WeatherModel import to.bitkit.ui.screens.widgets.blocks.toWeatherModel import to.bitkit.utils.Logger @@ -39,6 +41,7 @@ import javax.inject.Inject class AppWidgetConfigViewModel @Inject constructor( private val preferencesStore: AppWidgetPreferencesStore, private val dataRepository: AppWidgetDataRepository, + private val currencyRepo: CurrencyRepo, ) : ViewModel() { companion object { @@ -53,7 +56,7 @@ class AppWidgetConfigViewModel @Inject constructor( val entry = preferencesStore.getEntry(appWidgetId) val data = preferencesStore.data.first() val previewArticle = data.cachedArticles.randomOrNull()?.toArticleModel() ?: DEFAULT_PREVIEW_ARTICLE - val previewWeather = data.cachedWeather?.toWeatherModel() ?: DEFAULT_PREVIEW_WEATHER + val previewWeather = data.cachedWeather?.toPreviewWeather() ?: DEFAULT_PREVIEW_WEATHER _uiState.update { it.copy( @@ -72,7 +75,7 @@ class AppWidgetConfigViewModel @Inject constructor( dataRepository.fetchWeather() .onSuccess { fetched -> preferencesStore.cacheWeather(fetched) - _uiState.update { it.copy(previewWeather = fetched.toWeatherModel()) } + _uiState.update { it.copy(previewWeather = fetched.toPreviewWeather()) } } .onFailure { Logger.warn("Failed to fetch weather for config preview", it, context = TAG) } } @@ -239,6 +242,10 @@ class AppWidgetConfigViewModel @Inject constructor( .onSuccess { preferencesStore.cacheWeather(it) } .onFailure { Logger.warn("Failed to fetch initial weather", it, context = TAG) } } + + private fun WeatherDTO.toPreviewWeather() = toWeatherModel( + currentFee = currencyRepo.formatSatsAsFiatWithSymbol(avgFeeSats, withSpace = true) ?: currentFee, + ) } private val DEFAULT_PREVIEW_ARTICLE = ArticleModel( diff --git a/app/src/main/java/to/bitkit/appwidget/config/BlocksConfigContent.kt b/app/src/main/java/to/bitkit/appwidget/config/BlocksConfigContent.kt index 97b687f31..0467326e1 100644 --- a/app/src/main/java/to/bitkit/appwidget/config/BlocksConfigContent.kt +++ b/app/src/main/java/to/bitkit/appwidget/config/BlocksConfigContent.kt @@ -1,7 +1,6 @@ package to.bitkit.appwidget.config import androidx.annotation.DrawableRes -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -61,10 +60,7 @@ internal fun BlocksConfigContent( ) } - ScreenColumn( - noBackground = true, - modifier = Modifier.background(Colors.Gray7) - ) { + ScreenColumn { AppTopBar( titleText = stringResource(R.string.widgets__blocks__name), onBackClick = onCancel, diff --git a/app/src/main/java/to/bitkit/appwidget/config/HeadlinesConfigContent.kt b/app/src/main/java/to/bitkit/appwidget/config/HeadlinesConfigContent.kt index 9d389993e..1dc4d38a3 100644 --- a/app/src/main/java/to/bitkit/appwidget/config/HeadlinesConfigContent.kt +++ b/app/src/main/java/to/bitkit/appwidget/config/HeadlinesConfigContent.kt @@ -1,6 +1,5 @@ package to.bitkit.appwidget.config -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -43,10 +42,7 @@ internal fun HeadlinesConfigContent( val prefs = state.headlinePreferences val previewArticle = state.previewArticle - ScreenColumn( - noBackground = true, - modifier = Modifier.background(Colors.Gray7) - ) { + ScreenColumn { AppTopBar( titleText = stringResource(R.string.widgets__news__name), onBackClick = onCancel, diff --git a/app/src/main/java/to/bitkit/appwidget/config/PriceConfigContent.kt b/app/src/main/java/to/bitkit/appwidget/config/PriceConfigContent.kt index 9d4cd3037..6d3f230a6 100644 --- a/app/src/main/java/to/bitkit/appwidget/config/PriceConfigContent.kt +++ b/app/src/main/java/to/bitkit/appwidget/config/PriceConfigContent.kt @@ -1,6 +1,5 @@ package to.bitkit.appwidget.config -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -44,10 +43,7 @@ internal fun PriceConfigContent( val prefs = state.pricePreferences val selectedPair = prefs.enabledPairs.firstOrNull() ?: TradingPair.BTC_USD - ScreenColumn( - noBackground = true, - modifier = Modifier.background(Colors.Gray7) - ) { + ScreenColumn { AppTopBar( titleText = stringResource(R.string.widgets__price__name), onBackClick = onCancel, diff --git a/app/src/main/java/to/bitkit/appwidget/config/WeatherConfigContent.kt b/app/src/main/java/to/bitkit/appwidget/config/WeatherConfigContent.kt index bb74d87e3..2927cea41 100644 --- a/app/src/main/java/to/bitkit/appwidget/config/WeatherConfigContent.kt +++ b/app/src/main/java/to/bitkit/appwidget/config/WeatherConfigContent.kt @@ -1,6 +1,5 @@ package to.bitkit.appwidget.config -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -37,15 +36,11 @@ internal fun WeatherConfigContent( onReset: () -> Unit, onSave: () -> Unit, onCancel: () -> Unit, - modifier: Modifier = Modifier, ) { val prefs = state.weatherPreferences val weather = state.previewWeather - ScreenColumn( - noBackground = true, - modifier = modifier.background(Colors.Gray7) - ) { + ScreenColumn { AppTopBar( titleText = stringResource(R.string.widgets__weather__name), onBackClick = onCancel, diff --git a/app/src/main/java/to/bitkit/appwidget/ui/facts/FactsGlanceContent.kt b/app/src/main/java/to/bitkit/appwidget/ui/facts/FactsGlanceContent.kt index a580cef54..52367125f 100644 --- a/app/src/main/java/to/bitkit/appwidget/ui/facts/FactsGlanceContent.kt +++ b/app/src/main/java/to/bitkit/appwidget/ui/facts/FactsGlanceContent.kt @@ -1,5 +1,6 @@ package to.bitkit.appwidget.ui.facts +import android.content.Intent import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp import androidx.glance.GlanceModifier @@ -7,6 +8,7 @@ import androidx.glance.Image import androidx.glance.ImageProvider import androidx.glance.LocalContext import androidx.glance.LocalSize +import androidx.glance.appwidget.action.actionStartActivity import androidx.glance.layout.Alignment import androidx.glance.layout.Box import androidx.glance.layout.fillMaxSize @@ -18,6 +20,7 @@ import to.bitkit.appwidget.ui.components.CaptionB import to.bitkit.appwidget.ui.components.GlanceLayoutDimens import to.bitkit.appwidget.ui.components.GlanceWidgetScaffold import to.bitkit.appwidget.ui.theme.GlanceTextStyles +import to.bitkit.ui.MainActivity private val BADGE_SIZE = 32.dp private val BADGE_RESERVED_END = 40.dp @@ -28,8 +31,11 @@ fun FactsGlanceContent( fact: String?, ) { val context = LocalContext.current + val openAppIntent = Intent(context, MainActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + } - GlanceWidgetScaffold { + GlanceWidgetScaffold(onClick = actionStartActivity(openAppIntent)) { if (fact == null) { CaptionB(text = context.getString(R.string.appwidget__loading)) return@GlanceWidgetScaffold diff --git a/app/src/main/java/to/bitkit/appwidget/ui/weather/WeatherGlanceWidget.kt b/app/src/main/java/to/bitkit/appwidget/ui/weather/WeatherGlanceWidget.kt index fd2564d6c..0b477e015 100644 --- a/app/src/main/java/to/bitkit/appwidget/ui/weather/WeatherGlanceWidget.kt +++ b/app/src/main/java/to/bitkit/appwidget/ui/weather/WeatherGlanceWidget.kt @@ -3,6 +3,7 @@ package to.bitkit.appwidget.ui.weather import android.content.Context import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.glance.GlanceId import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.GlanceAppWidgetManager @@ -14,6 +15,8 @@ import to.bitkit.appwidget.model.AppWidgetData import to.bitkit.appwidget.model.AppWidgetEntry import to.bitkit.appwidget.model.AppWidgetType import to.bitkit.appwidget.ui.components.GlanceLayoutDimens +import to.bitkit.data.dto.WeatherDTO +import to.bitkit.repositories.CurrencyRepo import to.bitkit.ui.screens.widgets.blocks.toWeatherModel class WeatherGlanceWidget : GlanceAppWidget() { @@ -23,16 +26,20 @@ class WeatherGlanceWidget : GlanceAppWidget() { ) override suspend fun provideGlance(context: Context, id: GlanceId) { - val store = EntryPointAccessors + val accessor = EntryPointAccessors .fromApplication(context, AppWidgetEntryPoint::class.java) - .appWidgetPreferencesStore() + val store = accessor.appWidgetPreferencesStore() + val currencyRepo = accessor.currencyRepo() val appWidgetId = GlanceAppWidgetManager(context).getAppWidgetId(id) provideContent { val data by store.data.collectAsState(initial = AppWidgetData()) + val currencyState by currencyRepo.currencyState.collectAsState() val entry = data.entries.find { it.appWidgetId == appWidgetId } ?: AppWidgetEntry(appWidgetId = appWidgetId, type = AppWidgetType.WEATHER) - val weather = data.cachedWeather?.toWeatherModel() + val weather = remember(data.cachedWeather, currencyState) { + data.cachedWeather?.toGlanceWeather(currencyRepo) + } WeatherGlanceContent( entry = entry, @@ -48,4 +55,8 @@ class WeatherGlanceWidget : GlanceAppWidget() { .appWidgetPreferencesStore() .unregisterWidget(appWidgetId) } + + private fun WeatherDTO.toGlanceWeather(currencyRepo: CurrencyRepo) = toWeatherModel( + currentFee = currencyRepo.formatSatsAsFiatWithSymbol(avgFeeSats, withSpace = true) ?: currentFee, + ) } diff --git a/app/src/main/java/to/bitkit/data/widgets/PriceService.kt b/app/src/main/java/to/bitkit/data/widgets/PriceService.kt index 5da7a4938..5c5d9d782 100644 --- a/app/src/main/java/to/bitkit/data/widgets/PriceService.kt +++ b/app/src/main/java/to/bitkit/data/widgets/PriceService.kt @@ -22,7 +22,6 @@ import to.bitkit.models.WidgetType import to.bitkit.utils.AppError import to.bitkit.utils.Logger import java.text.NumberFormat -import java.util.Currency import java.util.Locale import javax.inject.Inject import javax.inject.Singleton @@ -147,24 +146,15 @@ class PriceService @Inject constructor( ) } - private fun formatPrice(pair: TradingPair, price: Double): String { + private fun formatPrice( + pair: TradingPair, + price: Double, + locale: Locale = Locale.getDefault(), + ): String { return runCatching { - val currency = Currency.getInstance(pair.quote) - val numberFormat = NumberFormat.getCurrencyInstance(Locale.US).apply { - this.currency = currency - maximumFractionDigits = when { - price >= 1000 -> 0 - price >= 1 -> 2 - else -> 6 - } - } - - // Format and remove currency symbol, keeping only the number with formatting - val formatted = numberFormat.format(price) - val currencySymbol = currency.symbol - formatted.replace(currencySymbol, "").trim() + formatPriceValue(price = price, locale = locale) }.onFailure { - Logger.warn("Error formatting price for ${pair.displayName}", e = it, context = TAG) + Logger.warn("Failed to format price for '${pair.displayName}'", it, context = TAG) }.getOrDefault(String.format(Locale.US, "%.2f", price)) } @@ -180,3 +170,25 @@ sealed class PriceError(message: String) : AppError(message) { class InvalidResponse(override val message: String) : PriceError(message) class NetworkError(override val message: String) : PriceError(message) } + +private const val GROUPED_PRICE_THRESHOLD = 1_000.0 +private const val STANDARD_PRICE_THRESHOLD = 1.0 +private const val GROUPED_PRICE_DECIMALS = 0 +private const val STANDARD_PRICE_DECIMALS = 2 +private const val SMALL_PRICE_DECIMALS = 6 +private const val MIN_PRICE_DECIMALS = 0 + +internal fun formatPriceValue( + price: Double, + locale: Locale = Locale.getDefault(), +): String { + return NumberFormat.getNumberInstance(locale).apply { + maximumFractionDigits = when { + price >= GROUPED_PRICE_THRESHOLD -> GROUPED_PRICE_DECIMALS + price >= STANDARD_PRICE_THRESHOLD -> STANDARD_PRICE_DECIMALS + else -> SMALL_PRICE_DECIMALS + } + minimumFractionDigits = MIN_PRICE_DECIMALS + isGroupingUsed = true + }.format(price) +} diff --git a/app/src/main/java/to/bitkit/data/widgets/WeatherService.kt b/app/src/main/java/to/bitkit/data/widgets/WeatherService.kt index d237dd2c2..f545b56ed 100644 --- a/app/src/main/java/to/bitkit/data/widgets/WeatherService.kt +++ b/app/src/main/java/to/bitkit/data/widgets/WeatherService.kt @@ -140,8 +140,7 @@ class WeatherService @Inject constructor( } private fun formatFeeForDisplay(sats: Long): String { - val selectedFiatValue = currencyRepo.convertSatsToFiat(sats).getOrNull() - return selectedFiatValue?.formattedWithSymbol(withSpace = true).orEmpty() + return currencyRepo.formatSatsAsFiatWithSymbol(sats, withSpace = true).orEmpty() } } diff --git a/app/src/main/java/to/bitkit/repositories/CurrencyRepo.kt b/app/src/main/java/to/bitkit/repositories/CurrencyRepo.kt index 42a792943..eac98c6cf 100644 --- a/app/src/main/java/to/bitkit/repositories/CurrencyRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/CurrencyRepo.kt @@ -242,6 +242,10 @@ class CurrencyRepo @Inject constructor( fun convertFiatToSats(fiat: Double, currency: String?) = convertFiatToSats(BigDecimal.valueOf(fiat), currency) + fun formatSatsAsFiatWithSymbol(sats: Long, withSpace: Boolean = false): String? { + return convertSatsToFiat(sats).getOrNull()?.formattedWithSymbol(withSpace = withSpace) + } + companion object { private const val TAG = "CurrencyRepo" } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeViewModel.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeViewModel.kt index ae3bc6047..21b8caf9d 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeViewModel.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeViewModel.kt @@ -31,6 +31,7 @@ import to.bitkit.models.widget.ArticleModel import to.bitkit.models.widget.toArticleModel import to.bitkit.models.widget.toBlockModel import to.bitkit.repositories.ActivityRepo +import to.bitkit.repositories.CurrencyRepo import to.bitkit.repositories.PubkyRepo import to.bitkit.repositories.TransferRepo import to.bitkit.repositories.WalletRepo @@ -45,6 +46,7 @@ class HomeViewModel @Inject constructor( @ApplicationContext private val context: Context, private val walletRepo: WalletRepo, private val widgetsRepo: WidgetsRepo, + private val currencyRepo: CurrencyRepo, private val settingsStore: SettingsStore, private val transferRepo: TransferRepo, private val pubkyRepo: PubkyRepo, @@ -76,9 +78,10 @@ class HomeViewModel @Inject constructor( combine( settingsStore.data, widgetsRepo.widgetsDataFlow, + currencyRepo.currencyState, _currentArticle, _currentFact, - ) { settings, widgetsData, currentArticle, currentFact -> + ) { settings, widgetsData, _, currentArticle, currentFact -> _uiState.update { it.copy( showWidgets = settings.showWidgets, @@ -97,7 +100,13 @@ class HomeViewModel @Inject constructor( currentArticle = currentArticle, currentFact = currentFact, currentBlock = widgetsData.block?.toBlockModel(), - currentWeather = widgetsData.weather?.toWeatherModel(), + currentWeather = widgetsData.weather?.let { weather -> + val currentFee = currencyRepo.formatSatsAsFiatWithSymbol( + sats = weather.avgFeeSats, + withSpace = true, + ) ?: weather.currentFee + weather.toWeatherModel(currentFee = currentFee) + }, currentPrice = widgetsData.price, showWidgetsOnboardingHint = settings.showWidgets && !settings.widgetsOnboardingHintDismissed, diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksEditScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksEditScreen.kt index c7ffc0af4..f852a0f4a 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksEditScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksEditScreen.kt @@ -1,7 +1,6 @@ package to.bitkit.ui.screens.widgets.blocks import androidx.annotation.DrawableRes -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -40,6 +39,7 @@ fun BlocksEditScreen( blocksViewModel: BlocksViewModel, onBack: () -> Unit, navigatePreview: () -> Unit, + modifier: Modifier = Modifier, ) { val customPreference by blocksViewModel.customPreferences.collectAsStateWithLifecycle() val currentBlock by blocksViewModel.currentBlock.collectAsStateWithLifecycle() @@ -67,6 +67,7 @@ fun BlocksEditScreen( onClickShowSource = { blocksViewModel.toggleShowSource() }, onClickReset = { blocksViewModel.resetCustomPreferences() }, onClickPreview = navigatePreview, + modifier = modifier ) } @@ -84,12 +85,10 @@ private fun Content( onClickPreview: () -> Unit, blocksPreferences: BlocksPreferences, block: BlockModel, + modifier: Modifier = Modifier, ) { ScreenColumn( - noBackground = true, - modifier = Modifier - .background(Colors.Gray7) - .testTag("blocks_edit_screen") + modifier = modifier.testTag("blocks_edit_screen") ) { AppTopBar( titleText = stringResource(R.string.widgets__blocks__name), diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksPreviewScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksPreviewScreen.kt index 8f6a30b39..bf451492d 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksPreviewScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksPreviewScreen.kt @@ -1,6 +1,5 @@ package to.bitkit.ui.screens.widgets.blocks -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -37,6 +36,7 @@ fun BlocksPreviewScreen( onClose: () -> Unit, onBack: () -> Unit, navigateEditWidget: () -> Unit, + modifier: Modifier = Modifier, ) { val customBlocksPreferences by blocksViewModel.customPreferences.collectAsStateWithLifecycle() val currentBlock by blocksViewModel.currentBlock.collectAsStateWithLifecycle() @@ -60,6 +60,7 @@ fun BlocksPreviewScreen( blocksViewModel.savePreferences() onClose() }, + modifier = modifier ) } @@ -72,12 +73,10 @@ private fun Content( isBlocksWidgetEnabled: Boolean, blocksPreferences: BlocksPreferences, block: BlockModel?, + modifier: Modifier = Modifier, ) { ScreenColumn( - noBackground = true, - modifier = Modifier - .background(Colors.Gray7) - .testTag("blocks_preview_screen") + modifier = modifier.testTag("blocks_preview_screen") ) { AppTopBar( titleText = stringResource(R.string.widgets__blocks__name), diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/WeatherModel.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/WeatherModel.kt index 607245f2d..59cb16427 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/WeatherModel.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/WeatherModel.kt @@ -21,7 +21,10 @@ data class WeatherModel( val icon: String, ) -fun WeatherDTO.toWeatherModel(locale: Locale = Locale.getDefault()): WeatherModel { +fun WeatherDTO.toWeatherModel( + currentFee: String = this.currentFee, + locale: Locale = Locale.getDefault(), +): WeatherModel { val title = when (condition) { FeeCondition.GOOD -> R.string.widgets__weather__condition__good__title FeeCondition.AVERAGE -> R.string.widgets__weather__condition__average__title diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt index d2492af4f..da173e3c1 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt @@ -23,17 +23,23 @@ import to.bitkit.ui.components.Caption13Up import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.theme.Colors -private const val PAGE_SMALL = 0 -private const val PAGE_WIDE = 1 -private const val PAGE_COUNT = 2 +private const val PAGE_WIDE = 0 +private const val PAGE_SMALL = 1 + +// temporarily removed until small size widgets variants are implemented +private const val PAGE_COUNT = 1 +// private const val PAGE_COUNT = 2 @Composable +@Suppress("UnusedParameter") fun WidgetSizeCarousel( smallContent: @Composable () -> Unit, wideContent: @Composable () -> Unit, modifier: Modifier = Modifier, ) { - val pagerState = rememberPagerState(pageCount = { PAGE_COUNT }) + val pagerState = rememberPagerState( + pageCount = { PAGE_COUNT }, + ) Column( verticalArrangement = Arrangement.Center, @@ -51,8 +57,9 @@ fun WidgetSizeCarousel( modifier = Modifier.fillMaxWidth() ) { when (page) { - PAGE_SMALL -> smallContent() PAGE_WIDE -> wideContent() + // temporarily removed until small size widgets variants are implemented + // PAGE_SMALL -> smallContent() } } } @@ -61,10 +68,9 @@ fun WidgetSizeCarousel( Caption13Up( text = stringResource( - if (pagerState.currentPage == PAGE_SMALL) { - R.string.widgets__widget__size_small - } else { - R.string.widgets__widget__size_wide + when (pagerState.currentPage) { + PAGE_SMALL -> R.string.widgets__widget__size_small + else -> R.string.widgets__widget__size_wide }, ), color = Colors.White64, @@ -76,22 +82,24 @@ fun WidgetSizeCarousel( VerticalSpacer(16.dp) - Row( - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxWidth() - .testTag("page_indicator") - ) { - repeat(PAGE_COUNT) { index -> - Box( - modifier = Modifier - .padding(horizontal = 4.dp) - .size(8.dp) - .background( - color = if (pagerState.currentPage == index) Colors.White else Colors.White32, - shape = CircleShape, - ) - ) + if (PAGE_COUNT > 1) { + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + .testTag("page_indicator") + ) { + repeat(PAGE_COUNT) { index -> + Box( + modifier = Modifier + .padding(horizontal = 4.dp) + .size(8.dp) + .background( + color = if (pagerState.currentPage == index) Colors.White else Colors.White32, + shape = CircleShape, + ) + ) + } } } } diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/facts/FactsPreviewScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/facts/FactsPreviewScreen.kt index 8405a105c..ced880e8a 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/facts/FactsPreviewScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/facts/FactsPreviewScreen.kt @@ -1,6 +1,5 @@ package to.bitkit.ui.screens.widgets.facts -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -32,6 +31,7 @@ fun FactsPreviewScreen( factsViewModel: FactsViewModel, onClose: () -> Unit, onBack: () -> Unit, + modifier: Modifier = Modifier, ) { val fact by factsViewModel.currentFact.collectAsStateWithLifecycle() val isFactsWidgetEnabled by factsViewModel.isFactsWidgetEnabled.collectAsStateWithLifecycle() @@ -52,6 +52,7 @@ fun FactsPreviewScreen( factsViewModel.saveWidget() onClose() }, + modifier = modifier ) } @@ -62,12 +63,10 @@ fun FactsPreviewContent( onClickSave: () -> Unit, isFactsWidgetEnabled: Boolean, fact: String, + modifier: Modifier = Modifier, ) { ScreenColumn( - noBackground = true, - modifier = Modifier - .background(Colors.Gray7) - .testTag("facts_preview_screen") + modifier = modifier.testTag("facts_preview_screen") ) { AppTopBar( titleText = stringResource(R.string.widgets__facts__name), diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesEditScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesEditScreen.kt index f8faed4e6..6d47f628c 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesEditScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesEditScreen.kt @@ -1,6 +1,5 @@ package to.bitkit.ui.screens.widgets.headlines -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -40,6 +39,7 @@ fun HeadlinesEditScreen( headlinesViewModel: HeadlinesViewModel, onBack: () -> Unit, navigatePreview: () -> Unit, + modifier: Modifier = Modifier, ) { val customHeadlinePreferences by headlinesViewModel.customPreferences.collectAsStateWithLifecycle() val article by headlinesViewModel.currentArticle.collectAsStateWithLifecycle() @@ -60,6 +60,7 @@ fun HeadlinesEditScreen( onClickPreview = { navigatePreview() }, + modifier = modifier ) } @@ -72,12 +73,10 @@ fun HeadlinesEditContent( onClickShowSource: () -> Unit, headlinePreferences: HeadlinePreferences, article: ArticleModel, + modifier: Modifier = Modifier, ) { ScreenColumn( - noBackground = true, - modifier = Modifier - .background(Colors.Gray7) - .testTag("headlines_edit_screen") + modifier = modifier.testTag("headlines_edit_screen") ) { AppTopBar( titleText = stringResource(R.string.widgets__news__name), diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesPreviewScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesPreviewScreen.kt index f7d9e2e00..ed2b628b1 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesPreviewScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/headlines/HeadlinesPreviewScreen.kt @@ -1,6 +1,5 @@ package to.bitkit.ui.screens.widgets.headlines -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -37,6 +36,7 @@ fun HeadlinesPreviewScreen( onClose: () -> Unit, onBack: () -> Unit, navigateEditWidget: () -> Unit, + modifier: Modifier = Modifier, ) { val customHeadlinePreferences by headlinesViewModel.customPreferences.collectAsStateWithLifecycle() val article by headlinesViewModel.currentArticle.collectAsStateWithLifecycle() @@ -60,6 +60,7 @@ fun HeadlinesPreviewScreen( headlinesViewModel.savePreferences() onClose() }, + modifier = modifier ) } @@ -72,12 +73,10 @@ fun HeadlinesPreviewContent( isHeadlinesImplemented: Boolean, headlinePreferences: HeadlinePreferences, article: ArticleModel, + modifier: Modifier = Modifier, ) { ScreenColumn( - noBackground = true, - modifier = Modifier - .background(Colors.Gray7) - .testTag("headlines_preview_screen") + modifier = modifier.testTag("headlines_preview_screen") ) { AppTopBar( titleText = stringResource(R.string.widgets__news__name), diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/price/PriceEditScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/price/PriceEditScreen.kt index 6728b79c3..75af1f960 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/price/PriceEditScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/price/PriceEditScreen.kt @@ -48,6 +48,7 @@ fun PriceEditScreen( viewModel: PriceViewModel, onBack: () -> Unit, navigatePreview: () -> Unit, + modifier: Modifier = Modifier, ) { val customPreferences by viewModel.customPreferences.collectAsStateWithLifecycle() val isLoading by viewModel.isLoading.collectAsStateWithLifecycle() @@ -60,6 +61,7 @@ fun PriceEditScreen( onSelectTradingPair = { viewModel.selectTradingPair(pair = it) }, onSelectPeriod = { viewModel.setPeriod(period = it) }, isLoading = isLoading, + modifier = modifier ) } @@ -72,14 +74,12 @@ fun PriceEditContent( onClickPreview: () -> Unit, preferences: PricePreferences, isLoading: Boolean, + modifier: Modifier = Modifier, ) { val selectedPair = preferences.enabledPairs.firstOrNull() ?: TradingPair.BTC_USD ScreenColumn( - noBackground = true, - modifier = Modifier - .background(Colors.Gray7) - .testTag("price_edit_screen") + modifier = modifier.testTag("price_edit_screen") ) { Box( modifier = Modifier diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/price/PricePreviewScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/price/PricePreviewScreen.kt index e1fe0be48..3877bc5ee 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/price/PricePreviewScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/price/PricePreviewScreen.kt @@ -1,6 +1,5 @@ package to.bitkit.ui.screens.widgets.price -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -44,6 +43,7 @@ fun PricePreviewScreen( onClose: () -> Unit, onBack: () -> Unit, navigateEditWidget: () -> Unit, + modifier: Modifier = Modifier, ) { val customPricePreferences by priceViewModel.customPreferences.collectAsStateWithLifecycle() val price by priceViewModel.currentPrice.collectAsStateWithLifecycle() @@ -77,6 +77,7 @@ fun PricePreviewScreen( priceViewModel.savePreferences() }, isLoading = isLoading, + modifier = modifier ) } @@ -90,12 +91,10 @@ fun PricePreviewContent( pricePreferences: PricePreferences, priceDTO: PriceDTO?, isLoading: Boolean, + modifier: Modifier = Modifier, ) { ScreenColumn( - noBackground = true, - modifier = Modifier - .background(Colors.Gray7) - .testTag("price_preview_screen") + modifier = modifier.testTag("price_preview_screen") ) { AppTopBar( titleText = stringResource(R.string.widgets__price__name), diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherEditScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherEditScreen.kt index 4790bc2a0..61d675308 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherEditScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherEditScreen.kt @@ -1,6 +1,5 @@ package to.bitkit.ui.screens.widgets.weather -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -69,10 +68,7 @@ fun WeatherEditContent( modifier: Modifier = Modifier, ) { ScreenColumn( - noBackground = true, - modifier = modifier - .background(Colors.Gray7) - .testTag("weather_edit_screen") + modifier = modifier.testTag("weather_edit_screen") ) { AppTopBar( titleText = stringResource(R.string.widgets__weather__name), diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherPreviewScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherPreviewScreen.kt index d69ebb41b..1dc13223f 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherPreviewScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherPreviewScreen.kt @@ -1,6 +1,5 @@ package to.bitkit.ui.screens.widgets.weather -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -79,10 +78,7 @@ fun WeatherPreviewContent( modifier: Modifier = Modifier, ) { ScreenColumn( - noBackground = true, - modifier = modifier - .background(Colors.Gray7) - .testTag("weather_preview_screen") + modifier = modifier.testTag("weather_preview_screen") ) { AppTopBar( titleText = stringResource(R.string.widgets__weather__name), diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherViewModel.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherViewModel.kt index cfb0625ff..a42860a94 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherViewModel.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/weather/WeatherViewModel.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update @@ -14,6 +15,7 @@ import kotlinx.coroutines.launch import to.bitkit.models.WidgetType import to.bitkit.models.widget.WeatherDataOption import to.bitkit.models.widget.WeatherPreferences +import to.bitkit.repositories.CurrencyRepo import to.bitkit.repositories.WidgetsRepo import to.bitkit.ui.screens.widgets.blocks.WeatherModel import to.bitkit.ui.screens.widgets.blocks.toWeatherModel @@ -21,7 +23,8 @@ import javax.inject.Inject @HiltViewModel class WeatherViewModel @Inject constructor( - private val widgetsRepo: WidgetsRepo + private val widgetsRepo: WidgetsRepo, + private val currencyRepo: CurrencyRepo, ) : ViewModel() { // MARK: - Public StateFlows @@ -51,8 +54,14 @@ class WeatherViewModel @Inject constructor( initialValue = true ) - val currentWeather: StateFlow = widgetsRepo.weatherFlow.map { weather -> - weather?.toWeatherModel() + val currentWeather: StateFlow = combine( + widgetsRepo.weatherFlow, + currencyRepo.currencyState, + ) { weather, _ -> + weather?.toWeatherModel( + currentFee = currencyRepo.formatSatsAsFiatWithSymbol(weather.avgFeeSats, withSpace = true) + ?: weather.currentFee, + ) }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(SUBSCRIPTION_TIMEOUT), diff --git a/app/src/main/java/to/bitkit/viewmodels/CurrencyViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/CurrencyViewModel.kt index 92af4aee6..72afc36d7 100644 --- a/app/src/main/java/to/bitkit/viewmodels/CurrencyViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/CurrencyViewModel.kt @@ -1,10 +1,15 @@ package to.bitkit.viewmodels +import android.content.Context +import androidx.glance.appwidget.updateAll import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import to.bitkit.appwidget.ui.weather.WeatherGlanceWidget import to.bitkit.models.BitcoinDisplayUnit import to.bitkit.models.ConvertedAmount import to.bitkit.models.PrimaryDisplay @@ -14,6 +19,7 @@ import javax.inject.Inject @HiltViewModel class CurrencyViewModel @Inject constructor( + @ApplicationContext private val context: Context, private val currencyRepo: CurrencyRepo, ) : ViewModel() { @@ -46,6 +52,8 @@ class CurrencyViewModel @Inject constructor( fun setSelectedCurrency(currency: String) { viewModelScope.launch { currencyRepo.setSelectedCurrency(currency) + currencyRepo.currencyState.first { it.selectedCurrency == currency } + WeatherGlanceWidget().updateAll(context) } } diff --git a/app/src/main/res/xml/appwidget_info_blocks.xml b/app/src/main/res/xml/appwidget_info_blocks.xml index 6264f6165..a373fd82c 100644 --- a/app/src/main/res/xml/appwidget_info_blocks.xml +++ b/app/src/main/res/xml/appwidget_info_blocks.xml @@ -13,5 +13,6 @@ android:previewLayout="@layout/appwidget_preview_blocks" android:description="@string/widgets__blocks__description" android:configure="to.bitkit.appwidget.config.AppWidgetConfigActivity" + android:widgetFeatures="reconfigurable" android:updatePeriodMillis="0" tools:targetApi="31" /> diff --git a/app/src/main/res/xml/appwidget_info_headlines.xml b/app/src/main/res/xml/appwidget_info_headlines.xml index e10bf14eb..98eab9286 100644 --- a/app/src/main/res/xml/appwidget_info_headlines.xml +++ b/app/src/main/res/xml/appwidget_info_headlines.xml @@ -13,5 +13,6 @@ android:previewLayout="@layout/appwidget_preview_headlines" android:description="@string/widgets__news__description" android:configure="to.bitkit.appwidget.config.AppWidgetConfigActivity" + android:widgetFeatures="reconfigurable" android:updatePeriodMillis="0" tools:targetApi="31" /> diff --git a/app/src/main/res/xml/appwidget_info_price.xml b/app/src/main/res/xml/appwidget_info_price.xml index 94fe99672..db4c37b04 100644 --- a/app/src/main/res/xml/appwidget_info_price.xml +++ b/app/src/main/res/xml/appwidget_info_price.xml @@ -13,5 +13,6 @@ android:previewLayout="@layout/appwidget_preview_price" android:description="@string/appwidget__price__description" android:configure="to.bitkit.appwidget.config.AppWidgetConfigActivity" + android:widgetFeatures="reconfigurable" android:updatePeriodMillis="0" tools:targetApi="31" /> diff --git a/app/src/main/res/xml/appwidget_info_weather.xml b/app/src/main/res/xml/appwidget_info_weather.xml index 596e504f0..db5b3d58f 100644 --- a/app/src/main/res/xml/appwidget_info_weather.xml +++ b/app/src/main/res/xml/appwidget_info_weather.xml @@ -13,5 +13,6 @@ android:previewLayout="@layout/appwidget_preview_weather" android:description="@string/widgets__weather__description" android:configure="to.bitkit.appwidget.config.AppWidgetConfigActivity" + android:widgetFeatures="reconfigurable" android:updatePeriodMillis="0" tools:targetApi="31" /> diff --git a/app/src/test/java/to/bitkit/data/widgets/PriceServiceTest.kt b/app/src/test/java/to/bitkit/data/widgets/PriceServiceTest.kt new file mode 100644 index 000000000..cdb376cf7 --- /dev/null +++ b/app/src/test/java/to/bitkit/data/widgets/PriceServiceTest.kt @@ -0,0 +1,31 @@ +package to.bitkit.data.widgets + +import org.junit.Test +import java.util.Locale +import kotlin.test.assertEquals +import kotlin.test.assertFalse + +class PriceServiceTest { + + @Test + fun `formatPriceValue excludes currency symbol`() { + val formatted = formatPriceValue(price = 81_444.12, locale = Locale.US) + + assertEquals("81,444", formatted) + assertFalse(formatted.contains('$')) + } + + @Test + fun `formatPriceValue uses provided locale grouping`() { + val formatted = formatPriceValue(price = 81_444.12, locale = Locale.GERMANY) + + assertEquals("81.444", formatted) + } + + @Test + fun `formatPriceValue keeps fractional precision by price range`() { + assertEquals("81,444", formatPriceValue(price = 81_444.12, locale = Locale.US)) + assertEquals("814.12", formatPriceValue(price = 814.12, locale = Locale.US)) + assertEquals("0.123457", formatPriceValue(price = 0.1234567, locale = Locale.US)) + } +} diff --git a/app/src/test/java/to/bitkit/ui/screens/widgets/blocks/WeatherModelTest.kt b/app/src/test/java/to/bitkit/ui/screens/widgets/blocks/WeatherModelTest.kt new file mode 100644 index 000000000..d8005a2ee --- /dev/null +++ b/app/src/test/java/to/bitkit/ui/screens/widgets/blocks/WeatherModelTest.kt @@ -0,0 +1,27 @@ +package to.bitkit.ui.screens.widgets.blocks + +import org.junit.Test +import to.bitkit.data.dto.FeeCondition +import to.bitkit.data.dto.WeatherDTO +import java.util.Locale +import kotlin.test.assertEquals + +class WeatherModelTest { + + @Test + fun `toWeatherModel uses current fee override`() { + val weather = WeatherDTO( + condition = FeeCondition.GOOD, + currentFee = "$ 0.52", + nextBlockFee = 6, + avgFeeSats = 520L, + ) + + val model = weather.toWeatherModel( + currentFee = "€ 0.48", + locale = Locale.US, + ) + + assertEquals("€ 0.48", model.currentFee) + } +} diff --git a/changelog.d/next/952.fixed.md b/changelog.d/next/952.fixed.md new file mode 100644 index 000000000..215bed0f7 --- /dev/null +++ b/changelog.d/next/952.fixed.md @@ -0,0 +1 @@ +Improved OS widgets so previews, settings, currency display, and OS-home interactions match v61 design iteration.