Refactor Converter

- no longer abusing mapLatest and StateFlow
- id-based system in UI and business layers
This commit is contained in:
Sad Ellie 2024-02-28 22:07:16 +03:00
parent eb00d8a76e
commit eb96868afc
69 changed files with 1934 additions and 2060 deletions

View File

@ -83,76 +83,5 @@ fun <T1, T2, T3, T4, T5, T6, T7, R> combine(
) )
} }
@Suppress("UNCHECKED_CAST", "UNUSED")
fun <T1, T2, T3, T4, T5, T6, T7, T8, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
flow3: Flow<T3>,
flow4: Flow<T4>,
flow5: Flow<T5>,
flow6: Flow<T6>,
flow7: Flow<T7>,
flow8: Flow<T8>,
transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R,
): Flow<R> =
kotlinx.coroutines.flow.combine(
flow,
flow2,
flow3,
flow4,
flow5,
flow6,
flow7,
flow8,
) { args: Array<*> ->
transform(
args[0] as T1,
args[1] as T2,
args[2] as T3,
args[3] as T4,
args[4] as T5,
args[5] as T6,
args[6] as T7,
args[7] as T8,
)
}
@Suppress("UNCHECKED_CAST", "UNUSED")
fun <T1, T2, T3, T4, T5, T6, T7, T8, T9, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
flow3: Flow<T3>,
flow4: Flow<T4>,
flow5: Flow<T5>,
flow6: Flow<T6>,
flow7: Flow<T7>,
flow8: Flow<T8>,
flow9: Flow<T9>,
transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8, T9) -> R,
): Flow<R> =
kotlinx.coroutines.flow.combine(
flow,
flow2,
flow3,
flow4,
flow5,
flow6,
flow7,
flow8,
flow9,
) { args: Array<*> ->
transform(
args[0] as T1,
args[1] as T2,
args[2] as T3,
args[3] as T4,
args[4] as T5,
args[5] as T6,
args[6] as T7,
args[7] as T8,
args[8] as T9,
)
}
fun <T> Flow<T>.stateIn(scope: CoroutineScope, initialValue: T): StateFlow<T> = fun <T> Flow<T>.stateIn(scope: CoroutineScope, initialValue: T): StateFlow<T> =
stateIn(scope, SharingStarted.WhileSubscribed(5000L), initialValue) stateIn(scope, SharingStarted.WhileSubscribed(5000L), initialValue)

View File

@ -35,11 +35,13 @@ android {
} }
dependencies { dependencies {
testImplementation(libs.org.robolectric.robolectric)
implementation(libs.com.squareup.moshi.moshi.kotlin) implementation(libs.com.squareup.moshi.moshi.kotlin)
implementation(libs.com.squareup.retrofit2.converter.moshi) implementation(libs.com.squareup.retrofit2.converter.moshi)
implementation(project(":core:base")) implementation(project(":core:base"))
implementation(project(":data:common")) implementation(project(":data:common"))
implementation(project(":data:database")) implementation(project(":data:database"))
implementation(project(":data:evaluatto"))
implementation(project(":data:model")) implementation(project(":data:model"))
} }

View File

@ -1,6 +1,6 @@
/* /*
* Unitto is a calculator for Android * Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev * Copyright (c) 2024 Elshan Agaev
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -16,10 +16,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sadellie.unitto.data.model.unit package com.sadellie.unitto.data.converter
import java.math.BigDecimal import java.math.BigDecimal
interface DefaultUnit : AbstractUnit { interface BatchConvertResult
fun convert(unitTo: DefaultUnit, value: BigDecimal): BigDecimal
} @JvmInline
value class DefaultBatchConvertResult(
val value: BigDecimal,
) : BatchConvertResult
@JvmInline
value class NumberBaseBatchConvertResult(
val value: String,
) : BatchConvertResult

View File

@ -0,0 +1,82 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.converter
import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import java.math.BigDecimal
sealed class ConverterResult {
sealed class Error : ConverterResult() {
data object Currency : Error()
data object BadInput : Error()
data object ConversionError : Error()
data object DivideByZero : Error()
}
data class Default(
val value: BigDecimal,
val calculation: BigDecimal,
) : ConverterResult()
data class NumberBase(val value: String) : ConverterResult()
data class Time(
val negative: Boolean = false,
val day: BigDecimal = BigDecimal.ZERO,
val hour: BigDecimal = BigDecimal.ZERO,
val minute: BigDecimal = BigDecimal.ZERO,
val second: BigDecimal = BigDecimal.ZERO,
val millisecond: BigDecimal = BigDecimal.ZERO,
val microsecond: BigDecimal = BigDecimal.ZERO,
val nanosecond: BigDecimal = BigDecimal.ZERO,
val attosecond: BigDecimal = BigDecimal.ZERO,
) : ConverterResult()
data class FootInch(
val foot: BigDecimal,
val inch: BigDecimal,
) : ConverterResult() {
companion object {
/**
* Creates an object for displaying formatted foot and inch output. Units are passed as objects so
* that changes in basic units don't require modifying the method. Also this method can't access
* units repository directly.
*
* @param input Input in feet.
* @param footUnit Foot unit [BasicUnit.Default].
* @param inchUnit Inch unit [BasicUnit.Default].
* @return Result where decimal places are converter into inches.
*/
fun fromBigDecimal(
input: BigDecimal,
footUnit: BasicUnit.Default,
inchUnit: BasicUnit.Default,
): FootInch {
val (integral, fractional) = input.divideAndRemainder(BigDecimal.ONE)
return FootInch(integral, footUnit.convert(inchUnit, fractional))
}
}
}
data object Loading : ConverterResult()
}

View File

