Make unit converter parsing more lenient and add units of volume

Close #1323
This commit is contained in:
MM20
2025-10-05 22:36:20 +02:00
parent f508ac65b5
commit 9205025761
8 changed files with 322 additions and 22 deletions

View File

@@ -13,13 +13,13 @@ import de.mm20.launcher2.unitconverter.converters.MassConverter
import de.mm20.launcher2.unitconverter.converters.TemperatureConverter
import de.mm20.launcher2.unitconverter.converters.TimeConverter
import de.mm20.launcher2.unitconverter.converters.VelocityConverter
import de.mm20.launcher2.unitconverter.converters.VolumeConverter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -27,7 +27,7 @@ import org.koin.core.component.KoinComponent
interface UnitConverterRepository {
fun search(query: String): Flow<UnitConverter?>
suspend fun getAvailableConverters(includeCurrencies: Boolean) : List<Converter>
suspend fun getAvailableConverters(includeCurrencies: Boolean): List<Converter>
}
internal class UnitConverterRepositoryImpl(
@@ -56,7 +56,7 @@ internal class UnitConverterRepositoryImpl(
}
}
override suspend fun getAvailableConverters(includeCurrencies: Boolean) : List<Converter> {
override suspend fun getAvailableConverters(includeCurrencies: Boolean): List<Converter> {
val converters = mutableListOf(
MassConverter(context),
LengthConverter(context),
@@ -64,7 +64,8 @@ internal class UnitConverterRepositoryImpl(
TimeConverter(context),
VelocityConverter(context),
AreaConverter(context),
TemperatureConverter(context)
TemperatureConverter(context),
VolumeConverter(context),
)
if (includeCurrencies) converters.add(CurrencyConverter(currencyRepository))
@@ -75,28 +76,51 @@ internal class UnitConverterRepositoryImpl(
query: String,
includeCurrencies: Boolean
): UnitConverter? {
if (!query.matches(Regex("[0-9,.:]+ [^\\s]+")) &&
!query.matches(Regex("[0-9,.:]+ [^\\s]+ >> [^\\s]+")) &&
!query.matches(Regex("[0-9,.:]+ [^\\s]+ > [^\\s]+")) &&
!query.matches(Regex("[0-9,.:]+ [^\\s]+ - [^\\s]+"))) return null
val valueStr: String
val unitStr: String
val targetUnitStr: String?
val regex = Regex("""([+\-]?[\d+\-e,.]+|[^\d>\-]+)""")
query.split(" ").also {
valueStr = it.get(0)
unitStr = it.get(1)
targetUnitStr = it.getOrNull(3)
val matches = regex.findAll(query)
var inputStr: String? = null
var inputValue: Double? = null
var inputUnit: String? = null
var outputUnit: String? = null
for ((i, match) in matches.withIndex()) {
when (i) {
0 -> {
inputStr = match.value.trim()
inputValue = inputStr.toDoubleOrNull()
?: inputStr.replace(',', '.').toDoubleOrNull()
?: return null
}
1 -> inputUnit = match.value.trim()
2 -> {
if (!match.value.contains("-") && !match.value.contains(">")) {
outputUnit = match.value.trim()
}
}
3 -> {
if (outputUnit == null) {
outputUnit = match.value.trim()
break
} else {
return null
}
}
else -> return null
}
}
if (inputValue == null || inputUnit == null) {
return null
}
val value = valueStr.toDoubleOrNull() ?: valueStr.replace(',', '.').toDoubleOrNull()
?: return null
val converters = getAvailableConverters(includeCurrencies)
for (converter in converters) {
if (!converter.isValidUnit(unitStr)) continue
if (targetUnitStr != null && !converter.isValidUnit(targetUnitStr)) continue
return converter.convert(context, unitStr, value, targetUnitStr)
if (!converter.isValidUnit(inputUnit)) continue
if (outputUnit != null && !converter.isValidUnit(outputUnit)) continue
return converter.convert(context, inputUnit, inputValue, outputUnit)
}
return null
}

View File

@@ -0,0 +1,107 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.R
internal class VolumeConverter(context: Context) : SimpleFactorConverter() {
override val dimension: Dimension = Dimension.Volume
override val standardUnits = listOf(
// SI units
MeasureUnitWithFactor(
factor = 1.0,
symbol = context.getString(R.string.unit_cubic_meter_symbol),
nameResource = R.plurals.unit_cubic_meter
),
MeasureUnitWithFactor(
factor = 1_000_000.0,
symbol = context.getString(R.string.unit_cubic_centimeter_symbol),
nameResource = R.plurals.unit_cubic_centimeter
),
MeasureUnitWithFactor(
factor = 1_000_000_000.0,
symbol = context.getString(R.string.unit_cubic_millimeter_symbol),
nameResource = R.plurals.unit_cubic_millimeter
),
// Other metric units
MeasureUnitWithFactor(
factor = 1000.0,
symbol = context.getString(R.string.unit_liter_symbol),
nameResource = R.plurals.unit_liter
),
MeasureUnitWithFactor(
factor = 1_000_000.0,
symbol = context.getString(R.string.unit_milliliter_symbol),
nameResource = R.plurals.unit_milliliter
),
// Imperial geometric units
MeasureUnitWithFactor(
factor = 61023.74409473228,
symbol = context.getString(R.string.unit_cubic_inch_symbol),
nameResource = R.plurals.unit_cubic_inch
),
MeasureUnitWithFactor(
factor = 61023.74409473228 / 1728.0,
symbol = context.getString(R.string.unit_cubic_foot_symbol),
nameResource = R.plurals.unit_cubic_foot
),
MeasureUnitWithFactor(
factor = 61023.74409473228 / (1728.0 * 27.0),
symbol = context.getString(R.string.unit_cubic_yard_symbol),
nameResource = R.plurals.unit_cubic_yard
),
// US fluid units
MeasureUnitWithFactor(
factor = 264.1720523581484,
symbol = context.getString(R.string.unit_gallon_us_symbol),
nameResource = R.plurals.unit_gallon_us
),
MeasureUnitWithFactor(
factor = 264.1720523581484 * 8,
symbol = context.getString(R.string.unit_pint_us_symbol),
nameResource = R.plurals.unit_pint_us
),
MeasureUnitWithFactor(
factor = 264.1720523581484 * 16,
symbol = context.getString(R.string.unit_cup_symbol),
nameResource = R.plurals.unit_cup
),
MeasureUnitWithFactor(
factor = 264.1720523581484 * 128,
symbol = context.getString(R.string.unit_fluid_ounce_us_symbol),
nameResource = R.plurals.unit_fluid_ounce_us
),
MeasureUnitWithFactor(
factor = 264.1720523581484 * 256,
symbol = context.getString(R.string.unit_tablespoon_symbol),
nameResource = R.plurals.unit_tablespoon
),
MeasureUnitWithFactor(
factor = 264.1720523581484 * 768,
symbol = context.getString(R.string.unit_teaspoon_symbol),
nameResource = R.plurals.unit_teaspoon
),
// Imperial fluid units
MeasureUnitWithFactor(
factor = 219.9692482990878,
symbol = context.getString(R.string.unit_gallon_imp_symbol),
nameResource = R.plurals.unit_gallon_imp
),
MeasureUnitWithFactor(
factor = 219.969248299087 * 8,
symbol = context.getString(R.string.unit_pint_imp_symbol),
nameResource = R.plurals.unit_pint_imp
),
MeasureUnitWithFactor(
factor = 219.969248299087 * 128,
symbol = context.getString(R.string.unit_fluid_ounce_imp_symbol),
nameResource = R.plurals.unit_fluid_ounce_imp
),
)
}