Refactor unit converter

This commit is contained in:
MM20 2021-12-12 21:27:10 +01:00
parent 733491ceda
commit 4e8819e559
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
16 changed files with 182 additions and 124 deletions

View File

@ -260,4 +260,21 @@
<item quantity="one">Short Ton</item>
<item quantity="other">Short Tons</item>
</plurals>
<!-- UNITS OF TEMPERATURE -->
<string name="unit_degree_celsius_symbol">°C</string>
<plurals name="unit_degree_celsius">
<item quantity="one">Grad Celsius</item>
<item quantity="other">Grad Celsius</item>
</plurals>
<string name="unit_degree_fahrenheit_symbol">°F</string>
<plurals name="unit_degree_fahrenheit">
<item quantity="one">Grad Fahrenheit</item>
<item quantity="other">Grad Fahrenheit</item>
</plurals>
<string name="unit_kelvin_symbol">K</string>
<plurals name="unit_kelvin">
<item quantity="one">Kelvin</item>
<item quantity="other">Kelvin</item>
</plurals>
</resources>

View File

@ -264,4 +264,21 @@
<item quantity="one">short ton</item>
<item quantity="other">short tons</item>
</plurals>
<!-- UNITS OF TEMPERATURE -->
<string name="unit_degree_celsius_symbol">°C</string>
<plurals name="unit_degree_celsius">
<item quantity="one">degree celsius</item>
<item quantity="other">degrees celsius</item>
</plurals>
<string name="unit_degree_fahrenheit_symbol">°F</string>
<plurals name="unit_degree_fahrenheit">
<item quantity="one">degree fahrenheit</item>
<item quantity="other">degrees fahrenheit</item>
</plurals>
<string name="unit_kelvin_symbol">K</string>
<plurals name="unit_kelvin">
<item quantity="one">kelvin</item>
<item quantity="other">kelvins</item>
</plurals>
</resources>

View File

