diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/unitconverter/UnitConverterResults.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/unitconverter/UnitConverterResults.kt
index be5ac712b..1c638fce5 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/unitconverter/UnitConverterResults.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/unitconverter/UnitConverterResults.kt
@@ -39,6 +39,7 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
+import de.mm20.launcher2.icons.Cube
import de.mm20.launcher2.search.data.CurrencyUnitConverter
import de.mm20.launcher2.search.data.UnitConverter
import de.mm20.launcher2.ui.R
@@ -220,7 +221,7 @@ fun getDimensionIcon(dimension: Dimension): ImageVector {
Dimension.Mass -> Icons.Rounded.FitnessCenter
Dimension.Length -> Icons.Rounded.Straighten
Dimension.Velocity -> Icons.Rounded.Speed
- Dimension.Volume -> TODO()
+ Dimension.Volume -> Icons.Rounded.Cube
Dimension.Area -> Icons.Rounded.SquareFoot
Dimension.Currency -> Icons.Rounded.Toll
Dimension.Data -> Icons.Rounded.Storage
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/searchactions/EditSearchActionSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/searchactions/EditSearchActionSheet.kt
index 6e8346489..7bfd12748 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/searchactions/EditSearchActionSheet.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/searchactions/EditSearchActionSheet.kt
@@ -714,7 +714,7 @@ fun CustomizeCustomIntent(viewModel: EditSearchActionSheetVM) {
supportingText = {
Text(
if (action.queryKey == null) {
- "The URI template that is used to construct the intent\\'s data URI. Use ‘\${1}’ as a placeholder for the actual search term, e.g. geo:0,0?q=,\${1}"
+ "The URI template that is used to construct the intent\\'s data URI. Use ‘\${1}’ as a placeholder for the actual search term"
} else {
"The template that is used to construct the string that is passed to the intent as a string extra. Use ‘\${1}’ as a placeholder for the actual search term"
}
diff --git a/core/base/src/main/java/de/mm20/launcher2/icons/Icons.kt b/core/base/src/main/java/de/mm20/launcher2/icons/Icons.kt
index 00e495a48..bc1553905 100644
--- a/core/base/src/main/java/de/mm20/launcher2/icons/Icons.kt
+++ b/core/base/src/main/java/de/mm20/launcher2/icons/Icons.kt
@@ -2305,3 +2305,47 @@ private val _Climbing = materialIcon("Icons.Rounded.Climbing") {
val Icons.Rounded.Climbing
get() = _Climbing
+
+private val _Cube = materialIcon("Icons.Rounded.Cube") {
+ materialPath {
+ moveTo(11f, 19.425f)
+ verticalLineToRelative(-6.85f)
+ lineTo(5f, 9.1f)
+ verticalLineToRelative(6.85f)
+ close()
+ moveToRelative(2f, 0f)
+ lineToRelative(6f, -3.475f)
+ verticalLineTo(9.1f)
+ lineToRelative(-6f, 3.475f)
+ close()
+ moveTo(12f, 10.85f)
+ lineTo(17.925f, 7.425f)
+ lineTo(12f, 4f)
+ lineTo(6.075f, 7.425f)
+ close()
+ moveTo(4f, 17.7f)
+ quadTo(3.525f, 17.425f, 3.2625f, 16.975f)
+ quadTo(3f, 16.525f, 3f, 15.975f)
+ verticalLineToRelative(-7.95f)
+ quadToRelative(0f, -0.55f, 0.2625f, -1f)
+ quadTo(3.525f, 6.575f, 4f, 6.3f)
+ lineTo(11f, 2.275f)
+ quadTo(11.475f, 2f, 12f, 2f)
+ quadTo(12.525f, 2f, 13f, 2.275f)
+ lineTo(20f, 6.3f)
+ quadTo(20.475f, 6.575f, 20.7375f, 7.025f)
+ quadTo(21f, 7.475f, 21f, 8.025f)
+ verticalLineToRelative(7.95f)
+ quadToRelative(0f, 0.55f, -0.2625f, 1f)
+ quadTo(20.475f, 17.425f, 20f, 17.7f)
+ lineToRelative(-7f, 4.025f)
+ quadTo(12.525f, 22f, 12f, 22f)
+ quadTo(11.475f, 22f, 11f, 21.725f)
+ close()
+ moveTo(12f, 12f)
+ close()
+ }
+}
+
+val Icons.Rounded.Cube
+ get() = _Cube
\ No newline at end of file
diff --git a/core/i18n/src/main/res/values-en-rGB/units.xml b/core/i18n/src/main/res/values-en-rGB/units.xml
new file mode 100644
index 000000000..8e85892ee
--- /dev/null
+++ b/core/i18n/src/main/res/values-en-rGB/units.xml
@@ -0,0 +1,18 @@
+
+
+ gal
+
+ - gallon
+ - gallons
+
+ pt
+
+ - pint
+ - pints
+
+ fl oz
+
+ - fluid ounce
+ - fluid ounces
+
+
\ No newline at end of file
diff --git a/core/i18n/src/main/res/values-en-rUS/units.xml b/core/i18n/src/main/res/values-en-rUS/units.xml
new file mode 100644
index 000000000..7f6aeedcf
--- /dev/null
+++ b/core/i18n/src/main/res/values-en-rUS/units.xml
@@ -0,0 +1,18 @@
+
+
+ gal
+
+ - gallon
+ - gallons
+
+ pt
+
+ - pint
+ - pints
+
+ fl oz
+
+ - fluid ounce
+ - fluid ounces
+
+
\ No newline at end of file
diff --git a/core/i18n/src/main/res/values/units.xml b/core/i18n/src/main/res/values/units.xml
index d80ac70c8..189d729f2 100644
--- a/core/i18n/src/main/res/values/units.xml
+++ b/core/i18n/src/main/res/values/units.xml
@@ -294,4 +294,92 @@
- kelvin
- kelvins
+
+ l
+
+ - liter
+ - liters
+
+ ml
+
+ - milliliter
+ - milliliters
+
+ m³
+
+ - cubic meter
+ - cubic meters
+
+ cm³
+
+ - cubic centimeter
+ - cubic centimeters
+
+ mm³
+
+ - cubic millimeter
+ - cubic millimeters
+
+ cuin
+
+ - cubic inch
+ - cubic inches
+
+ cuft
+
+ - cubic foot
+ - cubic feet
+
+ cuyd
+
+ - cubic yard
+ - cubic yards
+
+ gal US
+
+ - gallon (US)
+ - gallons (US)
+
+ gal imp
+
+ - gallon (imperial)
+ - gallons (imperial)
+
+ pt US
+
+ - pint (US)
+ - pints (US)
+
+ pt imp
+
+ - pint (imperial)
+ - pints (imperial)
+
+ fl.oz.US
+
+ - US fluid ounce
+ - US fluid ounces
+
+ fl.oz.imp
+
+ - fluid ounce (imperial)
+ - fluid ounces (imperial)
+
+ cup
+
+ - cup (US)
+ - cups (US)
+
+ tbsp
+
+ - tablespoon (US)
+ - tablespoons (US)
+
+ tsp
+
+ - teaspoon (US)
+ - teaspoons (US)
+
+
+
\ No newline at end of file
diff --git a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt
index ef8d99f8e..e8aa795ac 100644
--- a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt
+++ b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt
@@ -13,13 +13,13 @@ import de.mm20.launcher2.unitconverter.converters.MassConverter
import de.mm20.launcher2.unitconverter.converters.TemperatureConverter
import de.mm20.launcher2.unitconverter.converters.TimeConverter
import de.mm20.launcher2.unitconverter.converters.VelocityConverter
+import de.mm20.launcher2.unitconverter.converters.VolumeConverter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -27,7 +27,7 @@ import org.koin.core.component.KoinComponent
interface UnitConverterRepository {
fun search(query: String): Flow
- suspend fun getAvailableConverters(includeCurrencies: Boolean) : List
+ suspend fun getAvailableConverters(includeCurrencies: Boolean): List
}
internal class UnitConverterRepositoryImpl(
@@ -56,7 +56,7 @@ internal class UnitConverterRepositoryImpl(
}
}
- override suspend fun getAvailableConverters(includeCurrencies: Boolean) : List {
+ override suspend fun getAvailableConverters(includeCurrencies: Boolean): List {
val converters = mutableListOf(
MassConverter(context),
LengthConverter(context),
@@ -64,7 +64,8 @@ internal class UnitConverterRepositoryImpl(
TimeConverter(context),
VelocityConverter(context),
AreaConverter(context),
- TemperatureConverter(context)
+ TemperatureConverter(context),
+ VolumeConverter(context),
)
if (includeCurrencies) converters.add(CurrencyConverter(currencyRepository))
@@ -75,28 +76,51 @@ internal class UnitConverterRepositoryImpl(
query: String,
includeCurrencies: Boolean
): UnitConverter? {
- if (!query.matches(Regex("[0-9,.:]+ [^\\s]+")) &&
- !query.matches(Regex("[0-9,.:]+ [^\\s]+ >> [^\\s]+")) &&
- !query.matches(Regex("[0-9,.:]+ [^\\s]+ > [^\\s]+")) &&
- !query.matches(Regex("[0-9,.:]+ [^\\s]+ - [^\\s]+"))) return null
- val valueStr: String
- val unitStr: String
- val targetUnitStr: String?
+ val regex = Regex("""([+\-]?[\d+\-e,.]+|[^\d>\-]+)""")
- query.split(" ").also {
- valueStr = it.get(0)
- unitStr = it.get(1)
- targetUnitStr = it.getOrNull(3)
+ val matches = regex.findAll(query)
+
+ var inputStr: String? = null
+ var inputValue: Double? = null
+ var inputUnit: String? = null
+ var outputUnit: String? = null
+
+ for ((i, match) in matches.withIndex()) {
+ when (i) {
+ 0 -> {
+ inputStr = match.value.trim()
+ inputValue = inputStr.toDoubleOrNull()
+ ?: inputStr.replace(',', '.').toDoubleOrNull()
+ ?: return null
+ }
+ 1 -> inputUnit = match.value.trim()
+ 2 -> {
+ if (!match.value.contains("-") && !match.value.contains(">")) {
+ outputUnit = match.value.trim()
+ }
+ }
+ 3 -> {
+ if (outputUnit == null) {
+ outputUnit = match.value.trim()
+ break
+ } else {
+ return null
+ }
+ }
+ else -> return null
+ }
+ }
+
+ if (inputValue == null || inputUnit == null) {
+ return null
}
- val value = valueStr.toDoubleOrNull() ?: valueStr.replace(',', '.').toDoubleOrNull()
- ?: return null
val converters = getAvailableConverters(includeCurrencies)
for (converter in converters) {
- if (!converter.isValidUnit(unitStr)) continue
- if (targetUnitStr != null && !converter.isValidUnit(targetUnitStr)) continue
- return converter.convert(context, unitStr, value, targetUnitStr)
+ if (!converter.isValidUnit(inputUnit)) continue
+ if (outputUnit != null && !converter.isValidUnit(outputUnit)) continue
+ return converter.convert(context, inputUnit, inputValue, outputUnit)
}
return null
}
diff --git a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/VolumeConverter.kt b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/VolumeConverter.kt
new file mode 100644
index 000000000..db8bdfee9
--- /dev/null
+++ b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/VolumeConverter.kt
@@ -0,0 +1,107 @@
+package de.mm20.launcher2.unitconverter.converters
+
+import android.content.Context
+import de.mm20.launcher2.unitconverter.Dimension
+import de.mm20.launcher2.unitconverter.R
+
+internal class VolumeConverter(context: Context) : SimpleFactorConverter() {
+ override val dimension: Dimension = Dimension.Volume
+
+ override val standardUnits = listOf(
+ // SI units
+ MeasureUnitWithFactor(
+ factor = 1.0,
+ symbol = context.getString(R.string.unit_cubic_meter_symbol),
+ nameResource = R.plurals.unit_cubic_meter
+ ),
+ MeasureUnitWithFactor(
+ factor = 1_000_000.0,
+ symbol = context.getString(R.string.unit_cubic_centimeter_symbol),
+ nameResource = R.plurals.unit_cubic_centimeter
+ ),
+ MeasureUnitWithFactor(
+ factor = 1_000_000_000.0,
+ symbol = context.getString(R.string.unit_cubic_millimeter_symbol),
+ nameResource = R.plurals.unit_cubic_millimeter
+ ),
+ // Other metric units
+ MeasureUnitWithFactor(
+ factor = 1000.0,
+ symbol = context.getString(R.string.unit_liter_symbol),
+ nameResource = R.plurals.unit_liter
+ ),
+ MeasureUnitWithFactor(
+ factor = 1_000_000.0,
+ symbol = context.getString(R.string.unit_milliliter_symbol),
+ nameResource = R.plurals.unit_milliliter
+ ),
+
+ // Imperial geometric units
+ MeasureUnitWithFactor(
+ factor = 61023.74409473228,
+ symbol = context.getString(R.string.unit_cubic_inch_symbol),
+ nameResource = R.plurals.unit_cubic_inch
+ ),
+ MeasureUnitWithFactor(
+ factor = 61023.74409473228 / 1728.0,
+ symbol = context.getString(R.string.unit_cubic_foot_symbol),
+ nameResource = R.plurals.unit_cubic_foot
+ ),
+ MeasureUnitWithFactor(
+ factor = 61023.74409473228 / (1728.0 * 27.0),
+ symbol = context.getString(R.string.unit_cubic_yard_symbol),
+ nameResource = R.plurals.unit_cubic_yard
+ ),
+
+ // US fluid units
+ MeasureUnitWithFactor(
+ factor = 264.1720523581484,
+ symbol = context.getString(R.string.unit_gallon_us_symbol),
+ nameResource = R.plurals.unit_gallon_us
+ ),
+ MeasureUnitWithFactor(
+ factor = 264.1720523581484 * 8,
+ symbol = context.getString(R.string.unit_pint_us_symbol),
+ nameResource = R.plurals.unit_pint_us
+ ),
+ MeasureUnitWithFactor(
+ factor = 264.1720523581484 * 16,
+ symbol = context.getString(R.string.unit_cup_symbol),
+ nameResource = R.plurals.unit_cup
+ ),
+ MeasureUnitWithFactor(
+ factor = 264.1720523581484 * 128,
+ symbol = context.getString(R.string.unit_fluid_ounce_us_symbol),
+ nameResource = R.plurals.unit_fluid_ounce_us
+ ),
+ MeasureUnitWithFactor(
+ factor = 264.1720523581484 * 256,
+ symbol = context.getString(R.string.unit_tablespoon_symbol),
+ nameResource = R.plurals.unit_tablespoon
+ ),
+ MeasureUnitWithFactor(
+ factor = 264.1720523581484 * 768,
+ symbol = context.getString(R.string.unit_teaspoon_symbol),
+ nameResource = R.plurals.unit_teaspoon
+ ),
+
+ // Imperial fluid units
+ MeasureUnitWithFactor(
+ factor = 219.9692482990878,
+ symbol = context.getString(R.string.unit_gallon_imp_symbol),
+ nameResource = R.plurals.unit_gallon_imp
+ ),
+ MeasureUnitWithFactor(
+ factor = 219.969248299087 * 8,
+ symbol = context.getString(R.string.unit_pint_imp_symbol),
+ nameResource = R.plurals.unit_pint_imp
+ ),
+ MeasureUnitWithFactor(
+ factor = 219.969248299087 * 128,
+ symbol = context.getString(R.string.unit_fluid_ounce_imp_symbol),
+ nameResource = R.plurals.unit_fluid_ounce_imp
+ ),
+
+
+ )
+}
\ No newline at end of file