diff --git a/app/src/main/java/com/sadellie/unitto/screens/common/UnittoButton.kt b/app/src/main/java/com/sadellie/unitto/screens/common/UnittoButton.kt
new file mode 100644
index 00000000..f95a624a
--- /dev/null
+++ b/app/src/main/java/com/sadellie/unitto/screens/common/UnittoButton.kt
@@ -0,0 +1,86 @@
+/*
+ * Unitto is a unit converter for Android
+ * Copyright (c) 2023 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 .
+ */
+
+package com.sadellie.unitto.screens.common
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ProvideTextStyle
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.semantics.Role
+
+@Composable
+fun UnittoButton(
+ onClick: () -> Unit,
+ onLongClick: (() -> Unit)?,
+ modifier: Modifier = Modifier,
+ shape: Shape,
+ containerColor: Color,
+ contentColor: Color,
+ border: BorderStroke? = null,
+ contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
+ interactionSource: MutableInteractionSource,
+ content: @Composable RowScope.() -> Unit
+) {
+ Surface(
+ modifier = modifier.clip(shape).combinedClickable(
+ onClick = onClick,
+ onLongClick = onLongClick,
+ interactionSource = interactionSource,
+ indication = rememberRipple(),
+ role = Role.Button,
+ ),
+ color = containerColor,
+ contentColor = contentColor,
+ border = border
+ ) {
+ CompositionLocalProvider(LocalContentColor provides contentColor) {
+ ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
+ Row(
+ Modifier
+ .defaultMinSize(
+ minWidth = ButtonDefaults.MinWidth,
+ minHeight = ButtonDefaults.MinHeight
+ )
+ .padding(contentPadding),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically,
+ content = content
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/sadellie/unitto/screens/main/MainViewModel.kt b/app/src/main/java/com/sadellie/unitto/screens/main/MainViewModel.kt
index 6533d726..c65c909c 100644
--- a/app/src/main/java/com/sadellie/unitto/screens/main/MainViewModel.kt
+++ b/app/src/main/java/com/sadellie/unitto/screens/main/MainViewModel.kt
@@ -47,6 +47,7 @@ import com.sadellie.unitto.data.OPERATORS
import com.sadellie.unitto.data.combine
import com.sadellie.unitto.data.preferences.UserPreferences
import com.sadellie.unitto.data.preferences.UserPreferencesRepository
+import com.sadellie.unitto.data.setMinimumRequiredScale
import com.sadellie.unitto.data.toStringWith
import com.sadellie.unitto.data.trimZeros
import com.sadellie.unitto.data.units.AbstractUnit
@@ -59,6 +60,8 @@ import com.sadellie.unitto.data.units.database.MyBasedUnitsRepository
import com.sadellie.unitto.data.units.remote.CurrencyApi
import com.sadellie.unitto.data.units.remote.CurrencyUnitResponse
import dagger.hilt.android.lifecycle.HiltViewModel
+import java.math.BigDecimal
+import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -73,9 +76,6 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import java.math.BigDecimal
-import java.math.RoundingMode
-import javax.inject.Inject
@HiltViewModel
class MainViewModel @Inject constructor(
@@ -391,107 +391,84 @@ class MainViewModel @Inject constructor(
}
private suspend fun convertInput() {
- if (_unitFrom.value?.group == UnitGroup.NUMBER_BASE) {
- convertAsNumberBase()
- } else {
- convertAsExpression()
- }
- }
-
- private suspend fun convertAsNumberBase() {
+ // Loading don't do anything
+ if ((_unitFrom.value == null) or (_unitTo.value == null)) return
withContext(Dispatchers.Default) {
while (isActive) {
- // Units are still loading, don't convert anything yet
- val unitFrom = _unitFrom.value ?: return@withContext
- val unitTo = _unitTo.value ?: return@withContext
-
- val conversionResult = try {
- (unitFrom as NumberBaseUnit).convertToBase(
- input = _input.value,
- toBase = (unitTo as NumberBaseUnit).base
- )
- } catch (e: Exception) {
- when (e) {
- is ClassCastException -> {
- cancel()
- return@withContext
- }
- is NumberFormatException, is IllegalArgumentException -> ""
- else -> throw e
- }
+ when (_unitFrom.value?.group) {
+ UnitGroup.NUMBER_BASE -> convertAsNumberBase()
+ else -> convertAsExpression()
}
- _result.update { conversionResult }
cancel()
}
}
}
- private suspend fun convertAsExpression() {
- withContext(Dispatchers.Default) {
- while (isActive) {
- // Units are still loading, don't convert anything yet
- val unitFrom = _unitFrom.value ?: return@withContext
- val unitTo = _unitTo.value ?: return@withContext
-
- // First we clean the input from garbage at the end
- var cleanInput = _input.value.dropLastWhile { !it.isDigit() }
-
- // Now we close open brackets that user didn't close
- // AUTOCLOSE ALL BRACKETS
- val leftBrackets = _input.value.count { it.toString() == KEY_LEFT_BRACKET }
- val rightBrackets = _input.value.count { it.toString() == KEY_RIGHT_BRACKET }
- val neededBrackets = leftBrackets - rightBrackets
- if (neededBrackets > 0) cleanInput += KEY_RIGHT_BRACKET.repeat(neededBrackets)
-
- // Now we evaluate expression in input
- val evaluationResult: BigDecimal = try {
- Expressions().eval(cleanInput)
- .setScale(_userPrefs.value.digitsPrecision, RoundingMode.HALF_EVEN)
- .trimZeros()
- } catch (e: Exception) {
- when (e) {
- is ExpressionException,
- is ArrayIndexOutOfBoundsException,
- is IndexOutOfBoundsException,
- is NumberFormatException,
- is ArithmeticException -> {
- // Invalid expression, can't do anything further
- cancel()
- return@withContext
- }
- else -> throw e
- }
- }
-
- // Evaluated. Hide calculated result if no expression entered.
- // 123.456 will be true
- // -123.456 will be true
- // -123.456-123 will be false (first minus gets removed, ending with 123.456)
- if (_input.value.removePrefix(KEY_MINUS).all { it.toString() !in OPERATORS }) {
- // No operators
- _calculated.update { null }
- } else {
- _calculated.update {
- evaluationResult.toStringWith(
- _userPrefs.value.outputFormat
- )
- }
- }
-
- // Now we just convert.
- // We can use evaluation result here, input is valid
- val conversionResult: BigDecimal = unitFrom.convert(
- unitTo,
- evaluationResult,
- _userPrefs.value.digitsPrecision
- )
-
- // Converted
- _result.update { conversionResult.toStringWith(_userPrefs.value.outputFormat) }
-
- cancel()
+ private fun convertAsNumberBase() {
+ val conversionResult: String = try {
+ (_unitFrom.value as NumberBaseUnit).convertToBase(
+ input = _input.value,
+ toBase = (_unitTo.value as NumberBaseUnit).base
+ )
+ } catch (e: Exception) {
+ when (e) {
+ is ClassCastException -> return
+ is NumberFormatException, is IllegalArgumentException -> ""
+ else -> throw e
}
}
+ _result.update { conversionResult }
+ }
+
+ private fun convertAsExpression() {
+ // First we clean the input from garbage at the end
+ var cleanInput = _input.value.dropLastWhile { !it.isDigit() }
+
+ // Now we close open brackets that user didn't close
+ // AUTO-CLOSE ALL BRACKETS
+ val leftBrackets = _input.value.count { it.toString() == KEY_LEFT_BRACKET }
+ val rightBrackets = _input.value.count { it.toString() == KEY_RIGHT_BRACKET }
+ val neededBrackets = leftBrackets - rightBrackets
+ if (neededBrackets > 0) cleanInput += KEY_RIGHT_BRACKET.repeat(neededBrackets)
+
+ // Now we evaluate expression in input
+ val evaluationResult: BigDecimal = try {
+ Expressions().eval(cleanInput)
+ } catch (e: Exception) {
+ when (e) {
+ is ArrayIndexOutOfBoundsException,
+ is IndexOutOfBoundsException,
+ is NumberFormatException,
+ is ExpressionException,
+ is ArithmeticException -> return
+ else -> throw e
+ }
+ }
+
+ // Now we just convert.
+ // We can use evaluation result here, input is valid
+ val conversionResult: BigDecimal = _unitFrom.value!!.convert(
+ _unitTo.value!!,
+ evaluationResult,
+ _userPrefs.value.digitsPrecision
+ )
+
+ // Evaluated. Hide calculated result if no expression entered.
+ // 123.456 will be true
+ // -123.456 will be true
+ // -123.456-123 will be false (first minus gets removed, ending with 123.456)
+ _calculated.update {
+ if (_input.value.removePrefix(KEY_MINUS).all { it.toString() !in OPERATORS }) {
+ null
+ } else {
+ evaluationResult
+ .setMinimumRequiredScale(_userPrefs.value.digitsPrecision)
+ .trimZeros()
+ .toStringWith(_userPrefs.value.outputFormat)
+ }
+ }
+
+ _result.update { conversionResult.toStringWith(_userPrefs.value.outputFormat) }
}
private fun setInputSymbols(symbol: String, add: Boolean = true) {
diff --git a/app/src/main/java/com/sadellie/unitto/screens/main/components/KeyboardButton.kt b/app/src/main/java/com/sadellie/unitto/screens/main/components/KeyboardButton.kt
index b63a2b08..5f19f3fb 100644
--- a/app/src/main/java/com/sadellie/unitto/screens/main/components/KeyboardButton.kt
+++ b/app/src/main/java/com/sadellie/unitto/screens/main/components/KeyboardButton.kt
@@ -26,7 +26,6 @@ import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -34,6 +33,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import com.sadellie.unitto.screens.common.UnittoButton
import com.sadellie.unitto.ui.theme.NumbersTextStyleTitleLarge
/**
@@ -51,7 +51,7 @@ fun KeyboardButton(
modifier: Modifier = Modifier,
digit: String,
isPrimary: Boolean = true,
- onLongClick: () -> Unit = {},
+ onLongClick: (() -> Unit)? = null,
onClick: (String) -> Unit = {}
) {
val interactionSource = remember { MutableInteractionSource() }
@@ -59,24 +59,22 @@ fun KeyboardButton(
val cornerRadius: Int by animateIntAsState(
targetValue = if (isPressed) 30 else 50,
animationSpec = tween(easing = FastOutSlowInEasing),
- finishedListener = { if (it == 30) onLongClick() })
+ )
- Button(
- modifier = modifier,
- interactionSource = interactionSource,
- shape = RoundedCornerShape(cornerRadius),
- colors = ButtonDefaults.buttonColors(
- containerColor = if (isPrimary) MaterialTheme.colorScheme.inverseOnSurface else MaterialTheme.colorScheme.primaryContainer,
- contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
- disabledContentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f)
- ),
+ UnittoButton(
onClick = { onClick(digit) },
- contentPadding = PaddingValues(0.dp)
+ onLongClick = onLongClick,
+ modifier = modifier,
+ shape = RoundedCornerShape(cornerRadius),
+ containerColor = if (isPrimary) MaterialTheme.colorScheme.inverseOnSurface else MaterialTheme.colorScheme.primaryContainer,
+ contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
+ contentPadding = PaddingValues(0.dp),
+ interactionSource = interactionSource,
) {
Text(
text = digit,
style = NumbersTextStyleTitleLarge,
- color = if (isPrimary) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.onSecondaryContainer,
+ color = if (isPrimary) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.onSecondaryContainer
)
}
}
diff --git a/app/src/main/java/com/sadellie/unitto/screens/setttings/AboutScreen.kt b/app/src/main/java/com/sadellie/unitto/screens/setttings/AboutScreen.kt
index decc9358..924b9363 100644
--- a/app/src/main/java/com/sadellie/unitto/screens/setttings/AboutScreen.kt
+++ b/app/src/main/java/com/sadellie/unitto/screens/setttings/AboutScreen.kt
@@ -59,7 +59,7 @@ fun AboutScreen(
}
UnittoLargeTopAppBar(
- title = "About Unitto",
+ title = stringResource(R.string.about_unitto),
navigateUpAction = navigateUpAction
) { padding ->
LazyColumn(contentPadding = padding) {
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index c77d4a34..6c1f96e7 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -661,7 +661,6 @@
Séparateur
Format de sortie
Groupes d\'unités
- Wrong currency rates?
Note
Les taux de change sont mis à jour quotidiennement. L\'application ne permet pas de suivre le marché en temps réel.
Termes et conditions
@@ -681,20 +680,10 @@
Période (42.069,12)
Virgule (42,069.12)
Espaces (42 069.12)
-
-
- Result value formatting
- Engineering strings look like 1E-21
Défaut
- Allow engineering
- Force engineering
-
-
- App look and feel
Auto
Clair
Sombre
- Color theme
AMOLED Noir
Utiliser un fond noir pour les thèmes sombres
Couleurs dynamiques
@@ -703,7 +692,6 @@
Chargement…
Erreur
- Copied %1$s!
Annuler
Rechercher des unités
Aucun résultat trouvé