@ -1,46 +0,0 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.converter
import android.content.Context
import com.sadellie.unitto.data.database.CurrencyRatesDao
import com.sadellie.unitto.data.database.UnitsDao
import com.sadellie.unitto.data.model.repository.UnitsRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
class DataStoreModule {
@Provides
fun provideUnitsRepository(
unitsDao: UnitsDao,
currencyRatesDao: CurrencyRatesDao,
@ApplicationContext appContext: Context,
): UnitsRepository {
return UnitsRepositoryImpl(
unitsDao = unitsDao,
currencyRatesDao = currencyRatesDao,
mContext = appContext,
)
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Unitto is a calculator for Android * Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev * Copyright (c) 2024 Elshan Agaev
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -16,45 +16,32 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sadellie.unitto.data.model.unit package com.sadellie.unitto.data.converter
import android.content.Context import android.content.Context
import com.sadellie.unitto.data.common.lev import com.sadellie.unitto.data.common.lev
import com.sadellie.unitto.data.common.normalizeSuperscript import com.sadellie.unitto.data.common.normalizeSuperscript
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.database.UnitsEntity
import java.math.BigDecimal import com.sadellie.unitto.data.model.converter.unit.BasicUnit
interface AbstractUnit { data class UnitSearchResultItem(
val id: String val basicUnit: BasicUnit,
val basicUnit: BigDecimal val stats: UnitsEntity,
val group: UnitGroup val conversion: BatchConvertResult?,
val displayName: Int )
val shortName: Int
val isFavorite: Boolean
val pairId: String?
val counter: Int
fun clone( fun Sequence<UnitSearchResultItem>.filterByLev(
id: String = this.id, stringA: String,
basicUnit: BigDecimal = this.basicUnit, context: Context,
group: UnitGroup = this.group, ): Sequence<UnitSearchResultItem> {
displayName: Int = this.displayName,
shortName: Int = this.shortName,
isFavorite: Boolean = this.isFavorite,
pairId: String? = this.pairId,
counter: Int = this.counter,
): AbstractUnit
}
fun Sequence<AbstractUnit>.filterByLev(stringA: String, context: Context): Sequence<AbstractUnit> {
val stringToCompare = stringA.trim().lowercase() val stringToCompare = stringA.trim().lowercase()
// We don't need units where name is too different, half of the symbols is wrong in this situation // We don't need units where name is too different, half of the symbols is wrong in this situation
val threshold: Int = stringToCompare.length / 2 val threshold: Int = stringToCompare.length / 2
// List of pair: Unit and it's levDist // List of pair: Unit and it's levDist
val unitsWithDist = mutableListOf<Pair<AbstractUnit, Int>>() val unitsWithDist = mutableListOf<Pair<UnitSearchResultItem, Int>>()
this.forEach { unit -> this.forEach { unit ->
val unitShortName: String = context val unitShortName: String = context
.getString(unit.shortName) .getString(unit.basicUnit.shortName)
.lowercase() .lowercase()
.normalizeSuperscript() .normalizeSuperscript()
/** /**
@ -67,7 +54,7 @@ fun Sequence<AbstractUnit>.filterByLev(stringA: String, context: Context): Seque
} }
val unitFullName: String = context val unitFullName: String = context
.getString(unit.displayName) .getString(unit.basicUnit.displayName)
.lowercase() .lowercase()
.normalizeSuperscript() .normalizeSuperscript()

View File

@ -20,6 +20,10 @@ package com.sadellie.unitto.data.converter
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.data.common.isGreaterThan
import com.sadellie.unitto.data.common.isLessThan
import com.sadellie.unitto.data.common.trimZeros
import com.sadellie.unitto.data.converter.collections.accelerationCollection import com.sadellie.unitto.data.converter.collections.accelerationCollection
import com.sadellie.unitto.data.converter.collections.angleCollection import com.sadellie.unitto.data.converter.collections.angleCollection
import com.sadellie.unitto.data.converter.collections.areaCollection import com.sadellie.unitto.data.converter.collections.areaCollection
@ -49,22 +53,16 @@ import com.sadellie.unitto.data.database.CurrencyRatesDao
import com.sadellie.unitto.data.database.CurrencyRatesEntity import com.sadellie.unitto.data.database.CurrencyRatesEntity
import com.sadellie.unitto.data.database.UnitsDao import com.sadellie.unitto.data.database.UnitsDao
import com.sadellie.unitto.data.database.UnitsEntity import com.sadellie.unitto.data.database.UnitsEntity
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.converter.UnitsListSorting
import com.sadellie.unitto.data.model.repository.UnitsRepository import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.AbstractUnit
import com.sadellie.unitto.data.model.unit.ReverseUnit
import com.sadellie.unitto.data.model.unit.filterByLev
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.sadellie.evaluatto.Expression
import io.github.sadellie.evaluatto.ExpressionException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode
import java.time.LocalDate import java.time.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -72,9 +70,8 @@ class UnitsRepositoryImpl @Inject constructor(
private val unitsDao: UnitsDao, private val unitsDao: UnitsDao,
private val currencyRatesDao: CurrencyRatesDao, private val currencyRatesDao: CurrencyRatesDao,
@ApplicationContext private val mContext: Context, @ApplicationContext private val mContext: Context,
) : UnitsRepository { ) {
private val inMemoryUnits = MutableStateFlow( private val inMemory = lengthCollection +
lengthCollection +
currencyCollection + currencyCollection +
massCollection + massCollection +
speedCollection + speedCollection +
@ -97,62 +94,34 @@ class UnitsRepositoryImpl @Inject constructor(
torqueCollection + torqueCollection +
flowRateCollection + flowRateCollection +
luminanceCollection + luminanceCollection +
fuelConsumptionCollection, fuelConsumptionCollection
)
override val units: Flow<List<AbstractUnit>> = combine( suspend fun getById(id: String): BasicUnit = withContext(Dispatchers.Default) {
unitsDao.getAllFlow(), return@withContext inMemory.first { it.id == id }
inMemoryUnits,
) { basedList, inMemoryList ->
return@combine inMemoryList.map { inMemoryUnit ->
val inBaseUnit = basedList.find { it.unitId == inMemoryUnit.id }
?: return@map inMemoryUnit
inMemoryUnit.clone(
isFavorite = inBaseUnit.isFavorite,
counter = inBaseUnit.frequency,
pairId = inBaseUnit.pairedUnitId,
)
}
}
.flowOn(Dispatchers.IO)
override suspend fun getById(id: String): AbstractUnit {
return units.first().first { it.id == id }
} }
override suspend fun getCollection(group: UnitGroup): List<AbstractUnit> { suspend fun getPairId(id: String): String = withContext(Dispatchers.IO) {
return units.first().filter { it.group == group } val basedUnitPair = getUnitStats(id).pairedUnitId
if (basedUnitPair != null) return@withContext basedUnitPair
val inMemoryUnit = inMemory.first { it.id == id }
val collection = inMemory.filter { it.group == inMemoryUnit.group }
val pair = collection
.map { getById(it.id) to getUnitStats(it.id) }
.sortedByDescending { it.second.frequency }
.firstOrNull { it.second.isFavorite }?.first ?: collection.first()
return@withContext pair.id
} }
override suspend fun favorite(unit: AbstractUnit) = withContext(Dispatchers.IO) { suspend fun incrementCounter(id: String) = withContext(Dispatchers.IO) {
val basedUnit = unitsDao.getById(unit.id) val basedUnit = unitsDao.getById(id)
if (basedUnit == null) { if (basedUnit == null) {
unitsDao.insertUnit( unitsDao.insertUnit(
UnitsEntity( UnitsEntity(
unitId = unit.id, unitId = id,
isFavorite = true,
),
)
} else {
unitsDao.insertUnit(
UnitsEntity(
unitId = basedUnit.unitId,
isFavorite = !basedUnit.isFavorite,
pairedUnitId = basedUnit.pairedUnitId,
frequency = basedUnit.frequency,
),
)
}
}
override suspend fun incrementCounter(unit: AbstractUnit) = withContext(Dispatchers.IO) {
val basedUnit = unitsDao.getById(unit.id)
if (basedUnit == null) {
unitsDao.insertUnit(
UnitsEntity(
unitId = unit.id,
frequency = 1, frequency = 1,
), ),
) )
@ -168,14 +137,17 @@ class UnitsRepositoryImpl @Inject constructor(
} }
} }
override suspend fun setPair(unit: AbstractUnit, pair: AbstractUnit) = withContext(Dispatchers.IO) { suspend fun setPair(
val basedUnit = unitsDao.getById(unit.id) id: String,
pairId: String,
) = withContext(Dispatchers.IO) {
val basedUnit = unitsDao.getById(id)
if (basedUnit == null) { if (basedUnit == null) {
unitsDao.insertUnit( unitsDao.insertUnit(
UnitsEntity( UnitsEntity(
unitId = unit.id, unitId = id,
pairedUnitId = pair.id, pairedUnitId = pairId,
), ),
) )
} else { } else {
@ -183,95 +155,375 @@ class UnitsRepositoryImpl @Inject constructor(
UnitsEntity( UnitsEntity(
unitId = basedUnit.unitId, unitId = basedUnit.unitId,
isFavorite = basedUnit.isFavorite, isFavorite = basedUnit.isFavorite,
pairedUnitId = pair.id, pairedUnitId = pairId,
frequency = basedUnit.frequency, frequency = basedUnit.frequency,
), ),
) )
} }
} }
override suspend fun updateRates(unit: AbstractUnit): LocalDate? = withContext(Dispatchers.IO) { suspend fun favorite(id: String) = withContext(Dispatchers.IO) {
var basedConversions = currencyRatesDao.getLatestRates(baseId = unit.id) val basedUnit = unitsDao.getById(id)
val epochDay = LocalDate.now().toEpochDay()
if (basedConversions.firstOrNull()?.date != epochDay) { if (basedUnit == null) {
unitsDao.insertUnit(
UnitsEntity(
unitId = id,
isFavorite = true,
),
)
} else {
unitsDao.insertUnit(
UnitsEntity(
unitId = basedUnit.unitId,
isFavorite = !basedUnit.isFavorite,
pairedUnitId = basedUnit.pairedUnitId,
frequency = basedUnit.frequency,
),
)
}
}
suspend fun filterUnits(
query: String,
unitGroups: List<UnitGroup>,
favoritesOnly: Boolean,
sorting: UnitsListSorting,
): Map<UnitGroup, List<UnitSearchResultItem>> = withContext(Dispatchers.IO) {
return@withContext filterUnitCollections(
query = query,
unitGroups = unitGroups,
favoritesOnly = favoritesOnly,
sorting = sorting,
)
.groupBy { it.basicUnit.group }
}
suspend fun filterUnitsAndBatchConvert(
query: String,
unitGroup: UnitGroup,
favoritesOnly: Boolean,
sorting: UnitsListSorting,
unitFromId: String,
input: String?,
): Map<UnitGroup, List<UnitSearchResultItem>> = withContext(Dispatchers.IO) {
val units = filterUnitCollections(
query = query,
unitGroups = listOf(unitGroup),
favoritesOnly = favoritesOnly,
sorting = sorting,
)
if (input == null) {
return@withContext units.groupBy { it.basicUnit.group }
}
val unitWithConversions = try {
when (unitGroup) {
UnitGroup.CURRENCY -> {
val inputBD = BigDecimal(input)
val validCurrencyPairs = withContext(Dispatchers.IO) {
currencyRatesDao.getLatestRates(unitFromId)
}
.filter { it.pairUnitValue?.isGreaterThan(BigDecimal.ZERO) ?: false }
val validIds = validCurrencyPairs.map { it.pairUnitId }
units
.filter { it.basicUnit.id in validIds }
.map { unitTo ->
unitTo.basicUnit as BasicUnit.Default
val factor = validCurrencyPairs
.first { it.pairUnitId == unitTo.basicUnit.id }
.pairUnitValue
val conversion = inputBD.multiply(factor)
unitTo.copy(
conversion = DefaultBatchConvertResult(conversion),
)
}
}
UnitGroup.NUMBER_BASE -> {
val unitFrom = getById(unitFromId) as BasicUnit.NumberBase
units.map { unitTo ->
unitTo.basicUnit as BasicUnit.NumberBase
val conversion = unitFrom.convert(unitTo.basicUnit, input)
unitTo.copy(
conversion = NumberBaseBatchConvertResult(conversion),
)
}
}
else -> {
val unitFrom = getById(unitFromId) as BasicUnit.Default
val inputBD = BigDecimal(input)
units.map { unitTo ->
unitTo.basicUnit as BasicUnit.Default
val conversion = unitFrom.convert(unitTo.basicUnit, inputBD)
unitTo.copy(
conversion = DefaultBatchConvertResult(conversion),
)
}
}
}
} catch (e: Exception) {
Log.e("UnitsRepositoryImpl", "Failed to batch convert: $e")
units
}
return@withContext unitWithConversions.groupBy { it.basicUnit.group }
}
suspend fun convertDefault(
unitFromId: String,
unitToId: String,
value1: String,
value2: String,
formatTime: Boolean,
): ConverterResult = withContext(Dispatchers.Default) {
val calculated: BigDecimal = try {
// Calculate expression in first text field
var calculated1 = Expression(value1.ifEmpty { Token.Digit._0 }).calculate()
// Calculate expression in second text field
if (unitFromId == UnitID.foot) {
val calculatedInches = Expression(value2.ifEmpty { Token.Digit._0 }).calculate()
// turn inches into feet so that it all comes down to converting from feet only
val inches = getById(UnitID.inch) as BasicUnit.Default
val feet = getById(UnitID.foot) as BasicUnit.Default
val inchesConvertedToFeet = inches.convert(feet, calculatedInches)
calculated1 += inchesConvertedToFeet
}
calculated1
} catch (e: ExpressionException.DivideByZero) {
return@withContext ConverterResult.Error.DivideByZero
} catch (e: Exception) {
return@withContext ConverterResult.Error.BadInput
}
return@withContext try {
val unitFrom = getById(unitFromId) as BasicUnit.Default
val unitTo = getById(unitToId) as BasicUnit.Default
when {
(unitFrom.group == UnitGroup.TIME) and (formatTime) ->
convertTime(unitFrom, calculated)
unitTo.id == UnitID.foot ->
convertToFoot(unitFrom, unitTo, calculated)
unitFrom.group == UnitGroup.CURRENCY ->
convertCurrencies(unitFromId, unitToId, calculated)
else ->
convertDefault(unitFrom, unitTo, calculated)
}
} catch (e: Exception) {
ConverterResult.Error.ConversionError
}
}
suspend fun convertNumberBase(
unitFromId: String,
unitToId: String,
value: String,
): ConverterResult = withContext(Dispatchers.Default) {
return@withContext try {
val unitFrom = getById(unitFromId) as BasicUnit.NumberBase
val unitTo = getById(unitToId) as BasicUnit.NumberBase
val conversion = unitFrom.convert(unitTo, value)
ConverterResult.NumberBase(conversion)
} catch (e: Exception) {
ConverterResult.Error.ConversionError
}
}
private suspend fun getUnitStats(id: String): UnitsEntity = withContext(Dispatchers.IO) {
unitsDao.getById(id) ?: UnitsEntity(unitId = id)
}
private fun convertDefault(
unitFrom: BasicUnit.Default,
unitTo: BasicUnit.Default,
value: BigDecimal,
): ConverterResult.Default = ConverterResult.Default(unitFrom.convert(unitTo, value), value)
internal fun convertTime(
unitFrom: BasicUnit.Default,
value: BigDecimal,
): ConverterResult.Time {
val input = value.multiply(unitFrom.factor)
val negative = input < BigDecimal.ZERO
val inputAbs = input.abs()
if (inputAbs.isLessThan(attosecondBasicUnit)) {
return ConverterResult.Time(
negative = negative,
day = BigDecimal.ZERO,
hour = BigDecimal.ZERO,
minute = BigDecimal.ZERO,
second = BigDecimal.ZERO,
millisecond = BigDecimal.ZERO,
microsecond = BigDecimal.ZERO,
nanosecond = BigDecimal.ZERO,
attosecond = inputAbs,
)
}
if (inputAbs.isLessThan(nanosecondBasicUnit)) {
return ConverterResult.Time(
negative = negative,
day = BigDecimal.ZERO,
hour = BigDecimal.ZERO,
minute = BigDecimal.ZERO,
second = BigDecimal.ZERO,
millisecond = BigDecimal.ZERO,
microsecond = BigDecimal.ZERO,
nanosecond = BigDecimal.ZERO,
attosecond = inputAbs.trimZeros(),
)
}
// DAY
var division = inputAbs.divideAndRemainder(dayBasicUnit)
val day = division.component1().setScale(0, RoundingMode.HALF_EVEN)
var remainingSeconds = division.component2().setScale(0, RoundingMode.HALF_EVEN)
division = remainingSeconds.divideAndRemainder(hourBasicUnit)
val hour = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(minuteBasicUnit)
val minute = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(secondBasicUnit)
val second = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(millisecondBasicUnit)
val millisecond = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(microsecondBasicUnit)
val microsecond = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(nanosecondBasicUnit)
val nanosecond = division.component1()
remainingSeconds = division.component2()
val attosecond = remainingSeconds
return ConverterResult.Time(
negative = negative,
day = day,
hour = hour,
minute = minute,
second = second,
millisecond = millisecond,
microsecond = microsecond,
nanosecond = nanosecond,
attosecond = attosecond,
)
}
private suspend fun convertToFoot(
unitFrom: BasicUnit.Default,
unitTo: BasicUnit.Default,
value: BigDecimal,
): ConverterResult.FootInch = ConverterResult.FootInch.fromBigDecimal(
input = unitFrom.convert(unitTo, value),
footUnit = unitTo,
inchUnit = getById(UnitID.inch) as BasicUnit.Default,
)
private suspend fun convertCurrencies(
unitFromId: String,
unitToId: String,
value: BigDecimal,
): ConverterResult = withContext(Dispatchers.IO) {
refreshCurrencyRates(unitFromId)
val latestRate = currencyRatesDao.getLatestRate(unitFromId, unitToId)
if (latestRate?.pairUnitValue == null) return@withContext ConverterResult.Error.Currency
val conversion = value.multiply(latestRate.pairUnitValue)
return@withContext ConverterResult.Default(conversion, value)
}
private suspend fun filterUnitCollections(
query: String,
unitGroups: List<UnitGroup>,
favoritesOnly: Boolean,
sorting: UnitsListSorting,
): Sequence<UnitSearchResultItem> = withContext(Dispatchers.IO) {
var units = inMemory
.filter { it.group in unitGroups }
.map { UnitSearchResultItem(it, getUnitStats(it.id), null) }
.asSequence()
if (favoritesOnly) {
units = units.filter { it.stats.isFavorite }
}
units = when (sorting) {
UnitsListSorting.USAGE -> units.sortedByDescending { it.stats.frequency }
UnitsListSorting.ALPHABETICAL -> units.sortedBy { mContext.getString(it.basicUnit.displayName) }
UnitsListSorting.SCALE_ASC -> units.sortedBy { it.basicUnit.factor }
UnitsListSorting.SCALE_DESC -> units.sortedByDescending { it.basicUnit.factor }
else -> units
}
units = if (query.isEmpty()) {
units.sortedByDescending { it.stats.isFavorite }
} else {
units.filterByLev(query, mContext)
}
return@withContext units
}
private suspend fun refreshCurrencyRates(unitFromId: String) = withContext(Dispatchers.IO) {
val latestUpdateDate = currencyRatesDao.getLatestRateTimeStamp(unitFromId)
val currentDate = LocalDate.now().toEpochDay()
if (latestUpdateDate != currentDate) {
// Need to update cache needed
try { try {
val conversions = CurrencyApi.service.getCurrencyPairs(unit.id) val conversions = CurrencyApi.service.getCurrencyPairs(unitFromId)
val rates = conversions.currency val rates = conversions.currency
.map { (pairId, pairValue) -> .map { (pairId, pairValue) ->
CurrencyRatesEntity( CurrencyRatesEntity(
baseUnitId = unit.id, baseUnitId = unitFromId,
date = epochDay, date = currentDate,
pairUnitId = pairId, pairUnitId = pairId,
pairUnitValue = BigDecimal.valueOf(pairValue), pairUnitValue = BigDecimal.valueOf(pairValue),
) )
} }
currencyRatesDao.insertRates(rates) currencyRatesDao.insertRates(rates)
basedConversions = currencyRatesDao.getLatestRates(baseId = unit.id)
} catch (e: Exception) { } catch (e: Exception) {
Log.d("UnitsRepository", "Skipped update: $e") Log.d("UnitsRepositoryImpl", "Skipped update: $e")
} }
} }
inMemoryUnits.update { units ->
units.map { localUnit ->
if (localUnit.group != UnitGroup.CURRENCY) return@map localUnit
if (localUnit !is ReverseUnit) return@map localUnit
val rate = basedConversions
.firstOrNull { localUnit.id == it.pairUnitId }
?.pairUnitValue ?: BigDecimal.ZERO
return@map if (rate > BigDecimal.ZERO) {
localUnit.copy(basicUnit = rate)
} else {
localUnit.copy(basicUnit = BigDecimal.ZERO)
}
}
}
return@withContext basedConversions
.firstOrNull()
?.date
?.let { LocalDate.ofEpochDay(it) }
}
override suspend fun filterUnits(
query: String,
unitGroup: UnitGroup?,
favoritesOnly: Boolean,
hideBrokenUnits: Boolean,
sorting: UnitsListSorting,
shownUnitGroups: List<UnitGroup>,
): Map<UnitGroup, List<AbstractUnit>> {
// Leave only shown unit groups
var units: Sequence<AbstractUnit> = if (unitGroup == null) {
units.first().filter { it.group in shownUnitGroups }
} else {
getCollection(unitGroup)
}.asSequence()
if (favoritesOnly) {
units = units.filter { it.isFavorite }
}
if (hideBrokenUnits) {
units = units.filter { it.basicUnit > BigDecimal.ZERO }
}
units = when (sorting) {
UnitsListSorting.USAGE -> units.sortedByDescending { it.counter }
UnitsListSorting.ALPHABETICAL -> units.sortedBy { mContext.getString(it.displayName) }
UnitsListSorting.SCALE_ASC -> units.sortedBy { it.basicUnit }
UnitsListSorting.SCALE_DESC -> units.sortedByDescending { it.basicUnit }
else -> units
}
units = if (query.isEmpty()) {
units.sortedByDescending { it.isFavorite }
} else {
// For search we sort by popularity and Levenshtein distance (short and long name).
units.filterByLev(query, mContext)
}
return units.groupBy { it.group }
} }
} }
private val dayBasicUnit by lazy { BigDecimal("86400000000000000000000") }
private val hourBasicUnit by lazy { BigDecimal("3600000000000000000000") }
private val minuteBasicUnit by lazy { BigDecimal("60000000000000000000") }
private val secondBasicUnit by lazy { BigDecimal("1000000000000000000") }
private val millisecondBasicUnit by lazy { BigDecimal("1000000000000000") }
private val microsecondBasicUnit by lazy { BigDecimal("1000000000000") }
private val nanosecondBasicUnit by lazy { BigDecimal("1000000000") }
private val attosecondBasicUnit by lazy { BigDecimal("1") }

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val accelerationCollection: List<AbstractUnit> by lazy { internal val accelerationCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.attometer_per_square_second, BigDecimal("1"), UnitGroup.ACCELERATION, R.string.unit_attometer_per_square_second, R.string.unit_attometer_per_square_second_short), NormalUnit(UnitID.attometer_per_square_second, BigDecimal("1"), UnitGroup.ACCELERATION, R.string.unit_attometer_per_square_second, R.string.unit_attometer_per_square_second_short),
NormalUnit(UnitID.femtometer_per_square_second, BigDecimal("1000"), UnitGroup.ACCELERATION, R.string.unit_femtometer_per_square_second, R.string.unit_femtometer_per_square_second_short), NormalUnit(UnitID.femtometer_per_square_second, BigDecimal("1000"), UnitGroup.ACCELERATION, R.string.unit_femtometer_per_square_second, R.string.unit_femtometer_per_square_second_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val angleCollection: List<AbstractUnit> by lazy { internal val angleCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.angle_second, BigDecimal("1"), UnitGroup.ANGLE, R.string.unit_angle_second, R.string.unit_angle_second_short), NormalUnit(UnitID.angle_second, BigDecimal("1"), UnitGroup.ANGLE, R.string.unit_angle_second, R.string.unit_angle_second_short),
NormalUnit(UnitID.angle_minute, BigDecimal("60"), UnitGroup.ANGLE, R.string.unit_angle_minute, R.string.unit_angle_minute_short), NormalUnit(UnitID.angle_minute, BigDecimal("60"), UnitGroup.ANGLE, R.string.unit_angle_minute, R.string.unit_angle_minute_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val areaCollection: List<AbstractUnit> by lazy { internal val areaCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.cent, BigDecimal("6083246572000000000000000000000000"), UnitGroup.AREA, R.string.unit_cent, R.string.unit_cent_short), NormalUnit(UnitID.cent, BigDecimal("6083246572000000000000000000000000"), UnitGroup.AREA, R.string.unit_cent, R.string.unit_cent_short),
NormalUnit(UnitID.acre, BigDecimal("60832465720000000000000000000000"), UnitGroup.AREA, R.string.unit_acre, R.string.unit_acre_short), NormalUnit(UnitID.acre, BigDecimal("60832465720000000000000000000000"), UnitGroup.AREA, R.string.unit_acre, R.string.unit_acre_short),

View File

@ -19,221 +19,221 @@
package com.sadellie.unitto.data.converter.collections package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.model.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit
import com.sadellie.unitto.data.model.unit.ReverseUnit
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val currencyCollection: List<AbstractUnit> by lazy { internal val currencyCollection: List<BasicUnit> by lazy {
listOf( listOf(
ReverseUnit(UnitID.currency_1inch, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_1inch, R.string.unit_currency_1inch_short), NormalUnit(UnitID.currency_1inch, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_1inch, R.string.unit_currency_1inch_short, backward = true),
ReverseUnit(UnitID.currency_ada, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ada, R.string.unit_currency_ada_short), NormalUnit(UnitID.currency_ada, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ada, R.string.unit_currency_ada_short, backward = true),
ReverseUnit(UnitID.currency_aed, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_aed, R.string.unit_currency_aed_short), NormalUnit(UnitID.currency_aed, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_aed, R.string.unit_currency_aed_short, backward = true),
ReverseUnit(UnitID.currency_afn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_afn, R.string.unit_currency_afn_short), NormalUnit(UnitID.currency_afn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_afn, R.string.unit_currency_afn_short, backward = true),
ReverseUnit(UnitID.currency_algo, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_algo, R.string.unit_currency_algo_short), NormalUnit(UnitID.currency_algo, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_algo, R.string.unit_currency_algo_short, backward = true),
ReverseUnit(UnitID.currency_all, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_all, R.string.unit_currency_all_short), NormalUnit(UnitID.currency_all, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_all, R.string.unit_currency_all_short, backward = true),
ReverseUnit(UnitID.currency_amd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_amd, R.string.unit_currency_amd_short), NormalUnit(UnitID.currency_amd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_amd, R.string.unit_currency_amd_short, backward = true),
ReverseUnit(UnitID.currency_ang, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ang, R.string.unit_currency_ang_short), NormalUnit(UnitID.currency_ang, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ang, R.string.unit_currency_ang_short, backward = true),
ReverseUnit(UnitID.currency_aoa, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_aoa, R.string.unit_currency_aoa_short), NormalUnit(UnitID.currency_aoa, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_aoa, R.string.unit_currency_aoa_short, backward = true),
ReverseUnit(UnitID.currency_ars, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ars, R.string.unit_currency_ars_short), NormalUnit(UnitID.currency_ars, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ars, R.string.unit_currency_ars_short, backward = true),
ReverseUnit(UnitID.currency_atom, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_atom, R.string.unit_currency_atom_short), NormalUnit(UnitID.currency_atom, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_atom, R.string.unit_currency_atom_short, backward = true),
ReverseUnit(UnitID.currency_aud, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_aud, R.string.unit_currency_aud_short), NormalUnit(UnitID.currency_aud, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_aud, R.string.unit_currency_aud_short, backward = true),
ReverseUnit(UnitID.currency_avax, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_avax, R.string.unit_currency_avax_short), NormalUnit(UnitID.currency_avax, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_avax, R.string.unit_currency_avax_short, backward = true),
ReverseUnit(UnitID.currency_awg, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_awg, R.string.unit_currency_awg_short), NormalUnit(UnitID.currency_awg, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_awg, R.string.unit_currency_awg_short, backward = true),
ReverseUnit(UnitID.currency_azn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_azn, R.string.unit_currency_azn_short), NormalUnit(UnitID.currency_azn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_azn, R.string.unit_currency_azn_short, backward = true),
ReverseUnit(UnitID.currency_bam, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bam, R.string.unit_currency_bam_short), NormalUnit(UnitID.currency_bam, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bam, R.string.unit_currency_bam_short, backward = true),
ReverseUnit(UnitID.currency_bbd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bbd, R.string.unit_currency_bbd_short), NormalUnit(UnitID.currency_bbd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bbd, R.string.unit_currency_bbd_short, backward = true),
ReverseUnit(UnitID.currency_bch, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bch, R.string.unit_currency_bch_short), NormalUnit(UnitID.currency_bch, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bch, R.string.unit_currency_bch_short, backward = true),
ReverseUnit(UnitID.currency_bdt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bdt, R.string.unit_currency_bdt_short), NormalUnit(UnitID.currency_bdt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bdt, R.string.unit_currency_bdt_short, backward = true),
ReverseUnit(UnitID.currency_bgn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bgn, R.string.unit_currency_bgn_short), NormalUnit(UnitID.currency_bgn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bgn, R.string.unit_currency_bgn_short, backward = true),
ReverseUnit(UnitID.currency_bhd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bhd, R.string.unit_currency_bhd_short), NormalUnit(UnitID.currency_bhd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bhd, R.string.unit_currency_bhd_short, backward = true),
ReverseUnit(UnitID.currency_bif, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bif, R.string.unit_currency_bif_short), NormalUnit(UnitID.currency_bif, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bif, R.string.unit_currency_bif_short, backward = true),
ReverseUnit(UnitID.currency_bmd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bmd, R.string.unit_currency_bmd_short), NormalUnit(UnitID.currency_bmd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bmd, R.string.unit_currency_bmd_short, backward = true),
ReverseUnit(UnitID.currency_bnb, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bnb, R.string.unit_currency_bnb_short), NormalUnit(UnitID.currency_bnb, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bnb, R.string.unit_currency_bnb_short, backward = true),
ReverseUnit(UnitID.currency_bnd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bnd, R.string.unit_currency_bnd_short), NormalUnit(UnitID.currency_bnd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bnd, R.string.unit_currency_bnd_short, backward = true),
ReverseUnit(UnitID.currency_bob, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bob, R.string.unit_currency_bob_short), NormalUnit(UnitID.currency_bob, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bob, R.string.unit_currency_bob_short, backward = true),
ReverseUnit(UnitID.currency_brl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_brl, R.string.unit_currency_brl_short), NormalUnit(UnitID.currency_brl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_brl, R.string.unit_currency_brl_short, backward = true),
ReverseUnit(UnitID.currency_bsd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bsd, R.string.unit_currency_bsd_short), NormalUnit(UnitID.currency_bsd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bsd, R.string.unit_currency_bsd_short, backward = true),
ReverseUnit(UnitID.currency_btc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_btc, R.string.unit_currency_btc_short), NormalUnit(UnitID.currency_btc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_btc, R.string.unit_currency_btc_short, backward = true),
ReverseUnit(UnitID.currency_btn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_btn, R.string.unit_currency_btn_short), NormalUnit(UnitID.currency_btn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_btn, R.string.unit_currency_btn_short, backward = true),
ReverseUnit(UnitID.currency_busd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_busd, R.string.unit_currency_busd_short), NormalUnit(UnitID.currency_busd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_busd, R.string.unit_currency_busd_short, backward = true),
ReverseUnit(UnitID.currency_bwp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bwp, R.string.unit_currency_bwp_short), NormalUnit(UnitID.currency_bwp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bwp, R.string.unit_currency_bwp_short, backward = true),
ReverseUnit(UnitID.currency_byn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_byn, R.string.unit_currency_byn_short), NormalUnit(UnitID.currency_byn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_byn, R.string.unit_currency_byn_short, backward = true),
ReverseUnit(UnitID.currency_byr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_byr, R.string.unit_currency_byr_short), NormalUnit(UnitID.currency_byr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_byr, R.string.unit_currency_byr_short, backward = true),
ReverseUnit(UnitID.currency_bzd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bzd, R.string.unit_currency_bzd_short), NormalUnit(UnitID.currency_bzd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_bzd, R.string.unit_currency_bzd_short, backward = true),
ReverseUnit(UnitID.currency_cad, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cad, R.string.unit_currency_cad_short), NormalUnit(UnitID.currency_cad, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cad, R.string.unit_currency_cad_short, backward = true),
ReverseUnit(UnitID.currency_cdf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cdf, R.string.unit_currency_cdf_short), NormalUnit(UnitID.currency_cdf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cdf, R.string.unit_currency_cdf_short, backward = true),
ReverseUnit(UnitID.currency_chf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_chf, R.string.unit_currency_chf_short), NormalUnit(UnitID.currency_chf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_chf, R.string.unit_currency_chf_short, backward = true),
ReverseUnit(UnitID.currency_chz, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_chz, R.string.unit_currency_chz_short), NormalUnit(UnitID.currency_chz, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_chz, R.string.unit_currency_chz_short, backward = true),
ReverseUnit(UnitID.currency_clf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_clf, R.string.unit_currency_clf_short), NormalUnit(UnitID.currency_clf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_clf, R.string.unit_currency_clf_short, backward = true),
ReverseUnit(UnitID.currency_clp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_clp, R.string.unit_currency_clp_short), NormalUnit(UnitID.currency_clp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_clp, R.string.unit_currency_clp_short, backward = true),
ReverseUnit(UnitID.currency_cny, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cny, R.string.unit_currency_cny_short), NormalUnit(UnitID.currency_cny, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cny, R.string.unit_currency_cny_short, backward = true),
ReverseUnit(UnitID.currency_cop, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cop, R.string.unit_currency_cop_short), NormalUnit(UnitID.currency_cop, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cop, R.string.unit_currency_cop_short, backward = true),
ReverseUnit(UnitID.currency_crc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_crc, R.string.unit_currency_crc_short), NormalUnit(UnitID.currency_crc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_crc, R.string.unit_currency_crc_short, backward = true),
ReverseUnit(UnitID.currency_cro, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cro, R.string.unit_currency_cro_short), NormalUnit(UnitID.currency_cro, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cro, R.string.unit_currency_cro_short, backward = true),
ReverseUnit(UnitID.currency_cuc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cuc, R.string.unit_currency_cuc_short), NormalUnit(UnitID.currency_cuc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cuc, R.string.unit_currency_cuc_short, backward = true),
ReverseUnit(UnitID.currency_cup, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cup, R.string.unit_currency_cup_short), NormalUnit(UnitID.currency_cup, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cup, R.string.unit_currency_cup_short, backward = true),
ReverseUnit(UnitID.currency_cve, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cve, R.string.unit_currency_cve_short), NormalUnit(UnitID.currency_cve, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_cve, R.string.unit_currency_cve_short, backward = true),
ReverseUnit(UnitID.currency_czk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_czk, R.string.unit_currency_czk_short), NormalUnit(UnitID.currency_czk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_czk, R.string.unit_currency_czk_short, backward = true),
ReverseUnit(UnitID.currency_dai, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dai, R.string.unit_currency_dai_short), NormalUnit(UnitID.currency_dai, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dai, R.string.unit_currency_dai_short, backward = true),
ReverseUnit(UnitID.currency_djf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_djf, R.string.unit_currency_djf_short), NormalUnit(UnitID.currency_djf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_djf, R.string.unit_currency_djf_short, backward = true),
ReverseUnit(UnitID.currency_dkk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dkk, R.string.unit_currency_dkk_short), NormalUnit(UnitID.currency_dkk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dkk, R.string.unit_currency_dkk_short, backward = true),
ReverseUnit(UnitID.currency_doge, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_doge, R.string.unit_currency_doge_short), NormalUnit(UnitID.currency_doge, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_doge, R.string.unit_currency_doge_short, backward = true),
ReverseUnit(UnitID.currency_dop, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dop, R.string.unit_currency_dop_short), NormalUnit(UnitID.currency_dop, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dop, R.string.unit_currency_dop_short, backward = true),
ReverseUnit(UnitID.currency_dot, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dot, R.string.unit_currency_dot_short), NormalUnit(UnitID.currency_dot, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dot, R.string.unit_currency_dot_short, backward = true),
ReverseUnit(UnitID.currency_dzd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dzd, R.string.unit_currency_dzd_short), NormalUnit(UnitID.currency_dzd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_dzd, R.string.unit_currency_dzd_short, backward = true),
ReverseUnit(UnitID.currency_egld, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_egld, R.string.unit_currency_egld_short), NormalUnit(UnitID.currency_egld, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_egld, R.string.unit_currency_egld_short, backward = true),
ReverseUnit(UnitID.currency_egp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_egp, R.string.unit_currency_egp_short), NormalUnit(UnitID.currency_egp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_egp, R.string.unit_currency_egp_short, backward = true),
ReverseUnit(UnitID.currency_enj, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_enj, R.string.unit_currency_enj_short), NormalUnit(UnitID.currency_enj, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_enj, R.string.unit_currency_enj_short, backward = true),
ReverseUnit(UnitID.currency_ern, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ern, R.string.unit_currency_ern_short), NormalUnit(UnitID.currency_ern, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ern, R.string.unit_currency_ern_short, backward = true),
ReverseUnit(UnitID.currency_etb, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_etb, R.string.unit_currency_etb_short), NormalUnit(UnitID.currency_etb, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_etb, R.string.unit_currency_etb_short, backward = true),
ReverseUnit(UnitID.currency_etc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_etc, R.string.unit_currency_etc_short), NormalUnit(UnitID.currency_etc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_etc, R.string.unit_currency_etc_short, backward = true),
ReverseUnit(UnitID.currency_eth, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_eth, R.string.unit_currency_eth_short), NormalUnit(UnitID.currency_eth, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_eth, R.string.unit_currency_eth_short, backward = true),
ReverseUnit(UnitID.currency_eur, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_eur, R.string.unit_currency_eur_short), NormalUnit(UnitID.currency_eur, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_eur, R.string.unit_currency_eur_short, backward = true),
ReverseUnit(UnitID.currency_fil, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_fil, R.string.unit_currency_fil_short), NormalUnit(UnitID.currency_fil, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_fil, R.string.unit_currency_fil_short, backward = true),
ReverseUnit(UnitID.currency_fjd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_fjd, R.string.unit_currency_fjd_short), NormalUnit(UnitID.currency_fjd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_fjd, R.string.unit_currency_fjd_short, backward = true),
ReverseUnit(UnitID.currency_fkp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_fkp, R.string.unit_currency_fkp_short), NormalUnit(UnitID.currency_fkp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_fkp, R.string.unit_currency_fkp_short, backward = true),
ReverseUnit(UnitID.currency_ftt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ftt, R.string.unit_currency_ftt_short), NormalUnit(UnitID.currency_ftt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ftt, R.string.unit_currency_ftt_short, backward = true),
ReverseUnit(UnitID.currency_gbp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gbp, R.string.unit_currency_gbp_short), NormalUnit(UnitID.currency_gbp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gbp, R.string.unit_currency_gbp_short, backward = true),
ReverseUnit(UnitID.currency_gel, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gel, R.string.unit_currency_gel_short), NormalUnit(UnitID.currency_gel, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gel, R.string.unit_currency_gel_short, backward = true),
ReverseUnit(UnitID.currency_ggp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ggp, R.string.unit_currency_ggp_short), NormalUnit(UnitID.currency_ggp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ggp, R.string.unit_currency_ggp_short, backward = true),
ReverseUnit(UnitID.currency_ghs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ghs, R.string.unit_currency_ghs_short), NormalUnit(UnitID.currency_ghs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ghs, R.string.unit_currency_ghs_short, backward = true),
ReverseUnit(UnitID.currency_gip, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gip, R.string.unit_currency_gip_short), NormalUnit(UnitID.currency_gip, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gip, R.string.unit_currency_gip_short, backward = true),
ReverseUnit(UnitID.currency_gmd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gmd, R.string.unit_currency_gmd_short), NormalUnit(UnitID.currency_gmd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gmd, R.string.unit_currency_gmd_short, backward = true),
ReverseUnit(UnitID.currency_gnf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gnf, R.string.unit_currency_gnf_short), NormalUnit(UnitID.currency_gnf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gnf, R.string.unit_currency_gnf_short, backward = true),
ReverseUnit(UnitID.currency_grt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_grt, R.string.unit_currency_grt_short), NormalUnit(UnitID.currency_grt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_grt, R.string.unit_currency_grt_short, backward = true),
ReverseUnit(UnitID.currency_gtq, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gtq, R.string.unit_currency_gtq_short), NormalUnit(UnitID.currency_gtq, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gtq, R.string.unit_currency_gtq_short, backward = true),
ReverseUnit(UnitID.currency_gyd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gyd, R.string.unit_currency_gyd_short), NormalUnit(UnitID.currency_gyd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_gyd, R.string.unit_currency_gyd_short, backward = true),
ReverseUnit(UnitID.currency_hkd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_hkd, R.string.unit_currency_hkd_short), NormalUnit(UnitID.currency_hkd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_hkd, R.string.unit_currency_hkd_short, backward = true),
ReverseUnit(UnitID.currency_hnl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_hnl, R.string.unit_currency_hnl_short), NormalUnit(UnitID.currency_hnl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_hnl, R.string.unit_currency_hnl_short, backward = true),
ReverseUnit(UnitID.currency_hrk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_hrk, R.string.unit_currency_hrk_short), NormalUnit(UnitID.currency_hrk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_hrk, R.string.unit_currency_hrk_short, backward = true),
ReverseUnit(UnitID.currency_htg, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_htg, R.string.unit_currency_htg_short), NormalUnit(UnitID.currency_htg, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_htg, R.string.unit_currency_htg_short, backward = true),
ReverseUnit(UnitID.currency_huf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_huf, R.string.unit_currency_huf_short), NormalUnit(UnitID.currency_huf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_huf, R.string.unit_currency_huf_short, backward = true),
ReverseUnit(UnitID.currency_icp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_icp, R.string.unit_currency_icp_short), NormalUnit(UnitID.currency_icp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_icp, R.string.unit_currency_icp_short, backward = true),
ReverseUnit(UnitID.currency_idr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_idr, R.string.unit_currency_idr_short), NormalUnit(UnitID.currency_idr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_idr, R.string.unit_currency_idr_short, backward = true),
ReverseUnit(UnitID.currency_ils, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ils, R.string.unit_currency_ils_short), NormalUnit(UnitID.currency_ils, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ils, R.string.unit_currency_ils_short, backward = true),
ReverseUnit(UnitID.currency_imp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_imp, R.string.unit_currency_imp_short), NormalUnit(UnitID.currency_imp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_imp, R.string.unit_currency_imp_short, backward = true),
ReverseUnit(UnitID.currency_inj, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_inj, R.string.unit_currency_inj_short), NormalUnit(UnitID.currency_inj, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_inj, R.string.unit_currency_inj_short, backward = true),
ReverseUnit(UnitID.currency_inr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_inr, R.string.unit_currency_inr_short), NormalUnit(UnitID.currency_inr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_inr, R.string.unit_currency_inr_short, backward = true),
ReverseUnit(UnitID.currency_iqd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_iqd, R.string.unit_currency_iqd_short), NormalUnit(UnitID.currency_iqd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_iqd, R.string.unit_currency_iqd_short, backward = true),
ReverseUnit(UnitID.currency_irr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_irr, R.string.unit_currency_irr_short), NormalUnit(UnitID.currency_irr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_irr, R.string.unit_currency_irr_short, backward = true),
ReverseUnit(UnitID.currency_isk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_isk, R.string.unit_currency_isk_short), NormalUnit(UnitID.currency_isk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_isk, R.string.unit_currency_isk_short, backward = true),
ReverseUnit(UnitID.currency_jep, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_jep, R.string.unit_currency_jep_short), NormalUnit(UnitID.currency_jep, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_jep, R.string.unit_currency_jep_short, backward = true),
ReverseUnit(UnitID.currency_jmd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_jmd, R.string.unit_currency_jmd_short), NormalUnit(UnitID.currency_jmd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_jmd, R.string.unit_currency_jmd_short, backward = true),
ReverseUnit(UnitID.currency_jod, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_jod, R.string.unit_currency_jod_short), NormalUnit(UnitID.currency_jod, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_jod, R.string.unit_currency_jod_short, backward = true),
ReverseUnit(UnitID.currency_jpy, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_jpy, R.string.unit_currency_jpy_short), NormalUnit(UnitID.currency_jpy, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_jpy, R.string.unit_currency_jpy_short, backward = true),
ReverseUnit(UnitID.currency_kes, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kes, R.string.unit_currency_kes_short), NormalUnit(UnitID.currency_kes, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kes, R.string.unit_currency_kes_short, backward = true),
ReverseUnit(UnitID.currency_kgs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kgs, R.string.unit_currency_kgs_short), NormalUnit(UnitID.currency_kgs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kgs, R.string.unit_currency_kgs_short, backward = true),
ReverseUnit(UnitID.currency_khr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_khr, R.string.unit_currency_khr_short), NormalUnit(UnitID.currency_khr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_khr, R.string.unit_currency_khr_short, backward = true),
ReverseUnit(UnitID.currency_kmf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kmf, R.string.unit_currency_kmf_short), NormalUnit(UnitID.currency_kmf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kmf, R.string.unit_currency_kmf_short, backward = true),
ReverseUnit(UnitID.currency_kpw, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kpw, R.string.unit_currency_kpw_short), NormalUnit(UnitID.currency_kpw, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kpw, R.string.unit_currency_kpw_short, backward = true),
ReverseUnit(UnitID.currency_krw, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_krw, R.string.unit_currency_krw_short), NormalUnit(UnitID.currency_krw, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_krw, R.string.unit_currency_krw_short, backward = true),
ReverseUnit(UnitID.currency_ksm, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ksm, R.string.unit_currency_ksm_short), NormalUnit(UnitID.currency_ksm, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ksm, R.string.unit_currency_ksm_short, backward = true),
ReverseUnit(UnitID.currency_kwd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kwd, R.string.unit_currency_kwd_short), NormalUnit(UnitID.currency_kwd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kwd, R.string.unit_currency_kwd_short, backward = true),
ReverseUnit(UnitID.currency_kyd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kyd, R.string.unit_currency_kyd_short), NormalUnit(UnitID.currency_kyd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kyd, R.string.unit_currency_kyd_short, backward = true),
ReverseUnit(UnitID.currency_kzt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kzt, R.string.unit_currency_kzt_short), NormalUnit(UnitID.currency_kzt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_kzt, R.string.unit_currency_kzt_short, backward = true),
ReverseUnit(UnitID.currency_lak, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lak, R.string.unit_currency_lak_short), NormalUnit(UnitID.currency_lak, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lak, R.string.unit_currency_lak_short, backward = true),
ReverseUnit(UnitID.currency_lbp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lbp, R.string.unit_currency_lbp_short), NormalUnit(UnitID.currency_lbp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lbp, R.string.unit_currency_lbp_short, backward = true),
ReverseUnit(UnitID.currency_link, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_link, R.string.unit_currency_link_short), NormalUnit(UnitID.currency_link, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_link, R.string.unit_currency_link_short, backward = true),
ReverseUnit(UnitID.currency_lkr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lkr, R.string.unit_currency_lkr_short), NormalUnit(UnitID.currency_lkr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lkr, R.string.unit_currency_lkr_short, backward = true),
ReverseUnit(UnitID.currency_lrd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lrd, R.string.unit_currency_lrd_short), NormalUnit(UnitID.currency_lrd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lrd, R.string.unit_currency_lrd_short, backward = true),
ReverseUnit(UnitID.currency_lsl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lsl, R.string.unit_currency_lsl_short), NormalUnit(UnitID.currency_lsl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lsl, R.string.unit_currency_lsl_short, backward = true),
ReverseUnit(UnitID.currency_ltc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ltc, R.string.unit_currency_ltc_short), NormalUnit(UnitID.currency_ltc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ltc, R.string.unit_currency_ltc_short, backward = true),
ReverseUnit(UnitID.currency_ltl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ltl, R.string.unit_currency_ltl_short), NormalUnit(UnitID.currency_ltl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ltl, R.string.unit_currency_ltl_short, backward = true),
ReverseUnit(UnitID.currency_luna, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_luna, R.string.unit_currency_luna_short), NormalUnit(UnitID.currency_luna, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_luna, R.string.unit_currency_luna_short, backward = true),
ReverseUnit(UnitID.currency_lvl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lvl, R.string.unit_currency_lvl_short), NormalUnit(UnitID.currency_lvl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lvl, R.string.unit_currency_lvl_short, backward = true),
ReverseUnit(UnitID.currency_lyd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lyd, R.string.unit_currency_lyd_short), NormalUnit(UnitID.currency_lyd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_lyd, R.string.unit_currency_lyd_short, backward = true),
ReverseUnit(UnitID.currency_mad, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mad, R.string.unit_currency_mad_short), NormalUnit(UnitID.currency_mad, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mad, R.string.unit_currency_mad_short, backward = true),
ReverseUnit(UnitID.currency_matic, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_matic, R.string.unit_currency_matic_short), NormalUnit(UnitID.currency_matic, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_matic, R.string.unit_currency_matic_short, backward = true),
ReverseUnit(UnitID.currency_mdl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mdl, R.string.unit_currency_mdl_short), NormalUnit(UnitID.currency_mdl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mdl, R.string.unit_currency_mdl_short, backward = true),
ReverseUnit(UnitID.currency_mga, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mga, R.string.unit_currency_mga_short), NormalUnit(UnitID.currency_mga, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mga, R.string.unit_currency_mga_short, backward = true),
ReverseUnit(UnitID.currency_mkd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mkd, R.string.unit_currency_mkd_short), NormalUnit(UnitID.currency_mkd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mkd, R.string.unit_currency_mkd_short, backward = true),
ReverseUnit(UnitID.currency_mmk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mmk, R.string.unit_currency_mmk_short), NormalUnit(UnitID.currency_mmk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mmk, R.string.unit_currency_mmk_short, backward = true),
ReverseUnit(UnitID.currency_mnt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mnt, R.string.unit_currency_mnt_short), NormalUnit(UnitID.currency_mnt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mnt, R.string.unit_currency_mnt_short, backward = true),
ReverseUnit(UnitID.currency_mop, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mop, R.string.unit_currency_mop_short), NormalUnit(UnitID.currency_mop, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mop, R.string.unit_currency_mop_short, backward = true),
ReverseUnit(UnitID.currency_mro, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mro, R.string.unit_currency_mro_short), NormalUnit(UnitID.currency_mro, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mro, R.string.unit_currency_mro_short, backward = true),
ReverseUnit(UnitID.currency_mur, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mur, R.string.unit_currency_mur_short), NormalUnit(UnitID.currency_mur, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mur, R.string.unit_currency_mur_short, backward = true),
ReverseUnit(UnitID.currency_mvr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mvr, R.string.unit_currency_mvr_short), NormalUnit(UnitID.currency_mvr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mvr, R.string.unit_currency_mvr_short, backward = true),
ReverseUnit(UnitID.currency_mwk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mwk, R.string.unit_currency_mwk_short), NormalUnit(UnitID.currency_mwk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mwk, R.string.unit_currency_mwk_short, backward = true),
ReverseUnit(UnitID.currency_mxn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mxn, R.string.unit_currency_mxn_short), NormalUnit(UnitID.currency_mxn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mxn, R.string.unit_currency_mxn_short, backward = true),
ReverseUnit(UnitID.currency_myr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_myr, R.string.unit_currency_myr_short), NormalUnit(UnitID.currency_myr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_myr, R.string.unit_currency_myr_short, backward = true),
ReverseUnit(UnitID.currency_mzn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mzn, R.string.unit_currency_mzn_short), NormalUnit(UnitID.currency_mzn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_mzn, R.string.unit_currency_mzn_short, backward = true),
ReverseUnit(UnitID.currency_nad, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_nad, R.string.unit_currency_nad_short), NormalUnit(UnitID.currency_nad, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_nad, R.string.unit_currency_nad_short, backward = true),
ReverseUnit(UnitID.currency_ngn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ngn, R.string.unit_currency_ngn_short), NormalUnit(UnitID.currency_ngn, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ngn, R.string.unit_currency_ngn_short, backward = true),
ReverseUnit(UnitID.currency_nio, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_nio, R.string.unit_currency_nio_short), NormalUnit(UnitID.currency_nio, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_nio, R.string.unit_currency_nio_short, backward = true),
ReverseUnit(UnitID.currency_nok, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_nok, R.string.unit_currency_nok_short), NormalUnit(UnitID.currency_nok, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_nok, R.string.unit_currency_nok_short, backward = true),
ReverseUnit(UnitID.currency_npr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_npr, R.string.unit_currency_npr_short), NormalUnit(UnitID.currency_npr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_npr, R.string.unit_currency_npr_short, backward = true),
ReverseUnit(UnitID.currency_nzd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_nzd, R.string.unit_currency_nzd_short), NormalUnit(UnitID.currency_nzd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_nzd, R.string.unit_currency_nzd_short, backward = true),
ReverseUnit(UnitID.currency_omr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_omr, R.string.unit_currency_omr_short), NormalUnit(UnitID.currency_omr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_omr, R.string.unit_currency_omr_short, backward = true),
ReverseUnit(UnitID.currency_one, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_one, R.string.unit_currency_one_short), NormalUnit(UnitID.currency_one, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_one, R.string.unit_currency_one_short, backward = true),
ReverseUnit(UnitID.currency_pab, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pab, R.string.unit_currency_pab_short), NormalUnit(UnitID.currency_pab, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pab, R.string.unit_currency_pab_short, backward = true),
ReverseUnit(UnitID.currency_pen, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pen, R.string.unit_currency_pen_short), NormalUnit(UnitID.currency_pen, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pen, R.string.unit_currency_pen_short, backward = true),
ReverseUnit(UnitID.currency_pgk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pgk, R.string.unit_currency_pgk_short), NormalUnit(UnitID.currency_pgk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pgk, R.string.unit_currency_pgk_short, backward = true),
ReverseUnit(UnitID.currency_php, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_php, R.string.unit_currency_php_short), NormalUnit(UnitID.currency_php, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_php, R.string.unit_currency_php_short, backward = true),
ReverseUnit(UnitID.currency_pkr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pkr, R.string.unit_currency_pkr_short), NormalUnit(UnitID.currency_pkr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pkr, R.string.unit_currency_pkr_short, backward = true),
ReverseUnit(UnitID.currency_pln, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pln, R.string.unit_currency_pln_short), NormalUnit(UnitID.currency_pln, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pln, R.string.unit_currency_pln_short, backward = true),
ReverseUnit(UnitID.currency_pyg, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pyg, R.string.unit_currency_pyg_short), NormalUnit(UnitID.currency_pyg, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_pyg, R.string.unit_currency_pyg_short, backward = true),
ReverseUnit(UnitID.currency_qar, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_qar, R.string.unit_currency_qar_short), NormalUnit(UnitID.currency_qar, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_qar, R.string.unit_currency_qar_short, backward = true),
ReverseUnit(UnitID.currency_ron, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ron, R.string.unit_currency_ron_short), NormalUnit(UnitID.currency_ron, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ron, R.string.unit_currency_ron_short, backward = true),
ReverseUnit(UnitID.currency_rsd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_rsd, R.string.unit_currency_rsd_short), NormalUnit(UnitID.currency_rsd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_rsd, R.string.unit_currency_rsd_short, backward = true),
ReverseUnit(UnitID.currency_rub, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_rub, R.string.unit_currency_rub_short), NormalUnit(UnitID.currency_rub, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_rub, R.string.unit_currency_rub_short, backward = true),
ReverseUnit(UnitID.currency_rwf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_rwf, R.string.unit_currency_rwf_short), NormalUnit(UnitID.currency_rwf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_rwf, R.string.unit_currency_rwf_short, backward = true),
ReverseUnit(UnitID.currency_sar, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sar, R.string.unit_currency_sar_short), NormalUnit(UnitID.currency_sar, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sar, R.string.unit_currency_sar_short, backward = true),
ReverseUnit(UnitID.currency_sbd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sbd, R.string.unit_currency_sbd_short), NormalUnit(UnitID.currency_sbd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sbd, R.string.unit_currency_sbd_short, backward = true),
ReverseUnit(UnitID.currency_scr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_scr, R.string.unit_currency_scr_short), NormalUnit(UnitID.currency_scr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_scr, R.string.unit_currency_scr_short, backward = true),
ReverseUnit(UnitID.currency_sdg, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sdg, R.string.unit_currency_sdg_short), NormalUnit(UnitID.currency_sdg, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sdg, R.string.unit_currency_sdg_short, backward = true),
ReverseUnit(UnitID.currency_sek, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sek, R.string.unit_currency_sek_short), NormalUnit(UnitID.currency_sek, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sek, R.string.unit_currency_sek_short, backward = true),
ReverseUnit(UnitID.currency_sgd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sgd, R.string.unit_currency_sgd_short), NormalUnit(UnitID.currency_sgd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sgd, R.string.unit_currency_sgd_short, backward = true),
ReverseUnit(UnitID.currency_shib, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_shib, R.string.unit_currency_shib_short), NormalUnit(UnitID.currency_shib, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_shib, R.string.unit_currency_shib_short, backward = true),
ReverseUnit(UnitID.currency_shp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_shp, R.string.unit_currency_shp_short), NormalUnit(UnitID.currency_shp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_shp, R.string.unit_currency_shp_short, backward = true),
ReverseUnit(UnitID.currency_sll, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sll, R.string.unit_currency_sll_short), NormalUnit(UnitID.currency_sll, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sll, R.string.unit_currency_sll_short, backward = true),
ReverseUnit(UnitID.currency_sol, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sol, R.string.unit_currency_sol_short), NormalUnit(UnitID.currency_sol, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sol, R.string.unit_currency_sol_short, backward = true),
ReverseUnit(UnitID.currency_sos, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sos, R.string.unit_currency_sos_short), NormalUnit(UnitID.currency_sos, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_sos, R.string.unit_currency_sos_short, backward = true),
ReverseUnit(UnitID.currency_srd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_srd, R.string.unit_currency_srd_short), NormalUnit(UnitID.currency_srd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_srd, R.string.unit_currency_srd_short, backward = true),
ReverseUnit(UnitID.currency_std, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_std, R.string.unit_currency_std_short), NormalUnit(UnitID.currency_std, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_std, R.string.unit_currency_std_short, backward = true),
ReverseUnit(UnitID.currency_svc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_svc, R.string.unit_currency_svc_short), NormalUnit(UnitID.currency_svc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_svc, R.string.unit_currency_svc_short, backward = true),
ReverseUnit(UnitID.currency_syp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_syp, R.string.unit_currency_syp_short), NormalUnit(UnitID.currency_syp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_syp, R.string.unit_currency_syp_short, backward = true),
ReverseUnit(UnitID.currency_szl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_szl, R.string.unit_currency_szl_short), NormalUnit(UnitID.currency_szl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_szl, R.string.unit_currency_szl_short, backward = true),
ReverseUnit(UnitID.currency_thb, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_thb, R.string.unit_currency_thb_short), NormalUnit(UnitID.currency_thb, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_thb, R.string.unit_currency_thb_short, backward = true),
ReverseUnit(UnitID.currency_theta, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_theta, R.string.unit_currency_theta_short), NormalUnit(UnitID.currency_theta, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_theta, R.string.unit_currency_theta_short, backward = true),
ReverseUnit(UnitID.currency_tjs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_tjs, R.string.unit_currency_tjs_short), NormalUnit(UnitID.currency_tjs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_tjs, R.string.unit_currency_tjs_short, backward = true),
ReverseUnit(UnitID.currency_tmt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_tmt, R.string.unit_currency_tmt_short), NormalUnit(UnitID.currency_tmt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_tmt, R.string.unit_currency_tmt_short, backward = true),
ReverseUnit(UnitID.currency_tnd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_tnd, R.string.unit_currency_tnd_short), NormalUnit(UnitID.currency_tnd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_tnd, R.string.unit_currency_tnd_short, backward = true),
ReverseUnit(UnitID.currency_top, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_top, R.string.unit_currency_top_short), NormalUnit(UnitID.currency_top, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_top, R.string.unit_currency_top_short, backward = true),
ReverseUnit(UnitID.currency_trx, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_trx, R.string.unit_currency_trx_short), NormalUnit(UnitID.currency_trx, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_trx, R.string.unit_currency_trx_short, backward = true),
ReverseUnit(UnitID.currency_try, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_try, R.string.unit_currency_try_short), NormalUnit(UnitID.currency_try, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_try, R.string.unit_currency_try_short, backward = true),
ReverseUnit(UnitID.currency_ttd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ttd, R.string.unit_currency_ttd_short), NormalUnit(UnitID.currency_ttd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ttd, R.string.unit_currency_ttd_short, backward = true),
ReverseUnit(UnitID.currency_twd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_twd, R.string.unit_currency_twd_short), NormalUnit(UnitID.currency_twd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_twd, R.string.unit_currency_twd_short, backward = true),
ReverseUnit(UnitID.currency_tzs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_tzs, R.string.unit_currency_tzs_short), NormalUnit(UnitID.currency_tzs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_tzs, R.string.unit_currency_tzs_short, backward = true),
ReverseUnit(UnitID.currency_uah, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_uah, R.string.unit_currency_uah_short), NormalUnit(UnitID.currency_uah, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_uah, R.string.unit_currency_uah_short, backward = true),
ReverseUnit(UnitID.currency_ugx, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ugx, R.string.unit_currency_ugx_short), NormalUnit(UnitID.currency_ugx, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_ugx, R.string.unit_currency_ugx_short, backward = true),
ReverseUnit(UnitID.currency_uni, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_uni, R.string.unit_currency_uni_short), NormalUnit(UnitID.currency_uni, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_uni, R.string.unit_currency_uni_short, backward = true),
ReverseUnit(UnitID.currency_usd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_usd, R.string.unit_currency_usd_short), NormalUnit(UnitID.currency_usd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_usd, R.string.unit_currency_usd_short, backward = true),
ReverseUnit(UnitID.currency_usdc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_usdc, R.string.unit_currency_usdc_short), NormalUnit(UnitID.currency_usdc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_usdc, R.string.unit_currency_usdc_short, backward = true),
ReverseUnit(UnitID.currency_usdt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_usdt, R.string.unit_currency_usdt_short), NormalUnit(UnitID.currency_usdt, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_usdt, R.string.unit_currency_usdt_short, backward = true),
ReverseUnit(UnitID.currency_uyu, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_uyu, R.string.unit_currency_uyu_short), NormalUnit(UnitID.currency_uyu, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_uyu, R.string.unit_currency_uyu_short, backward = true),
ReverseUnit(UnitID.currency_uzs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_uzs, R.string.unit_currency_uzs_short), NormalUnit(UnitID.currency_uzs, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_uzs, R.string.unit_currency_uzs_short, backward = true),
ReverseUnit(UnitID.currency_vef, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_vef, R.string.unit_currency_vef_short), NormalUnit(UnitID.currency_vef, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_vef, R.string.unit_currency_vef_short, backward = true),
ReverseUnit(UnitID.currency_vet, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_vet, R.string.unit_currency_vet_short), NormalUnit(UnitID.currency_vet, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_vet, R.string.unit_currency_vet_short, backward = true),
ReverseUnit(UnitID.currency_vnd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_vnd, R.string.unit_currency_vnd_short), NormalUnit(UnitID.currency_vnd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_vnd, R.string.unit_currency_vnd_short, backward = true),
ReverseUnit(UnitID.currency_vuv, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_vuv, R.string.unit_currency_vuv_short), NormalUnit(UnitID.currency_vuv, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_vuv, R.string.unit_currency_vuv_short, backward = true),
ReverseUnit(UnitID.currency_wbtc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_wbtc, R.string.unit_currency_wbtc_short), NormalUnit(UnitID.currency_wbtc, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_wbtc, R.string.unit_currency_wbtc_short, backward = true),
ReverseUnit(UnitID.currency_wst, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_wst, R.string.unit_currency_wst_short), NormalUnit(UnitID.currency_wst, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_wst, R.string.unit_currency_wst_short, backward = true),
ReverseUnit(UnitID.currency_xaf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xaf, R.string.unit_currency_xaf_short), NormalUnit(UnitID.currency_xaf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xaf, R.string.unit_currency_xaf_short, backward = true),
ReverseUnit(UnitID.currency_xag, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xag, R.string.unit_currency_xag_short), NormalUnit(UnitID.currency_xag, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xag, R.string.unit_currency_xag_short, backward = true),
ReverseUnit(UnitID.currency_xau, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xau, R.string.unit_currency_xau_short), NormalUnit(UnitID.currency_xau, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xau, R.string.unit_currency_xau_short, backward = true),
ReverseUnit(UnitID.currency_xcd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xcd, R.string.unit_currency_xcd_short), NormalUnit(UnitID.currency_xcd, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xcd, R.string.unit_currency_xcd_short, backward = true),
ReverseUnit(UnitID.currency_xdr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xdr, R.string.unit_currency_xdr_short), NormalUnit(UnitID.currency_xdr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xdr, R.string.unit_currency_xdr_short, backward = true),
ReverseUnit(UnitID.currency_xlm, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xlm, R.string.unit_currency_xlm_short), NormalUnit(UnitID.currency_xlm, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xlm, R.string.unit_currency_xlm_short, backward = true),
ReverseUnit(UnitID.currency_xmr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xmr, R.string.unit_currency_xmr_short), NormalUnit(UnitID.currency_xmr, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xmr, R.string.unit_currency_xmr_short, backward = true),
ReverseUnit(UnitID.currency_xof, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xof, R.string.unit_currency_xof_short), NormalUnit(UnitID.currency_xof, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xof, R.string.unit_currency_xof_short, backward = true),
ReverseUnit(UnitID.currency_xpf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xpf, R.string.unit_currency_xpf_short), NormalUnit(UnitID.currency_xpf, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xpf, R.string.unit_currency_xpf_short, backward = true),
ReverseUnit(UnitID.currency_xrp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xrp, R.string.unit_currency_xrp_short), NormalUnit(UnitID.currency_xrp, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_xrp, R.string.unit_currency_xrp_short, backward = true),
ReverseUnit(UnitID.currency_yer, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_yer, R.string.unit_currency_yer_short), NormalUnit(UnitID.currency_yer, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_yer, R.string.unit_currency_yer_short, backward = true),
ReverseUnit(UnitID.currency_zar, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_zar, R.string.unit_currency_zar_short), NormalUnit(UnitID.currency_zar, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_zar, R.string.unit_currency_zar_short, backward = true),
ReverseUnit(UnitID.currency_zmk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_zmk, R.string.unit_currency_zmk_short), NormalUnit(UnitID.currency_zmk, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_zmk, R.string.unit_currency_zmk_short, backward = true),
ReverseUnit(UnitID.currency_zmw, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_zmw, R.string.unit_currency_zmw_short), NormalUnit(UnitID.currency_zmw, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_zmw, R.string.unit_currency_zmw_short, backward = true),
ReverseUnit(UnitID.currency_zwl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_zwl, R.string.unit_currency_zwl_short), NormalUnit(UnitID.currency_zwl, BigDecimal.ZERO, UnitGroup.CURRENCY, R.string.unit_currency_zwl, R.string.unit_currency_zwl_short, backward = true),
) )
} }

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val dataCollection: List<AbstractUnit> by lazy { internal val dataCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.bit, BigDecimal("1"), UnitGroup.DATA, R.string.unit_bit, R.string.unit_bit_short), NormalUnit(UnitID.bit, BigDecimal("1"), UnitGroup.DATA, R.string.unit_bit, R.string.unit_bit_short),
NormalUnit(UnitID.kibibit, BigDecimal("1024"), UnitGroup.DATA, R.string.unit_kibibit, R.string.unit_kibibit_short), NormalUnit(UnitID.kibibit, BigDecimal("1024"), UnitGroup.DATA, R.string.unit_kibibit, R.string.unit_kibibit_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val dataTransferCollection: List<AbstractUnit> by lazy { internal val dataTransferCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.bit_per_second, BigDecimal("1"), UnitGroup.DATA_TRANSFER, R.string.unit_bit_per_second, R.string.unit_bit_per_second_short), NormalUnit(UnitID.bit_per_second, BigDecimal("1"), UnitGroup.DATA_TRANSFER, R.string.unit_bit_per_second, R.string.unit_bit_per_second_short),
NormalUnit(UnitID.kibibit_per_second, BigDecimal("1024"), UnitGroup.DATA_TRANSFER, R.string.unit_kibibit_per_second, R.string.unit_kibibit_per_second_short), NormalUnit(UnitID.kibibit_per_second, BigDecimal("1024"), UnitGroup.DATA_TRANSFER, R.string.unit_kibibit_per_second, R.string.unit_kibibit_per_second_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val electrostaticCapacitance: List<AbstractUnit> by lazy { internal val electrostaticCapacitance: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.attofarad, BigDecimal("1"), UnitGroup.ELECTROSTATIC_CAPACITANCE, R.string.unit_attofarad, R.string.unit_attofarad_short), NormalUnit(UnitID.attofarad, BigDecimal("1"), UnitGroup.ELECTROSTATIC_CAPACITANCE, R.string.unit_attofarad, R.string.unit_attofarad_short),
NormalUnit(UnitID.picofarad, BigDecimal("1000000"), UnitGroup.ELECTROSTATIC_CAPACITANCE, R.string.unit_picofarad, R.string.unit_picofarad_short), NormalUnit(UnitID.picofarad, BigDecimal("1000000"), UnitGroup.ELECTROSTATIC_CAPACITANCE, R.string.unit_picofarad, R.string.unit_picofarad_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val energyCollection: List<AbstractUnit> by lazy { internal val energyCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.electron_volt, BigDecimal("0.160217733"), UnitGroup.ENERGY, R.string.unit_electron_volt, R.string.unit_electron_volt_short), NormalUnit(UnitID.electron_volt, BigDecimal("0.160217733"), UnitGroup.ENERGY, R.string.unit_electron_volt, R.string.unit_electron_volt_short),
NormalUnit(UnitID.attojoule, BigDecimal("1.00"), UnitGroup.ENERGY, R.string.unit_attojoule, R.string.unit_attojoule_short), NormalUnit(UnitID.attojoule, BigDecimal("1.00"), UnitGroup.ENERGY, R.string.unit_attojoule, R.string.unit_attojoule_short),

View File

@ -20,33 +20,33 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.ReverseUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
val flowRateCollection: List<AbstractUnit> by lazy { val flowRateCollection: List<BasicUnit> by lazy {
listOf( listOf(
ReverseUnit(UnitID.liter_per_hour, BigDecimal("3600000"), UnitGroup.FLOW_RATE, R.string.unit_liter_per_hour, R.string.unit_liter_per_hour_short), NormalUnit(UnitID.liter_per_hour, BigDecimal("3600000"), UnitGroup.FLOW_RATE, R.string.unit_liter_per_hour, R.string.unit_liter_per_hour_short, true),
ReverseUnit(UnitID.liter_per_minute, BigDecimal("60000"), UnitGroup.FLOW_RATE, R.string.unit_liter_per_minute, R.string.unit_liter_per_minute_short), NormalUnit(UnitID.liter_per_minute, BigDecimal("60000"), UnitGroup.FLOW_RATE, R.string.unit_liter_per_minute, R.string.unit_liter_per_minute_short, true),
ReverseUnit(UnitID.liter_per_second, BigDecimal("1000"), UnitGroup.FLOW_RATE, R.string.unit_liter_per_second, R.string.unit_liter_per_second_short), NormalUnit(UnitID.liter_per_second, BigDecimal("1000"), UnitGroup.FLOW_RATE, R.string.unit_liter_per_second, R.string.unit_liter_per_second_short, true),
ReverseUnit(UnitID.milliliter_per_hour, BigDecimal("3600000000"), UnitGroup.FLOW_RATE, R.string.unit_milliliter_per_hour, R.string.unit_milliliter_per_hour_short), NormalUnit(UnitID.milliliter_per_hour, BigDecimal("3600000000"), UnitGroup.FLOW_RATE, R.string.unit_milliliter_per_hour, R.string.unit_milliliter_per_hour_short, true),
ReverseUnit(UnitID.milliliter_per_minute, BigDecimal("60000000"), UnitGroup.FLOW_RATE, R.string.unit_milliliter_per_minute, R.string.unit_milliliter_per_minute_short), NormalUnit(UnitID.milliliter_per_minute, BigDecimal("60000000"), UnitGroup.FLOW_RATE, R.string.unit_milliliter_per_minute, R.string.unit_milliliter_per_minute_short, true),
ReverseUnit(UnitID.milliliter_per_second, BigDecimal("1000000"), UnitGroup.FLOW_RATE, R.string.unit_milliliter_per_second, R.string.unit_milliliter_per_second_short), NormalUnit(UnitID.milliliter_per_second, BigDecimal("1000000"), UnitGroup.FLOW_RATE, R.string.unit_milliliter_per_second, R.string.unit_milliliter_per_second_short, true),
ReverseUnit(UnitID.cubic_meter_per_hour, BigDecimal("3600"), UnitGroup.FLOW_RATE, R.string.unit_cubic_meter_per_hour, R.string.unit_cubic_meter_per_hour_short), NormalUnit(UnitID.cubic_meter_per_hour, BigDecimal("3600"), UnitGroup.FLOW_RATE, R.string.unit_cubic_meter_per_hour, R.string.unit_cubic_meter_per_hour_short, true),
ReverseUnit(UnitID.cubic_meter_per_minute, BigDecimal("60"), UnitGroup.FLOW_RATE, R.string.unit_cubic_meter_per_minute, R.string.unit_cubic_meter_per_minute_short), NormalUnit(UnitID.cubic_meter_per_minute, BigDecimal("60"), UnitGroup.FLOW_RATE, R.string.unit_cubic_meter_per_minute, R.string.unit_cubic_meter_per_minute_short, true),
ReverseUnit(UnitID.cubic_meter_per_second, BigDecimal("1"), UnitGroup.FLOW_RATE, R.string.unit_cubic_meter_per_second, R.string.unit_cubic_meter_per_second_short), NormalUnit(UnitID.cubic_meter_per_second, BigDecimal("1"), UnitGroup.FLOW_RATE, R.string.unit_cubic_meter_per_second, R.string.unit_cubic_meter_per_second_short, true),
ReverseUnit(UnitID.cubic_millimeter_per_hour, BigDecimal("3600000000000"), UnitGroup.FLOW_RATE, R.string.unit_cubic_millimeter_per_hour, R.string.unit_cubic_millimeter_per_hour_short), NormalUnit(UnitID.cubic_millimeter_per_hour, BigDecimal("3600000000000"), UnitGroup.FLOW_RATE, R.string.unit_cubic_millimeter_per_hour, R.string.unit_cubic_millimeter_per_hour_short, true),
ReverseUnit(UnitID.cubic_millimeter_per_minute, BigDecimal("60000000000"), UnitGroup.FLOW_RATE, R.string.unit_cubic_millimeter_per_minute, R.string.unit_cubic_millimeter_per_minute_short), NormalUnit(UnitID.cubic_millimeter_per_minute, BigDecimal("60000000000"), UnitGroup.FLOW_RATE, R.string.unit_cubic_millimeter_per_minute, R.string.unit_cubic_millimeter_per_minute_short, true),
ReverseUnit(UnitID.cubic_millimeter_per_second, BigDecimal("1000000000"), UnitGroup.FLOW_RATE, R.string.unit_cubic_millimeter_per_second, R.string.unit_cubic_millimeter_per_second_short), NormalUnit(UnitID.cubic_millimeter_per_second, BigDecimal("1000000000"), UnitGroup.FLOW_RATE, R.string.unit_cubic_millimeter_per_second, R.string.unit_cubic_millimeter_per_second_short, true),
ReverseUnit(UnitID.cubic_foot_per_hour, BigDecimal("127132.80019736"), UnitGroup.FLOW_RATE, R.string.unit_cubic_foot_per_hour, R.string.unit_cubic_foot_per_hour_short), NormalUnit(UnitID.cubic_foot_per_hour, BigDecimal("127132.80019736"), UnitGroup.FLOW_RATE, R.string.unit_cubic_foot_per_hour, R.string.unit_cubic_foot_per_hour_short, true),
ReverseUnit(UnitID.cubic_foot_per_minute, BigDecimal("2118.8800032893"), UnitGroup.FLOW_RATE, R.string.unit_cubic_foot_per_minute, R.string.unit_cubic_foot_per_minute_short), NormalUnit(UnitID.cubic_foot_per_minute, BigDecimal("2118.8800032893"), UnitGroup.FLOW_RATE, R.string.unit_cubic_foot_per_minute, R.string.unit_cubic_foot_per_minute_short, true),
ReverseUnit(UnitID.cubic_foot_per_second, BigDecimal("35.314666721489"), UnitGroup.FLOW_RATE, R.string.unit_cubic_foot_per_second, R.string.unit_cubic_foot_per_second_short), NormalUnit(UnitID.cubic_foot_per_second, BigDecimal("35.314666721489"), UnitGroup.FLOW_RATE, R.string.unit_cubic_foot_per_second, R.string.unit_cubic_foot_per_second_short, true),
ReverseUnit(UnitID.gallons_per_hour_us, BigDecimal("951019.38848933"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_hour_us, R.string.unit_gallon_per_hour_us_short), NormalUnit(UnitID.gallons_per_hour_us, BigDecimal("951019.38848933"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_hour_us, R.string.unit_gallon_per_hour_us_short, true),
ReverseUnit(UnitID.gallons_per_minute_us, BigDecimal("15850.323141489"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_minute_us, R.string.unit_gallon_per_minute_us_short), NormalUnit(UnitID.gallons_per_minute_us, BigDecimal("15850.323141489"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_minute_us, R.string.unit_gallon_per_minute_us_short, true),
ReverseUnit(UnitID.gallons_per_second_us, BigDecimal("264.17205235815"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_second_us, R.string.unit_gallon_per_second_us_short), NormalUnit(UnitID.gallons_per_second_us, BigDecimal("264.17205235815"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_second_us, R.string.unit_gallon_per_second_us_short, true),
ReverseUnit(UnitID.gallons_per_hour_imperial, BigDecimal("791889.29387672"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_hour_imperial, R.string.unit_gallon_per_hour_imperial_short), NormalUnit(UnitID.gallons_per_hour_imperial, BigDecimal("791889.29387672"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_hour_imperial, R.string.unit_gallon_per_hour_imperial_short, true),
ReverseUnit(UnitID.gallons_per_minute_imperial, BigDecimal("13198.154897945"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_minute_imperial, R.string.unit_gallon_per_minute_imperial_short), NormalUnit(UnitID.gallons_per_minute_imperial, BigDecimal("13198.154897945"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_minute_imperial, R.string.unit_gallon_per_minute_imperial_short, true),
ReverseUnit(UnitID.gallons_per_second_imperial, BigDecimal("219.96924829909"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_second_imperial, R.string.unit_gallon_per_second_imperial_short), NormalUnit(UnitID.gallons_per_second_imperial, BigDecimal("219.96924829909"), UnitGroup.FLOW_RATE, R.string.unit_gallon_per_second_imperial, R.string.unit_gallon_per_second_imperial_short, true),
) )
} }

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val fluxCollection: List<AbstractUnit> by lazy { internal val fluxCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.maxwell, BigDecimal("1"), UnitGroup.FLUX, R.string.unit_maxwell, R.string.unit_maxwell_short), NormalUnit(UnitID.maxwell, BigDecimal("1"), UnitGroup.FLUX, R.string.unit_maxwell, R.string.unit_maxwell_short),
NormalUnit(UnitID.microweber, BigDecimal("100"), UnitGroup.FLUX, R.string.unit_microweber, R.string.unit_microweber_short), NormalUnit(UnitID.microweber, BigDecimal("100"), UnitGroup.FLUX, R.string.unit_microweber, R.string.unit_microweber_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
val forceCollection: List<AbstractUnit> by lazy { val forceCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.attonewton, BigDecimal("1"), UnitGroup.FORCE, R.string.unit_attonewton, R.string.unit_attonewton_short), NormalUnit(UnitID.attonewton, BigDecimal("1"), UnitGroup.FORCE, R.string.unit_attonewton, R.string.unit_attonewton_short),
NormalUnit(UnitID.dyne, BigDecimal("10000000000000"), UnitGroup.FORCE, R.string.unit_dyne, R.string.unit_dyne_short), NormalUnit(UnitID.dyne, BigDecimal("10000000000000"), UnitGroup.FORCE, R.string.unit_dyne, R.string.unit_dyne_short),

View File

@ -20,23 +20,22 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.BackwardUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import com.sadellie.unitto.data.model.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
val fuelConsumptionCollection: List<AbstractUnit> by lazy { val fuelConsumptionCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit( UnitID.kilometer_per_liter, BigDecimal("1"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_km_per_l, R.string.unit_km_per_l_short), NormalUnit( UnitID.kilometer_per_liter, BigDecimal("1"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_km_per_l, R.string.unit_km_per_l_short),
BackwardUnit(UnitID.liter_per_kilometer, BigDecimal("1"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_l_per_km, R.string.unit_l_per_km_short), NormalUnit(UnitID.liter_per_kilometer, BigDecimal("1"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_l_per_km, R.string.unit_l_per_km_short, true),
BackwardUnit(UnitID.liter_per_100_kilometer, BigDecimal("100"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_l_per_100_km, R.string.unit_l_per_100_km_short), NormalUnit(UnitID.liter_per_100_kilometer, BigDecimal("100"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_l_per_100_km, R.string.unit_l_per_100_km_short, true),
NormalUnit( UnitID.mile_per_gallon_uk, BigDecimal("0.35400619"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_mi_per_gallon_uk, R.string.unit_mi_per_gallon_uk_short), NormalUnit( UnitID.mile_per_gallon_uk, BigDecimal("0.35400619"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_mi_per_gallon_uk, R.string.unit_mi_per_gallon_uk_short),
NormalUnit( UnitID.mile_per_gallon_us, BigDecimal("0.4251437075"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_mi_per_gallon_us, R.string.unit_mi_per_gallon_us_short), NormalUnit( UnitID.mile_per_gallon_us, BigDecimal("0.4251437075"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_mi_per_gallon_us, R.string.unit_mi_per_gallon_us_short),
NormalUnit( UnitID.mile_us_per_liter, BigDecimal("1.609344"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_mi_us_per_l, R.string.unit_mi_us_per_l_short), NormalUnit( UnitID.mile_us_per_liter, BigDecimal("1.609344"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_mi_us_per_l, R.string.unit_mi_us_per_l_short),
BackwardUnit(UnitID.gallon_us_per_mile, BigDecimal("0.4251437075"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_gallon_us_per_mile, R.string.unit_gallon_us_per_mile_short), NormalUnit(UnitID.gallon_us_per_mile, BigDecimal("0.4251437075"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_gallon_us_per_mile, R.string.unit_gallon_us_per_mile_short, true),
BackwardUnit(UnitID.gallon_uk_per_mile, BigDecimal("0.35400619"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_gallon_uk_per_mile, R.string.unit_gallon_uk_per_mile_short), NormalUnit(UnitID.gallon_uk_per_mile, BigDecimal("0.35400619"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_gallon_uk_per_mile, R.string.unit_gallon_uk_per_mile_short, true),
BackwardUnit(UnitID.gallon_us_per_100_mile, BigDecimal("42.51437075"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_gallon_us_per_100_mile, R.string.unit_gallon_us_per_100_mile_short), NormalUnit(UnitID.gallon_us_per_100_mile, BigDecimal("42.51437075"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_gallon_us_per_100_mile, R.string.unit_gallon_us_per_100_mile_short, true),
BackwardUnit(UnitID.gallon_uk_per_100_mile, BigDecimal("35.400618996"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_gallon_uk_per_100_mile, R.string.unit_gallon_uk_per_100_mile_short), NormalUnit(UnitID.gallon_uk_per_100_mile, BigDecimal("35.400618996"), UnitGroup.FUEL_CONSUMPTION, R.string.unit_gallon_uk_per_100_mile, R.string.unit_gallon_uk_per_100_mile_short, true),
) )
} }

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val lengthCollection: List<AbstractUnit> by lazy { internal val lengthCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.attometer, BigDecimal("1"), UnitGroup.LENGTH, R.string.unit_attometer, R.string.unit_attometer_short), NormalUnit(UnitID.attometer, BigDecimal("1"), UnitGroup.LENGTH, R.string.unit_attometer, R.string.unit_attometer_short),
NormalUnit(UnitID.nanometer, BigDecimal("1000000000"), UnitGroup.LENGTH, R.string.unit_nanometer, R.string.unit_nanometer_short), NormalUnit(UnitID.nanometer, BigDecimal("1000000000"), UnitGroup.LENGTH, R.string.unit_nanometer, R.string.unit_nanometer_short),
@ -55,3 +55,4 @@ internal val lengthCollection: List<AbstractUnit> by lazy {
NormalUnit(UnitID.sun_equatorial_radius, BigDecimal("695508000000000000000000000"), UnitGroup.LENGTH, R.string.unit_sun_equatorial_radius, R.string.unit_sun_equatorial_radius_short), NormalUnit(UnitID.sun_equatorial_radius, BigDecimal("695508000000000000000000000"), UnitGroup.LENGTH, R.string.unit_sun_equatorial_radius, R.string.unit_sun_equatorial_radius_short),
) )
} }

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
val luminanceCollection: List<AbstractUnit> by lazy { val luminanceCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.candela_per_square_meter, BigDecimal("31415926.5359"), UnitGroup.LUMINANCE, R.string.unit_candela_per_square_meter, R.string.unit_candela_per_square_meter_short), NormalUnit(UnitID.candela_per_square_meter, BigDecimal("31415926.5359"), UnitGroup.LUMINANCE, R.string.unit_candela_per_square_meter, R.string.unit_candela_per_square_meter_short),
NormalUnit(UnitID.candela_per_square_centimeter, BigDecimal("314159265359"), UnitGroup.LUMINANCE, R.string.unit_candela_per_square_centimeter, R.string.unit_candela_per_square_centimeter_short), NormalUnit(UnitID.candela_per_square_centimeter, BigDecimal("314159265359"), UnitGroup.LUMINANCE, R.string.unit_candela_per_square_centimeter, R.string.unit_candela_per_square_centimeter_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val massCollection: List<AbstractUnit> by lazy { internal val massCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.electron_mass_rest, BigDecimal("0.00000000000000000000000000091093897"), UnitGroup.MASS, R.string.unit_electron_mass_rest, R.string.unit_electron_mass_rest_short), NormalUnit(UnitID.electron_mass_rest, BigDecimal("0.00000000000000000000000000091093897"), UnitGroup.MASS, R.string.unit_electron_mass_rest, R.string.unit_electron_mass_rest_short),
NormalUnit(UnitID.atomic_mass_unit, BigDecimal("0.0000000000000000000000016605402"), UnitGroup.MASS, R.string.unit_atomic_mass_unit, R.string.unit_atomic_mass_unit_short), NormalUnit(UnitID.atomic_mass_unit, BigDecimal("0.0000000000000000000000016605402"), UnitGroup.MASS, R.string.unit_atomic_mass_unit, R.string.unit_atomic_mass_unit_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NumberBaseUnit import com.sadellie.unitto.data.model.converter.unit.NumberBaseUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val numberBaseCollection: List<AbstractUnit> by lazy { internal val numberBaseCollection: List<BasicUnit> by lazy {
listOf( listOf(
NumberBaseUnit(UnitID.binary, BigDecimal("2.0"), UnitGroup.NUMBER_BASE, R.string.unit_binary, R.string.unit_binary_short), NumberBaseUnit(UnitID.binary, BigDecimal("2.0"), UnitGroup.NUMBER_BASE, R.string.unit_binary, R.string.unit_binary_short),
NumberBaseUnit(UnitID.ternary, BigDecimal("3.0"), UnitGroup.NUMBER_BASE, R.string.unit_ternary, R.string.unit_ternary_short), NumberBaseUnit(UnitID.ternary, BigDecimal("3.0"), UnitGroup.NUMBER_BASE, R.string.unit_ternary, R.string.unit_ternary_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val powerCollection: List<AbstractUnit> by lazy { internal val powerCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.attowatt, BigDecimal("1"), UnitGroup.POWER, R.string.unit_attowatt, R.string.unit_attowatt_short), NormalUnit(UnitID.attowatt, BigDecimal("1"), UnitGroup.POWER, R.string.unit_attowatt, R.string.unit_attowatt_short),
NormalUnit(UnitID.watt, BigDecimal("1000000000000000000"), UnitGroup.POWER, R.string.unit_watt, R.string.unit_watt_short), NormalUnit(UnitID.watt, BigDecimal("1000000000000000000"), UnitGroup.POWER, R.string.unit_watt, R.string.unit_watt_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
val prefixCollection: List<AbstractUnit> by lazy { val prefixCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.prefix_quetta, BigDecimal("1000000000000000000000000000000"), UnitGroup.PREFIX, R.string.unit_prefix_quetta, R.string.unit_prefix_quetta_short), NormalUnit(UnitID.prefix_quetta, BigDecimal("1000000000000000000000000000000"), UnitGroup.PREFIX, R.string.unit_prefix_quetta, R.string.unit_prefix_quetta_short),
NormalUnit(UnitID.prefix_ronna, BigDecimal("1000000000000000000000000000"), UnitGroup.PREFIX, R.string.unit_prefix_ronna, R.string.unit_prefix_ronna_short), NormalUnit(UnitID.prefix_ronna, BigDecimal("1000000000000000000000000000"), UnitGroup.PREFIX, R.string.unit_prefix_ronna, R.string.unit_prefix_ronna_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val pressureCollection: List<AbstractUnit> by lazy { internal val pressureCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.attopascal, BigDecimal("1"), UnitGroup.PRESSURE, R.string.unit_attopascal, R.string.unit_attopascal_short), NormalUnit(UnitID.attopascal, BigDecimal("1"), UnitGroup.PRESSURE, R.string.unit_attopascal, R.string.unit_attopascal_short),
NormalUnit(UnitID.femtopascal, BigDecimal("1000"), UnitGroup.PRESSURE, R.string.unit_femtopascal, R.string.unit_femtopascal_short), NormalUnit(UnitID.femtopascal, BigDecimal("1000"), UnitGroup.PRESSURE, R.string.unit_femtopascal, R.string.unit_femtopascal_short),

View File

@ -20,13 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.BackwardUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import com.sadellie.unitto.data.model.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val speedCollection: List<AbstractUnit> by lazy { internal val speedCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit( UnitID.millimeter_per_hour, BigDecimal("1"), UnitGroup.SPEED, R.string.unit_millimeter_per_hour, R.string.unit_millimeter_per_hour_short), NormalUnit( UnitID.millimeter_per_hour, BigDecimal("1"), UnitGroup.SPEED, R.string.unit_millimeter_per_hour, R.string.unit_millimeter_per_hour_short),
NormalUnit( UnitID.millimeter_per_minute, BigDecimal("60"), UnitGroup.SPEED, R.string.unit_millimeter_per_minute, R.string.unit_millimeter_per_minute_short), NormalUnit( UnitID.millimeter_per_minute, BigDecimal("60"), UnitGroup.SPEED, R.string.unit_millimeter_per_minute, R.string.unit_millimeter_per_minute_short),
@ -49,10 +48,10 @@ internal val speedCollection: List<AbstractUnit> by lazy {
NormalUnit( UnitID.mile_per_hour, BigDecimal("1609344"), UnitGroup.SPEED, R.string.unit_mile_per_hour, R.string.unit_mile_per_hour_short), NormalUnit( UnitID.mile_per_hour, BigDecimal("1609344"), UnitGroup.SPEED, R.string.unit_mile_per_hour, R.string.unit_mile_per_hour_short),
NormalUnit( UnitID.mile_per_minute, BigDecimal("96560640"), UnitGroup.SPEED, R.string.unit_mile_per_minute, R.string.unit_mile_per_minute_short), NormalUnit( UnitID.mile_per_minute, BigDecimal("96560640"), UnitGroup.SPEED, R.string.unit_mile_per_minute, R.string.unit_mile_per_minute_short),
NormalUnit( UnitID.mile_per_second, BigDecimal("5793638400"), UnitGroup.SPEED, R.string.unit_mile_per_second, R.string.unit_mile_per_second_short), NormalUnit( UnitID.mile_per_second, BigDecimal("5793638400"), UnitGroup.SPEED, R.string.unit_mile_per_second, R.string.unit_mile_per_second_short),
BackwardUnit(UnitID.minute_per_kilometer, BigDecimal("60000000"), UnitGroup.SPEED, R.string.unit_minute_per_kilometer, R.string.unit_minute_per_kilometer_short), NormalUnit(UnitID.minute_per_kilometer, BigDecimal("60000000"), UnitGroup.SPEED, R.string.unit_minute_per_kilometer, R.string.unit_minute_per_kilometer_short, true),
BackwardUnit(UnitID.minute_per_mile, BigDecimal("96560640"), UnitGroup.SPEED, R.string.unit_minute_per_mile, R.string.unit_minute_per_mile_short), NormalUnit(UnitID.minute_per_mile, BigDecimal("96560640"), UnitGroup.SPEED, R.string.unit_minute_per_mile, R.string.unit_minute_per_mile_short, true),
BackwardUnit(UnitID.hour_per_kilometer, BigDecimal("1000000"), UnitGroup.SPEED, R.string.unit_hour_per_kilometer, R.string.unit_hour_per_kilometer_short), NormalUnit(UnitID.hour_per_kilometer, BigDecimal("1000000"), UnitGroup.SPEED, R.string.unit_hour_per_kilometer, R.string.unit_hour_per_kilometer_short, true),
BackwardUnit(UnitID.hour_per_mile, BigDecimal("1609344"), UnitGroup.SPEED, R.string.unit_hour_per_mile, R.string.unit_hour_per_mile_short), NormalUnit(UnitID.hour_per_mile, BigDecimal("1609344"), UnitGroup.SPEED, R.string.unit_hour_per_mile, R.string.unit_hour_per_mile_short, true),
NormalUnit( UnitID.knot, BigDecimal("1852000"), UnitGroup.SPEED, R.string.unit_knot, R.string.unit_knot_short), NormalUnit( UnitID.knot, BigDecimal("1852000"), UnitGroup.SPEED, R.string.unit_knot, R.string.unit_knot_short),
NormalUnit( UnitID.velocity_of_light_in_vacuum, BigDecimal("1079252848799998"), UnitGroup.SPEED, R.string.unit_velocity_of_light_in_vacuum, R.string.unit_velocity_of_light_in_vacuum_short), NormalUnit( UnitID.velocity_of_light_in_vacuum, BigDecimal("1079252848799998"), UnitGroup.SPEED, R.string.unit_velocity_of_light_in_vacuum, R.string.unit_velocity_of_light_in_vacuum_short),
NormalUnit( UnitID.cosmic_velocity_first, BigDecimal("28440000000"), UnitGroup.SPEED, R.string.unit_cosmic_velocity_first, R.string.unit_cosmic_velocity_first_short), NormalUnit( UnitID.cosmic_velocity_first, BigDecimal("28440000000"), UnitGroup.SPEED, R.string.unit_cosmic_velocity_first, R.string.unit_cosmic_velocity_first_short),

View File

@ -21,22 +21,21 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.MAX_PRECISION import com.sadellie.unitto.core.base.MAX_PRECISION
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.TemperatureUnit
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
internal val temperatureCollection: List<AbstractUnit> by lazy { internal val temperatureCollection: List<BasicUnit> by lazy {
listOf( val celsius = object : BasicUnit.Default {
TemperatureUnit( override val id: String = UnitID.celsius
id = UnitID.celsius, override val group: UnitGroup = UnitGroup.TEMPERATURE
basicUnit = BigDecimal.ONE, override val displayName: Int = R.string.unit_celsius
group = UnitGroup.TEMPERATURE, override val shortName: Int = R.string.unit_celsius_short
displayName = R.string.unit_celsius, override val factor: BigDecimal = BigDecimal.ONE
shortName = R.string.unit_celsius_short, override val backward: Boolean = false
customConvert = { unitTo, value ->
when (unitTo.id) { override fun convert(unitTo: BasicUnit.Default, value: BigDecimal): BigDecimal = when (unitTo.id) {
UnitID.fahrenheit -> { UnitID.fahrenheit -> {
value value
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN) .setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
@ -53,15 +52,16 @@ internal val temperatureCollection: List<AbstractUnit> by lazy {
else -> value else -> value
} }
} }
),
TemperatureUnit( val fahrenheit = object : BasicUnit.Default {
id = UnitID.fahrenheit, override val id: String = UnitID.fahrenheit
basicUnit = BigDecimal.ONE, override val group: UnitGroup = UnitGroup.TEMPERATURE
group = UnitGroup.TEMPERATURE, override val displayName: Int = R.string.unit_fahrenheit
displayName = R.string.unit_fahrenheit, override val shortName: Int = R.string.unit_fahrenheit_short
shortName = R.string.unit_fahrenheit_short, override val factor: BigDecimal = BigDecimal.ONE
customConvert = { unitTo, value -> override val backward: Boolean = false
when (unitTo.id) {
override fun convert(unitTo: BasicUnit.Default, value: BigDecimal): BigDecimal = when (unitTo.id) {
UnitID.celsius -> { UnitID.celsius -> {
value value
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN) .setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
@ -80,15 +80,16 @@ internal val temperatureCollection: List<AbstractUnit> by lazy {
else -> value else -> value
} }
} }
),
TemperatureUnit( val kelvin = object : BasicUnit.Default {
id = UnitID.kelvin, override val id: String = UnitID.kelvin
basicUnit = BigDecimal.ONE, override val group: UnitGroup = UnitGroup.TEMPERATURE
group = UnitGroup.TEMPERATURE, override val displayName: Int = R.string.unit_kelvin
displayName = R.string.unit_kelvin, override val shortName: Int = R.string.unit_kelvin_short
shortName = R.string.unit_kelvin_short, override val factor: BigDecimal = BigDecimal.ONE
customConvert = { unitTo, value -> override val backward: Boolean = false
when (unitTo.id) {
override fun convert(unitTo: BasicUnit.Default, value: BigDecimal): BigDecimal = when (unitTo.id) {
UnitID.celsius -> { UnitID.celsius -> {
value value
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN) .setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
@ -104,6 +105,6 @@ internal val temperatureCollection: List<AbstractUnit> by lazy {
else -> value else -> value
} }
} }
),
) listOf(celsius, fahrenheit, kelvin)
} }

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val timeCollection: List<AbstractUnit> by lazy { internal val timeCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.attosecond, BigDecimal("1"), UnitGroup.TIME, R.string.unit_attosecond, R.string.unit_attosecond_short), NormalUnit(UnitID.attosecond, BigDecimal("1"), UnitGroup.TIME, R.string.unit_attosecond, R.string.unit_attosecond_short),
NormalUnit(UnitID.nanosecond, BigDecimal("1000000000"), UnitGroup.TIME, R.string.unit_nanosecond, R.string.unit_nanosecond_short), NormalUnit(UnitID.nanosecond, BigDecimal("1000000000"), UnitGroup.TIME, R.string.unit_nanosecond, R.string.unit_nanosecond_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
val torqueCollection: List<AbstractUnit> by lazy { val torqueCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.dyne_millimeter, BigDecimal("1"), UnitGroup.TORQUE, R.string.unit_dyne_millimeter, R.string.unit_dyne_millimeter_short), NormalUnit(UnitID.dyne_millimeter, BigDecimal("1"), UnitGroup.TORQUE, R.string.unit_dyne_millimeter, R.string.unit_dyne_millimeter_short),
NormalUnit(UnitID.dyne_centimeter, BigDecimal("10"), UnitGroup.TORQUE, R.string.unit_dyne_centimeter, R.string.unit_dyne_centimeter_short), NormalUnit(UnitID.dyne_centimeter, BigDecimal("10"), UnitGroup.TORQUE, R.string.unit_dyne_centimeter, R.string.unit_dyne_centimeter_short),

View File

@ -20,12 +20,12 @@ package com.sadellie.unitto.data.converter.collections
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
internal val volumeCollection: List<AbstractUnit> by lazy { internal val volumeCollection: List<BasicUnit> by lazy {
listOf( listOf(
NormalUnit(UnitID.attoliter, BigDecimal("1"), UnitGroup.VOLUME, R.string.unit_attoliter, R.string.unit_attoliter_short), NormalUnit(UnitID.attoliter, BigDecimal("1"), UnitGroup.VOLUME, R.string.unit_attoliter, R.string.unit_attoliter_short),
NormalUnit(UnitID.milliliter, BigDecimal("1000000000000000"), UnitGroup.VOLUME, R.string.unit_milliliter, R.string.unit_milliliter_short), NormalUnit(UnitID.milliliter, BigDecimal("1000000000000000"), UnitGroup.VOLUME, R.string.unit_milliliter, R.string.unit_milliliter_short),

View File

@ -44,10 +44,9 @@ import com.sadellie.unitto.data.converter.collections.temperatureCollection
import com.sadellie.unitto.data.converter.collections.timeCollection import com.sadellie.unitto.data.converter.collections.timeCollection
import com.sadellie.unitto.data.converter.collections.torqueCollection import com.sadellie.unitto.data.converter.collections.torqueCollection
import com.sadellie.unitto.data.converter.collections.volumeCollection import com.sadellie.unitto.data.converter.collections.volumeCollection
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.DefaultUnit import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.NumberBaseUnit import com.sadellie.unitto.data.model.converter.unit.NumberBaseUnit
import com.sadellie.unitto.data.model.unit.ReverseUnit
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -595,13 +594,11 @@ class AllUnitsTest {
val actual = when (unitFrom.group) { val actual = when (unitFrom.group) {
UnitGroup.NUMBER_BASE -> (unitFrom as NumberBaseUnit).convert((unitTo as NumberBaseUnit), value) UnitGroup.NUMBER_BASE -> (unitFrom as NumberBaseUnit).convert((unitTo as NumberBaseUnit), value)
UnitGroup.FLOW_RATE -> (unitFrom as ReverseUnit) else -> (unitFrom as BasicUnit.Default)
.convert((unitTo as DefaultUnit), BigDecimal(value)) .convert((unitTo as BasicUnit.Default), BigDecimal(value))
.format(5, OutputFormat.PLAIN)
else -> (unitFrom as DefaultUnit)
.convert((unitTo as DefaultUnit), BigDecimal(value))
.format(5, OutputFormat.PLAIN) .format(5, OutputFormat.PLAIN)
} }
assertEquals("Failed at $this to $checkingId", expected, actual) assertEquals("Failed at $this to $checkingId", expected, actual)
println("PASSED: $this -> $expected == $actual") println("PASSED: $this -> $expected == $actual")
val content: Set<String> = history.getOrDefault(unitFrom.group, setOf()) val content: Set<String> = history.getOrDefault(unitFrom.group, setOf())

View File

@ -0,0 +1,247 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.converter
import android.content.Context
import com.sadellie.unitto.data.database.CurrencyRatesDao
import com.sadellie.unitto.data.database.CurrencyRatesEntity
import com.sadellie.unitto.data.database.UnitsDao
import com.sadellie.unitto.data.database.UnitsEntity
import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import java.math.BigDecimal
@RunWith(RobolectricTestRunner::class)
class ConverterUIStateKtTest {
@Test
fun convertTime() {
var basicValue = BigDecimal("1")
val mContext: Context = RuntimeEnvironment.getApplication().applicationContext
val repo = UnitsRepositoryImpl(
unitsDao = object : UnitsDao {
override fun getAllFlow(): Flow<List<UnitsEntity>> = emptyFlow()
override suspend fun insertUnit(unit: UnitsEntity) {}
override suspend fun getById(unitId: String): UnitsEntity? = null
override suspend fun clear() {}
},
currencyRatesDao = object : CurrencyRatesDao {
override suspend fun insertRates(currencyRates: List<CurrencyRatesEntity>) {}
override suspend fun getLatestRateTimeStamp(baseId: String): Long? = null
override suspend fun getLatestRates(baseId: String): List<CurrencyRatesEntity> = emptyList()
override suspend fun getLatestRate(baseId: String, pairId: String): CurrencyRatesEntity? = null
override fun size(): Flow<Int> = emptyFlow()
override suspend fun clear() {}
},
mContext = mContext,
)
fun String.formatTime(): ConverterResult.Time {
val unitFrom = NormalUnit(
id = "",
factor = basicValue,
group = UnitGroup.TIME,
displayName = 0,
shortName = 0,
backward = false,
)
return repo.convertTime(unitFrom, BigDecimal(this))
}
// Edgy things (minus, decimals and zeros)
assertEquals(
ConverterResult.Time(
negative = true,
attosecond = BigDecimal("28"),
),
"-28".formatTime(),
)
assertEquals(
ConverterResult.Time(
negative = true,
attosecond = BigDecimal("0.05"),
),
"-0.05".formatTime(),
)
assertEquals(
ConverterResult.Time(),
"0".formatTime(),
)
basicValue = BigDecimal("86400000000000000000000")
assertEquals(
ConverterResult.Time(
negative = true,
day = BigDecimal("28"),
),
"-28".formatTime(),
)
assertEquals(
ConverterResult.Time(
negative = true,
hour = BigDecimal("1"),
minute = BigDecimal("12"),
),
"-0.05".formatTime(),
)
assertEquals(
ConverterResult.Time(),
"0".formatTime(),
)
assertEquals(
ConverterResult.Time(),
"-0".formatTime(),
)
// DAYS
basicValue = BigDecimal("86400000000000000000000")
assertEquals(
ConverterResult.Time(
hour = BigDecimal("12"),
),
"0.5".formatTime(),
)
assertEquals(
ConverterResult.Time(
day = BigDecimal("90"),
minute = BigDecimal("7"),
second = BigDecimal("12"),
),
"90.005".formatTime(),
)
// HOURS
basicValue = BigDecimal("3600000000000000000000")
assertEquals(
ConverterResult.Time(
minute = BigDecimal("30"),
),
"0.5".formatTime(),
)
assertEquals(
ConverterResult.Time(
day = BigDecimal("3"),
hour = BigDecimal("18"),
second = BigDecimal("18"),
),
"90.005".formatTime(),
)
// MINUTES
basicValue = BigDecimal("60000000000000000000")
assertEquals(
ConverterResult.Time(
second = BigDecimal("30"),
),
"0.5".formatTime(),
)
assertEquals(
ConverterResult.Time(
hour = BigDecimal("1"),
minute = BigDecimal("30"),
millisecond = BigDecimal("300"),
),
"90.005".formatTime(),
)
// SECONDS
basicValue = BigDecimal("1000000000000000000")
assertEquals(
ConverterResult.Time(
millisecond = BigDecimal("500"),
),
"0.5".formatTime(),
)
assertEquals(
ConverterResult.Time(
minute = BigDecimal("1"),
second = BigDecimal("30"),
millisecond = BigDecimal("5"),
),
"90.005".formatTime(),
)
// MILLISECONDS
basicValue = BigDecimal("1000000000000000")
assertEquals(
ConverterResult.Time(
microsecond = BigDecimal("500"),
),
"0.5".formatTime(),
)
assertEquals(
ConverterResult.Time(
millisecond = BigDecimal("90"),
microsecond = BigDecimal("5"),
),
"90.005".formatTime(),
)
// MICROSECONDS
basicValue = BigDecimal("1000000000000")
assertEquals(
ConverterResult.Time(
nanosecond = BigDecimal("500"),
),
"0.5".formatTime(),
)
assertEquals(
ConverterResult.Time(
microsecond = BigDecimal("90"),
nanosecond = BigDecimal("5"),
),
"90.005".formatTime(),
)
// NANOSECONDS
basicValue = BigDecimal("1000000000")
assertEquals(
ConverterResult.Time(
nanosecond = BigDecimal("90"),
attosecond = BigDecimal("5000000"),
),
"90.005".formatTime(),
)
// ATTOSECONDS
basicValue = BigDecimal("1")
assertEquals(
ConverterResult.Time(
attosecond = BigDecimal("0.5"),
),
"0.5".formatTime(),
)
assertEquals(
ConverterResult.Time(
attosecond = BigDecimal("90.005"),
),
"90.005".formatTime(),
)
}
}

View File

@ -30,9 +30,15 @@ interface CurrencyRatesDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertRates(currencyRates: List<CurrencyRatesEntity>) suspend fun insertRates(currencyRates: List<CurrencyRatesEntity>)
@Query("SELECT DISTINCT timestamp FROM currency_rates WHERE base_unit_id = :baseId")
suspend fun getLatestRateTimeStamp(baseId: String): Long?
@Query("SELECT DISTINCT * FROM currency_rates WHERE timestamp = (SELECT MAX(timestamp) FROM currency_rates) AND base_unit_id = :baseId") @Query("SELECT DISTINCT * FROM currency_rates WHERE timestamp = (SELECT MAX(timestamp) FROM currency_rates) AND base_unit_id = :baseId")
suspend fun getLatestRates(baseId: String): List<CurrencyRatesEntity> suspend fun getLatestRates(baseId: String): List<CurrencyRatesEntity>
@Query("SELECT DISTINCT * FROM currency_rates WHERE timestamp = (SELECT MAX(timestamp) FROM currency_rates) AND base_unit_id = :baseId AND pair_unit_id = :pairId")
suspend fun getLatestRate(baseId: String, pairId: String): CurrencyRatesEntity?
@Query("SELECT COUNT(*) from currency_rates") @Query("SELECT COUNT(*) from currency_rates")
fun size(): Flow<Int> fun size(): Flow<Int>

View File

@ -30,7 +30,7 @@ import androidx.room.PrimaryKey
* @param frequency Show the amount of time this unit was used * @param frequency Show the amount of time this unit was used
*/ */
@Entity(tableName = "units") @Entity(tableName = "units")
class UnitsEntity( data class UnitsEntity(
@PrimaryKey val unitId: String, @PrimaryKey val unitId: String,
@ColumnInfo(name = "is_favorite") val isFavorite: Boolean = false, @ColumnInfo(name = "is_favorite") val isFavorite: Boolean = false,
@ColumnInfo(name = "paired_unit_id") val pairedUnitId: String? = null, @ColumnInfo(name = "paired_unit_id") val pairedUnitId: String? = null,

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sadellie.unitto.data.model package com.sadellie.unitto.data.model.converter
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R

View File

@ -16,6 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sadellie.unitto.data.model package com.sadellie.unitto.data.model.converter
enum class UnitsListSorting { USAGE, ALPHABETICAL, SCALE_DESC, SCALE_ASC } enum class UnitsListSorting { USAGE, ALPHABETICAL, SCALE_DESC, SCALE_ASC }

View File

@ -0,0 +1,47 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.model.converter.unit
import androidx.annotation.StringRes
import com.sadellie.unitto.data.model.converter.UnitGroup
import java.math.BigDecimal
sealed interface BasicUnit {
val id: String
val group: UnitGroup
@get:StringRes
val displayName: Int
@get:StringRes val shortName: Int
val factor: BigDecimal
interface NumberBase : BasicUnit {
fun convert(unitTo: NumberBase, value: String): String
}
interface Default : BasicUnit {
val backward: Boolean
fun convert(unitTo: Default, value: BigDecimal): BigDecimal
}
}

View File

@ -16,64 +16,56 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sadellie.unitto.data.model.unit package com.sadellie.unitto.data.model.converter.unit
import com.sadellie.unitto.core.base.MAX_PRECISION import com.sadellie.unitto.core.base.MAX_PRECISION
import com.sadellie.unitto.data.common.isEqualTo import com.sadellie.unitto.data.common.isEqualTo
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
data class BackwardUnit( data class NormalUnit(
override val id: String, override val id: String,
override val basicUnit: BigDecimal, override val factor: BigDecimal,
override val group: UnitGroup, override val group: UnitGroup,
override val displayName: Int, override val displayName: Int,
override val shortName: Int, override val shortName: Int,
override val isFavorite: Boolean = false, override val backward: Boolean = false,
override val pairId: String? = null, ) : BasicUnit.Default {
override val counter: Int = 0, override fun convert(unitTo: BasicUnit.Default, value: BigDecimal): BigDecimal {
) : DefaultUnit { if (value.isEqualTo(BigDecimal.ZERO)) return BigDecimal.ZERO
override fun convert(unitTo: DefaultUnit, value: BigDecimal): BigDecimal { return when {
// Avoid division by zero // BACKWARD -> BACKWARD
if (unitTo.basicUnit.isEqualTo(BigDecimal.ZERO)) return BigDecimal.ZERO backward and unitTo.backward ->
return when (unitTo) {
is NormalUnit ->
this
.basicUnit
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
.div(unitTo.basicUnit)
.div(value)
is BackwardUnit ->
unitTo unitTo
.basicUnit .factor
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN) .setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
.multiply(value) .multiply(value)
.div(this.basicUnit) .div(this.factor)
else -> BigDecimal.ZERO // BACKWARD -> FORWARD
backward and !unitTo.backward ->
this
.factor
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
.div(unitTo.factor)
.div(value)
// FORWARD -> BACKWARD
!backward and unitTo.backward ->
unitTo
.factor
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
.div(this.factor)
.div(value)
// FORWARD -> FORWARD
else ->
this
.factor
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
.multiply(value)
.div(unitTo.factor)
} }
} }
override fun clone(
id: String,
basicUnit: BigDecimal,
group: UnitGroup,
displayName: Int,
shortName: Int,
isFavorite: Boolean,
pairId: String?,
counter: Int,
): BackwardUnit = copy(
id = id,
basicUnit = basicUnit,
group = group,
displayName = displayName,
shortName = shortName,
isFavorite = isFavorite,
pairId = pairId,
counter = counter,
)
} }

View File

@ -0,0 +1,34 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.model.converter.unit
import com.sadellie.unitto.data.model.converter.UnitGroup
import java.math.BigDecimal
data class NumberBaseUnit(
override val id: String,
override val factor: BigDecimal,
override val group: UnitGroup,
override val displayName: Int,
override val shortName: Int,
) : BasicUnit.NumberBase {
override fun convert(unitTo: BasicUnit.NumberBase, value: String): String = value
.toBigInteger(factor.intValueExact())
.toString(unitTo.factor.intValueExact())
}

View File

@ -1,50 +0,0 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.model.repository
import com.sadellie.unitto.data.model.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting
import com.sadellie.unitto.data.model.unit.AbstractUnit
import kotlinx.coroutines.flow.Flow
import java.time.LocalDate
interface UnitsRepository {
val units: Flow<List<AbstractUnit>>
suspend fun getById(id: String): AbstractUnit
suspend fun getCollection(group: UnitGroup): List<AbstractUnit>
suspend fun favorite(unit: AbstractUnit)
suspend fun incrementCounter(unit: AbstractUnit)
suspend fun setPair(unit: AbstractUnit, pair: AbstractUnit)
suspend fun updateRates(unit: AbstractUnit): LocalDate?
suspend fun filterUnits(
query: String,
unitGroup: UnitGroup?,
favoritesOnly: Boolean,
hideBrokenUnits: Boolean,
sorting: UnitsListSorting,
shownUnitGroups: List<UnitGroup> = emptyList(),
): Map<UnitGroup, List<AbstractUnit>>
}

View File

@ -18,9 +18,8 @@
package com.sadellie.unitto.data.model.repository package com.sadellie.unitto.data.model.repository
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.converter.UnitsListSorting
import com.sadellie.unitto.data.model.unit.AbstractUnit
import com.sadellie.unitto.data.model.userprefs.AboutPreferences import com.sadellie.unitto.data.model.userprefs.AboutPreferences
import com.sadellie.unitto.data.model.userprefs.AddSubtractPreferences import com.sadellie.unitto.data.model.userprefs.AddSubtractPreferences
import com.sadellie.unitto.data.model.userprefs.AppPreferences import com.sadellie.unitto.data.model.userprefs.AppPreferences
@ -55,7 +54,7 @@ interface UserPreferencesRepository {
suspend fun updateOutputFormat(outputFormat: Int) suspend fun updateOutputFormat(outputFormat: Int)
suspend fun updateLatestPairOfUnits(unitFrom: AbstractUnit, unitTo: AbstractUnit) suspend fun updateLatestPairOfUnits(unitFrom: String, unitTo: String)
suspend fun updateThemingMode(themingMode: ThemingMode) suspend fun updateThemingMode(themingMode: ThemingMode)

View File

@ -1,79 +0,0 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.model.unit
import com.sadellie.unitto.core.base.MAX_PRECISION
import com.sadellie.unitto.data.common.isEqualTo
import com.sadellie.unitto.data.model.UnitGroup
import java.math.BigDecimal
import java.math.RoundingMode
data class NormalUnit(
override val id: String,
override val basicUnit: BigDecimal,
override val group: UnitGroup,
override val displayName: Int,
override val shortName: Int,
override val isFavorite: Boolean = false,
override val pairId: String? = null,
override val counter: Int = 0,
) : DefaultUnit {
override fun convert(unitTo: DefaultUnit, value: BigDecimal): BigDecimal {
// Avoid division by zero
if (unitTo.basicUnit.isEqualTo(BigDecimal.ZERO)) return BigDecimal.ZERO
return when (unitTo) {
is NormalUnit ->
this
.basicUnit
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
.multiply(value)
.div(unitTo.basicUnit)
is BackwardUnit ->
unitTo
.basicUnit
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
.div(this.basicUnit)
.div(value)
else -> BigDecimal.ZERO
}
}
override fun clone(
id: String,
basicUnit: BigDecimal,
group: UnitGroup,
displayName: Int,
shortName: Int,
isFavorite: Boolean,
pairId: String?,
counter: Int,
): NormalUnit = copy(
id = id,
basicUnit = basicUnit,
group = group,
displayName = displayName,
shortName = shortName,
isFavorite = isFavorite,
pairId = pairId,
counter = counter,
)
}

View File

@ -1,57 +0,0 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.model.unit
import com.sadellie.unitto.data.model.UnitGroup
import java.math.BigDecimal
data class NumberBaseUnit(
override val id: String,
override val basicUnit: BigDecimal,
override val group: UnitGroup,
override val displayName: Int,
override val shortName: Int,
override val isFavorite: Boolean = false,
override val pairId: String? = null,
override val counter: Int = 0,
) : AbstractUnit {
fun convert(toBase: NumberBaseUnit, input: String): String {
return input.toBigInteger(basicUnit.intValueExact()).toString(toBase.basicUnit.intValueExact())
}
override fun clone(
id: String,
basicUnit: BigDecimal,
group: UnitGroup,
displayName: Int,
shortName: Int,
isFavorite: Boolean,
pairId: String?,
counter: Int,
): NumberBaseUnit = copy(
id = id,
basicUnit = basicUnit,
group = group,
displayName = displayName,
shortName = shortName,
isFavorite = isFavorite,
pairId = pairId,
counter = counter,
)
}

View File

@ -1,63 +0,0 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.model.unit
import com.sadellie.unitto.core.base.MAX_PRECISION
import com.sadellie.unitto.data.model.UnitGroup
import java.math.BigDecimal
import java.math.RoundingMode
data class ReverseUnit(
override val id: String,
override val basicUnit: BigDecimal,
override val group: UnitGroup,
override val displayName: Int,
override val shortName: Int,
override val isFavorite: Boolean = false,
override val pairId: String? = null,
override val counter: Int = 0,
) : DefaultUnit {
override fun convert(unitTo: DefaultUnit, value: BigDecimal): BigDecimal {
return unitTo
.basicUnit
.setScale(MAX_PRECISION, RoundingMode.HALF_EVEN)
.div(this.basicUnit)
.multiply(value)
}
override fun clone(
id: String,
basicUnit: BigDecimal,
group: UnitGroup,
displayName: Int,
shortName: Int,
isFavorite: Boolean,
pairId: String?,
counter: Int,
): ReverseUnit = copy(
id = id,
basicUnit = basicUnit,
group = group,
displayName = displayName,
shortName = shortName,
isFavorite = isFavorite,
pairId = pairId,
counter = counter,
)
}

View File

@ -1,56 +0,0 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.data.model.unit
import com.sadellie.unitto.data.model.UnitGroup
import java.math.BigDecimal
data class TemperatureUnit(
override val id: String,
override val basicUnit: BigDecimal,
override val group: UnitGroup,
override val displayName: Int,
override val shortName: Int,
override val isFavorite: Boolean = false,
override val pairId: String? = null,
override val counter: Int = 0,
val customConvert: (unitTo: DefaultUnit, value: BigDecimal) -> BigDecimal,
) : DefaultUnit {
override fun convert(unitTo: DefaultUnit, value: BigDecimal): BigDecimal = customConvert(unitTo, value)
override fun clone(
id: String,
basicUnit: BigDecimal,
group: UnitGroup,
displayName: Int,
shortName: Int,
isFavorite: Boolean,
pairId: String?,
counter: Int,
): TemperatureUnit = copy(
id = id,
basicUnit = basicUnit,
group = group,
displayName = displayName,
shortName = shortName,
isFavorite = isFavorite,
pairId = pairId,
counter = counter,
)
}

View File

@ -19,8 +19,8 @@
package com.sadellie.unitto.data.model.userprefs package com.sadellie.unitto.data.model.userprefs
import com.sadellie.unitto.core.base.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.converter.UnitsListSorting
interface ConverterPreferences { interface ConverterPreferences {
val formatterSymbols: FormatterSymbols val formatterSymbols: FormatterSymbols

View File

@ -18,7 +18,7 @@
package com.sadellie.unitto.data.model.userprefs package com.sadellie.unitto.data.model.userprefs
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
interface UnitGroupsPreferences { interface UnitGroupsPreferences {
val shownUnitGroups: List<UnitGroup> val shownUnitGroups: List<UnitGroup>

View File

@ -24,8 +24,8 @@ import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.converter.UnitsListSorting
import io.github.sadellie.themmo.core.MonetMode import io.github.sadellie.themmo.core.MonetMode
import io.github.sadellie.themmo.core.ThemingMode import io.github.sadellie.themmo.core.ThemingMode

View File

@ -19,8 +19,8 @@
package com.sadellie.unitto.data.userprefs package com.sadellie.unitto.data.userprefs
import com.sadellie.unitto.core.base.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.converter.UnitsListSorting
import com.sadellie.unitto.data.model.userprefs.AboutPreferences import com.sadellie.unitto.data.model.userprefs.AboutPreferences
import com.sadellie.unitto.data.model.userprefs.AddSubtractPreferences import com.sadellie.unitto.data.model.userprefs.AddSubtractPreferences
import com.sadellie.unitto.data.model.userprefs.AppPreferences import com.sadellie.unitto.data.model.userprefs.AppPreferences

View File

@ -22,10 +22,9 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences import androidx.datastore.preferences.core.emptyPreferences
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.converter.UnitsListSorting
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import com.sadellie.unitto.data.model.unit.AbstractUnit
import com.sadellie.unitto.data.model.userprefs.AboutPreferences import com.sadellie.unitto.data.model.userprefs.AboutPreferences
import com.sadellie.unitto.data.model.userprefs.AddSubtractPreferences import com.sadellie.unitto.data.model.userprefs.AddSubtractPreferences
import com.sadellie.unitto.data.model.userprefs.AppPreferences import com.sadellie.unitto.data.model.userprefs.AppPreferences
@ -182,10 +181,10 @@ class UserPreferencesRepositoryImpl @Inject constructor(
} }
} }
override suspend fun updateLatestPairOfUnits(unitFrom: AbstractUnit, unitTo: AbstractUnit) { override suspend fun updateLatestPairOfUnits(unitFrom: String, unitTo: String) {
dataStore.edit { preferences -> dataStore.edit { preferences ->
preferences[PrefsKeys.LATEST_LEFT_SIDE] = unitFrom.id preferences[PrefsKeys.LATEST_LEFT_SIDE] = unitFrom
preferences[PrefsKeys.LATEST_RIGHT_SIDE] = unitTo.id preferences[PrefsKeys.LATEST_RIGHT_SIDE] = unitTo
} }
} }

View File

@ -55,6 +55,7 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -83,24 +84,30 @@ import com.sadellie.unitto.core.ui.common.ScaffoldWithTopBar
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField
import com.sadellie.unitto.core.ui.common.textfield.NumberBaseTextField import com.sadellie.unitto.core.ui.common.textfield.NumberBaseTextField
import com.sadellie.unitto.core.ui.common.textfield.SimpleTextField import com.sadellie.unitto.core.ui.common.textfield.SimpleTextField
import com.sadellie.unitto.core.ui.common.textfield.addBracket
import com.sadellie.unitto.core.ui.common.textfield.addTokens
import com.sadellie.unitto.core.ui.common.textfield.deleteTokens
import com.sadellie.unitto.core.ui.datetime.formatDateWeekDayMonthYear import com.sadellie.unitto.core.ui.datetime.formatDateWeekDayMonthYear
import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.format
import com.sadellie.unitto.data.common.isEqualTo
import com.sadellie.unitto.data.common.isExpression
import com.sadellie.unitto.data.converter.ConverterResult
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit
import com.sadellie.unitto.feature.converter.components.DefaultKeyboard import com.sadellie.unitto.feature.converter.components.DefaultKeyboard
import com.sadellie.unitto.feature.converter.components.NumberBaseKeyboard import com.sadellie.unitto.feature.converter.components.NumberBaseKeyboard
import com.sadellie.unitto.feature.converter.components.UnitSelectionButton import com.sadellie.unitto.feature.converter.components.UnitSelectionButton
import java.math.BigDecimal
import java.util.Locale import java.util.Locale
@Composable @Composable
internal fun ConverterRoute( internal fun ConverterRoute(
viewModel: ConverterViewModel = hiltViewModel(), viewModel: ConverterViewModel = hiltViewModel(),
navigateToLeftScreen: (uiState: UnitConverterUIState) -> Unit, navigateToLeftScreen: (unitFromId: String, group: UnitGroup) -> Unit,
navigateToRightScreen: (uiState: UnitConverterUIState) -> Unit, navigateToRightScreen: (unitFromId: String, unitToId: String, group: UnitGroup, input: String) -> Unit,
openDrawer: () -> Unit, openDrawer: () -> Unit,
) { ) {
val uiState = viewModel.converterUiState.collectAsStateWithLifecycle() val uiState = viewModel.converterUIState.collectAsStateWithLifecycle()
ConverterScreen( ConverterScreen(
uiState = uiState.value, uiState = uiState.value,
@ -108,30 +115,24 @@ internal fun ConverterRoute(
navigateToRightScreen = navigateToRightScreen, navigateToRightScreen = navigateToRightScreen,
openDrawer = openDrawer, openDrawer = openDrawer,
swapUnits = viewModel::swapUnits, swapUnits = viewModel::swapUnits,
processInput = viewModel::addTokens, updateInput1 = viewModel::updateInput1,
deleteDigit = viewModel::deleteTokens, updateInput2 = viewModel::updateInput2,
clearInput = viewModel::clearInput, convertDefault = viewModel::convertDefault,
onValueChange = viewModel::updateInput, convertNumberBase = viewModel::convertNumberBase,
onFocusOnInput2 = viewModel::updateFocused,
onErrorClick = viewModel::updateCurrencyRates,
addBracket = viewModel::addBracket,
) )
} }
@Composable @Composable
private fun ConverterScreen( private fun ConverterScreen(
uiState: UnitConverterUIState, uiState: UnitConverterUIState,
navigateToLeftScreen: (uiState: UnitConverterUIState) -> Unit, navigateToLeftScreen: (unitFromId: String, group: UnitGroup) -> Unit,
navigateToRightScreen: (uiState: UnitConverterUIState) -> Unit, navigateToRightScreen: (unitFromId: String, unitToId: String, group: UnitGroup, input: String) -> Unit,
openDrawer: () -> Unit, openDrawer: () -> Unit,
swapUnits: () -> Unit, swapUnits: (String, String) -> Unit,
processInput: (String) -> Unit, updateInput1: (TextFieldValue) -> Unit,
deleteDigit: () -> Unit, updateInput2: (TextFieldValue) -> Unit,
clearInput: () -> Unit, convertDefault: () -> Unit,
onValueChange: (TextFieldValue) -> Unit, convertNumberBase: () -> Unit,
onFocusOnInput2: (Boolean) -> Unit,
onErrorClick: (AbstractUnit) -> Unit,
addBracket: () -> Unit,
) { ) {
when (uiState) { when (uiState) {
UnitConverterUIState.Loading -> EmptyScreen() UnitConverterUIState.Loading -> EmptyScreen()
@ -143,13 +144,11 @@ private fun ConverterScreen(
NumberBase( NumberBase(
modifier = Modifier.padding(it), modifier = Modifier.padding(it),
uiState = uiState, uiState = uiState,
onValueChange = onValueChange, updateInput1 = updateInput1,
processInput = processInput,
deleteDigit = deleteDigit,
navigateToLeftScreen = navigateToLeftScreen, navigateToLeftScreen = navigateToLeftScreen,
swapUnits = swapUnits, swapUnits = swapUnits,
navigateToRightScreen = navigateToRightScreen, navigateToRightScreen = navigateToRightScreen,
clearInput = clearInput, convert = convertNumberBase,
) )
} }
} }
@ -161,16 +160,12 @@ private fun ConverterScreen(
Default( Default(
modifier = Modifier.padding(it), modifier = Modifier.padding(it),
uiState = uiState, uiState = uiState,
onValueChange = onValueChange, updateInput1 = updateInput1,
onFocusOnInput2 = onFocusOnInput2, updateInput2 = updateInput2,
processInput = processInput,
deleteDigit = deleteDigit,
navigateToLeftScreen = navigateToLeftScreen, navigateToLeftScreen = navigateToLeftScreen,
swapUnits = swapUnits, swapUnits = swapUnits,
navigateToRightScreen = navigateToRightScreen, navigateToRightScreen = navigateToRightScreen,
clearInput = clearInput, convert = convertDefault,
refreshCurrencyRates = onErrorClick,
addBracket = addBracket,
) )
} }
} }
@ -194,14 +189,20 @@ private fun UnitConverterTopBar(
private fun NumberBase( private fun NumberBase(
modifier: Modifier, modifier: Modifier,
uiState: UnitConverterUIState.NumberBase, uiState: UnitConverterUIState.NumberBase,
onValueChange: (TextFieldValue) -> Unit, updateInput1: (TextFieldValue) -> Unit,
processInput: (String) -> Unit, navigateToLeftScreen: (unitFromId: String, group: UnitGroup) -> Unit,
deleteDigit: () -> Unit, swapUnits: (String, String) -> Unit,
navigateToLeftScreen: (uiState: UnitConverterUIState) -> Unit, navigateToRightScreen: (unitFromId: String, unitToId: String, group: UnitGroup, input: String) -> Unit,
swapUnits: () -> Unit, convert: () -> Unit,
navigateToRightScreen: (uiState: UnitConverterUIState) -> Unit,
clearInput: () -> Unit,
) { ) {
LaunchedEffect(
uiState.input.text,
uiState.unitFrom.id,
uiState.unitTo.id,
) {
convert()
}
PortraitLandscape( PortraitLandscape(
modifier = modifier.fillMaxSize(), modifier = modifier.fillMaxSize(),
content1 = { contentModifier -> content1 = { contentModifier ->
@ -213,7 +214,7 @@ private fun NumberBase(
minRatio = 0.7f, minRatio = 0.7f,
placeholder = Token.Digit._0, placeholder = Token.Digit._0,
value = uiState.input, value = uiState.input,
onValueChange = onValueChange, onValueChange = updateInput1,
) )
AnimatedUnitShortName(stringResource(uiState.unitFrom.shortName)) AnimatedUnitShortName(stringResource(uiState.unitFrom.shortName))
@ -228,18 +229,27 @@ private fun NumberBase(
UnitSelectionButtons( UnitSelectionButtons(
unitFromLabel = stringResource(uiState.unitFrom.displayName), unitFromLabel = stringResource(uiState.unitFrom.displayName),
unitToLabel = stringResource(uiState.unitTo.displayName), unitToLabel = stringResource(uiState.unitTo.displayName),
swapUnits = swapUnits, swapUnits = { swapUnits(uiState.unitTo.id, uiState.unitFrom.id) },
navigateToLeftScreen = { navigateToLeftScreen(uiState) }, navigateToLeftScreen = {
navigateToRightScreen = { navigateToRightScreen(uiState) }, navigateToLeftScreen(uiState.unitFrom.id, uiState.unitFrom.group)
},
navigateToRightScreen = {
navigateToRightScreen(
uiState.unitFrom.id,
uiState.unitTo.id,
uiState.unitFrom.group,
uiState.input.text,
)
},
) )
} }
}, },
content2 = { content2 = { modifier2 ->
NumberBaseKeyboard( NumberBaseKeyboard(
modifier = it, modifier = modifier2,
addDigit = processInput, addDigit = { updateInput1(uiState.input.addTokens(it)) },
deleteDigit = deleteDigit, deleteDigit = { updateInput1(uiState.input.deleteTokens()) },
clearInput = clearInput, clearInput = { updateInput1(TextFieldValue()) },
) )
}, },
) )
@ -249,22 +259,21 @@ private fun NumberBase(
private fun Default( private fun Default(
modifier: Modifier, modifier: Modifier,
uiState: UnitConverterUIState.Default, uiState: UnitConverterUIState.Default,
onValueChange: (TextFieldValue) -> Unit, updateInput1: (TextFieldValue) -> Unit,
onFocusOnInput2: (Boolean) -> Unit, updateInput2: (TextFieldValue) -> Unit,
processInput: (String) -> Unit, navigateToLeftScreen: (unitFromId: String, group: UnitGroup) -> Unit,
deleteDigit: () -> Unit, swapUnits: (String, String) -> Unit,
navigateToLeftScreen: (uiState: UnitConverterUIState) -> Unit, navigateToRightScreen: (unitFromId: String, unitToId: String, group: UnitGroup, input: String) -> Unit,
swapUnits: () -> Unit, convert: () -> Unit,
navigateToRightScreen: (uiState: UnitConverterUIState) -> Unit,
clearInput: () -> Unit,
refreshCurrencyRates: (AbstractUnit) -> Unit,
addBracket: () -> Unit,
) { ) {
val locale: Locale = LocalLocale.current val locale: Locale = LocalLocale.current
var calculation by remember(uiState.calculation) { val showCalculation = remember(uiState.input1.text, uiState.result) {
mutableStateOf( if (uiState.input1.text.isExpression()) {
TextFieldValue(uiState.calculation?.format(uiState.scale, uiState.outputFormat) ?: ""), if (uiState.result is ConverterResult.Default) {
) return@remember !uiState.result.calculation.isEqualTo(BigDecimal.ZERO)
}
}
false
} }
val connection by connectivityState() val connection by connectivityState()
val lastUpdate by remember(uiState.currencyRateUpdateState) { val lastUpdate by remember(uiState.currencyRateUpdateState) {
@ -273,14 +282,25 @@ private fun Default(
uiState.currencyRateUpdateState.date.formatDateWeekDayMonthYear(locale) uiState.currencyRateUpdateState.date.formatDateWeekDayMonthYear(locale)
} }
} }
var focusedOnInput1 by rememberSaveable { mutableStateOf(true) }
LaunchedEffect(connection) { LaunchedEffect(connection) {
if ((connection == ConnectionState.Available) and (uiState.result == ConverterResult.Error)) { if ((connection == ConnectionState.Available) and (uiState.result is ConverterResult.Error)) {
val unitFrom = uiState.unitFrom val unitFrom = uiState.unitFrom
if (unitFrom.group == UnitGroup.CURRENCY) refreshCurrencyRates(unitFrom) if (unitFrom.group == UnitGroup.CURRENCY) convert()
} }
} }
LaunchedEffect(
uiState.input1.text,
uiState.input2.text,
uiState.unitFrom.id,
uiState.unitTo.id,
uiState.formatTime,
) {
convert()
}
PortraitLandscape( PortraitLandscape(
modifier = modifier.fillMaxSize(), modifier = modifier.fillMaxSize(),
content1 = { contentModifier -> content1 = { contentModifier ->
@ -322,7 +342,7 @@ private fun Default(
.weight(1f), .weight(1f),
value = uiState.input1, value = uiState.input1,
minRatio = 0.7f, minRatio = 0.7f,
onValueChange = onValueChange, onValueChange = updateInput1,
formatterSymbols = uiState.formatterSymbols, formatterSymbols = uiState.formatterSymbols,
placeholder = Token.Digit._0, placeholder = Token.Digit._0,
) )
@ -340,10 +360,10 @@ private fun Default(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.weight(1f) .weight(1f)
.onFocusEvent { state -> onFocusOnInput2(state.hasFocus) }, .onFocusEvent { state -> focusedOnInput1 = !state.hasFocus },
value = uiState.input2, value = uiState.input2,
minRatio = 0.7f, minRatio = 0.7f,
onValueChange = onValueChange, onValueChange = updateInput2,
formatterSymbols = uiState.formatterSymbols, formatterSymbols = uiState.formatterSymbols,
placeholder = Token.Digit._0, placeholder = Token.Digit._0,
) )
@ -355,21 +375,30 @@ private fun Default(
modifier = textFieldModifier, modifier = textFieldModifier,
value = uiState.input1, value = uiState.input1,
minRatio = 0.7f, minRatio = 0.7f,
onValueChange = onValueChange, onValueChange = updateInput1,
formatterSymbols = uiState.formatterSymbols, formatterSymbols = uiState.formatterSymbols,
placeholder = Token.Digit._0, placeholder = Token.Digit._0,
) )
AnimatedVisibility( AnimatedVisibility(
visible = calculation.text.isNotEmpty(), visible = showCalculation,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
enter = expandVertically(clip = false), enter = expandVertically(clip = false),
exit = shrinkVertically(clip = false), exit = shrinkVertically(clip = false),
) { ) {
var calculationTextField by remember(uiState.result) {
val text = if (uiState.result is ConverterResult.Default) {
uiState.result.calculation
.format(uiState.scale, uiState.outputFormat)
} else {
""
}
mutableStateOf(TextFieldValue(text))
}
ExpressionTextField( ExpressionTextField(
modifier = Modifier, modifier = Modifier,
value = calculation, value = calculationTextField,
minRatio = 0.7f, minRatio = 0.7f,
onValueChange = { calculation = it }, onValueChange = { calculationTextField = it },
textColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f), textColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
formatterSymbols = uiState.formatterSymbols, formatterSymbols = uiState.formatterSymbols,
readOnly = true, readOnly = true,
@ -384,14 +413,14 @@ private fun Default(
scale = uiState.scale, scale = uiState.scale,
outputFormat = uiState.outputFormat, outputFormat = uiState.outputFormat,
formatterSymbols = uiState.formatterSymbols, formatterSymbols = uiState.formatterSymbols,
onErrorClick = { refreshCurrencyRates(uiState.unitFrom) }, onErrorClick = convert,
) )
AnimatedUnitShortName( AnimatedUnitShortName(
stringResource( stringResource(
if (uiState.result is ConverterResult.Error) { when (uiState.result) {
R.string.click_to_try_again_label // Currency conversion can be retried
} else { is ConverterResult.Error.Currency -> R.string.click_to_try_again_label
uiState.unitTo.shortName else -> uiState.unitTo.shortName
}, },
), ),
) )
@ -401,22 +430,58 @@ private fun Default(
UnitSelectionButtons( UnitSelectionButtons(
unitFromLabel = stringResource(uiState.unitFrom.displayName), unitFromLabel = stringResource(uiState.unitFrom.displayName),
unitToLabel = stringResource(uiState.unitTo.displayName), unitToLabel = stringResource(uiState.unitTo.displayName),
swapUnits = swapUnits, swapUnits = { swapUnits(uiState.unitTo.id, uiState.unitFrom.id) },
navigateToLeftScreen = { navigateToLeftScreen(uiState) }, navigateToLeftScreen = {
navigateToRightScreen = { navigateToRightScreen(uiState) }, navigateToLeftScreen(uiState.unitFrom.id, uiState.unitFrom.group)
},
navigateToRightScreen = {
val input = if (uiState.result is ConverterResult.Default) {
uiState.result.calculation.toPlainString()
} else {
uiState.input1.text
}
navigateToRightScreen(
uiState.unitFrom.id,
uiState.unitTo.id,
uiState.unitFrom.group,
input,
)
},
) )
} }
}, },
content2 = { content2 = { modifier2 ->
DefaultKeyboard( DefaultKeyboard(
modifier = it, modifier = modifier2,
addDigit = processInput, addDigit = {
deleteDigit = deleteDigit, if (focusedOnInput1) {
clearInput = clearInput, updateInput1(uiState.input1.addTokens(it))
} else {
updateInput2(uiState.input2.addTokens(it))
}
},
deleteDigit = {
if (focusedOnInput1) {
updateInput1(uiState.input1.deleteTokens())
} else {
updateInput2(uiState.input2.deleteTokens())
}
},
clearInput = {
updateInput1(TextFieldValue())
updateInput2(TextFieldValue())
},
fractional = uiState.formatterSymbols.fractional, fractional = uiState.formatterSymbols.fractional,
middleZero = uiState.middleZero, middleZero = uiState.middleZero,
acButton = uiState.acButton, acButton = uiState.acButton,
addBracket = addBracket, addBracket = {
if (focusedOnInput1) {
updateInput1(uiState.input1.addBracket())
} else {
updateInput2(uiState.input2.addBracket())
}
},
) )
}, },
) )
@ -454,6 +519,17 @@ private fun ConverterResultTextField(
) )
} }
is ConverterResult.Error.DivideByZero -> {
SimpleTextField(
modifier = modifier,
value = TextFieldValue(stringResource(R.string.calculator_divide_by_zero_error)),
onValueChange = { onErrorClick() },
minRatio = 0.7f,
readOnly = true,
textColor = MaterialTheme.colorScheme.error,
)
}
is ConverterResult.Error -> { is ConverterResult.Error -> {
SimpleTextField( SimpleTextField(
modifier = modifier, modifier = modifier,
@ -578,16 +654,13 @@ private fun UnitSelectionButtons(
private fun PreviewConverterScreen() { private fun PreviewConverterScreen() {
ConverterScreen( ConverterScreen(
uiState = UnitConverterUIState.Loading, uiState = UnitConverterUIState.Loading,
navigateToLeftScreen = {}, navigateToLeftScreen = { _, _ -> },
navigateToRightScreen = {}, navigateToRightScreen = { _, _, _, _ -> },
openDrawer = {}, openDrawer = {},
swapUnits = {}, swapUnits = { _, _ -> },
processInput = {}, updateInput1 = {},
deleteDigit = {}, updateInput2 = {},
clearInput = {}, convertDefault = {},
onValueChange = {}, convertNumberBase = {},
onFocusOnInput2 = {},
onErrorClick = {},
addBracket = {},
) )
} }

View File

@ -25,14 +25,10 @@ import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.common.textfield.formatExpression
import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.format
import com.sadellie.unitto.data.common.isEqualTo
import com.sadellie.unitto.data.common.isGreaterThan import com.sadellie.unitto.data.common.isGreaterThan
import com.sadellie.unitto.data.common.isLessThan import com.sadellie.unitto.data.converter.ConverterResult
import com.sadellie.unitto.data.common.trimZeros import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.unit.DefaultUnit
import com.sadellie.unitto.data.model.unit.NumberBaseUnit
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode
internal sealed class UnitConverterUIState { internal sealed class UnitConverterUIState {
data object Loading : UnitConverterUIState() data object Loading : UnitConverterUIState()
@ -40,10 +36,9 @@ internal sealed class UnitConverterUIState {
data class Default( data class Default(
val input1: TextFieldValue, val input1: TextFieldValue,
val input2: TextFieldValue, val input2: TextFieldValue,
val calculation: BigDecimal?,
val result: ConverterResult, val result: ConverterResult,
val unitFrom: DefaultUnit, val unitFrom: BasicUnit.Default,
val unitTo: DefaultUnit, val unitTo: BasicUnit.Default,
val middleZero: Boolean, val middleZero: Boolean,
val formatterSymbols: FormatterSymbols, val formatterSymbols: FormatterSymbols,
val scale: Int, val scale: Int,
@ -56,45 +51,11 @@ internal sealed class UnitConverterUIState {
data class NumberBase( data class NumberBase(
val input: TextFieldValue, val input: TextFieldValue,
val result: ConverterResult, val result: ConverterResult,
val unitFrom: NumberBaseUnit, val unitFrom: BasicUnit.NumberBase,
val unitTo: NumberBaseUnit, val unitTo: BasicUnit.NumberBase,
) : UnitConverterUIState() ) : UnitConverterUIState()
} }
internal sealed class ConverterResult {
data class Default(val value: BigDecimal) : ConverterResult() {
override fun equals(other: Any?): Boolean {
if (other !is Default) return false
return this.value.isEqualTo(other.value)
}
override fun hashCode(): Int = value.hashCode()
}
data class NumberBase(val value: String) : ConverterResult()
data class Time(
val negative: Boolean,
val day: BigDecimal,
val hour: BigDecimal,
val minute: BigDecimal,
val second: BigDecimal,
val millisecond: BigDecimal,
val microsecond: BigDecimal,
val nanosecond: BigDecimal,
val attosecond: BigDecimal,
) : ConverterResult()
data class FootInch(
val foot: BigDecimal,
val inch: BigDecimal,
) : ConverterResult()
data object Loading : ConverterResult()
data object Error : ConverterResult()
}
internal fun ConverterResult.Time.format( internal fun ConverterResult.Time.format(
mContext: Context, mContext: Context,
formatterSymbols: FormatterSymbols, formatterSymbols: FormatterSymbols,
@ -154,110 +115,3 @@ internal fun ConverterResult.FootInch.format(
return result return result
} }
internal fun formatTime(
input: BigDecimal,
): ConverterResult.Time {
val negative = input < BigDecimal.ZERO
val inputAbs = input.abs()
if (inputAbs.isLessThan(attosecondBasicUnit)) {
return ConverterResult.Time(
negative = negative,
day = BigDecimal.ZERO,
hour = BigDecimal.ZERO,
minute = BigDecimal.ZERO,
second = BigDecimal.ZERO,
millisecond = BigDecimal.ZERO,
microsecond = BigDecimal.ZERO,
nanosecond = BigDecimal.ZERO,
attosecond = inputAbs,
)
}
if (inputAbs.isLessThan(nanosecondBasicUnit)) {
return ConverterResult.Time(
negative = negative,
day = BigDecimal.ZERO,
hour = BigDecimal.ZERO,
minute = BigDecimal.ZERO,
second = BigDecimal.ZERO,
millisecond = BigDecimal.ZERO,
microsecond = BigDecimal.ZERO,
nanosecond = BigDecimal.ZERO,
attosecond = inputAbs.trimZeros(),
)
}
// DAY
var division = inputAbs.divideAndRemainder(dayBasicUnit)
val day = division.component1().setScale(0, RoundingMode.HALF_EVEN)
var remainingSeconds = division.component2().setScale(0, RoundingMode.HALF_EVEN)
division = remainingSeconds.divideAndRemainder(hourBasicUnit)
val hour = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(minuteBasicUnit)
val minute = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(secondBasicUnit)
val second = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(millisecondBasicUnit)
val millisecond = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(microsecondBasicUnit)
val microsecond = division.component1()
remainingSeconds = division.component2()
division = remainingSeconds.divideAndRemainder(nanosecondBasicUnit)
val nanosecond = division.component1()
remainingSeconds = division.component2()
val attosecond = remainingSeconds
return ConverterResult.Time(
negative = negative,
day = day,
hour = hour,
minute = minute,
second = second,
millisecond = millisecond,
microsecond = microsecond,
nanosecond = nanosecond,
attosecond = attosecond,
)
}
/**
* Creates an object for displaying formatted foot and inch output. Units are passed as objects so
* that changes in basic units don't require modifying the method. Also this method can't access
* units repository directly.
*
* @param input Input in feet.
* @param footUnit Foot unit [DefaultUnit].
* @param inchUnit Inch unit [DefaultUnit].
* @return Result where decimal places are converter into inches.
*/
internal fun formatFootInch(
input: BigDecimal,
footUnit: DefaultUnit,
inchUnit: DefaultUnit,
): ConverterResult.FootInch {
val (integral, fractional) = input.divideAndRemainder(BigDecimal.ONE)
return ConverterResult.FootInch(integral, footUnit.convert(inchUnit, fractional))
}
private val dayBasicUnit by lazy { BigDecimal("86400000000000000000000") }
private val hourBasicUnit by lazy { BigDecimal("3600000000000000000000") }
private val minuteBasicUnit by lazy { BigDecimal("60000000000000000000") }
private val secondBasicUnit by lazy { BigDecimal("1000000000000000000") }
private val millisecondBasicUnit by lazy { BigDecimal("1000000000000000") }
private val microsecondBasicUnit by lazy { BigDecimal("1000000000000") }
private val nanosecondBasicUnit by lazy { BigDecimal("1000000000") }
private val attosecondBasicUnit by lazy { BigDecimal("1") }

View File

@ -22,365 +22,210 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.ui.common.textfield.addBracket
import com.sadellie.unitto.core.ui.common.textfield.addTokens
import com.sadellie.unitto.core.ui.common.textfield.deleteTokens
import com.sadellie.unitto.core.ui.common.textfield.getTextField import com.sadellie.unitto.core.ui.common.textfield.getTextField
import com.sadellie.unitto.data.common.combine import com.sadellie.unitto.data.common.combine
import com.sadellie.unitto.data.common.isExpression
import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.common.stateIn
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.ConverterResult
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.converter.UnitsRepositoryImpl
import com.sadellie.unitto.data.model.repository.UnitsRepository import com.sadellie.unitto.data.model.converter.unit.BasicUnit
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import com.sadellie.unitto.data.model.unit.AbstractUnit
import com.sadellie.unitto.data.model.unit.DefaultUnit
import com.sadellie.unitto.data.model.unit.NumberBaseUnit
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import io.github.sadellie.evaluatto.Expression
import io.github.sadellie.evaluatto.ExpressionException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.math.BigDecimal
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class ConverterViewModel @Inject constructor( internal class ConverterViewModel @Inject constructor(
private val userPrefsRepository: UserPreferencesRepository, private val userPrefsRepository: UserPreferencesRepository,
private val unitsRepo: UnitsRepository, private val unitsRepo: UnitsRepositoryImpl,
private val savedStateHandle: SavedStateHandle, private val savedStateHandle: SavedStateHandle,
) : ViewModel() { ) : ViewModel() {
private var conversionJob: Job? = null
private val converterInputKey1 = "CONVERTER_INPUT_1" private val converterInputKey1 = "CONVERTER_INPUT_1"
private val converterInputKey2 = "CONVERTER_INPUT_2" private val converterInputKey2 = "CONVERTER_INPUT_2"
private val input1 = MutableStateFlow(savedStateHandle.getTextField(converterInputKey1)) private val input1 = MutableStateFlow(savedStateHandle.getTextField(converterInputKey1))
private val input2 = MutableStateFlow(savedStateHandle.getTextField(converterInputKey2)) private val input2 = MutableStateFlow(savedStateHandle.getTextField(converterInputKey2))
private val focusedOnInput2 = MutableStateFlow(false) private val output = MutableStateFlow<ConverterResult>(ConverterResult.Loading)
private val calculation = MutableStateFlow<BigDecimal?>(null)
private val result = MutableStateFlow<ConverterResult>(ConverterResult.Loading)
private val unitFrom = MutableStateFlow<AbstractUnit?>(null)
private val unitTo = MutableStateFlow<AbstractUnit?>(null)
private val currenciesState = MutableStateFlow<CurrencyRateUpdateState>(CurrencyRateUpdateState.Nothing) private val unitFromId = MutableStateFlow<String?>(null)
private var loadCurrenciesJob: Job? = null private val unitToId = MutableStateFlow<String?>(null)
val converterUiState: StateFlow<UnitConverterUIState> = combine( private val unitFrom = unitFromId
.mapLatest { it?.let { unitsRepo.getById(it) } }
.flowOn(Dispatchers.Default)
.stateIn(viewModelScope, null)
private val unitTo = unitToId
.mapLatest { it?.let { unitsRepo.getById(it) } }
.flowOn(Dispatchers.Default)
.stateIn(viewModelScope, null)
val converterUIState: StateFlow<UnitConverterUIState> = combine(
input1, input1,
input2, input2,
calculation, output,
result,
unitFrom, unitFrom,
unitTo, unitTo,
userPrefsRepository.converterPrefs, userPrefsRepository.converterPrefs,
currenciesState, ) { input1Value, input2Value, outputValue, unitFromValue, unitToValue, prefs ->
) { input1, input2, calculation, result, unitFrom, unitTo, prefs, currenciesState -> if (unitFromValue == null) return@combine UnitConverterUIState.Loading
return@combine when { if (unitToValue == null) return@combine UnitConverterUIState.Loading
(unitFrom is DefaultUnit) and (unitTo is DefaultUnit) -> {
UnitConverterUIState.Default( whenBothAre<BasicUnit.Default>(
input1 = input1, value1 = unitFromValue,
input2 = input2, value2 = unitToValue,
calculation = calculation, ) { unitFrom, unitTo ->
result = result, return@combine UnitConverterUIState.Default(
unitFrom = unitFrom as DefaultUnit, input1 = input1Value,
unitTo = unitTo as DefaultUnit, input2 = input2Value,
result = outputValue,
unitFrom = unitFrom,
unitTo = unitTo,
middleZero = prefs.middleZero, middleZero = prefs.middleZero,
formatterSymbols = prefs.formatterSymbols, formatterSymbols = prefs.formatterSymbols,
scale = prefs.precision, scale = prefs.precision,
outputFormat = prefs.outputFormat, outputFormat = OutputFormat.PLAIN,
formatTime = prefs.unitConverterFormatTime, formatTime = prefs.unitConverterFormatTime,
currencyRateUpdateState = currenciesState, currencyRateUpdateState = CurrencyRateUpdateState.Nothing,
acButton = prefs.acButton, acButton = prefs.acButton,
) )
} }
(unitFrom is NumberBaseUnit) and (unitTo is NumberBaseUnit) -> {
UnitConverterUIState.NumberBase( whenBothAre<BasicUnit.NumberBase>(
input = input1, value1 = unitFromValue,
result = result, value2 = unitToValue,
unitFrom = unitFrom as NumberBaseUnit, ) { unitFrom, unitTo ->
unitTo = unitTo as NumberBaseUnit, return@combine UnitConverterUIState.NumberBase(
input = input1Value,
result = outputValue,
unitFrom = unitFrom,
unitTo = unitTo,
) )
} }
else -> UnitConverterUIState.Loading
}
}
.mapLatest { ui ->
when (currenciesState.value) {
is CurrencyRateUpdateState.Loading -> {
result.update { ConverterResult.Loading }
return@mapLatest ui
}
is CurrencyRateUpdateState.Error -> {
result.update { ConverterResult.Error }
return@mapLatest ui
}
is CurrencyRateUpdateState.Ready, is CurrencyRateUpdateState.Nothing -> {}
}
when (ui) { return@combine UnitConverterUIState.Loading
is UnitConverterUIState.Default -> {
convertDefault(
unitFrom = ui.unitFrom,
unitTo = ui.unitTo,
input1 = ui.input1,
input2 = ui.input2,
formatTime = ui.formatTime,
)
}
is UnitConverterUIState.NumberBase -> {
convertNumberBase(
unitFrom = ui.unitFrom,
unitTo = ui.unitTo,
input = ui.input,
)
}
is UnitConverterUIState.Loading -> {}
}
ui
} }
.stateIn(viewModelScope, UnitConverterUIState.Loading) .stateIn(viewModelScope, UnitConverterUIState.Loading)
fun swapUnits() { fun updateInput1(value: TextFieldValue) {
unitFrom input1.update { value }
.getAndUpdate { unitTo.value } savedStateHandle[converterInputKey1] = value.text
.also { oldUnitFrom -> unitTo.update { oldUnitFrom } }
loadCurrenciesJob?.cancel()
currenciesState.update { CurrencyRateUpdateState.Nothing }
unitFrom.value?.let {
if (it.group == UnitGroup.CURRENCY) updateCurrencyRates(it)
} }
viewModelScope.launch { fun updateInput2(value: TextFieldValue) {
val unitTo = unitTo.value ?: return@launch input2.update { value }
val unitFrom = unitFrom.value ?: return@launch savedStateHandle[converterInputKey2] = value.text
userPrefsRepository.updateLatestPairOfUnits(unitFrom = unitFrom, unitTo = unitTo)
} }
fun updateUnitFromId(id: String) = viewModelScope.launch {
val pairId = unitsRepo.getPairId(id)
unitFromId.update { id }
unitToId.update { pairId }
unitsRepo.incrementCounter(id)
updateLatestPairOfUnits()
}
fun updateUnitToId(id: String) = viewModelScope.launch {
unitToId.update { id }
unitsRepo.incrementCounter(id)
setPair()
updateLatestPairOfUnits()
}
fun swapUnits(
newUnitFromId: String,
newInputToId: String,
) = viewModelScope.launch {
unitFromId.update { newUnitFromId }
unitToId.update { newInputToId }
setPair()
updateLatestPairOfUnits()
}
fun convertDefault() {
conversionJob?.cancel()
conversionJob = viewModelScope.launch {
val result = unitsRepo.convertDefault(
unitFromId = unitFromId.value ?: return@launch,
unitToId = unitToId.value ?: return@launch,
value1 = input1.value.text,
value2 = input2.value.text,
formatTime = userPrefsRepository.converterPrefs.first().unitConverterFormatTime,
)
when (result) {
is ConverterResult.Error.BadInput -> Unit
else -> output.update { result }
}
}
}
fun convertNumberBase() {
conversionJob?.cancel()
conversionJob = viewModelScope.launch {
unitsRepo.convertNumberBase(
unitFromId = unitFromId.value ?: return@launch,
unitToId = unitToId.value ?: return@launch,
value = input1.value.text,
)
}
}
private fun loadInitialUnits() = viewModelScope.launch {
val prefs = userPrefsRepository.converterPrefs.first()
unitFromId.update { prefs.latestLeftSideUnit }
unitToId.update { prefs.latestRightSideUnit }
}
private fun setPair() = viewModelScope.launch {
unitsRepo.setPair(
id = unitFromId.value ?: return@launch,
pairId = unitToId.value ?: return@launch,
)
}
private fun updateLatestPairOfUnits() = viewModelScope.launch {
userPrefsRepository
.updateLatestPairOfUnits(
unitFrom = unitFromId.value ?: return@launch,
unitTo = unitToId.value ?: return@launch,
)
} }
/** /**
* Change currently focused text field. For feet and inches input * Will call [block] if both [value1] and [value2] are [T].
* *
* @param focusOnInput2 `true` if focus is on inches input. `false`if focus on feet input. * @param T What to check against.
* @param value1 Value to check.
* @param value2 Value to check.
* @param block Block that will be called if the has passed.
*/ */
fun updateFocused(focusOnInput2: Boolean) = focusedOnInput2.update { focusOnInput2 } private inline fun <reified T> whenBothAre(
value1: Any?,
fun addTokens(tokens: String) { value2: Any?,
if (focusedOnInput2.value) { block: (v1: T, v2: T) -> Unit,
input2.update { ) {
val newValue = it.addTokens(tokens) if ((value1 is T) and (value2 is T)) block(value1 as T, value2 as T)
savedStateHandle[converterInputKey2] = newValue.text
newValue
}
} else {
input1.update {
val newValue = it.addTokens(tokens)
savedStateHandle[converterInputKey1] = newValue.text
newValue
}
}
}
fun addBracket() {
if (focusedOnInput2.value) {
input2.update {
val newValue = it.addBracket()
savedStateHandle[converterInputKey2] = newValue.text
newValue
}
} else {
input1.update {
val newValue = it.addBracket()
savedStateHandle[converterInputKey1] = newValue.text
newValue
}
}
}
fun deleteTokens() {
if (focusedOnInput2.value) {
input2.update {
val newValue = it.deleteTokens()
savedStateHandle[converterInputKey2] = newValue.text
newValue
}
} else {
input1.update {
val newValue = it.deleteTokens()
savedStateHandle[converterInputKey1] = newValue.text
newValue
}
}
}
fun clearInput() {
input1.update {
savedStateHandle[converterInputKey1] = ""
TextFieldValue()
}
input2.update {
savedStateHandle[converterInputKey2] = ""
TextFieldValue()
}
}
fun updateInput(value: TextFieldValue) = input1.update { value }
fun updateCurrencyRates(unit: AbstractUnit) {
loadCurrenciesJob = viewModelScope.launch(Dispatchers.IO) {
try {
currenciesState.update { CurrencyRateUpdateState.Loading }
val updateDate = unitsRepo.updateRates(unit) ?: throw Exception("Empty cache")
// Set to fresh objects with updated basic unit values
unitFrom.update { unitFrom -> unitFrom?.id?.let { unitsRepo.getById(it) } }
unitTo.update { unitTo -> unitTo?.id?.let { unitsRepo.getById(it) } }
currenciesState.update { CurrencyRateUpdateState.Ready(updateDate) }
} catch (e: Exception) {
currenciesState.update { CurrencyRateUpdateState.Error }
}
}
}
fun updateUnitFrom(unit: AbstractUnit) = viewModelScope.launch {
val pairId = unit.pairId
val pair: AbstractUnit = if (pairId == null) {
val collection = unitsRepo.getCollection(unit.group).sortedByDescending { it.counter }
collection.firstOrNull { it.isFavorite } ?: collection.first()
} else {
unitsRepo.getById(pairId)
}
withContext(Dispatchers.Default) {
unitsRepo.incrementCounter(unit)
userPrefsRepository.updateLatestPairOfUnits(unitFrom = unit, unitTo = pair)
}
loadCurrenciesJob?.cancel()
currenciesState.update { CurrencyRateUpdateState.Nothing }
if (unit.group == UnitGroup.CURRENCY) updateCurrencyRates(unit)
unitFrom.update {
// We change from something to base converter or the other way around
if ((it?.group == UnitGroup.NUMBER_BASE) xor (unit.group == UnitGroup.NUMBER_BASE)) {
clearInput()
}
unit
}
unitTo.update { pair }
}
fun updateUnitTo(unit: AbstractUnit) {
unitTo.update { unit }
viewModelScope.launch {
val unitTo = unitTo.value ?: return@launch
val unitFrom = unitFrom.value ?: return@launch
unitsRepo.incrementCounter(unitTo)
unitsRepo.setPair(unitFrom, unitTo)
userPrefsRepository.updateLatestPairOfUnits(unitFrom = unitFrom, unitTo = unitTo)
}
}
private fun convertDefault(
unitFrom: DefaultUnit,
unitTo: DefaultUnit,
input1: TextFieldValue,
input2: TextFieldValue,
formatTime: Boolean,
) = viewModelScope.launch(Dispatchers.Default) {
val footInchInput = unitFrom.id == UnitID.foot
if (footInchInput) {
calculation.update { null }
}
// Calculate
val calculated1 = try {
Expression(input1.text.ifEmpty { Token.Digit._0 }).calculate()
} catch (e: ExpressionException.DivideByZero) {
calculation.update { null }
return@launch
} catch (e: Exception) {
return@launch
}
// Update calculation
calculation.update { if (input1.text.isExpression()) calculated1 else null }
// Convert
val result: ConverterResult = try {
var conversion = unitFrom.convert(unitTo, calculated1)
if (footInchInput) {
// Converted from second text field too
val inches = unitsRepo.getById(UnitID.inch) as DefaultUnit
val calculated2 = try {
Expression(input2.text.ifEmpty { Token.Digit._0 }).calculate()
} catch (e: ExpressionException.DivideByZero) {
calculation.update { null }
return@launch
} catch (e: Exception) {
return@launch
}
conversion += inches.convert(unitTo, calculated2)
}
when {
(unitFrom.group == UnitGroup.TIME) and (formatTime) -> formatTime(calculated1.multiply(unitFrom.basicUnit))
unitTo.id == UnitID.foot -> formatFootInch(conversion, unitTo, unitsRepo.getById(UnitID.inch) as DefaultUnit)
else -> ConverterResult.Default(conversion)
}
} catch (e: Exception) {
ConverterResult.Default(BigDecimal.ZERO)
}
// Update result
this@ConverterViewModel.result.update { result }
}
private fun convertNumberBase(
unitFrom: NumberBaseUnit,
unitTo: NumberBaseUnit,
input: TextFieldValue,
) = viewModelScope.launch(Dispatchers.Default) {
val conversion = try {
unitFrom.convert(unitTo, input.text.ifEmpty { Token.Digit._0 })
} catch (e: Exception) {
""
}
result.update { ConverterResult.NumberBase(conversion) }
} }
init { init {
viewModelScope.launch(Dispatchers.Default) { loadInitialUnits()
try {
val userPrefs = userPrefsRepository.converterPrefs.first()
val unitFrom = unitsRepo.getById(userPrefs.latestLeftSideUnit)
val unitTo = unitsRepo.getById(userPrefs.latestRightSideUnit)
this@ConverterViewModel.unitFrom.update { unitFrom }
this@ConverterViewModel.unitTo.update { unitTo }
if (unitFrom.group == UnitGroup.CURRENCY) updateCurrencyRates(unitFrom)
} catch (e: NoSuchElementException) {
val unitFrom = unitsRepo.getById(UnitID.kilometer)
val unitTo = unitsRepo.getById(UnitID.mile)
this@ConverterViewModel.unitFrom.update { unitFrom }
this@ConverterViewModel.unitTo.update { unitTo }
}
} }
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
} }
} }

View File

@ -22,12 +22,10 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -39,10 +37,11 @@ import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.EmptyScreen import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.SearchBar import com.sadellie.unitto.core.ui.common.SearchBar
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.converter.UnitSearchResultItem
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.database.UnitsEntity
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.UnitsListSorting
import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import com.sadellie.unitto.feature.converter.components.ChipsRow import com.sadellie.unitto.feature.converter.components.ChipsRow
import com.sadellie.unitto.feature.converter.components.FavoritesButton import com.sadellie.unitto.feature.converter.components.FavoritesButton
import com.sadellie.unitto.feature.converter.components.UnitsList import com.sadellie.unitto.feature.converter.components.UnitsList
@ -50,7 +49,7 @@ import java.math.BigDecimal
@Composable @Composable
internal fun UnitFromSelectorRoute( internal fun UnitFromSelectorRoute(
unitSelectorViewModel: UnitSelectorViewModel, unitSelectorViewModel: UnitFromSelectorViewModel,
converterViewModel: ConverterViewModel, converterViewModel: ConverterViewModel,
navigateUp: () -> Unit, navigateUp: () -> Unit,
navigateToUnitGroups: () -> Unit, navigateToUnitGroups: () -> Unit,
@ -62,7 +61,7 @@ internal fun UnitFromSelectorRoute(
uiState = uiState, uiState = uiState,
onQueryChange = unitSelectorViewModel::updateSelectorQuery, onQueryChange = unitSelectorViewModel::updateSelectorQuery,
toggleFavoritesOnly = unitSelectorViewModel::updateShowFavoritesOnly, toggleFavoritesOnly = unitSelectorViewModel::updateShowFavoritesOnly,
updateUnitFrom = converterViewModel::updateUnitFrom, updateUnitFrom = converterViewModel::updateUnitFromId,
updateUnitGroup = unitSelectorViewModel::updateSelectedUnitGroup, updateUnitGroup = unitSelectorViewModel::updateSelectedUnitGroup,
favoriteUnit = unitSelectorViewModel::favoriteUnit, favoriteUnit = unitSelectorViewModel::favoriteUnit,
navigateUp = navigateUp, navigateUp = navigateUp,
@ -77,25 +76,14 @@ private fun UnitFromSelectorScreen(
uiState: UnitSelectorUIState.UnitFrom, uiState: UnitSelectorUIState.UnitFrom,
onQueryChange: (TextFieldValue) -> Unit, onQueryChange: (TextFieldValue) -> Unit,
toggleFavoritesOnly: (Boolean) -> Unit, toggleFavoritesOnly: (Boolean) -> Unit,
updateUnitFrom: (AbstractUnit) -> Unit, updateUnitFrom: (String) -> Unit,
updateUnitGroup: (UnitGroup?) -> Unit, updateUnitGroup: (UnitGroup?) -> Unit,
favoriteUnit: (AbstractUnit) -> Unit, favoriteUnit: (UnitSearchResultItem) -> Unit,
navigateUp: () -> Unit, navigateUp: () -> Unit,
navigateToUnitGroups: () -> Unit, navigateToUnitGroups: () -> Unit,
) { ) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val chipsRowLazyListState = rememberLazyListState()
LaunchedEffect(uiState.unitFrom, uiState.shownUnitGroups) {
kotlin.runCatching {
val groupToSelect = uiState.shownUnitGroups.indexOf(uiState.unitFrom.group)
if (groupToSelect > -1) {
chipsRowLazyListState.scrollToItem(groupToSelect)
}
}
}
Scaffold( Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { topBar = {
@ -127,15 +115,16 @@ private fun UnitFromSelectorScreen(
}, },
) { paddingValues -> ) { paddingValues ->
val resources = LocalContext.current.resources val resources = LocalContext.current.resources
UnitsList( UnitsList(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
searchResult = uiState.units, searchResult = uiState.units,
navigateToUnitGroups = navigateToUnitGroups, navigateToUnitGroups = navigateToUnitGroups,
currentUnitId = uiState.unitFrom.id, currentUnitId = uiState.unitFromId,
supportLabel = { resources.getString(it.shortName) }, supportLabel = { resources.getString(it.basicUnit.shortName) },
onClick = { onClick = {
onQueryChange(TextFieldValue()) onQueryChange(TextFieldValue())
updateUnitFrom(it) updateUnitFrom(it.basicUnit.id)
navigateUp() navigateUp()
}, },
favoriteUnit = { favoriteUnit(it) }, favoriteUnit = { favoriteUnit(it) },
@ -146,7 +135,7 @@ private fun UnitFromSelectorScreen(
@Preview @Preview
@Composable @Composable
private fun UnitFromSelectorScreenPreview() { private fun UnitFromSelectorScreenPreview() {
val units: Map<UnitGroup, List<AbstractUnit>> = mapOf( val units: Map<UnitGroup, List<UnitSearchResultItem>> = mapOf(
UnitGroup.LENGTH to listOf( UnitGroup.LENGTH to listOf(
NormalUnit(UnitID.meter, BigDecimal("1000000000000000000"), UnitGroup.LENGTH, R.string.unit_meter, R.string.unit_meter_short), NormalUnit(UnitID.meter, BigDecimal("1000000000000000000"), UnitGroup.LENGTH, R.string.unit_meter, R.string.unit_meter_short),
NormalUnit(UnitID.kilometer, BigDecimal("1000000000000000000000"), UnitGroup.LENGTH, R.string.unit_kilometer, R.string.unit_kilometer_short), NormalUnit(UnitID.kilometer, BigDecimal("1000000000000000000000"), UnitGroup.LENGTH, R.string.unit_kilometer, R.string.unit_kilometer_short),
@ -155,14 +144,15 @@ private fun UnitFromSelectorScreenPreview() {
NormalUnit(UnitID.foot, BigDecimal("304800000000000000"), UnitGroup.LENGTH, R.string.unit_foot, R.string.unit_foot_short), NormalUnit(UnitID.foot, BigDecimal("304800000000000000"), UnitGroup.LENGTH, R.string.unit_foot, R.string.unit_foot_short),
NormalUnit(UnitID.yard, BigDecimal("914400000000000000"), UnitGroup.LENGTH, R.string.unit_yard, R.string.unit_yard_short), NormalUnit(UnitID.yard, BigDecimal("914400000000000000"), UnitGroup.LENGTH, R.string.unit_yard, R.string.unit_yard_short),
NormalUnit(UnitID.mile, BigDecimal("1609344000000000000000"), UnitGroup.LENGTH, R.string.unit_mile, R.string.unit_mile_short), NormalUnit(UnitID.mile, BigDecimal("1609344000000000000000"), UnitGroup.LENGTH, R.string.unit_mile, R.string.unit_mile_short),
), )
.map { UnitSearchResultItem(it, UnitsEntity(unitId = it.id), null) },
) )
UnitFromSelectorScreen( UnitFromSelectorScreen(
uiState = UnitSelectorUIState.UnitFrom( uiState = UnitSelectorUIState.UnitFrom(
unitFrom = units.values.first().first(), unitFromId = UnitID.kilometer,
query = TextFieldValue("test"), query = TextFieldValue("test"),
units = UnitSearchResult.Success(units), units = units,
selectedUnitGroup = UnitGroup.SPEED, selectedUnitGroup = UnitGroup.SPEED,
shownUnitGroups = UnitGroup.entries, shownUnitGroups = UnitGroup.entries,
showFavoritesOnly = false, showFavoritesOnly = false,

View File

@ -0,0 +1,124 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.feature.converter
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.data.common.stateIn
import com.sadellie.unitto.data.converter.UnitSearchResultItem
import com.sadellie.unitto.data.converter.UnitsRepositoryImpl
import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import com.sadellie.unitto.feature.converter.navigation.UNIT_FROM_ID_ARG
import com.sadellie.unitto.feature.converter.navigation.UNIT_GROUP_ARG
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
internal class UnitFromSelectorViewModel @Inject constructor(
private val userPrefsRepository: UserPreferencesRepository,
private val unitsRepo: UnitsRepositoryImpl,
savedStateHandle: SavedStateHandle,
) : ViewModel() {
private var searchJob: Job? = null
private val query = MutableStateFlow(TextFieldValue())
private val searchResults = MutableStateFlow<Map<UnitGroup, List<UnitSearchResultItem>>?>(null)
private val selectedUnitGroup = MutableStateFlow(savedStateHandle.get<UnitGroup>(UNIT_GROUP_ARG))
private val unitFromId = savedStateHandle.get<String>(UNIT_FROM_ID_ARG)
val unitFromUIState: StateFlow<UnitSelectorUIState> = combine(
query,
searchResults,
selectedUnitGroup,
userPrefsRepository.converterPrefs,
) { query, searchResults, selectedUnitGroup, prefs ->
if (searchResults == null) return@combine UnitSelectorUIState.Loading
return@combine UnitSelectorUIState.UnitFrom(
query = query,
unitFromId = unitFromId ?: "",
shownUnitGroups = prefs.shownUnitGroups,
showFavoritesOnly = prefs.unitConverterFavoritesOnly,
units = searchResults,
selectedUnitGroup = selectedUnitGroup,
sorting = prefs.unitConverterSorting,
)
}
.stateIn(viewModelScope, UnitSelectorUIState.Loading)
fun updateSelectorQuery(value: TextFieldValue) {
query.update { value }
onSearch()
}
fun updateShowFavoritesOnly(value: Boolean) = viewModelScope.launch {
userPrefsRepository.updateUnitConverterFavoritesOnly(value)
onSearch()
}
fun updateSelectedUnitGroup(value: UnitGroup?) {
selectedUnitGroup.update { value }
onSearch()
}
fun favoriteUnit(unit: UnitSearchResultItem) = viewModelScope.launch {
unitsRepo.favorite(unit.basicUnit.id)
onSearch()
}
private fun onSearch() {
searchJob?.cancel()
searchJob = viewModelScope.launch {
val prefs = userPrefsRepository.converterPrefs.first()
val selectedGroupValue = selectedUnitGroup.value
val result = unitsRepo.filterUnits(
query = query.value.text,
favoritesOnly = prefs.unitConverterFavoritesOnly,
sorting = prefs.unitConverterSorting,
unitGroups = if (selectedGroupValue == null) {
prefs.shownUnitGroups
} else {
listOf(selectedGroupValue)
},
)
searchResults.update { result }
}
}
init {
onSearch()
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
}

View File

@ -20,29 +20,30 @@ package com.sadellie.unitto.feature.converter
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import com.sadellie.unitto.core.base.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.converter.UnitSearchResultItem
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.UnitsListSorting
import com.sadellie.unitto.data.model.converter.unit.BasicUnit
internal sealed class UnitSelectorUIState { internal sealed class UnitSelectorUIState {
data object Loading : UnitSelectorUIState() data object Loading : UnitSelectorUIState()
data class UnitFrom( data class UnitFrom(
val query: TextFieldValue, val query: TextFieldValue,
val unitFrom: AbstractUnit, val unitFromId: String,
val shownUnitGroups: List<UnitGroup>, val shownUnitGroups: List<UnitGroup>,
val showFavoritesOnly: Boolean, val showFavoritesOnly: Boolean,
val units: UnitSearchResult, val units: Map<UnitGroup, List<UnitSearchResultItem>>,
val selectedUnitGroup: UnitGroup?, val selectedUnitGroup: UnitGroup?,
val sorting: UnitsListSorting, val sorting: UnitsListSorting,
) : UnitSelectorUIState() ) : UnitSelectorUIState()
data class UnitTo( data class UnitTo(
val query: TextFieldValue, val query: TextFieldValue,
val unitFrom: AbstractUnit, val unitFrom: BasicUnit,
val unitTo: AbstractUnit, val unitTo: BasicUnit,
val showFavoritesOnly: Boolean, val showFavoritesOnly: Boolean,
val units: UnitSearchResult, val units: Map<UnitGroup, List<UnitSearchResultItem>>,
val input: String?, val input: String?,
val sorting: UnitsListSorting, val sorting: UnitsListSorting,
val scale: Int, val scale: Int,
@ -50,13 +51,3 @@ internal sealed class UnitSelectorUIState {
val formatterSymbols: FormatterSymbols, val formatterSymbols: FormatterSymbols,
) : UnitSelectorUIState() ) : UnitSelectorUIState()
} }
internal sealed class UnitSearchResult {
data object Empty : UnitSearchResult()
data object Loading : UnitSearchResult()
data class Success(
val units: Map<UnitGroup, List<AbstractUnit>>,
) : UnitSearchResult()
}

View File

@ -36,20 +36,21 @@ import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.SearchBar import com.sadellie.unitto.core.ui.common.SearchBar
import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.common.textfield.formatExpression
import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.format
import com.sadellie.unitto.data.converter.DefaultBatchConvertResult
import com.sadellie.unitto.data.converter.NumberBaseBatchConvertResult
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.converter.UnitSearchResultItem
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.database.UnitsEntity
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.unit.DefaultUnit import com.sadellie.unitto.data.model.converter.UnitsListSorting
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import com.sadellie.unitto.data.model.unit.NumberBaseUnit
import com.sadellie.unitto.feature.converter.components.FavoritesButton import com.sadellie.unitto.feature.converter.components.FavoritesButton
import com.sadellie.unitto.feature.converter.components.UnitsList import com.sadellie.unitto.feature.converter.components.UnitsList
import java.math.BigDecimal import java.math.BigDecimal
@Composable @Composable
internal fun UnitToSelectorRoute( internal fun UnitToSelectorRoute(
unitSelectorViewModel: UnitSelectorViewModel, unitSelectorViewModel: UnitToSelectorViewModel,
converterViewModel: ConverterViewModel, converterViewModel: ConverterViewModel,
navigateUp: () -> Unit, navigateUp: () -> Unit,
navigateToUnitGroups: () -> Unit, navigateToUnitGroups: () -> Unit,
@ -61,7 +62,7 @@ internal fun UnitToSelectorRoute(
uiState = uiState, uiState = uiState,
onQueryChange = unitSelectorViewModel::updateSelectorQuery, onQueryChange = unitSelectorViewModel::updateSelectorQuery,
toggleFavoritesOnly = unitSelectorViewModel::updateShowFavoritesOnly, toggleFavoritesOnly = unitSelectorViewModel::updateShowFavoritesOnly,
updateUnitTo = converterViewModel::updateUnitTo, updateUnitTo = converterViewModel::updateUnitToId,
favoriteUnit = unitSelectorViewModel::favoriteUnit, favoriteUnit = unitSelectorViewModel::favoriteUnit,
navigateUp = navigateUp, navigateUp = navigateUp,
navigateToUnitGroups = navigateToUnitGroups, navigateToUnitGroups = navigateToUnitGroups,
@ -75,8 +76,8 @@ private fun UnitToSelectorScreen(
uiState: UnitSelectorUIState.UnitTo, uiState: UnitSelectorUIState.UnitTo,
onQueryChange: (TextFieldValue) -> Unit, onQueryChange: (TextFieldValue) -> Unit,
toggleFavoritesOnly: (Boolean) -> Unit, toggleFavoritesOnly: (Boolean) -> Unit,
updateUnitTo: (AbstractUnit) -> Unit, updateUnitTo: (String) -> Unit,
favoriteUnit: (AbstractUnit) -> Unit, favoriteUnit: (UnitSearchResultItem) -> Unit,
navigateUp: () -> Unit, navigateUp: () -> Unit,
navigateToUnitGroups: () -> Unit, navigateToUnitGroups: () -> Unit,
) { ) {
@ -105,19 +106,25 @@ private fun UnitToSelectorScreen(
navigateToUnitGroups = navigateToUnitGroups, navigateToUnitGroups = navigateToUnitGroups,
currentUnitId = uiState.unitTo.id, currentUnitId = uiState.unitTo.id,
supportLabel = { supportLabel = {
formatUnitToSupportLabel( val label = resources.getString(it.basicUnit.shortName)
unitFrom = uiState.unitFrom,
unitTo = it, when (val conversion = it.conversion) {
input = uiState.input, is DefaultBatchConvertResult -> {
shortName = resources.getString(it.shortName), val formattedConversion = conversion.value
scale = uiState.scale, .format(uiState.scale, uiState.outputFormat)
outputFormat = uiState.outputFormat, .formatExpression(uiState.formatterSymbols)
formatterSymbols = uiState.formatterSymbols,
) "$formattedConversion $label"
}
is NumberBaseBatchConvertResult -> {
"${conversion.value} $label"
}
else -> label
}
}, },
onClick = { onClick = {
onQueryChange(TextFieldValue()) onQueryChange(TextFieldValue())
updateUnitTo(it) updateUnitTo(it.basicUnit.id)
navigateUp() navigateUp()
}, },
favoriteUnit = { favoriteUnit(it) }, favoriteUnit = { favoriteUnit(it) },
@ -125,49 +132,10 @@ private fun UnitToSelectorScreen(
} }
} }
private fun formatUnitToSupportLabel(
unitFrom: AbstractUnit?,
unitTo: AbstractUnit?,
input: String?,
shortName: String,
scale: Int,
outputFormat: Int,
formatterSymbols: FormatterSymbols,
): String {
if (input.isNullOrEmpty()) return shortName
try {
if ((unitFrom is DefaultUnit) and (unitTo is DefaultUnit)) {
unitFrom as DefaultUnit
unitTo as DefaultUnit
val conversion = unitFrom
.convert(unitTo, BigDecimal(input))
.format(scale, outputFormat)
.formatExpression(formatterSymbols)
return "$conversion $shortName"
}
if ((unitFrom is NumberBaseUnit) and (unitTo is NumberBaseUnit)) {
unitFrom as NumberBaseUnit
unitTo as NumberBaseUnit
val conversion = unitFrom.convert(unitTo, input).uppercase()
return "$conversion $shortName"
}
} catch (e: Exception) {
return shortName
}
return shortName
}
@Preview @Preview
@Composable @Composable
private fun UnitToSelectorPreview() { private fun UnitToSelectorPreview() {
val units: Map<UnitGroup, List<AbstractUnit>> = mapOf( val units: Map<UnitGroup, List<UnitSearchResultItem>> = mapOf(
UnitGroup.LENGTH to listOf( UnitGroup.LENGTH to listOf(
NormalUnit(UnitID.meter, BigDecimal("1000000000000000000"), UnitGroup.LENGTH, R.string.unit_meter, R.string.unit_meter_short), NormalUnit(UnitID.meter, BigDecimal("1000000000000000000"), UnitGroup.LENGTH, R.string.unit_meter, R.string.unit_meter_short),
NormalUnit(UnitID.kilometer, BigDecimal("1000000000000000000000"), UnitGroup.LENGTH, R.string.unit_kilometer, R.string.unit_kilometer_short), NormalUnit(UnitID.kilometer, BigDecimal("1000000000000000000000"), UnitGroup.LENGTH, R.string.unit_kilometer, R.string.unit_kilometer_short),
@ -176,15 +144,16 @@ private fun UnitToSelectorPreview() {
NormalUnit(UnitID.foot, BigDecimal("304800000000000000"), UnitGroup.LENGTH, R.string.unit_foot, R.string.unit_foot_short), NormalUnit(UnitID.foot, BigDecimal("304800000000000000"), UnitGroup.LENGTH, R.string.unit_foot, R.string.unit_foot_short),
NormalUnit(UnitID.yard, BigDecimal("914400000000000000"), UnitGroup.LENGTH, R.string.unit_yard, R.string.unit_yard_short), NormalUnit(UnitID.yard, BigDecimal("914400000000000000"), UnitGroup.LENGTH, R.string.unit_yard, R.string.unit_yard_short),
NormalUnit(UnitID.mile, BigDecimal("1609344000000000000000"), UnitGroup.LENGTH, R.string.unit_mile, R.string.unit_mile_short), NormalUnit(UnitID.mile, BigDecimal("1609344000000000000000"), UnitGroup.LENGTH, R.string.unit_mile, R.string.unit_mile_short),
), )
.map { UnitSearchResultItem(it, UnitsEntity(unitId = it.id), null) },
) )
UnitToSelectorScreen( UnitToSelectorScreen(
uiState = UnitSelectorUIState.UnitTo( uiState = UnitSelectorUIState.UnitTo(
unitFrom = units.values.first().first(), unitFrom = units.values.first().first().basicUnit,
unitTo = units.values.first().first(), unitTo = units.values.first().first().basicUnit,
query = TextFieldValue("test"), query = TextFieldValue("test"),
units = UnitSearchResult.Success(units), units = units,
showFavoritesOnly = false, showFavoritesOnly = false,
sorting = UnitsListSorting.USAGE, sorting = UnitsListSorting.USAGE,
input = "100", input = "100",

View File

@ -23,83 +23,48 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.common.stateIn
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.converter.UnitSearchResultItem
import com.sadellie.unitto.data.model.repository.UnitsRepository import com.sadellie.unitto.data.converter.UnitsRepositoryImpl
import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import com.sadellie.unitto.data.model.unit.AbstractUnit
import com.sadellie.unitto.feature.converter.navigation.INPUT_ARG import com.sadellie.unitto.feature.converter.navigation.INPUT_ARG
import com.sadellie.unitto.feature.converter.navigation.UNIT_FROM_ID_ARG import com.sadellie.unitto.feature.converter.navigation.UNIT_FROM_ID_ARG
import com.sadellie.unitto.feature.converter.navigation.UNIT_GROUP_ARG import com.sadellie.unitto.feature.converter.navigation.UNIT_GROUP_ARG
import com.sadellie.unitto.feature.converter.navigation.UNIT_TO_ID_ARG import com.sadellie.unitto.feature.converter.navigation.UNIT_TO_ID_ARG
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class UnitSelectorViewModel @Inject constructor( internal class UnitToSelectorViewModel @Inject constructor(
private val userPrefsRepository: UserPreferencesRepository, private val userPrefsRepository: UserPreferencesRepository,
private val unitsRepo: UnitsRepository, private val unitsRepo: UnitsRepositoryImpl,
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
) : ViewModel() { ) : ViewModel() {
private var searchJob: Job? = null
private val query = MutableStateFlow(TextFieldValue()) private val query = MutableStateFlow(TextFieldValue())
private val searchResults = MutableStateFlow<UnitSearchResult>(UnitSearchResult.Loading) private val searchResults = MutableStateFlow<Map<UnitGroup, List<UnitSearchResultItem>>?>(null)
private val selectedUnitGroup = MutableStateFlow(savedStateHandle.get<UnitGroup>(UNIT_GROUP_ARG)) private val selectedUnitGroup = MutableStateFlow(savedStateHandle.get<UnitGroup>(UNIT_GROUP_ARG))
private val unitFromId = savedStateHandle.get<String>(UNIT_FROM_ID_ARG) private val unitFromId = savedStateHandle.get<String>(UNIT_FROM_ID_ARG)
private val unitToId = savedStateHandle.get<String>(UNIT_TO_ID_ARG) private val unitToId = savedStateHandle.get<String>(UNIT_TO_ID_ARG)
private val input = savedStateHandle.get<String>(INPUT_ARG) private val input = savedStateHandle.get<String>(INPUT_ARG)
val unitFromUIState: StateFlow<UnitSelectorUIState> = combine(
query,
searchResults,
selectedUnitGroup,
userPrefsRepository.converterPrefs,
) { query, searchResults, selectedUnitGroup, prefs ->
if (unitFromId.isNullOrEmpty()) return@combine UnitSelectorUIState.Loading
return@combine UnitSelectorUIState.UnitFrom(
query = query,
unitFrom = unitsRepo.getById(unitFromId),
shownUnitGroups = prefs.shownUnitGroups,
showFavoritesOnly = prefs.unitConverterFavoritesOnly,
units = searchResults,
selectedUnitGroup = selectedUnitGroup,
sorting = prefs.unitConverterSorting,
)
}
.mapLatest { ui ->
if (ui is UnitSelectorUIState.UnitFrom) {
searchResults.update {
val result = unitsRepo.filterUnits(
query = ui.query.text,
unitGroup = ui.selectedUnitGroup,
favoritesOnly = ui.showFavoritesOnly,
hideBrokenUnits = false,
sorting = ui.sorting,
shownUnitGroups = ui.shownUnitGroups,
)
if (result.isEmpty()) UnitSearchResult.Empty else UnitSearchResult.Success(result)
}
}
ui
}
.stateIn(viewModelScope, UnitSelectorUIState.Loading)
val unitToUIState: StateFlow<UnitSelectorUIState> = combine( val unitToUIState: StateFlow<UnitSelectorUIState> = combine(
query, query,
searchResults, searchResults,
userPrefsRepository.converterPrefs, userPrefsRepository.converterPrefs,
unitsRepo.units, ) { query, searchResults, prefs ->
) { query, searchResults, prefs, _ ->
if (unitFromId.isNullOrEmpty()) return@combine UnitSelectorUIState.Loading if (unitFromId.isNullOrEmpty()) return@combine UnitSelectorUIState.Loading
if (unitToId.isNullOrEmpty()) return@combine UnitSelectorUIState.Loading if (unitToId.isNullOrEmpty()) return@combine UnitSelectorUIState.Loading
if (searchResults == null) return@combine UnitSelectorUIState.Loading
UnitSelectorUIState.UnitTo( UnitSelectorUIState.UnitTo(
query = query, query = query,
@ -113,36 +78,47 @@ internal class UnitSelectorViewModel @Inject constructor(
outputFormat = prefs.outputFormat, outputFormat = prefs.outputFormat,
formatterSymbols = prefs.formatterSymbols, formatterSymbols = prefs.formatterSymbols,
) )
}
.mapLatest { ui ->
if (ui is UnitSelectorUIState.UnitTo) {
searchResults.update {
if (ui.unitFrom.group == UnitGroup.CURRENCY) unitsRepo.updateRates(ui.unitFrom)
val result = unitsRepo.filterUnits(
query = ui.query.text,
unitGroup = ui.unitFrom.group,
favoritesOnly = ui.showFavoritesOnly,
hideBrokenUnits = true,
sorting = ui.sorting,
)
if (result.isEmpty()) UnitSearchResult.Empty else UnitSearchResult.Success(result)
}
}
ui
} }
.stateIn(viewModelScope, UnitSelectorUIState.Loading) .stateIn(viewModelScope, UnitSelectorUIState.Loading)
fun updateSelectorQuery(value: TextFieldValue) = query.update { value } fun updateSelectorQuery(value: TextFieldValue) {
query.update { value }
onSearch()
}
fun updateShowFavoritesOnly(value: Boolean) = viewModelScope.launch { fun updateShowFavoritesOnly(value: Boolean) = viewModelScope.launch {
userPrefsRepository.updateUnitConverterFavoritesOnly(value) userPrefsRepository.updateUnitConverterFavoritesOnly(value)
onSearch()
} }
fun updateSelectedUnitGroup(value: UnitGroup?) = selectedUnitGroup.update { value } fun favoriteUnit(unit: UnitSearchResultItem) = viewModelScope.launch {
unitsRepo.favorite(unit.basicUnit.id)
onSearch()
}
fun favoriteUnit(unit: AbstractUnit) = viewModelScope.launch(Dispatchers.IO) { private fun onSearch() {
unitsRepo.favorite(unit) searchJob?.cancel()
searchJob = viewModelScope.launch {
val prefs = userPrefsRepository.converterPrefs.first()
val result = unitsRepo.filterUnitsAndBatchConvert(
query = query.value.text,
unitGroup = selectedUnitGroup.value ?: return@launch,
favoritesOnly = prefs.unitConverterFavoritesOnly,
sorting = prefs.unitConverterSorting,
unitFromId = unitFromId ?: return@launch,
input = input,
)
searchResults.update { result }
}
}
init {
onSearch()
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
} }
} }

View File

@ -48,7 +48,7 @@ import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.AssistChip import com.sadellie.unitto.core.ui.common.AssistChip
import com.sadellie.unitto.core.ui.common.FilterChip import com.sadellie.unitto.core.ui.common.FilterChip
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
/** /**
* Row of chips with [UnitGroup]s. Temporary solution * Row of chips with [UnitGroup]s. Temporary solution

View File

@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.Header
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
@Composable @Composable
internal fun UnitGroupHeader(modifier: Modifier, unitGroup: UnitGroup) { internal fun UnitGroupHeader(modifier: Modifier, unitGroup: UnitGroup) {

View File

@ -19,6 +19,7 @@
package com.sadellie.unitto.feature.converter.components package com.sadellie.unitto.feature.converter.components
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
@ -30,57 +31,55 @@ import androidx.compose.ui.tooling.preview.Preview
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.SearchPlaceholder import com.sadellie.unitto.core.ui.common.SearchPlaceholder
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.converter.UnitSearchResultItem
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.database.UnitsEntity
import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.feature.converter.UnitSearchResult import com.sadellie.unitto.data.model.converter.unit.NormalUnit
import java.math.BigDecimal import java.math.BigDecimal
@Composable @Composable
internal fun UnitsList( internal fun UnitsList(
modifier: Modifier, modifier: Modifier,
searchResult: UnitSearchResult, searchResult: Map<UnitGroup, List<UnitSearchResultItem>>,
navigateToUnitGroups: () -> Unit, navigateToUnitGroups: () -> Unit,
currentUnitId: String, currentUnitId: String,
supportLabel: (AbstractUnit) -> String, supportLabel: (UnitSearchResultItem) -> String,
onClick: (AbstractUnit) -> Unit, onClick: (UnitSearchResultItem) -> Unit,
favoriteUnit: (AbstractUnit) -> Unit, favoriteUnit: (UnitSearchResultItem) -> Unit,
) { ) {
Crossfade( Crossfade(
modifier = modifier, modifier = modifier,
targetState = searchResult, targetState = searchResult,
label = "Units list", label = "Units list",
animationSpec = tween(200),
) { result -> ) { result ->
when (result) { when {
is UnitSearchResult.Success -> LazyColumn( result.isEmpty() -> SearchPlaceholder(
onButtonClick = navigateToUnitGroups,
supportText = stringResource(R.string.converter_no_results_support),
buttonLabel = stringResource(R.string.open_settings_label),
)
else -> LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
) { ) {
result.units.forEach { (group, units) -> result.forEach { (group, units) ->
item(group.name) { item(group.name) {
UnitGroupHeader(Modifier.animateItemPlacement(), group) UnitGroupHeader(Modifier.animateItemPlacement(), group)
} }
items(units, { it.id }) { items(units, { it.basicUnit.id }) {
BasicUnitListItem( BasicUnitListItem(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemPlacement(),
name = stringResource(it.displayName), name = stringResource(it.basicUnit.displayName),
supportLabel = supportLabel(it), supportLabel = supportLabel(it),
isFavorite = it.isFavorite, isFavorite = it.stats.isFavorite,
isSelected = it.id == currentUnitId, isSelected = it.basicUnit.id == currentUnitId,
onClick = { onClick(it) }, onClick = { onClick(it) },
favoriteUnit = { favoriteUnit(it) }, favoriteUnit = { favoriteUnit(it) },
) )
} }
} }
} }
UnitSearchResult.Empty -> SearchPlaceholder(
onButtonClick = navigateToUnitGroups,
supportText = stringResource(R.string.converter_no_results_support),
buttonLabel = stringResource(R.string.open_settings_label),
)
UnitSearchResult.Loading -> Unit
} }
} }
} }
@ -89,7 +88,7 @@ internal fun UnitsList(
@Composable @Composable
private fun PreviewUnitsList() { private fun PreviewUnitsList() {
val resources = LocalContext.current.resources val resources = LocalContext.current.resources
val groupedUnits: Map<UnitGroup, List<AbstractUnit>> = mapOf( val units: Map<UnitGroup, List<UnitSearchResultItem>> = mapOf(
UnitGroup.LENGTH to listOf( UnitGroup.LENGTH to listOf(
NormalUnit(UnitID.meter, BigDecimal("1000000000000000000"), UnitGroup.LENGTH, R.string.unit_meter, R.string.unit_meter_short), NormalUnit(UnitID.meter, BigDecimal("1000000000000000000"), UnitGroup.LENGTH, R.string.unit_meter, R.string.unit_meter_short),
NormalUnit(UnitID.kilometer, BigDecimal("1000000000000000000000"), UnitGroup.LENGTH, R.string.unit_kilometer, R.string.unit_kilometer_short), NormalUnit(UnitID.kilometer, BigDecimal("1000000000000000000000"), UnitGroup.LENGTH, R.string.unit_kilometer, R.string.unit_kilometer_short),
@ -98,15 +97,16 @@ private fun PreviewUnitsList() {
NormalUnit(UnitID.foot, BigDecimal("304800000000000000"), UnitGroup.LENGTH, R.string.unit_foot, R.string.unit_foot_short), NormalUnit(UnitID.foot, BigDecimal("304800000000000000"), UnitGroup.LENGTH, R.string.unit_foot, R.string.unit_foot_short),
NormalUnit(UnitID.yard, BigDecimal("914400000000000000"), UnitGroup.LENGTH, R.string.unit_yard, R.string.unit_yard_short), NormalUnit(UnitID.yard, BigDecimal("914400000000000000"), UnitGroup.LENGTH, R.string.unit_yard, R.string.unit_yard_short),
NormalUnit(UnitID.mile, BigDecimal("1609344000000000000000"), UnitGroup.LENGTH, R.string.unit_mile, R.string.unit_mile_short), NormalUnit(UnitID.mile, BigDecimal("1609344000000000000000"), UnitGroup.LENGTH, R.string.unit_mile, R.string.unit_mile_short),
), )
.map { UnitSearchResultItem(it, UnitsEntity(unitId = it.id), null) },
) )
UnitsList( UnitsList(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
searchResult = UnitSearchResult.Success(units = groupedUnits), searchResult = units,
navigateToUnitGroups = {}, navigateToUnitGroups = {},
currentUnitId = UnitID.mile, currentUnitId = UnitID.mile,
supportLabel = { resources.getString(it.shortName) }, supportLabel = { resources.getString(it.basicUnit.shortName) },
onClick = {}, onClick = {},
favoriteUnit = {}, favoriteUnit = {},
) )

View File

@ -28,11 +28,9 @@ import androidx.navigation.navDeepLink
import com.sadellie.unitto.core.ui.model.DrawerItem import com.sadellie.unitto.core.ui.model.DrawerItem
import com.sadellie.unitto.core.ui.unittoComposable import com.sadellie.unitto.core.ui.unittoComposable
import com.sadellie.unitto.core.ui.unittoNavigation import com.sadellie.unitto.core.ui.unittoNavigation
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.feature.converter.ConverterRoute import com.sadellie.unitto.feature.converter.ConverterRoute
import com.sadellie.unitto.feature.converter.ConverterViewModel import com.sadellie.unitto.feature.converter.ConverterViewModel
import com.sadellie.unitto.feature.converter.CurrencyRateUpdateState
import com.sadellie.unitto.feature.converter.UnitConverterUIState
import com.sadellie.unitto.feature.converter.UnitFromSelectorRoute import com.sadellie.unitto.feature.converter.UnitFromSelectorRoute
import com.sadellie.unitto.feature.converter.UnitToSelectorRoute import com.sadellie.unitto.feature.converter.UnitToSelectorRoute
@ -47,7 +45,7 @@ internal const val UNIT_TO_ID_ARG = "unitToIdArg"
internal const val INPUT_ARG = "inputArg" internal const val INPUT_ARG = "inputArg"
private const val UNIT_FROM_ROUTE = "$UNIT_FROM/{$UNIT_FROM_ID_ARG}/{$UNIT_GROUP_ARG}" private const val UNIT_FROM_ROUTE = "$UNIT_FROM/{$UNIT_FROM_ID_ARG}/{$UNIT_GROUP_ARG}"
private const val UNIT_TO_ROUTE = "$UNIT_TO/{$UNIT_FROM_ID_ARG}/{$UNIT_TO_ID_ARG}/{$INPUT_ARG}" private const val UNIT_TO_ROUTE = "$UNIT_TO/{$UNIT_FROM_ID_ARG}/{$UNIT_TO_ID_ARG}/{$UNIT_GROUP_ARG}/{$INPUT_ARG}"
private fun NavHostController.navigateLeft( private fun NavHostController.navigateLeft(
unitFromId: String, unitFromId: String,
unitGroup: UnitGroup, unitGroup: UnitGroup,
@ -56,8 +54,9 @@ private fun NavHostController.navigateLeft(
private fun NavHostController.navigateRight( private fun NavHostController.navigateRight(
unitFromId: String, unitFromId: String,
unitToId: String, unitToId: String,
input: String?, unitGroup: UnitGroup,
) = navigate("$UNIT_TO/$unitFromId/$unitToId/$input") input: String,
) = navigate("$UNIT_TO/$unitFromId/$unitToId/$unitGroup/${input.ifEmpty { null }}")
fun NavGraphBuilder.converterGraph( fun NavGraphBuilder.converterGraph(
openDrawer: () -> Unit, openDrawer: () -> Unit,
@ -80,54 +79,8 @@ fun NavGraphBuilder.converterGraph(
ConverterRoute( ConverterRoute(
viewModel = parentViewModel, viewModel = parentViewModel,
// Navigation logic is here, but should actually be in ConverterScreen navigateToLeftScreen = navController::navigateLeft,
navigateToLeftScreen = { uiState: UnitConverterUIState -> navigateToRightScreen = navController::navigateRight,
when (uiState) {
is UnitConverterUIState.Default ->
navController
.navigateLeft(uiState.unitFrom.id, uiState.unitFrom.group)
is UnitConverterUIState.NumberBase ->
navController
.navigateLeft(uiState.unitFrom.id, uiState.unitFrom.group)
else -> Unit
}
},
navigateToRightScreen = { uiState: UnitConverterUIState ->
when (uiState) {
is UnitConverterUIState.Default -> {
// Don't allow converting if still loading currencies
val convertingCurrencies = uiState.unitFrom.group == UnitGroup.CURRENCY
val currenciesReady =
uiState.currencyRateUpdateState is CurrencyRateUpdateState.Ready
val input: String? = if (convertingCurrencies and !currenciesReady) {
null
} else {
(uiState.calculation?.toPlainString() ?: uiState.input1.text)
.ifEmpty { null }
}
navController.navigateRight(
uiState.unitFrom.id,
uiState.unitTo.id,
input,
)
}
is UnitConverterUIState.NumberBase -> {
val input = uiState.input.text.ifEmpty { null }
navController.navigateRight(
uiState.unitFrom.id,
uiState.unitTo.id,
input,
)
}
UnitConverterUIState.Loading -> Unit
}
},
openDrawer = openDrawer, openDrawer = openDrawer,
) )
} }
@ -166,6 +119,9 @@ fun NavGraphBuilder.converterGraph(
navArgument(UNIT_TO_ID_ARG) { navArgument(UNIT_TO_ID_ARG) {
type = NavType.StringType type = NavType.StringType
}, },
navArgument(UNIT_GROUP_ARG) {
type = NavType.EnumType(UnitGroup::class.java)
},
navArgument(INPUT_ARG) { navArgument(INPUT_ARG) {
type = NavType.StringType type = NavType.StringType
nullable = true nullable = true

View File

@ -1,134 +0,0 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.feature.converter
import android.content.Context
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.Token
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import java.math.BigDecimal
@RunWith(RobolectricTestRunner::class)
class ConverterUIStateKtTest {
@Test
fun format() {
val formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD)
var basicValue = BigDecimal("1")
val mContext: Context = RuntimeEnvironment.getApplication().applicationContext
fun String.formatTime() = formatTime(basicValue.multiply(BigDecimal(this)))
.format(mContext, formatterSymbols)
// Edgy things (minus, decimals and zeros)
Assert.assertEquals("28as", "-28".formatTime())
Assert.assertEquals("0.05as", "-0.05".formatTime())
Assert.assertEquals("0", "0".formatTime())
basicValue = BigDecimal("86400000000000000000000")
Assert.assertEquals("28d", "-28".formatTime())
Assert.assertEquals("1h 12m", "-0.05".formatTime())
Assert.assertEquals("0", "0".formatTime())
Assert.assertEquals("0", "-0".formatTime())
// DAYS
basicValue = BigDecimal("86400000000000000000000")
Assert.assertEquals("12h", "0.5".formatTime())
Assert.assertEquals("1h 12m", "0.05".formatTime())
Assert.assertEquals("7m 12s", "0.005".formatTime())
Assert.assertEquals("28d", "28".formatTime())
Assert.assertEquals("90d", "90".formatTime())
Assert.assertEquals("90d 12h", "90.5".formatTime())
Assert.assertEquals("90d 7m 12s", "90.005".formatTime())
// HOURS
basicValue = BigDecimal("3600000000000000000000")
Assert.assertEquals("30m", "0.5".formatTime())
Assert.assertEquals("3m", "0.05".formatTime())
Assert.assertEquals("18s", "0.005".formatTime())
Assert.assertEquals("1d 4h", "28".formatTime())
Assert.assertEquals("3d 18h", "90".formatTime())
Assert.assertEquals("3d 18h 30m", "90.5".formatTime())
Assert.assertEquals("3d 18h 18s", "90.005".formatTime())
// MINUTES
basicValue = BigDecimal("60000000000000000000")
Assert.assertEquals("30s", "0.5".formatTime())
Assert.assertEquals("3s", "0.05".formatTime())
Assert.assertEquals("300ms", "0.005".formatTime())
Assert.assertEquals("28m", "28".formatTime())
Assert.assertEquals("1h 30m", "90".formatTime())
Assert.assertEquals("1h 30m 30s", "90.5".formatTime())
Assert.assertEquals("1h 30m 300ms", "90.005".formatTime())
// SECONDS
basicValue = BigDecimal("1000000000000000000")
Assert.assertEquals("500ms", "0.5".formatTime())
Assert.assertEquals("50ms", "0.05".formatTime())
Assert.assertEquals("5ms", "0.005".formatTime())
Assert.assertEquals("28s", "28".formatTime())
Assert.assertEquals("1m 30s", "90".formatTime())
Assert.assertEquals("1m 30s 500ms", "90.5".formatTime())
Assert.assertEquals("1m 30s 5ms", "90.005".formatTime())
// MILLISECONDS
basicValue = BigDecimal("1000000000000000")
Assert.assertEquals("500µs", "0.5".formatTime())
Assert.assertEquals("50µs", "0.05".formatTime())
Assert.assertEquals("5µs", "0.005".formatTime())
Assert.assertEquals("28ms", "28".formatTime())
Assert.assertEquals("90ms", "90".formatTime())
Assert.assertEquals("90ms 500µs", "90.5".formatTime())
Assert.assertEquals("90ms 5µs", "90.005".formatTime())
// MICROSECONDS
basicValue = BigDecimal("1000000000000")
Assert.assertEquals("500ns", "0.5".formatTime())
Assert.assertEquals("50ns", "0.05".formatTime())
Assert.assertEquals("5ns", "0.005".formatTime())
Assert.assertEquals("28µs", "28".formatTime())
Assert.assertEquals("90µs", "90".formatTime())
Assert.assertEquals("90µs 500ns", "90.5".formatTime())
Assert.assertEquals("90µs 5ns", "90.005".formatTime())
// NANOSECONDS
basicValue = BigDecimal("1000000000")
Assert.assertEquals("500 000 000as", "0.5".formatTime())
Assert.assertEquals("50 000 000as", "0.05".formatTime())
Assert.assertEquals("5 000 000as", "0.005".formatTime())
Assert.assertEquals("28ns", "28".formatTime())
Assert.assertEquals("90ns", "90".formatTime())
Assert.assertEquals("90ns 500 000 000as", "90.5".formatTime())
Assert.assertEquals("90ns 5 000 000as", "90.005".formatTime())
// ATTOSECONDS
basicValue = BigDecimal("1")
Assert.assertEquals("0.5as", "0.5".formatTime())
Assert.assertEquals("0.05as", "0.05".formatTime())
Assert.assertEquals("0.005as", "0.005".formatTime())
Assert.assertEquals("28as", "28".formatTime())
Assert.assertEquals("90as", "90".formatTime())
Assert.assertEquals("90.5as", "90.5".formatTime())
Assert.assertEquals("90.005as", "90.005".formatTime())
}
}

View File

@ -42,8 +42,8 @@ import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.ListItem import com.sadellie.unitto.core.ui.common.ListItem
import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.NavigateUpButton
import com.sadellie.unitto.core.ui.common.ScaffoldWithLargeTopBar import com.sadellie.unitto.core.ui.common.ScaffoldWithLargeTopBar
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.converter.UnitsListSorting
import com.sadellie.unitto.data.model.userprefs.ConverterPreferences import com.sadellie.unitto.data.model.userprefs.ConverterPreferences
import com.sadellie.unitto.data.userprefs.ConverterPreferencesImpl import com.sadellie.unitto.data.userprefs.ConverterPreferencesImpl
import com.sadellie.unitto.feature.settings.components.AlertDialogWithList import com.sadellie.unitto.feature.settings.components.AlertDialogWithList

View File

@ -21,7 +21,7 @@ package com.sadellie.unitto.feature.settings.converter
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.common.stateIn
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.converter.UnitsListSorting
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -56,7 +56,7 @@ import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.Header
import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.NavigateUpButton
import com.sadellie.unitto.core.ui.common.ScaffoldWithLargeTopBar import com.sadellie.unitto.core.ui.common.ScaffoldWithLargeTopBar
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import org.burnoutcrew.reorderable.ReorderableItem import org.burnoutcrew.reorderable.ReorderableItem
import org.burnoutcrew.reorderable.detectReorder import org.burnoutcrew.reorderable.detectReorder
import org.burnoutcrew.reorderable.detectReorderAfterLongPress import org.burnoutcrew.reorderable.detectReorderAfterLongPress

View File

@ -18,7 +18,7 @@
package com.sadellie.unitto.feature.settings.unitgroups package com.sadellie.unitto.feature.settings.unitgroups
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
internal sealed class UnitGroupsUIState { internal sealed class UnitGroupsUIState {
data object Loading : UnitGroupsUIState() data object Loading : UnitGroupsUIState()

View File

@ -21,7 +21,7 @@ package com.sadellie.unitto.feature.settings.unitgroups
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.common.stateIn
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.converter.UnitGroup
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map