Initial commit

This commit is contained in:
MM20
2021-09-18 23:37:52 +02:00
commit 749e4e3073
938 changed files with 50475 additions and 0 deletions
@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.mm20.launcher2.unitconverter">
/
</manifest>
@@ -0,0 +1,7 @@
package de.mm20.launcher2.search.data
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.UnitValue
class CurrencyUnitConverter(dimension: Dimension, inputValue: UnitValue, values: List<UnitValue>, val updateTimestamp: Long)
: UnitConverter(dimension, inputValue, values)
@@ -0,0 +1,51 @@
package de.mm20.launcher2.search.data
import android.content.Context
import android.os.Bundle
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.UnitValue
import de.mm20.launcher2.unitconverter.converters.*
open class UnitConverter(
val dimension: Dimension,
val inputValue: UnitValue,
val values: List<UnitValue>
) {
companion object {
suspend fun search(context: Context, query: String): UnitConverter? {
if (!LauncherPreferences.instance.searchUnitConverter) return null
if (!query.matches(Regex("[0-9,.:]+ [A-Za-z/²³°.]+")) && !query.matches(Regex("[0-9,.:]+ [A-Za-z/²³°.]+ >> [A-Za-z/²³°]+"))) return null
val valueStr: String
val unitStr: String
val targetUnitStr: String?
query.split(" ").also {
valueStr = it.get(0)
unitStr = it.get(1)
targetUnitStr = it.getOrNull(3)
}
val value = valueStr.toDoubleOrNull() ?: valueStr.replace(',', '.').toDoubleOrNull()
?: return null
val converters = listOf(
lazy { MassConverter(context) },
lazy { LengthConverter(context) },
lazy { CurrencyConverter(context) },
lazy { DataConverter(context) },
lazy { TimeConverter(context) },
lazy { VelocityConverter(context) },
lazy { AreaConverter(context) }
)
for (conv in converters) {
val converter = conv.value
if (!converter.isValidUnit(unitStr)) continue
if (targetUnitStr != null && !converter.isValidUnit(targetUnitStr)) continue
return converter.convert(context, unitStr, value, targetUnitStr)
}
return null
}
}
}
@@ -0,0 +1,17 @@
package de.mm20.launcher2.unitconverter
enum class Dimension {
Length,
Mass,
Velocity,
Volume,
Area,
Currency,
Data,
Bitrate,
Pressure,
Energy,
Frequency,
Temperature,
Time
}
@@ -0,0 +1,7 @@
package de.mm20.launcher2.unitconverter
data class MeasureUnit(
val factor: Double,
val symbol: String,
val nameResource: Int
)
@@ -0,0 +1,30 @@
package de.mm20.launcher2.unitconverter
import android.content.Context
import androidx.lifecycle.MutableLiveData
import de.mm20.launcher2.currencies.CurrencyRepository
import de.mm20.launcher2.search.BaseSearchableRepository
import de.mm20.launcher2.search.data.UnitConverter
class UnitConverterRepository private constructor(val context: Context) : BaseSearchableRepository() {
init {
CurrencyRepository.getInstance(context)
}
val unitConverter = MutableLiveData<UnitConverter?>()
override suspend fun search(query: String) {
unitConverter.value = UnitConverter.search(context, query)
}
companion object {
private lateinit var instance : UnitConverterRepository
fun getInstance(context: Context) : UnitConverterRepository {
if(!::instance.isInitialized) instance = UnitConverterRepository(context.applicationContext)
return instance
}
}
}
@@ -0,0 +1,8 @@
package de.mm20.launcher2.unitconverter
import android.app.Application
import androidx.lifecycle.AndroidViewModel
class UnitConverterViewModel(app: Application): AndroidViewModel(app) {
val unitConverter = UnitConverterRepository.getInstance(app).unitConverter
}
@@ -0,0 +1,8 @@
package de.mm20.launcher2.unitconverter
data class UnitValue(
val value: Double,
val symbol: String,
val formattedName: String,
val formattedValue: String
)
@@ -0,0 +1,58 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class AreaConverter(context: Context) : Converter() {
override val dimension = Dimension.Area
override val standardUnits = listOf(
MeasureUnit(
0.000001,
context.getString(R.string.unit_sqkilometer_symbol),
R.plurals.unit_sqkilometer
),
MeasureUnit(
0.0001,
context.getString(R.string.unit_hectare_symbol),
R.plurals.unit_hectare
),
MeasureUnit(
1.0,
context.getString(R.string.unit_sqmeter_symbol),
R.plurals.unit_sqmeter
),
MeasureUnit(
10000.0,
context.getString(R.string.unit_sqcentimeter_symbol),
R.plurals.unit_sqcentimeter
),
MeasureUnit(
1000000.0,
context.getString(R.string.unit_sqmillimeter_symbol),
R.plurals.unit_sqmillimeter
),
MeasureUnit(
100 * 100 / (2.54 * 2.54),
context.getString(R.string.unit_sqinch_symbol),
R.plurals.unit_sqinch
),
MeasureUnit(
100 * 100 / (2.54 * 2.54 * 144),
context.getString(R.string.unit_sqfoot_symbol),
R.plurals.unit_sqfoot
),
MeasureUnit(
100 * 100 / (2.54 * 2.54 * 1296),
context.getString(R.string.unit_sqyard_symbol),
R.plurals.unit_sqyard
),
MeasureUnit(
100 * 100 / (2.54 * 2.54 * 144 * 43560),
context.getString(R.string.unit_acre_symbol),
R.plurals.unit_acre
)
)
}
@@ -0,0 +1,59 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.search.data.UnitConverter
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.UnitValue
import java.text.DecimalFormat
import kotlin.math.abs
import kotlin.math.roundToInt
abstract class Converter {
abstract val dimension: Dimension
open val standardUnits: List<MeasureUnit> = emptyList()
/**
* Returns true if a symbol is a valid unit of this converter
*/
open suspend fun isValidUnit(symbol: String): Boolean {
return standardUnits.any { it.symbol == symbol }
}
open suspend fun convert(context: Context, fromUnit: String, value: Double, toUnit: String?): UnitConverter {
val results = mutableListOf<UnitValue>()
val unit = standardUnits.first { it.symbol == fromUnit }
if (toUnit == null) {
for (targetUnit in standardUnits) {
if (targetUnit.symbol == unit.symbol) continue
val v = value * targetUnit.factor / unit.factor
results += UnitValue(v, targetUnit.symbol, formatName(context, targetUnit, v), formatValue(context, unit, v))
}
} else {
val targetUnit = standardUnits.first { it.symbol == toUnit }
val v = value * targetUnit.factor / unit.factor
results += UnitValue(v, targetUnit.symbol, formatName(context, targetUnit, v), formatValue(context, unit, v))
}
val inputValue = UnitValue(value, fromUnit, formatName(context, unit, value), formatValue(context, unit, value))
return UnitConverter(dimension, inputValue, results)
}
open suspend fun formatName(context: Context, unit: MeasureUnit, value: Double): String {
val resId = unit.nameResource
val text = context.resources.getQuantityString(resId, value.roundToInt())
return text
}
open suspend fun formatValue(context: Context, unit: MeasureUnit, value: Double): String {
if (abs(value) > 1e5 || abs(value) < 1e-3) {
return DecimalFormat("#.###E0").apply {
}.format(value)
}
return DecimalFormat("#.###").apply {
}.format(value)
}
}
@@ -0,0 +1,98 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import android.icu.text.PluralRules
import android.icu.util.Currency
import android.os.Build
import de.mm20.launcher2.currencies.CurrencyRepository
import de.mm20.launcher2.search.data.CurrencyUnitConverter
import de.mm20.launcher2.search.data.UnitConverter
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.UnitValue
import java.text.DecimalFormat
import java.util.Locale
import java.util.Currency as JCurrency
import kotlin.math.abs
class CurrencyConverter(context: Context) : Converter() {
override val dimension: Dimension = Dimension.Currency
val repository = CurrencyRepository.getInstance(context)
private val topCurrencies = arrayOf("USD", "EUR", "JPY", "GBP", "AUD")
override suspend fun isValidUnit(symbol: String): Boolean {
return repository.isValidCurrency(symbol)
}
private fun formatName(symbol: String, value: Double): String {
val text = StringBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val currency = Currency.getInstance(symbol) ?: return formatNameFallback(symbol)
val pluralCount = PluralRules.forLocale(Locale.getDefault()).select(value)
text.append(currency.getName(Locale.getDefault(), Currency.PLURAL_LONG_NAME, pluralCount, booleanArrayOf(false)))
.append(" ")
.append(repository.getFlag(symbol))
} else {
val currency = JCurrency.getInstance(symbol) ?: return formatNameFallback(symbol)
text.append(currency.displayName)
.append(" ")
.append(repository.getFlag(symbol))
}
return text.toString()
}
private fun formatNameFallback(symbol: String): String {
return "$symbol ${repository.getFlag(symbol)}"
}
private fun formatValue(symbol: String, value: Double): String {
if (abs(value) > 1e10) {
return DecimalFormat("#.###E0").apply {
}.format(value)
}
val currency = JCurrency.getInstance(symbol)
return if (currency != null) {
val format = StringBuilder("#,##0")
val digits = currency.defaultFractionDigits
if (digits > 0) format.append(".")
for (i in 0 until digits) format.append("0")
DecimalFormat(format.toString()).format(value)
} else {
DecimalFormat("#,##0.00").format(value)
}
}
override suspend fun convert(context: Context, fromUnit: String, value: Double, toUnit: String?): UnitConverter {
val values = repository.convertCurrency(fromUnit, value, toUnit).map {
UnitValue(it.second, it.first, formatName(it.first, it.second), formatValue(it.first, it.second))
}.toMutableList()
val ownCurrencySymbol = JCurrency.getInstance(Locale.getDefault()).currencyCode ?: "USD"
val index = values.indexOfFirst { it.symbol == ownCurrencySymbol }
val ownCurrency = if (index != -1) {
values.removeAt(index)
} else {
null
}
values.sortBy {
val i = topCurrencies.indexOf(it.symbol)
if (i != -1) i.toString()
else it.formattedName
}
if (ownCurrency != null) {
values.add(0, ownCurrency)
}
val inputValue = UnitValue(value, fromUnit, formatName(fromUnit, value), formatValue(fromUnit, value))
val lastUpdate = repository.getLastUpdate(fromUnit)
return CurrencyUnitConverter(dimension, inputValue, values, lastUpdate)
}
}
@@ -0,0 +1,84 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class DataConverter(context: Context) : Converter() {
override val dimension = Dimension.Data
override val standardUnits = listOf(
MeasureUnit(
1.0,
context.getString(R.string.unit_byte_symbol),
R.plurals.unit_byte
),
MeasureUnit(
0.001,
context.getString(R.string.unit_kilobyte_symbol),
R.plurals.unit_kilobyte
),
MeasureUnit(
0.000001,
context.getString(R.string.unit_megabyte_symbol),
R.plurals.unit_megabyte
),
MeasureUnit(
0.000000001,
context.getString(R.string.unit_gigabyte_symbol),
R.plurals.unit_gigabyte
),
MeasureUnit(
0.000000000001,
context.getString(R.string.unit_terabyte_symbol),
R.plurals.unit_terabyte
),
MeasureUnit(
1.0 / 1024,
context.getString(R.string.unit_kibibyte_symbol),
R.plurals.unit_kibibyte
),
MeasureUnit(
1.0 / (1024 * 1024),
context.getString(R.string.unit_mebibyte_symbol),
R.plurals.unit_mebibyte
),
MeasureUnit(
1.0 / (1024 * 1024 * 1024),
context.getString(R.string.unit_gibibyte_symbol),
R.plurals.unit_gibibyte
),
MeasureUnit(
1.0 / (1024.0 * 1024 * 1024 * 1024),
context.getString(R.string.unit_tebibyte_symbol),
R.plurals.unit_tebibyte
),
MeasureUnit(
8.0,
context.getString(R.string.unit_bit_symbol),
R.plurals.unit_bit
),
MeasureUnit(
8.0 / 1000,
context.getString(R.string.unit_kilobit_symbol),
R.plurals.unit_kilobit
),
MeasureUnit(
8.0 / 1000000,
context.getString(R.string.unit_megabit_symbol),
R.plurals.unit_megabit
),
MeasureUnit(
8.0 / 1000000000,
context.getString(R.string.unit_gigabit_symbol),
R.plurals.unit_gigabit
),
MeasureUnit(
8.0 / 1000000000000,
context.getString(R.string.unit_terabit_symbol),
R.plurals.unit_terabit
)
)
}
@@ -0,0 +1,59 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class LengthConverter(context: Context) : Converter() {
override val dimension = Dimension.Length
override val standardUnits = listOf(
MeasureUnit(
1.0,
context.getString(R.string.unit_meter_symbol),
R.plurals.unit_meter
),
MeasureUnit(
0.001,
context.getString(R.string.unit_kilometer_symbol),
R.plurals.unit_kilometer
),
MeasureUnit(
100.0,
context.getString(R.string.unit_centimeter_symbol),
R.plurals.unit_centimeter
),
MeasureUnit(
1000.0,
context.getString(R.string.unit_millimeter_symbol),
R.plurals.unit_millimeter
),
MeasureUnit(
100 / 2.54,
context.getString(R.string.unit_inch_symbol),
R.plurals.unit_inch
),
MeasureUnit(
100 / (2.54 * 12),
context.getString(R.string.unit_foot_symbol),
R.plurals.unit_foot
),
MeasureUnit(
100 / (2.54 * 12 * 3),
context.getString(R.string.unit_yard_symbol),
R.plurals.unit_yard
),
MeasureUnit(
100 / (2.54 * 12 * 3 * 1760),
context.getString(R.string.unit_mile_symbol),
R.plurals.unit_mile
),
MeasureUnit(
1 / 1852.0,
context.getString(R.string.unit_nautic_mile_symbol),
R.plurals.unit_nautic_mile
)
)
}
@@ -0,0 +1,54 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class MassConverter(context: Context): Converter() {
override val dimension = Dimension.Mass
override val standardUnits = listOf(
MeasureUnit(
1.0,
context.getString(R.string.unit_kilogram_symbol),
R.plurals.unit_kilogram
),
MeasureUnit(
1000.0,
context.getString(R.string.unit_gram_symbol),
R.plurals.unit_gram
),
MeasureUnit(
0.001,
context.getString(R.string.unit_metric_ton_symbol),
R.plurals.unit_metric_ton
),
MeasureUnit(
1000.0 / (453.59237 * 2240.0),
context.getString(R.string.unit_long_ton_symbol),
R.plurals.unit_long_ton
),
MeasureUnit(
1000.0 / (453.59237 * 14.0),
context.getString(R.string.unit_stone_symbol),
R.plurals.unit_stone
),
MeasureUnit(
1000.0 / 453.59237,
context.getString(R.string.unit_pound_symbol),
R.plurals.unit_pound
),
MeasureUnit(
16.0 * 1000.0 / 453.59237,
context.getString(R.string.unit_ounce_symbol),
R.plurals.unit_ounce
),
MeasureUnit(
1000.0 / (453.59237 * 2000.0),
context.getString(R.string.unit_short_ton_symbol),
R.plurals.unit_short_ton
)
)
}
@@ -0,0 +1,8 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
class TemperatureConverter(context: Context): Converter() {
override val dimension = Dimension.Temperature
}
@@ -0,0 +1,44 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class TimeConverter(context: Context) : Converter() {
override val dimension = Dimension.Time
override val standardUnits = listOf(
MeasureUnit(
1.0,
context.getString(R.string.unit_second_symbol),
R.plurals.unit_second
),
MeasureUnit(
1000.0,
context.getString(R.string.unit_millisecond_symbol),
R.plurals.unit_millisecond
),
MeasureUnit(
1.0 / 60,
context.getString(R.string.unit_minute_symbol),
R.plurals.unit_minute
),
MeasureUnit(
1.0 / (60 * 60),
context.getString(R.string.unit_hour_symbol),
R.plurals.unit_hour
),
MeasureUnit(
1.0 / (60 * 60 * 24),
context.getString(R.string.unit_day_symbol),
R.plurals.unit_day
),
MeasureUnit(
1.0 / (60 * 60 * 24 * 365),
context.getString(R.string.unit_year_symbol),
R.plurals.unit_year
)
)
}
@@ -0,0 +1,34 @@
package de.mm20.launcher2.unitconverter.converters
import android.content.Context
import de.mm20.launcher2.unitconverter.Dimension
import de.mm20.launcher2.unitconverter.MeasureUnit
import de.mm20.launcher2.unitconverter.R
class VelocityConverter(context: Context) : Converter() {
override val dimension = Dimension.Velocity
override val standardUnits = listOf(
MeasureUnit(
1.0,
context.getString(R.string.unit_meter_per_second_symbol),
R.plurals.unit_meter_per_second
),
MeasureUnit(
3.6,
context.getString(R.string.unit_kilometer_per_hour_symbol),
R.plurals.unit_kilometer_per_hour
),
MeasureUnit(
3600.0 / 1609.344,
context.getString(R.string.unit_mile_per_hour_symbol),
R.plurals.unit_mile_per_hour
),
MeasureUnit(
3600.0 / 1852.0,
context.getString(R.string.unit_knot_symbol),
R.plurals.unit_knot
)
)
}