@ -1,7 +1,6 @@
package de.mm20.launcher2.search.data
import android.content.Context
import android.os.Bundle
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.UnitValue
@ -32,7 +31,7 @@ open class UnitConverter(
val converters = listOf(
lazy { MassConverter(context) },
lazy { LengthConverter(context) },
lazy { CurrencyConverter(context) },
lazy { CurrencyConverter() },
lazy { DataConverter(context) },
lazy { TimeConverter(context) },
lazy { VelocityConverter(context) },

View File

@ -0,0 +1,27 @@
package de.mm20.launcher2.unitconverter
import android.content.Context
import java.text.DecimalFormat
import kotlin.math.abs
import kotlin.math.roundToInt
internal object ConverterUtils {
fun formatName(context: Context, unit: MeasureUnit, value: Double): String {
val resId = unit.nameResource
val text = context.resources.getQuantityString(resId, value.roundToInt())
return text
}
fun formatValue(context: Context, unit: MeasureUnit, value: Double): String {
if (abs(value) > 1e5 || abs(value) < 1e-3) {
return DecimalFormat("#.###E0").apply {
}.format(value)
}
return DecimalFormat("#.###").apply {
}.format(value)
}
}

View File

@ -1,7 +0,0 @@
package de.mm20.launcher2.unitconverter
data class MeasureUnit(
val factor: Double,
val symbol: String,
val nameResource: Int
)

View File

@ -0,0 +1,6 @@
package de.mm20.launcher2.unitconverter
interface MeasureUnit {
val symbol: String
val nameResource: Int
}

View File

@ -2,54 +2,53 @@ package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class AreaConverter(context: Context) : Converter() {
class AreaConverter(context: Context) : SimpleFactorConverter() {
override val dimension = Dimension.Area
override val standardUnits = listOf(
MeasureUnit(
MeasureUnitWithFactor(
0.000001,
context.getString(R.string.unit_sqkilometer_symbol),
R.plurals.unit_sqkilometer
),
MeasureUnit(
MeasureUnitWithFactor(
0.0001,
context.getString(R.string.unit_hectare_symbol),
R.plurals.unit_hectare
),
MeasureUnit(
MeasureUnitWithFactor(
1.0,
context.getString(R.string.unit_sqmeter_symbol),
R.plurals.unit_sqmeter
),
MeasureUnit(
MeasureUnitWithFactor(
10000.0,
context.getString(R.string.unit_sqcentimeter_symbol),
R.plurals.unit_sqcentimeter
),
MeasureUnit(
MeasureUnitWithFactor(
1000000.0,
context.getString(R.string.unit_sqmillimeter_symbol),
R.plurals.unit_sqmillimeter
),
MeasureUnit(
MeasureUnitWithFactor(
100 * 100 / (2.54 * 2.54),
context.getString(R.string.unit_sqinch_symbol),
R.plurals.unit_sqinch
),
MeasureUnit(
MeasureUnitWithFactor(
100 * 100 / (2.54 * 2.54 * 144),
context.getString(R.string.unit_sqfoot_symbol),
R.plurals.unit_sqfoot
),
MeasureUnit(
MeasureUnitWithFactor(
100 * 100 / (2.54 * 2.54 * 1296),
context.getString(R.string.unit_sqyard_symbol),
R.plurals.unit_sqyard
),
MeasureUnit(
MeasureUnitWithFactor(
100 * 100 / (2.54 * 2.54 * 144 * 43560),
context.getString(R.string.unit_acre_symbol),
R.plurals.unit_acre

View File

@ -3,57 +3,17 @@ package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.search.data.UnitConverter
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.UnitValue
import java.text.DecimalFormat
import kotlin.math.abs
import kotlin.math.roundToInt
abstract class Converter {
interface Converter {
val dimension: Dimension
abstract val dimension: Dimension
suspend fun isValidUnit(symbol: String): Boolean
open val standardUnits: List<MeasureUnit> = emptyList()
suspend fun convert(
context: Context,
fromUnit: String,
value: Double,
toUnit: String?
): UnitConverter
}
/**
* Returns true if a symbol is a valid unit of this converter
*/
open suspend fun isValidUnit(symbol: String): Boolean {
return standardUnits.any { it.symbol == symbol }
}
open suspend fun convert(context: Context, fromUnit: String, value: Double, toUnit: String?): UnitConverter {
val results = mutableListOf<UnitValue>()
val unit = standardUnits.first { it.symbol == fromUnit }
if (toUnit == null) {
for (targetUnit in standardUnits) {
if (targetUnit.symbol == unit.symbol) continue
val v = value * targetUnit.factor / unit.factor
results += UnitValue(v, targetUnit.symbol, formatName(context, targetUnit, v), formatValue(context, unit, v))
}
} else {
val targetUnit = standardUnits.first { it.symbol == toUnit }
val v = value * targetUnit.factor / unit.factor
results += UnitValue(v, targetUnit.symbol, formatName(context, targetUnit, v), formatValue(context, unit, v))
}
val inputValue = UnitValue(value, fromUnit, formatName(context, unit, value), formatValue(context, unit, value))
return UnitConverter(dimension, inputValue, results)
}
open suspend fun formatName(context: Context, unit: MeasureUnit, value: Double): String {
val resId = unit.nameResource
val text = context.resources.getQuantityString(resId, value.roundToInt())
return text
}
open suspend fun formatValue(context: Context, unit: MeasureUnit, value: Double): String {
if (abs(value) > 1e5 || abs(value) < 1e-3) {
return DecimalFormat("#.###E0").apply {
}.format(value)
}
return DecimalFormat("#.###").apply {
}.format(value)
}
}

View File

@ -15,11 +15,11 @@ import kotlin.math.abs
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class CurrencyConverter(context: Context) : Converter(), KoinComponent {
class CurrencyConverter : Converter, KoinComponent {
override val dimension: Dimension = Dimension.Currency
val repository: CurrencyRepository by inject()
private val repository: CurrencyRepository by inject()
private val topCurrencies = arrayOf("USD", "EUR", "JPY", "GBP", "AUD")

View File

@ -2,79 +2,78 @@ package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class DataConverter(context: Context) : Converter() {
class DataConverter(context: Context) : SimpleFactorConverter() {
override val dimension = Dimension.Data
override val standardUnits = listOf(
MeasureUnit(
MeasureUnitWithFactor(
1.0,
context.getString(R.string.unit_byte_symbol),
R.plurals.unit_byte
),
MeasureUnit(
MeasureUnitWithFactor(
0.001,
context.getString(R.string.unit_kilobyte_symbol),
R.plurals.unit_kilobyte
),
MeasureUnit(
MeasureUnitWithFactor(
0.000001,
context.getString(R.string.unit_megabyte_symbol),
R.plurals.unit_megabyte
),
MeasureUnit(
MeasureUnitWithFactor(
0.000000001,
context.getString(R.string.unit_gigabyte_symbol),
R.plurals.unit_gigabyte
),
MeasureUnit(
MeasureUnitWithFactor(
0.000000000001,
context.getString(R.string.unit_terabyte_symbol),
R.plurals.unit_terabyte
),
MeasureUnit(
MeasureUnitWithFactor(
1.0 / 1024,
context.getString(R.string.unit_kibibyte_symbol),
R.plurals.unit_kibibyte
),
MeasureUnit(
MeasureUnitWithFactor(
1.0 / (1024 * 1024),
context.getString(R.string.unit_mebibyte_symbol),
R.plurals.unit_mebibyte
),
MeasureUnit(
MeasureUnitWithFactor(
1.0 / (1024 * 1024 * 1024),
context.getString(R.string.unit_gibibyte_symbol),
R.plurals.unit_gibibyte
),
MeasureUnit(
MeasureUnitWithFactor(
1.0 / (1024.0 * 1024 * 1024 * 1024),
context.getString(R.string.unit_tebibyte_symbol),
R.plurals.unit_tebibyte
),
MeasureUnit(
MeasureUnitWithFactor(
8.0,
context.getString(R.string.unit_bit_symbol),
R.plurals.unit_bit
),
MeasureUnit(
MeasureUnitWithFactor(
8.0 / 1000,
context.getString(R.string.unit_kilobit_symbol),
R.plurals.unit_kilobit
),
MeasureUnit(
MeasureUnitWithFactor(
8.0 / 1000000,
context.getString(R.string.unit_megabit_symbol),
R.plurals.unit_megabit
),
MeasureUnit(
MeasureUnitWithFactor(
8.0 / 1000000000,
context.getString(R.string.unit_gigabit_symbol),
R.plurals.unit_gigabit
),
MeasureUnit(
MeasureUnitWithFactor(
8.0 / 1000000000000,
context.getString(R.string.unit_terabit_symbol),
R.plurals.unit_terabit

View File

@ -2,54 +2,53 @@ package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class LengthConverter(context: Context) : Converter() {
class LengthConverter(context: Context) : SimpleFactorConverter() {
override val dimension = Dimension.Length
override val standardUnits = listOf(
MeasureUnit(
MeasureUnitWithFactor(
1.0,
context.getString(R.string.unit_meter_symbol),
R.plurals.unit_meter
),
MeasureUnit(
MeasureUnitWithFactor(
0.001,
context.getString(R.string.unit_kilometer_symbol),
R.plurals.unit_kilometer
),
MeasureUnit(
MeasureUnitWithFactor(
100.0,
context.getString(R.string.unit_centimeter_symbol),
R.plurals.unit_centimeter
),
MeasureUnit(
MeasureUnitWithFactor(
1000.0,
context.getString(R.string.unit_millimeter_symbol),
R.plurals.unit_millimeter
),
MeasureUnit(
MeasureUnitWithFactor(
100 / 2.54,
context.getString(R.string.unit_inch_symbol),
R.plurals.unit_inch
),
MeasureUnit(
MeasureUnitWithFactor(
100 / (2.54 * 12),
context.getString(R.string.unit_foot_symbol),
R.plurals.unit_foot
),
MeasureUnit(
MeasureUnitWithFactor(
100 / (2.54 * 12 * 3),
context.getString(R.string.unit_yard_symbol),
R.plurals.unit_yard
),
MeasureUnit(
MeasureUnitWithFactor(
100 / (2.54 * 12 * 3 * 1760),
context.getString(R.string.unit_mile_symbol),
R.plurals.unit_mile
),
MeasureUnit(
MeasureUnitWithFactor(
1 / 1852.0,
context.getString(R.string.unit_nautic_mile_symbol),
R.plurals.unit_nautic_mile

View File

@ -2,49 +2,48 @@ package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class MassConverter(context: Context): Converter() {
class MassConverter(context: Context): SimpleFactorConverter() {
override val dimension = Dimension.Mass
override val standardUnits = listOf(
MeasureUnit(
MeasureUnitWithFactor(
1.0,
context.getString(R.string.unit_kilogram_symbol),
R.plurals.unit_kilogram
),
MeasureUnit(
MeasureUnitWithFactor(
1000.0,
context.getString(R.string.unit_gram_symbol),
R.plurals.unit_gram
),
MeasureUnit(
MeasureUnitWithFactor(
0.001,
context.getString(R.string.unit_metric_ton_symbol),
R.plurals.unit_metric_ton
),
MeasureUnit(
MeasureUnitWithFactor(
1000.0 / (453.59237 * 2240.0),
context.getString(R.string.unit_long_ton_symbol),
R.plurals.unit_long_ton
),
MeasureUnit(
MeasureUnitWithFactor(
1000.0 / (453.59237 * 14.0),
context.getString(R.string.unit_stone_symbol),
R.plurals.unit_stone
),
MeasureUnit(
MeasureUnitWithFactor(
1000.0 / 453.59237,
context.getString(R.string.unit_pound_symbol),
R.plurals.unit_pound
),
MeasureUnit(
MeasureUnitWithFactor(
16.0 * 1000.0 / 453.59237,
context.getString(R.string.unit_ounce_symbol),
R.plurals.unit_ounce
),
MeasureUnit(
MeasureUnitWithFactor(
1000.0 / (453.59237 * 2000.0),
context.getString(R.string.unit_short_ton_symbol),
R.plurals.unit_short_ton

View File

@ -0,0 +1,45 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.search.data.UnitConverter
import de.mm20.launcher2.unitconverter.ConverterUtils
import de.mm20.launcher2.unitconverter.UnitValue
/**
* A converter for units that can converted into each other by simply multiplicating with a constant factor
*/
abstract class SimpleFactorConverter: Converter {
open val standardUnits: List<MeasureUnitWithFactor> = emptyList()
/**
* Returns true if a symbol is a valid unit of this converter
*/
override suspend fun isValidUnit(symbol: String): Boolean {
return standardUnits.any { it.symbol == symbol }
}
override suspend fun convert(context: Context, fromUnit: String, value: Double, toUnit: String?): UnitConverter {
val results = mutableListOf<UnitValue>()
val unit = standardUnits.first { it.symbol == fromUnit }
if (toUnit == null) {
for (targetUnit in standardUnits) {
if (targetUnit.symbol == unit.symbol) continue
val v = value * targetUnit.factor / unit.factor
results += UnitValue(v, targetUnit.symbol, ConverterUtils.formatName(context, targetUnit, v), ConverterUtils.formatValue(context, unit, v))
}
} else {
val targetUnit = standardUnits.first { it.symbol == toUnit }
val v = value * targetUnit.factor / unit.factor
results += UnitValue(v, targetUnit.symbol, ConverterUtils.formatName(context, targetUnit, v), ConverterUtils.formatValue(context, unit, v))
}
val inputValue = UnitValue(value, fromUnit, ConverterUtils.formatName(context, unit, value), ConverterUtils.formatValue(context, unit, value))
return UnitConverter(dimension, inputValue, results)
}
}
data class MeasureUnitWithFactor(
val factor: Double,
val symbol: String,
val nameResource: Int
)

View File

@ -3,6 +3,6 @@ package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
class TemperatureConverter(context: Context): Converter() {
class TemperatureConverter(context: Context): SimpleFactorConverter() {
override val dimension = Dimension.Temperature
}

View File

@ -2,39 +2,38 @@ package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class TimeConverter(context: Context) : Converter() {
class TimeConverter(context: Context) : SimpleFactorConverter() {
override val dimension = Dimension.Time
override val standardUnits = listOf(
MeasureUnit(
MeasureUnitWithFactor(
1.0,
context.getString(R.string.unit_second_symbol),
R.plurals.unit_second
),
MeasureUnit(
MeasureUnitWithFactor(
1000.0,
context.getString(R.string.unit_millisecond_symbol),
R.plurals.unit_millisecond
),
MeasureUnit(
MeasureUnitWithFactor(
1.0 / 60,
context.getString(R.string.unit_minute_symbol),
R.plurals.unit_minute
),
MeasureUnit(
MeasureUnitWithFactor(
1.0 / (60 * 60),
context.getString(R.string.unit_hour_symbol),
R.plurals.unit_hour
),
MeasureUnit(
MeasureUnitWithFactor(
1.0 / (60 * 60 * 24),
context.getString(R.string.unit_day_symbol),
R.plurals.unit_day
),
MeasureUnit(
MeasureUnitWithFactor(
1.0 / (60 * 60 * 24 * 365),
context.getString(R.string.unit_year_symbol),
R.plurals.unit_year

View File

@ -2,29 +2,28 @@ package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class VelocityConverter(context: Context) : Converter() {
class VelocityConverter(context: Context) : SimpleFactorConverter() {
override val dimension = Dimension.Velocity
override val standardUnits = listOf(
MeasureUnit(
MeasureUnitWithFactor(
1.0,
context.getString(R.string.unit_meter_per_second_symbol),
R.plurals.unit_meter_per_second
),
MeasureUnit(
MeasureUnitWithFactor(
3.6,
context.getString(R.string.unit_kilometer_per_hour_symbol),
R.plurals.unit_kilometer_per_hour
),
MeasureUnit(
MeasureUnitWithFactor(
3600.0 / 1609.344,
context.getString(R.string.unit_mile_per_hour_symbol),
R.plurals.unit_mile_per_hour
),
MeasureUnit(
MeasureUnitWithFactor(
3600.0 / 1852.0,
context.getString(R.string.unit_knot_symbol),
R.plurals.unit_knot