Allow toggling comma/dot as decimal/thousands

This commit is contained in:
Naveen 2022-05-30 22:31:28 +05:30
parent e87784f840
commit 65ee3c38c3
8 changed files with 149 additions and 64 deletions

View File

@ -24,8 +24,11 @@ import me.grantland.widget.AutofitHelper
class MainActivity : SimpleActivity(), Calculator { class MainActivity : SimpleActivity(), Calculator {
private var storedTextColor = 0 private var storedTextColor = 0
private var vibrateOnButtonPress = true private var vibrateOnButtonPress = true
private var storedUseCommaAsDecimalMark = false
private var decimalSeparator = DOT
private var groupingSeparator = COMMA
lateinit var calc: CalculatorImpl private lateinit var calc: CalculatorImpl
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -50,7 +53,9 @@ class MainActivity : SimpleActivity(), Calculator {
btn_clear.setOnLongClickListener { calc.handleReset(); true } btn_clear.setOnLongClickListener { calc.handleReset(); true }
getButtonIds().forEach { getButtonIds().forEach {
it.setOnClickListener { calc.numpadClicked(it.id); checkHaptic(it) } it.setOnClickListener { view ->
calc.numpadClicked(view.id); checkHaptic(view)
}
} }
btn_equals.setOnClickListener { calc.handleEquals(); checkHaptic(it) } btn_equals.setOnClickListener { calc.handleEquals(); checkHaptic(it) }
@ -61,6 +66,7 @@ class MainActivity : SimpleActivity(), Calculator {
AutofitHelper.create(formula) AutofitHelper.create(formula)
storeStateVariables() storeStateVariables()
updateViewColors(calculator_holder, getProperTextColor()) updateViewColors(calculator_holder, getProperTextColor())
setupDecimalSeparator()
checkWhatsNewDialog() checkWhatsNewDialog()
checkAppOnSDCard() checkAppOnSDCard()
} }
@ -75,6 +81,10 @@ class MainActivity : SimpleActivity(), Calculator {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} }
if (storedUseCommaAsDecimalMark != config.useCommaAsDecimalMark) {
setupDecimalSeparator()
}
vibrateOnButtonPress = config.vibrateOnButtonPress vibrateOnButtonPress = config.vibrateOnButtonPress
val properPrimaryColor = getProperPrimaryColor() val properPrimaryColor = getProperPrimaryColor()
@ -117,6 +127,7 @@ class MainActivity : SimpleActivity(), Calculator {
private fun storeStateVariables() { private fun storeStateVariables() {
config.apply { config.apply {
storedTextColor = textColor storedTextColor = textColor
storedUseCommaAsDecimalMark = useCommaAsDecimalMark
} }
} }
@ -186,4 +197,17 @@ class MainActivity : SimpleActivity(), Calculator {
override fun showNewFormula(value: String, context: Context) { override fun showNewFormula(value: String, context: Context) {
formula.text = value formula.text = value
} }
private fun setupDecimalSeparator() {
storedUseCommaAsDecimalMark = config.useCommaAsDecimalMark
if (storedUseCommaAsDecimalMark) {
decimalSeparator = COMMA
groupingSeparator = DOT
} else {
decimalSeparator = DOT
groupingSeparator = COMMA
}
calc.updateSeparators(decimalSeparator, groupingSeparator)
btn_decimal.text = decimalSeparator
}
} }

View File

@ -26,6 +26,7 @@ class SettingsActivity : SimpleActivity() {
setupUseEnglish() setupUseEnglish()
setupVibrate() setupVibrate()
setupPreventPhoneFromSleeping() setupPreventPhoneFromSleeping()
setupUseCommaAsDecimalMark()
setupCustomizeWidgetColors() setupCustomizeWidgetColors()
updateTextColors(settings_scrollview) updateTextColors(settings_scrollview)
@ -94,6 +95,14 @@ class SettingsActivity : SimpleActivity() {
} }
} }
private fun setupUseCommaAsDecimalMark() {
settings_use_comma_as_decimal_mark.isChecked = config.useCommaAsDecimalMark
settings_use_comma_as_decimal_mark_holder.setOnClickListener {
settings_use_comma_as_decimal_mark.toggle()
config.useCommaAsDecimalMark = settings_use_comma_as_decimal_mark.isChecked
}
}
private fun setupCustomizeWidgetColors() { private fun setupCustomizeWidgetColors() {
settings_customize_widget_colors_holder.setOnClickListener { settings_customize_widget_colors_holder.setOnClickListener {
Intent(this, WidgetConfigureActivity::class.java).apply { Intent(this, WidgetConfigureActivity::class.java).apply {

View File

@ -8,7 +8,12 @@ import com.simplemobiletools.commons.extensions.toast
import net.objecthunter.exp4j.ExpressionBuilder import net.objecthunter.exp4j.ExpressionBuilder
import java.math.BigDecimal import java.math.BigDecimal
class CalculatorImpl(calculator: Calculator, private val context: Context) { class CalculatorImpl(
calculator: Calculator,
private val context: Context,
private var decimalSeparator: String = DOT,
private var groupingSeparator: String = COMMA
) {
private var callback: Calculator? = calculator private var callback: Calculator? = calculator
private var baseValue = 0.0 private var baseValue = 0.0
@ -20,6 +25,10 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
private val operationsRegex = "[-+×÷^%√]".toPattern() private val operationsRegex = "[-+×÷^%√]".toPattern()
private val numbersRegex = "[^0-9,.]".toRegex() private val numbersRegex = "[^0-9,.]".toRegex()
private val formatter = NumberFormatHelper(
decimalSeparator = decimalSeparator, groupingSeparator = groupingSeparator
)
init { init {
showNewResult("0") showNewResult("0")
} }
@ -35,21 +44,21 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
} }
private fun zeroClicked() { private fun zeroClicked() {
val valueToCheck = inputDisplayedFormula.trimStart('-').replace(",", "") val valueToCheck = inputDisplayedFormula.trimStart('-').removeGroupSeparator()
val value = valueToCheck.substring(valueToCheck.indexOfAny(operations) + 1) val value = valueToCheck.substring(valueToCheck.indexOfAny(operations) + 1)
if (value != "0" || value.contains(".")) { if (value != "0" || value.contains(decimalSeparator)) {
addDigit(0) addDigit(0)
} }
} }
private fun decimalClicked() { private fun decimalClicked() {
val valueToCheck = inputDisplayedFormula.trimStart('-').replace(",", "") val valueToCheck = inputDisplayedFormula.trimStart('-').replace(groupingSeparator, "")
val value = valueToCheck.substring(valueToCheck.indexOfAny(operations) + 1) val value = valueToCheck.substring(valueToCheck.indexOfAny(operations) + 1)
if (!value.contains(".")) { if (!value.contains(decimalSeparator)) {
when { when {
value == "0" && !valueToCheck.contains(operationsRegex.toRegex()) -> inputDisplayedFormula = "0." value == "0" && !valueToCheck.contains(operationsRegex.toRegex()) -> inputDisplayedFormula = "0$decimalSeparator"
value == "" -> inputDisplayedFormula += "0." value == "" -> inputDisplayedFormula += "0$decimalSeparator"
else -> inputDisplayedFormula += "." else -> inputDisplayedFormula += decimalSeparator
} }
} }
@ -60,10 +69,13 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
private fun addThousandsDelimiter() { private fun addThousandsDelimiter() {
val valuesToCheck = numbersRegex.split(inputDisplayedFormula).filter { it.trim().isNotEmpty() } val valuesToCheck = numbersRegex.split(inputDisplayedFormula).filter { it.trim().isNotEmpty() }
valuesToCheck.forEach { valuesToCheck.forEach {
var newString = Formatter.addGroupingSeparators(it) var newString = formatter.addGroupingSeparators(it)
// allow writing numbers like 0.003 // allow writing numbers like 0.003
if (it.contains(".")) { if (it.contains(decimalSeparator)) {
newString = newString.substringBefore(".") + ".${it.substringAfter(".")}" val firstPart = newString.substringBefore(decimalSeparator)
val lastPart = it.substringAfter(decimalSeparator)
newString = "$firstPart$decimalSeparator$lastPart"
} }
inputDisplayedFormula = inputDisplayedFormula.replace(it, newString) inputDisplayedFormula = inputDisplayedFormula.replace(it, newString)
@ -86,7 +98,7 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
} }
val lastChar = inputDisplayedFormula.last().toString() val lastChar = inputDisplayedFormula.last().toString()
if (lastChar == ".") { if (lastChar == decimalSeparator) {
inputDisplayedFormula = inputDisplayedFormula.dropLast(1) inputDisplayedFormula = inputDisplayedFormula.dropLast(1)
} else if (operations.contains(lastChar)) { } else if (operations.contains(lastChar)) {
inputDisplayedFormula = inputDisplayedFormula.dropLast(1) inputDisplayedFormula = inputDisplayedFormula.dropLast(1)
@ -137,7 +149,7 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
return false return false
} }
if (!inputDisplayedFormula.trimStart('-').any { it.toString() in operations } && inputDisplayedFormula.replace(",", "").toDouble() != 0.0) { if (!inputDisplayedFormula.trimStart('-').any { it.toString() in operations } && inputDisplayedFormula.removeGroupSeparator().toDouble() != 0.0) {
inputDisplayedFormula = if (inputDisplayedFormula.first() == '-') { inputDisplayedFormula = if (inputDisplayedFormula.first() == '-') {
inputDisplayedFormula.substring(1) inputDisplayedFormula.substring(1)
} else { } else {
@ -185,7 +197,7 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
} }
private fun getSecondValue(): Double { private fun getSecondValue(): Double {
val valueToCheck = inputDisplayedFormula.trimStart('-').replace(",", "") val valueToCheck = inputDisplayedFormula.trimStart('-').removeGroupSeparator()
var value = valueToCheck.substring(valueToCheck.indexOfAny(operations) + 1) var value = valueToCheck.substring(valueToCheck.indexOfAny(operations) + 1)
if (value == "") { if (value == "") {
value = "0" value = "0"
@ -205,23 +217,28 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
} }
if (lastKey != EQUALS) { if (lastKey != EQUALS) {
val valueToCheck = inputDisplayedFormula.trimStart('-').replace(",", "") val valueToCheck = inputDisplayedFormula.trimStart('-').removeGroupSeparator()
val parts = valueToCheck.split(operationsRegex).filter { it != "" } val parts = valueToCheck.split(operationsRegex).filter { it != "" }
if (parts.isEmpty()) { if (parts.isEmpty()) {
return return
} }
baseValue = Formatter.stringToDouble(parts.first()) baseValue = parts.first().toDouble()
if (inputDisplayedFormula.startsWith("-")) { if (inputDisplayedFormula.startsWith("-")) {
baseValue *= -1 baseValue *= -1
} }
secondValue = parts.getOrNull(1)?.replace(",", "")?.toDouble() ?: secondValue secondValue = parts.getOrNull(1)?.toDouble() ?: secondValue
} }
if (lastOperation != "") { if (lastOperation != "") {
val sign = getSign(lastOperation) val sign = getSign(lastOperation)
val expression = "${baseValue.format()}$sign${secondValue.format()}".replace("", "sqrt").replace("×", "*").replace("÷", "/") val formattedBaseValue = baseValue.format().removeGroupSeparator()
val formatterSecondValue = secondValue.format().removeGroupSeparator()
val expression = "$formattedBaseValue$sign$formatterSecondValue"
.replace("", "sqrt")
.replace("×", "*")
.replace("÷", "/")
try { try {
if (sign == "÷" && secondValue == 0.0) { if (sign == "÷" && secondValue == 0.0) {
@ -229,16 +246,11 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
return return
} }
if (sign == "%") {
val second = secondValue / 100f
"${baseValue.format()}*${second.format()}".replace("", "sqrt").replace("×", "*").replace("÷", "/")
}
// handle percents manually, it doesn't seem to be possible via net.objecthunter:exp4j. "%" is used only for modulo there // handle percents manually, it doesn't seem to be possible via net.objecthunter:exp4j. "%" is used only for modulo there
// handle cases like 10%200 here // handle cases like 10%200 here
val result = if (sign == "%") { val result = if (sign == "%") {
val second = secondValue / 100f val second = (secondValue / 100f).format().removeGroupSeparator()
ExpressionBuilder("${baseValue.format().replace(",", "")}*${second.format()}").build().evaluate() ExpressionBuilder("$formattedBaseValue*$second").build().evaluate()
} else { } else {
// avoid Double rounding errors at expressions like 5250,74 + 14,98 // avoid Double rounding errors at expressions like 5250,74 + 14,98
if (sign == "+" || sign == "-") { if (sign == "+" || sign == "-") {
@ -250,7 +262,7 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
} }
bigDecimalResult.toDouble() bigDecimalResult.toDouble()
} else { } else {
ExpressionBuilder(expression.replace(",", "")).build().evaluate() ExpressionBuilder(expression).build().evaluate()
} }
} }
@ -317,19 +329,13 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
} }
val lastValue = newValue.last().toString() val lastValue = newValue.last().toString()
lastKey = when { lastKey = when {
operations.contains(lastValue) -> { operations.contains(lastValue) -> CLEAR
CLEAR lastValue == decimalSeparator -> DECIMAL
} else -> DIGIT
lastValue == "." -> {
DECIMAL
}
else -> {
DIGIT
}
} }
} }
newValue = newValue.trimEnd(',') newValue = newValue.trimEnd(groupingSeparator.single())
inputDisplayedFormula = newValue inputDisplayedFormula = newValue
addThousandsDelimiter() addThousandsDelimiter()
showNewResult(inputDisplayedFormula) showNewResult(inputDisplayedFormula)
@ -391,4 +397,19 @@ class CalculatorImpl(calculator: Calculator, private val context: Context) {
addThousandsDelimiter() addThousandsDelimiter()
showNewResult(inputDisplayedFormula) showNewResult(inputDisplayedFormula)
} }
fun updateSeparators(decimalSeparator: String, groupingSeparator: String) {
if (this.decimalSeparator != decimalSeparator || this.groupingSeparator != groupingSeparator) {
this.decimalSeparator = decimalSeparator
this.groupingSeparator = groupingSeparator
formatter.decimalSeparator = decimalSeparator
formatter.groupingSeparator = groupingSeparator
// future: maybe update the formulas with new separators instead of resetting the whole thing
handleReset()
}
}
private fun Double.format() = formatter.doubleToString(this)
private fun String.removeGroupSeparator() = formatter.removeGroupingSeparator(this)
} }

View File

@ -7,4 +7,8 @@ class Config(context: Context) : BaseConfig(context) {
companion object { companion object {
fun newInstance(context: Context) = Config(context) fun newInstance(context: Context) = Config(context)
} }
var useCommaAsDecimalMark: Boolean
get() = prefs.getBoolean(USE_COMMA_AS_DECIMAL_MARK, false)
set(useCommaAsDecimalMark) = prefs.edit().putBoolean(USE_COMMA_AS_DECIMAL_MARK, useCommaAsDecimalMark).apply()
} }

View File

@ -24,3 +24,9 @@ const val SIX = "six"
const val SEVEN = "seven" const val SEVEN = "seven"
const val EIGHT = "eight" const val EIGHT = "eight"
const val NINE = "nine" const val NINE = "nine"
const val DOT = "."
const val COMMA = ","
// shared prefs
const val USE_COMMA_AS_DECIMAL_MARK = "use_comma_as_decimal_mark"

View File

@ -1,25 +0,0 @@
package com.simplemobiletools.calculator.helpers
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.*
object Formatter {
fun doubleToString(d: Double): String {
val symbols = DecimalFormatSymbols(Locale.US)
symbols.decimalSeparator = '.'
symbols.groupingSeparator = ','
val formatter = DecimalFormat()
formatter.maximumFractionDigits = 12
formatter.decimalFormatSymbols = symbols
formatter.isGroupingUsed = true
return formatter.format(d)
}
fun stringToDouble(str: String) = str.replace(",", "").toDouble()
fun addGroupingSeparators(str: String) = doubleToString(stringToDouble(str))
}
fun Double.format(): String = Formatter.doubleToString(this)

View File

@ -0,0 +1,30 @@
package com.simplemobiletools.calculator.helpers
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.*
class NumberFormatHelper(
var decimalSeparator: String = DOT,
var groupingSeparator: String = COMMA
) {
fun doubleToString(d: Double): String {
val symbols = DecimalFormatSymbols(Locale.US)
symbols.decimalSeparator = decimalSeparator.single()
symbols.groupingSeparator = groupingSeparator.single()
val formatter = DecimalFormat()
formatter.maximumFractionDigits = 12
formatter.decimalFormatSymbols = symbols
formatter.isGroupingUsed = true
return formatter.format(d)
}
fun addGroupingSeparators(str: String) = doubleToString(
removeGroupingSeparator(str).toDouble()
)
fun removeGroupingSeparator(str: String) =
str.replace(groupingSeparator, "").replace(decimalSeparator, DOT)
}

View File

@ -126,7 +126,7 @@
style="@style/SettingsHolderCheckboxStyle" style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/ripple_bottom_corners"> android:background="@drawable/ripple_background">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox <com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_prevent_phone_from_sleeping" android:id="@+id/settings_prevent_phone_from_sleeping"
@ -136,6 +136,22 @@
android:text="@string/prevent_phone_from_sleeping" /> android:text="@string/prevent_phone_from_sleeping" />
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/settings_use_comma_as_decimal_mark_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_bottom_corners">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_use_comma_as_decimal_mark"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/use_comma_as_decimal_mark" />
</RelativeLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>