From 199f683659edca11fc3a651d2c0084a1c20e0219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C9=91rry=20Shiv=C9=91m?= Date: Sat, 13 Jan 2024 18:09:24 +0530 Subject: [PATCH] Refactor currency formatter & handle currencies with zero decimals (#65) Signed-off-by: starry-shivam --- app/build.gradle | 6 +- .../com/starry/greenstash/di/MainModule.kt | 1 + .../reminder/ReminderNotificationSender.kt | 20 +- .../greenstash/ui/navigation/DrawerScreens.kt | 4 +- .../info/composables/GoalInfoScreen.kt | 34 ++-- .../starry/greenstash/utils/GoalTextUtils.kt | 27 ++- .../starry/greenstash/utils/PreferenceUtil.kt | 4 +- .../java/com/starry/greenstash/utils/Utils.kt | 15 +- .../starry/greenstash/widget/GoalWidget.kt | 17 +- app/src/main/res/values/arrays.xml | 185 +++++++++--------- build.gradle | 2 +- 11 files changed, 162 insertions(+), 153 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 15736257..5ed5d160 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -82,10 +82,10 @@ dependencies { // Android core components. implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' implementation 'androidx.activity:activity-compose:1.8.2' - implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.2" - implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.7.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0" implementation "androidx.navigation:navigation-compose:2.7.6" // Jetpack compose. implementation "androidx.compose.ui:ui" diff --git a/app/src/main/java/com/starry/greenstash/di/MainModule.kt b/app/src/main/java/com/starry/greenstash/di/MainModule.kt index 2de4e2ae..c6730469 100644 --- a/app/src/main/java/com/starry/greenstash/di/MainModule.kt +++ b/app/src/main/java/com/starry/greenstash/di/MainModule.kt @@ -70,6 +70,7 @@ class MainModule { fun provideWidgetDao(appDatabase: AppDatabase) = appDatabase.getWidgetDao() @Provides + @Singleton fun providePreferenceUtil(@ApplicationContext context: Context) = PreferenceUtil(context) @Provides diff --git a/app/src/main/java/com/starry/greenstash/reminder/ReminderNotificationSender.kt b/app/src/main/java/com/starry/greenstash/reminder/ReminderNotificationSender.kt index ee3c7247..3b47152e 100644 --- a/app/src/main/java/com/starry/greenstash/reminder/ReminderNotificationSender.kt +++ b/app/src/main/java/com/starry/greenstash/reminder/ReminderNotificationSender.kt @@ -83,7 +83,7 @@ class ReminderNotificationSender( .setContentIntent(createActivityIntent()) val remainingAmount = (goal.targetAmount - goalItem.getCurrentlySavedAmount()) - val defCurrency = preferenceUtil.getString(PreferenceUtil.DEFAULT_CURRENCY_STR, "") + val defCurrency = preferenceUtil.getString(PreferenceUtil.DEFAULT_CURRENCY_STR, "")!! if (goal.deadline.isNotEmpty() && goal.deadline.isNotBlank()) { val calculatedDays = GoalTextUtils(preferenceUtil).calcRemainingDays(goal) @@ -92,8 +92,8 @@ class ReminderNotificationSender( val amountDay = remainingAmount / calculatedDays.remainingDays notification.addAction( R.drawable.ic_notification_deposit, - "${context.getString(R.string.deposit_button)} $defCurrency${ - Utils.formatCurrency(Utils.roundDecimal(amountDay)) + "${context.getString(R.string.deposit_button)} ${ + Utils.formatCurrency(Utils.roundDecimal(amountDay), defCurrency) }", createDepositIntent(goal.goalId, amountDay) ) @@ -103,8 +103,8 @@ class ReminderNotificationSender( val amountSemiWeek = remainingAmount / (calculatedDays.remainingDays / 4) notification.addAction( R.drawable.ic_notification_deposit, - "${context.getString(R.string.deposit_button)} $defCurrency${ - Utils.formatCurrency(Utils.roundDecimal(amountSemiWeek)) + "${context.getString(R.string.deposit_button)} ${ + Utils.formatCurrency(Utils.roundDecimal(amountSemiWeek), defCurrency) }", createDepositIntent(goal.goalId, amountSemiWeek) ) @@ -114,8 +114,8 @@ class ReminderNotificationSender( val amountWeek = remainingAmount / (calculatedDays.remainingDays / 7) notification.addAction( R.drawable.ic_notification_deposit, - "${context.getString(R.string.deposit_button)} $defCurrency${ - Utils.formatCurrency(Utils.roundDecimal(amountWeek)) + "${context.getString(R.string.deposit_button)} ${ + Utils.formatCurrency(Utils.roundDecimal(amountWeek), defCurrency) }", createDepositIntent(goal.goalId, amountWeek) ) @@ -138,11 +138,7 @@ class ReminderNotificationSender( .setContentTitle(context.getString(R.string.notification_deposited_title)) .setContentText( context.getString(R.string.notification_deposited_desc) - .format( - "$defCurrency${ - Utils.formatCurrency(Utils.roundDecimal(amount)) - }" - ) + .format(Utils.formatCurrency(Utils.roundDecimal(amount), defCurrency!!)) ) .setStyle(NotificationCompat.BigTextStyle()) .setContentIntent(createActivityIntent()) diff --git a/app/src/main/java/com/starry/greenstash/ui/navigation/DrawerScreens.kt b/app/src/main/java/com/starry/greenstash/ui/navigation/DrawerScreens.kt index 6f9c628a..addb8463 100644 --- a/app/src/main/java/com/starry/greenstash/ui/navigation/DrawerScreens.kt +++ b/app/src/main/java/com/starry/greenstash/ui/navigation/DrawerScreens.kt @@ -29,7 +29,9 @@ import com.starry.greenstash.R sealed class DrawerScreens(val route: String, val nameResId: Int, val iconResId: Int) { data object Home : DrawerScreens("home", R.string.drawer_home, R.drawable.ic_nav_home) - data object Backups : DrawerScreens("backups", R.string.drawer_backups, R.drawable.ic_nav_backups) + data object Backups : + DrawerScreens("backups", R.string.drawer_backups, R.drawable.ic_nav_backups) + data object Settings : DrawerScreens("settings", R.string.drawer_settings, R.drawable.ic_nav_settings) } diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/GoalInfoScreen.kt b/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/GoalInfoScreen.kt index 81b5bf88..ff2cb5e9 100644 --- a/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/GoalInfoScreen.kt +++ b/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/GoalInfoScreen.kt @@ -225,8 +225,10 @@ fun GoalInfoCard( daysLeftText: String, progress: Float ) { - val formattedTargetAmount = Utils.formatCurrency(Utils.roundDecimal(targetAmount)) - val formattedSavedAmount = Utils.formatCurrency(Utils.roundDecimal(savedAmount)) + val formattedTargetAmount = + Utils.formatCurrency(Utils.roundDecimal(targetAmount), currencySymbol) + val formattedSavedAmount = + Utils.formatCurrency(Utils.roundDecimal(savedAmount), currencySymbol) Card( modifier = Modifier @@ -254,25 +256,21 @@ fun GoalInfoCard( Spacer(modifier = Modifier.height(8.dp)) - Row { - Text( - text = currencySymbol, - fontWeight = FontWeight.Bold, - fontSize = 38.sp, - modifier = Modifier.padding(start = 12.dp) - ) - Text( - text = formattedSavedAmount, - fontWeight = FontWeight.Bold, - fontSize = 38.sp, - modifier = Modifier.padding(start = 4.dp) - ) - } + Text( + text = formattedSavedAmount, + fontWeight = FontWeight.Bold, + fontSize = 38.sp, + modifier = Modifier.padding(start = 4.dp) + ) + Spacer(modifier = Modifier.height(2.dp)) Text( - text = stringResource(id = R.string.info_card_remaining_amount).format("$currencySymbol $formattedTargetAmount"), + text = stringResource( + id = R.string.info_card_remaining_amount, + formattedTargetAmount + ), fontSize = 14.sp, fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(start = 12.dp) @@ -363,7 +361,7 @@ fun TransactionCard(transactions: List, currencySymbol: String) { transactions.forEach { TransactionItem( transactionType = it.type, - amount = "$currencySymbol${Utils.formatCurrency(Utils.roundDecimal(it.amount))}", + amount = Utils.formatCurrency(Utils.roundDecimal(it.amount), currencySymbol), date = it.getTransactionDate() ) } diff --git a/app/src/main/java/com/starry/greenstash/utils/GoalTextUtils.kt b/app/src/main/java/com/starry/greenstash/utils/GoalTextUtils.kt index 0d6fc974..7de937cf 100644 --- a/app/src/main/java/com/starry/greenstash/utils/GoalTextUtils.kt +++ b/app/src/main/java/com/starry/greenstash/utils/GoalTextUtils.kt @@ -73,8 +73,8 @@ class GoalTextUtils(private val preferenceUtil: PreferenceUtil) { "\n" + context.getString(R.string.currently_saved_complete) } text = text.format( - "$defCurrency${Utils.formatCurrency(item.getCurrentlySavedAmount())}", - "$defCurrency${Utils.formatCurrency(item.goal.targetAmount)}" + Utils.formatCurrency(item.getCurrentlySavedAmount(), defCurrency!!), + Utils.formatCurrency(item.goal.targetAmount, defCurrency) ) return text } @@ -84,29 +84,28 @@ class GoalTextUtils(private val preferenceUtil: PreferenceUtil) { if ((remainingAmount > 0f)) { if (item.goal.deadline.isNotEmpty() && item.goal.deadline.isNotBlank()) { val calculatedDays = calcRemainingDays(item.goal) - val defCurrency = preferenceUtil.getString(PreferenceUtil.DEFAULT_CURRENCY_STR, "") + val defCurrency = + preferenceUtil.getString(PreferenceUtil.DEFAULT_CURRENCY_STR, "")!! // build description string. var text = context.getString(R.string.goal_days_left) .format(calculatedDays.parsedEndDate, calculatedDays.remainingDays) + "\n" if (calculatedDays.remainingDays > 2) { text += context.getString(R.string.goal_approx_saving).format( - "$defCurrency${ - Utils.formatCurrency( - Utils.roundDecimal( - remainingAmount / calculatedDays.remainingDays - ) - ) - }" + Utils.formatCurrency( + Utils.roundDecimal( + remainingAmount / calculatedDays.remainingDays + ), defCurrency + ) ) text += context.getString(R.string.goal_approx_saving_day) if (calculatedDays.remainingDays > 14) { val weeks = calculatedDays.remainingDays / 7 text = text.dropLast(1) // remove full stop - text += ", $defCurrency${ + text += ", ${ Utils.formatCurrency( Utils.roundDecimal( remainingAmount / weeks - ) + ), defCurrency ) }/${ context.getString( @@ -116,11 +115,11 @@ class GoalTextUtils(private val preferenceUtil: PreferenceUtil) { if (calculatedDays.remainingDays > 60) { val months = calculatedDays.remainingDays / 30 text = text.dropLast(1) // remove full stop - text += ", $defCurrency${ + text += ", ${ Utils.formatCurrency( Utils.roundDecimal( remainingAmount / months - ) + ), defCurrency ) }/${ context.getString( diff --git a/app/src/main/java/com/starry/greenstash/utils/PreferenceUtil.kt b/app/src/main/java/com/starry/greenstash/utils/PreferenceUtil.kt index f2e7ec9d..9333303c 100644 --- a/app/src/main/java/com/starry/greenstash/utils/PreferenceUtil.kt +++ b/app/src/main/java/com/starry/greenstash/utils/PreferenceUtil.kt @@ -37,7 +37,7 @@ class PreferenceUtil(context: Context) { // Preference keys const val APP_THEME_INT = "theme_settings" const val MATERIAL_YOU_BOOL = "material_you" - const val DEFAULT_CURRENCY_STR = "default_currency" + const val DEFAULT_CURRENCY_STR = "default_currency_code" const val DATE_FORMAT_STR = "date_format" const val APP_LOCK_BOOL = "app_lock" } @@ -48,7 +48,7 @@ class PreferenceUtil(context: Context) { prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) // Pre-populate some preference data with default values if (!keyExists(DEFAULT_CURRENCY_STR)) { - putString(DEFAULT_CURRENCY_STR, "$") + putString(DEFAULT_CURRENCY_STR, "USD") } if (!keyExists(DATE_FORMAT_STR)) { putString(DATE_FORMAT_STR, DateStyle.DateMonthYear.pattern) diff --git a/app/src/main/java/com/starry/greenstash/utils/Utils.kt b/app/src/main/java/com/starry/greenstash/utils/Utils.kt index 6e42e1e4..3596ab3c 100644 --- a/app/src/main/java/com/starry/greenstash/utils/Utils.kt +++ b/app/src/main/java/com/starry/greenstash/utils/Utils.kt @@ -32,7 +32,9 @@ import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL import java.math.RoundingMode import java.text.DecimalFormat import java.text.DecimalFormatSymbols +import java.text.NumberFormat import java.text.SimpleDateFormat +import java.util.Currency import java.util.Date import java.util.Locale @@ -64,9 +66,16 @@ object Utils { } /** Convert double into currency format */ - fun formatCurrency(number: Double): String { - val df = DecimalFormat("#,###.00") - return df.format(number) + fun formatCurrency(amount: Double, currencyCode: String): String { + val nf = NumberFormat.getCurrencyInstance().apply { + currency = Currency.getInstance(currencyCode) + maximumFractionDigits = if (currencyCode in setOf( + "JPY", "DJF", "GNF", "IDR", "KMF", "KRW", "LAK", + "PYG", "RWF", "VND", "VUV", "XAF", "XOF", "XPF" + ) + ) 0 else 2 + } + return nf.format(amount) } /** diff --git a/app/src/main/java/com/starry/greenstash/widget/GoalWidget.kt b/app/src/main/java/com/starry/greenstash/widget/GoalWidget.kt index 728f24f9..f19126ee 100644 --- a/app/src/main/java/com/starry/greenstash/widget/GoalWidget.kt +++ b/app/src/main/java/com/starry/greenstash/widget/GoalWidget.kt @@ -105,13 +105,16 @@ class GoalWidget : AppWidgetProvider() { views.setCharSequence(R.id.widgetTitle, "setText", goalItem.goal.title) // Set Widget description. - val defCurrency = preferenceUtil.getString(PreferenceUtil.DEFAULT_CURRENCY_STR, "$") + val defCurrency = preferenceUtil.getString(PreferenceUtil.DEFAULT_CURRENCY_STR, "")!! val widgetDesc = context.getString(R.string.goal_widget_desc) .format( - "$defCurrency${Utils.formatCurrency(goalItem.getCurrentlySavedAmount())} / $defCurrency${ + "${ Utils.formatCurrency( - goalItem.goal.targetAmount + goalItem.getCurrentlySavedAmount(), + defCurrency ) + } / $defCurrency${ + Utils.formatCurrency(goalItem.goal.targetAmount, defCurrency) }" ) views.setCharSequence(R.id.widgetDesc, "setText", widgetDesc) @@ -122,22 +125,22 @@ class GoalWidget : AppWidgetProvider() { if (goalItem.goal.deadline.isNotEmpty() && goalItem.goal.deadline.isNotBlank()) { val calculatedDays = goalTextUtils.calcRemainingDays(goalItem.goal) if (calculatedDays.remainingDays > 2) { - val amountDays = "$defCurrency${ + val amountDays = "${ Utils.formatCurrency( Utils.roundDecimal( remainingAmount / calculatedDays.remainingDays - ) + ), defCurrency ) }/${context.getString(R.string.goal_approx_saving_day)}" views.setCharSequence(R.id.widgetAmountDay, "setText", amountDays) views.setViewVisibility(R.id.widgetAmountDay, View.VISIBLE) } if (calculatedDays.remainingDays > 7) { - val amountWeeks = "$defCurrency${ + val amountWeeks = "${ Utils.formatCurrency( Utils.roundDecimal( remainingAmount / (calculatedDays.remainingDays / 7) - ) + ), defCurrency ) }/${ context.getString( diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index eb573bab..6e47282c 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -123,124 +123,125 @@ - $ - CA$ - + USD + CAD + EUR AED - Af + AFN ALL AMD - AR$ - AU$ - man. - KM - Tk + ARS + AUD + AZN + BAM + BDT BGN - BD - FBu - BN$ - Bs - R$ + BHD + BIF + BND + BOB + BRL BWP - Br - BZ$ + BYN + BZD CDF CHF - CL$ - CN¥ - CO$ - - CV$ - - Fdj - Dkr - RD$ - DA - Ekr + CLP + CNY + COP + CRC + CVE + CZK + DJF + DKK + DOP + DZD + EEK EGP - Nfk - Br - £ + ERN + ETB + GBP GEL - GH₵ - FG + GHS + GNF GTQ - HK$ + HKD HNL - kn - Ft - Rp - - + HRK + HUF + IDR + ILS + INR IQD IRR - Ikr - J$ - JD - ¥ - Ksh + ISK + JMD + JOD + JPY + KES KHR - CF - - KD + KMF + KRW + KWD KZT - L.L. - SLRs - Lt - Ls - LD + LBP + LKR + LTL + LVL + LYD MAD MDL MGA MKD MMK - MOP$ - MURs - MX$ - RM - MTn - N$ - - C$ + MOP + MUR + MXN + MYR + MZN + NAD + NGN + NIO NOK - NPRs - NZ$ + NPR + NZD OMR - B/. - S/. - - PKRs - - - QR + PAB + PEN + PHP + PKR + PLN + PYG + QAR RON - din. + RSD RUB RWF - SR + SAR SDG - Skr - S$ - Ssh - SY£ - ฿ - DT - T$ - TL - TT$ - NT$ - TSh - - USh - $U + SEK + SGD + SOS + SYP + THB + TND + TOP + TRY + TTD + TWD + TZS + UAH + UGX + UYU UZS - Bs.F. - - FCFA - CFA - YR - R - ZK - ZWL$ + VES + VND + XAF + XOF + YER + ZAR + ZMW + ZWL + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 16d1daef..fa81f155 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { kotlin_version = '1.9.21' - gradle_version = '8.2.0' + gradle_version = '8.2.1' hilt_version = '2.49' room_version = '2.6.1' }