mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-23 18:50:31 +02:00
Refactor formatter
This commit is contained in:
parent
8d98cbfddf
commit
7150fc9133
@ -115,8 +115,7 @@ object Token {
|
|||||||
multiply, multiplyDisplay,
|
multiply, multiplyDisplay,
|
||||||
plus, minus, minusDisplay, divide, divideDisplay,
|
plus, minus, minusDisplay, divide, divideDisplay,
|
||||||
baseA, baseB, baseC, baseD, baseE, baseF,
|
baseA, baseB, baseC, baseD, baseE, baseF,
|
||||||
_1, _2, _3, _4, _5, _6, _7, _8, _9, _0,
|
_1, _2, _3, _4, _5, _6, _7, _8, _9, _0
|
||||||
dot
|
|
||||||
).sortedByDescending { it.length }
|
).sortedByDescending { it.length }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,9 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.core.ui
|
package com.sadellie.unitto.core.ui
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import android.content.Context
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import com.sadellie.unitto.core.base.Token
|
|
||||||
import com.sadellie.unitto.core.base.Separator
|
import com.sadellie.unitto.core.base.Separator
|
||||||
|
import com.sadellie.unitto.core.base.Token
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
|
|
||||||
@ -90,8 +89,6 @@ open class UnittoFormatter {
|
|||||||
if (input.contains(Token.E)) return input.replace(Token.dot, fractional)
|
if (input.contains(Token.E)) return input.replace(Token.dot, fractional)
|
||||||
|
|
||||||
var output = input
|
var output = input
|
||||||
|
|
||||||
// We may receive expressions. Find all numbers in this expression
|
|
||||||
val allNumbers: List<String> = input.getOnlyNumbers()
|
val allNumbers: List<String> = input.getOnlyNumbers()
|
||||||
|
|
||||||
allNumbers.forEach {
|
allNumbers.forEach {
|
||||||
@ -116,7 +113,11 @@ open class UnittoFormatter {
|
|||||||
// Remove grouping
|
// Remove grouping
|
||||||
// 12345,6789
|
// 12345,6789
|
||||||
// Replace fractional with "." because formatter accepts only numbers where fractional is a dot
|
// Replace fractional with "." because formatter accepts only numbers where fractional is a dot
|
||||||
return format(removeFormat(input))
|
return format(
|
||||||
|
input
|
||||||
|
.replace(grouping, "")
|
||||||
|
.replace(fractional, Token.dot)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,17 +130,67 @@ open class UnittoFormatter {
|
|||||||
Separator.COMMA -> COMMA
|
Separator.COMMA -> COMMA
|
||||||
else -> SPACE
|
else -> SPACE
|
||||||
}
|
}
|
||||||
|
.also { if (it == grouping) return input }
|
||||||
val sFractional = if (separator == Separator.PERIOD) Token.comma else Token.dot
|
val sFractional = if (separator == Separator.PERIOD) Token.comma else Token.dot
|
||||||
|
|
||||||
return input
|
return input
|
||||||
.replace(sGrouping, grouping)
|
.replace(sGrouping, "\t")
|
||||||
.replace(sFractional, fractional)
|
.replace(sFractional, fractional)
|
||||||
|
.replace("\t", grouping)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeFormat(input: String): String {
|
fun toSeparator(input: String, separator: Int): String {
|
||||||
return input
|
val output = filterUnknownSymbols(input).replace(fractional, Token.dot)
|
||||||
.replace(grouping, "")
|
val sGrouping = when (separator) {
|
||||||
.replace(fractional, Token.dot)
|
Separator.PERIOD -> PERIOD
|
||||||
|
Separator.COMMA -> COMMA
|
||||||
|
else -> SPACE
|
||||||
|
}
|
||||||
|
val sFractional = if (separator == Separator.PERIOD) Token.comma else Token.dot
|
||||||
|
|
||||||
|
return format(output)
|
||||||
|
.replace(grouping, "\t")
|
||||||
|
.replace(fractional, sFractional)
|
||||||
|
.replace("\t", sGrouping)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeGrouping(input: String): String = input.replace(grouping, "")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes [input] and [basicUnit] of the unit to format it to be more human readable.
|
||||||
|
*
|
||||||
|
* @return String like "1d 12h 12s".
|
||||||
|
*/
|
||||||
|
fun formatTime(context: Context, input: String, basicUnit: BigDecimal?): String {
|
||||||
|
if (basicUnit == null) return Token._0
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Don't need magic if the input is zero
|
||||||
|
if (BigDecimal(input).compareTo(BigDecimal.ZERO) == 0) return Token._0
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
// For case such as "10-" and "("
|
||||||
|
return Token._0
|
||||||
|
}
|
||||||
|
// Attoseconds don't need "magic"
|
||||||
|
if (basicUnit.compareTo(BigDecimal.ONE) == 0) return formatNumber(input)
|
||||||
|
|
||||||
|
var result = if (input.startsWith(Token.minus)) Token.minus else ""
|
||||||
|
var remainingSeconds = BigDecimal(input)
|
||||||
|
.abs()
|
||||||
|
.multiply(basicUnit)
|
||||||
|
.setScale(0, RoundingMode.HALF_EVEN)
|
||||||
|
|
||||||
|
if (remainingSeconds.compareTo(BigDecimal.ZERO) == 0) return Token._0
|
||||||
|
|
||||||
|
timeDivisions.forEach { (timeStr, divider) ->
|
||||||
|
val division = remainingSeconds.divideAndRemainder(divider)
|
||||||
|
val time = division.component1()
|
||||||
|
remainingSeconds = division.component2()
|
||||||
|
if (time.compareTo(BigDecimal.ZERO) == 1) {
|
||||||
|
result += "${formatNumber(time.toPlainString())}${context.getString(timeStr)} "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.trimEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -170,47 +221,31 @@ open class UnittoFormatter {
|
|||||||
return (output + remainingPart.replace(".", fractional))
|
return (output + remainingPart.replace(".", fractional))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes [input] and [basicUnit] of the unit to format it to be more human readable.
|
|
||||||
*
|
|
||||||
* @return String like "1d 12h 12s".
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
fun formatTime(input: String, basicUnit: BigDecimal?): String {
|
|
||||||
if (basicUnit == null) return Token._0
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Don't need magic if the input is zero
|
|
||||||
if (BigDecimal(input).compareTo(BigDecimal.ZERO) == 0) return Token._0
|
|
||||||
} catch (e: NumberFormatException) {
|
|
||||||
// For case such as "10-" and "("
|
|
||||||
return Token._0
|
|
||||||
}
|
|
||||||
// Attoseconds don't need "magic"
|
|
||||||
if (basicUnit.compareTo(BigDecimal.ONE) == 0) return formatNumber(input)
|
|
||||||
|
|
||||||
var result = if (input.startsWith(Token.minus)) Token.minus else ""
|
|
||||||
var remainingSeconds = BigDecimal(input)
|
|
||||||
.abs()
|
|
||||||
.multiply(basicUnit)
|
|
||||||
.setScale(0, RoundingMode.HALF_EVEN)
|
|
||||||
|
|
||||||
if (remainingSeconds.compareTo(BigDecimal.ZERO) == 0) return Token._0
|
|
||||||
|
|
||||||
timeDivisions.forEach { (timeStr, divider) ->
|
|
||||||
val division = remainingSeconds.divideAndRemainder(divider)
|
|
||||||
val time = division.component1()
|
|
||||||
remainingSeconds = division.component2()
|
|
||||||
if (time.compareTo(BigDecimal.ZERO) == 1) {
|
|
||||||
result += "${formatNumber(time.toPlainString())}${stringResource(timeStr)} "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.trimEnd()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @receiver Must be a string with a dot (".") used as a fractional.
|
* @receiver Must be a string with a dot (".") used as a fractional.
|
||||||
*/
|
*/
|
||||||
private fun String.getOnlyNumbers(): List<String> =
|
private fun String.getOnlyNumbers(): List<String> =
|
||||||
numbersRegex.findAll(this).map(MatchResult::value).toList()
|
numbersRegex.findAll(this).map(MatchResult::value).toList()
|
||||||
|
|
||||||
|
fun filterUnknownSymbols(input: String): String {
|
||||||
|
var clearStr = input.replace(" ", "")
|
||||||
|
var garbage = clearStr
|
||||||
|
|
||||||
|
// String with unknown symbols
|
||||||
|
Token.knownSymbols.plus(fractional).forEach {
|
||||||
|
garbage = garbage.replace(it, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove unknown symbols from input
|
||||||
|
garbage.split(" ").forEach {
|
||||||
|
clearStr = clearStr.replace(it, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
clearStr = clearStr
|
||||||
|
.replace(Token.divide, Token.divideDisplay)
|
||||||
|
.replace(Token.multiply, Token.multiplyDisplay)
|
||||||
|
.replace(Token.minus, Token.minusDisplay)
|
||||||
|
|
||||||
|
return clearStr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.core.ui
|
package com.sadellie.unitto.core.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.compose.ui.test.junit4.createComposeRule
|
import androidx.compose.ui.test.junit4.createComposeRule
|
||||||
import com.sadellie.unitto.core.base.Separator
|
import com.sadellie.unitto.core.base.Separator
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
@ -25,6 +26,7 @@ import org.junit.Rule
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.robolectric.RobolectricTestRunner
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.RuntimeEnvironment
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
private val formatter = Formatter
|
private val formatter = Formatter
|
||||||
@ -93,98 +95,121 @@ class FormatterTest {
|
|||||||
@Test
|
@Test
|
||||||
fun formatTimeTest() {
|
fun formatTimeTest() {
|
||||||
formatter.setSeparator(Separator.SPACES)
|
formatter.setSeparator(Separator.SPACES)
|
||||||
composeTestRule.setContent {
|
var basicValue = BigDecimal.valueOf(1)
|
||||||
var basicValue = BigDecimal.valueOf(1)
|
val mContext: Context = RuntimeEnvironment.getApplication().applicationContext
|
||||||
assertEquals("-28", formatter.formatTime("-28", basicValue))
|
assertEquals("-28", formatter.formatTime(mContext, "-28", basicValue))
|
||||||
assertEquals("-0.05", formatter.formatTime("-0.05", basicValue))
|
assertEquals("-0.05", formatter.formatTime(mContext, "-0.05", basicValue))
|
||||||
assertEquals("0", formatter.formatTime("0", basicValue))
|
assertEquals("0", formatter.formatTime(mContext, "0", basicValue))
|
||||||
assertEquals("0", formatter.formatTime("-0", basicValue))
|
assertEquals("0", formatter.formatTime(mContext, "-0", basicValue))
|
||||||
|
|
||||||
basicValue = BigDecimal.valueOf(86_400_000_000_000_000_000_000.0)
|
basicValue = BigDecimal.valueOf(86_400_000_000_000_000_000_000.0)
|
||||||
assertEquals("-28d", formatter.formatTime("-28", basicValue))
|
assertEquals("-28d", formatter.formatTime(mContext, "-28", basicValue))
|
||||||
assertEquals("-1h 12m", formatter.formatTime("-0.05", basicValue))
|
assertEquals("-1h 12m", formatter.formatTime(mContext, "-0.05", basicValue))
|
||||||
assertEquals("0", formatter.formatTime("0", basicValue))
|
assertEquals("0", formatter.formatTime(mContext, "0", basicValue))
|
||||||
assertEquals("0", formatter.formatTime("-0", basicValue))
|
assertEquals("0", formatter.formatTime(mContext, "-0", basicValue))
|
||||||
|
|
||||||
// DAYS
|
// DAYS
|
||||||
basicValue = BigDecimal.valueOf(86_400_000_000_000_000_000_000.0)
|
basicValue = BigDecimal.valueOf(86_400_000_000_000_000_000_000.0)
|
||||||
assertEquals("12h", formatter.formatTime("0.5", basicValue))
|
assertEquals("12h", formatter.formatTime(mContext, "0.5", basicValue))
|
||||||
assertEquals("1h 12m", formatter.formatTime("0.05", basicValue))
|
assertEquals("1h 12m", formatter.formatTime(mContext, "0.05", basicValue))
|
||||||
assertEquals("7m 12s", formatter.formatTime("0.005", basicValue))
|
assertEquals("7m 12s", formatter.formatTime(mContext, "0.005", basicValue))
|
||||||
assertEquals("28d", formatter.formatTime("28", basicValue))
|
assertEquals("28d", formatter.formatTime(mContext, "28", basicValue))
|
||||||
assertEquals("90d", formatter.formatTime("90", basicValue))
|
assertEquals("90d", formatter.formatTime(mContext, "90", basicValue))
|
||||||
assertEquals("90d 12h", formatter.formatTime("90.5", basicValue))
|
assertEquals("90d 12h", formatter.formatTime(mContext, "90.5", basicValue))
|
||||||
assertEquals("90d 7m 12s", formatter.formatTime("90.005", basicValue))
|
assertEquals("90d 7m 12s", formatter.formatTime(mContext, "90.005", basicValue))
|
||||||
|
|
||||||
// HOURS
|
// HOURS
|
||||||
basicValue = BigDecimal.valueOf(3_600_000_000_000_000_000_000.0)
|
basicValue = BigDecimal.valueOf(3_600_000_000_000_000_000_000.0)
|
||||||
assertEquals("30m", formatter.formatTime("0.5", basicValue))
|
assertEquals("30m", formatter.formatTime(mContext, "0.5", basicValue))
|
||||||
assertEquals("3m", formatter.formatTime("0.05", basicValue))
|
assertEquals("3m", formatter.formatTime(mContext, "0.05", basicValue))
|
||||||
assertEquals("18s", formatter.formatTime("0.005", basicValue))
|
assertEquals("18s", formatter.formatTime(mContext, "0.005", basicValue))
|
||||||
assertEquals("1d 4h", formatter.formatTime("28", basicValue))
|
assertEquals("1d 4h", formatter.formatTime(mContext, "28", basicValue))
|
||||||
assertEquals("3d 18h", formatter.formatTime("90", basicValue))
|
assertEquals("3d 18h", formatter.formatTime(mContext, "90", basicValue))
|
||||||
assertEquals("3d 18h 30m", formatter.formatTime("90.5", basicValue))
|
assertEquals("3d 18h 30m", formatter.formatTime(mContext, "90.5", basicValue))
|
||||||
assertEquals("3d 18h 18s", formatter.formatTime("90.005", basicValue))
|
assertEquals("3d 18h 18s", formatter.formatTime(mContext, "90.005", basicValue))
|
||||||
|
|
||||||
// MINUTES
|
// MINUTES
|
||||||
basicValue = BigDecimal.valueOf(60_000_000_000_000_000_000.0)
|
basicValue = BigDecimal.valueOf(60_000_000_000_000_000_000.0)
|
||||||
assertEquals("30s", formatter.formatTime("0.5", basicValue))
|
assertEquals("30s", formatter.formatTime(mContext, "0.5", basicValue))
|
||||||
assertEquals("3s", formatter.formatTime("0.05", basicValue))
|
assertEquals("3s", formatter.formatTime(mContext, "0.05", basicValue))
|
||||||
assertEquals("300ms", formatter.formatTime("0.005", basicValue))
|
assertEquals("300ms", formatter.formatTime(mContext, "0.005", basicValue))
|
||||||
assertEquals("28m", formatter.formatTime("28", basicValue))
|
assertEquals("28m", formatter.formatTime(mContext, "28", basicValue))
|
||||||
assertEquals("1h 30m", formatter.formatTime("90", basicValue))
|
assertEquals("1h 30m", formatter.formatTime(mContext, "90", basicValue))
|
||||||
assertEquals("1h 30m 30s", formatter.formatTime("90.5", basicValue))
|
assertEquals("1h 30m 30s", formatter.formatTime(mContext, "90.5", basicValue))
|
||||||
assertEquals("1h 30m 300ms", formatter.formatTime("90.005", basicValue))
|
assertEquals("1h 30m 300ms", formatter.formatTime(mContext, "90.005", basicValue))
|
||||||
|
|
||||||
// SECONDS
|
// SECONDS
|
||||||
basicValue = BigDecimal.valueOf(1_000_000_000_000_000_000)
|
basicValue = BigDecimal.valueOf(1_000_000_000_000_000_000)
|
||||||
assertEquals("500ms", formatter.formatTime("0.5", basicValue))
|
assertEquals("500ms", formatter.formatTime(mContext, "0.5", basicValue))
|
||||||
assertEquals("50ms", formatter.formatTime("0.05", basicValue))
|
assertEquals("50ms", formatter.formatTime(mContext, "0.05", basicValue))
|
||||||
assertEquals("5ms", formatter.formatTime("0.005", basicValue))
|
assertEquals("5ms", formatter.formatTime(mContext, "0.005", basicValue))
|
||||||
assertEquals("28s", formatter.formatTime("28", basicValue))
|
assertEquals("28s", formatter.formatTime(mContext, "28", basicValue))
|
||||||
assertEquals("1m 30s", formatter.formatTime("90", basicValue))
|
assertEquals("1m 30s", formatter.formatTime(mContext, "90", basicValue))
|
||||||
assertEquals("1m 30s 500ms", formatter.formatTime("90.5", basicValue))
|
assertEquals("1m 30s 500ms", formatter.formatTime(mContext, "90.5", basicValue))
|
||||||
assertEquals("1m 30s 5ms", formatter.formatTime("90.005", basicValue))
|
assertEquals("1m 30s 5ms", formatter.formatTime(mContext, "90.005", basicValue))
|
||||||
|
|
||||||
// MILLISECONDS
|
// MILLISECONDS
|
||||||
basicValue = BigDecimal.valueOf(1_000_000_000_000_000)
|
basicValue = BigDecimal.valueOf(1_000_000_000_000_000)
|
||||||
assertEquals("500µs", formatter.formatTime("0.5", basicValue))
|
assertEquals("500µs", formatter.formatTime(mContext, "0.5", basicValue))
|
||||||
assertEquals("50µs", formatter.formatTime("0.05", basicValue))
|
assertEquals("50µs", formatter.formatTime(mContext, "0.05", basicValue))
|
||||||
assertEquals("5µs", formatter.formatTime("0.005", basicValue))
|
assertEquals("5µs", formatter.formatTime(mContext, "0.005", basicValue))
|
||||||
assertEquals("28ms", formatter.formatTime("28", basicValue))
|
assertEquals("28ms", formatter.formatTime(mContext, "28", basicValue))
|
||||||
assertEquals("90ms", formatter.formatTime("90", basicValue))
|
assertEquals("90ms", formatter.formatTime(mContext, "90", basicValue))
|
||||||
assertEquals("90ms 500µs", formatter.formatTime("90.5", basicValue))
|
assertEquals("90ms 500µs", formatter.formatTime(mContext, "90.5", basicValue))
|
||||||
assertEquals("90ms 5µs", formatter.formatTime("90.005", basicValue))
|
assertEquals("90ms 5µs", formatter.formatTime(mContext, "90.005", basicValue))
|
||||||
|
|
||||||
// MICROSECONDS
|
// MICROSECONDS
|
||||||
basicValue = BigDecimal.valueOf(1_000_000_000_000)
|
basicValue = BigDecimal.valueOf(1_000_000_000_000)
|
||||||
assertEquals("500ns", formatter.formatTime("0.5", basicValue))
|
assertEquals("500ns", formatter.formatTime(mContext, "0.5", basicValue))
|
||||||
assertEquals("50ns", formatter.formatTime("0.05", basicValue))
|
assertEquals("50ns", formatter.formatTime(mContext, "0.05", basicValue))
|
||||||
assertEquals("5ns", formatter.formatTime("0.005", basicValue))
|
assertEquals("5ns", formatter.formatTime(mContext, "0.005", basicValue))
|
||||||
assertEquals("28µs", formatter.formatTime("28", basicValue))
|
assertEquals("28µs", formatter.formatTime(mContext, "28", basicValue))
|
||||||
assertEquals("90µs", formatter.formatTime("90", basicValue))
|
assertEquals("90µs", formatter.formatTime(mContext, "90", basicValue))
|
||||||
assertEquals("90µs 500ns", formatter.formatTime("90.5", basicValue))
|
assertEquals("90µs 500ns", formatter.formatTime(mContext, "90.5", basicValue))
|
||||||
assertEquals("90µs 5ns", formatter.formatTime("90.005", basicValue))
|
assertEquals("90µs 5ns", formatter.formatTime(mContext, "90.005", basicValue))
|
||||||
|
|
||||||
// NANOSECONDS
|
// NANOSECONDS
|
||||||
basicValue = BigDecimal.valueOf(1_000_000_000)
|
basicValue = BigDecimal.valueOf(1_000_000_000)
|
||||||
assertEquals("500 000 000as", formatter.formatTime("0.5", basicValue))
|
assertEquals("500 000 000as", formatter.formatTime(mContext, "0.5", basicValue))
|
||||||
assertEquals("50 000 000as", formatter.formatTime("0.05", basicValue))
|
assertEquals("50 000 000as", formatter.formatTime(mContext, "0.05", basicValue))
|
||||||
assertEquals("5 000 000as", formatter.formatTime("0.005", basicValue))
|
assertEquals("5 000 000as", formatter.formatTime(mContext, "0.005", basicValue))
|
||||||
assertEquals("28ns", formatter.formatTime("28", basicValue))
|
assertEquals("28ns", formatter.formatTime(mContext, "28", basicValue))
|
||||||
assertEquals("90ns", formatter.formatTime("90", basicValue))
|
assertEquals("90ns", formatter.formatTime(mContext, "90", basicValue))
|
||||||
assertEquals("90ns 500 000 000as", formatter.formatTime("90.5", basicValue))
|
assertEquals("90ns 500 000 000as", formatter.formatTime(mContext, "90.5", basicValue))
|
||||||
assertEquals("90ns 5 000 000as", formatter.formatTime("90.005", basicValue))
|
assertEquals("90ns 5 000 000as", formatter.formatTime(mContext, "90.005", basicValue))
|
||||||
|
|
||||||
// ATTOSECONDS
|
// ATTOSECONDS
|
||||||
basicValue = BigDecimal.valueOf(1)
|
basicValue = BigDecimal.valueOf(1)
|
||||||
assertEquals("0.5", formatter.formatTime("0.5", basicValue))
|
assertEquals("0.5", formatter.formatTime(mContext, "0.5", basicValue))
|
||||||
assertEquals("0.05", formatter.formatTime("0.05", basicValue))
|
assertEquals("0.05", formatter.formatTime(mContext, "0.05", basicValue))
|
||||||
assertEquals("0.005", formatter.formatTime("0.005", basicValue))
|
assertEquals("0.005", formatter.formatTime(mContext, "0.005", basicValue))
|
||||||
assertEquals("28", formatter.formatTime("28", basicValue))
|
assertEquals("28", formatter.formatTime(mContext, "28", basicValue))
|
||||||
assertEquals("90", formatter.formatTime("90", basicValue))
|
assertEquals("90", formatter.formatTime(mContext, "90", basicValue))
|
||||||
assertEquals("90.5", formatter.formatTime("90.5", basicValue))
|
assertEquals("90.5", formatter.formatTime(mContext, "90.5", basicValue))
|
||||||
assertEquals("90.005", formatter.formatTime("90.005", basicValue))
|
assertEquals("90.005", formatter.formatTime(mContext, "90.005", basicValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun fromSeparatorToSpacesTest() {
|
||||||
|
formatter.setSeparator(Separator.SPACES)
|
||||||
|
assertEquals("123 456.789", formatter.fromSeparator("123,456.789", Separator.COMMA))
|
||||||
|
assertEquals("123 456.789", formatter.fromSeparator("123 456.789", Separator.SPACES))
|
||||||
|
assertEquals("123 456.789", formatter.fromSeparator("123.456,789", Separator.PERIOD))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun fromSeparatorToPeriodTest() {
|
||||||
|
formatter.setSeparator(Separator.PERIOD)
|
||||||
|
assertEquals("123.456,789", formatter.fromSeparator("123,456.789", Separator.COMMA))
|
||||||
|
assertEquals("123.456,789", formatter.fromSeparator("123 456.789", Separator.SPACES))
|
||||||
|
assertEquals("123.456,789", formatter.fromSeparator("123.456,789", Separator.PERIOD))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun fromSeparatorToCommaTest() {
|
||||||
|
formatter.setSeparator(Separator.COMMA)
|
||||||
|
assertEquals("123,456.789", formatter.fromSeparator("123,456.789", Separator.COMMA))
|
||||||
|
assertEquals("123,456.789", formatter.fromSeparator("123 456.789", Separator.SPACES))
|
||||||
|
assertEquals("123,456.789", formatter.fromSeparator("123.456,789", Separator.PERIOD))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -100,7 +100,9 @@ internal fun CalculatorRoute(
|
|||||||
// This method is called immediately after copying formatted text, we replace it with the
|
// This method is called immediately after copying formatted text, we replace it with the
|
||||||
// the unformatted version.
|
// the unformatted version.
|
||||||
clipboardManager.setText(
|
clipboardManager.setText(
|
||||||
AnnotatedString(Formatter.removeFormat(clipboardText.text))
|
AnnotatedString(
|
||||||
|
clipboardText.text.replace(Formatter.grouping, "")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,27 +128,7 @@ class TextFieldController @Inject constructor() {
|
|||||||
|
|
||||||
private fun String.fixFormat(): String = localFormatter.reFormat(this)
|
private fun String.fixFormat(): String = localFormatter.reFormat(this)
|
||||||
|
|
||||||
private fun String.filterUnknownSymbols(): String {
|
private fun String.filterUnknownSymbols() = localFormatter.filterUnknownSymbols(this)
|
||||||
var clearStr = this.replace(" ", "")
|
|
||||||
var garbage = clearStr
|
|
||||||
|
|
||||||
// String with unknown symbols
|
|
||||||
Token.knownSymbols.forEach {
|
|
||||||
garbage = garbage.replace(it, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove unknown symbols from input
|
|
||||||
garbage.split(" ").forEach {
|
|
||||||
clearStr = clearStr.replace(it, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
clearStr = clearStr
|
|
||||||
.replace(Token.divide, Token.divideDisplay)
|
|
||||||
.replace(Token.multiply, Token.multiplyDisplay)
|
|
||||||
.replace(Token.minus, Token.minusDisplay)
|
|
||||||
|
|
||||||
return clearStr
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class CursorFixer {
|
inner class CursorFixer {
|
||||||
private val illegalTokens by lazy {
|
private val illegalTokens by lazy {
|
||||||
|
@ -31,6 +31,7 @@ import androidx.compose.ui.platform.LocalClipboardManager
|
|||||||
import androidx.compose.ui.platform.LocalTextInputService
|
import androidx.compose.ui.platform.LocalTextInputService
|
||||||
import androidx.compose.ui.platform.LocalTextToolbar
|
import androidx.compose.ui.platform.LocalTextToolbar
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import com.sadellie.unitto.core.base.Separator
|
import com.sadellie.unitto.core.base.Separator
|
||||||
@ -46,6 +47,7 @@ internal fun InputTextField(
|
|||||||
cutCallback: () -> Unit
|
cutCallback: () -> Unit
|
||||||
) {
|
) {
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboardManager = LocalClipboardManager.current
|
||||||
|
|
||||||
val formattedInput: TextFieldValue by remember(value) {
|
val formattedInput: TextFieldValue by remember(value) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
value.copy(
|
value.copy(
|
||||||
@ -57,7 +59,11 @@ internal fun InputTextField(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun copyToClipboard() = clipboardManager.setText(
|
fun copyToClipboard() = clipboardManager.setText(
|
||||||
formattedInput.annotatedString.subSequence(formattedInput.selection)
|
AnnotatedString(
|
||||||
|
Formatter.removeGrouping(
|
||||||
|
formattedInput.annotatedString.subSequence(formattedInput.selection).text
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
@ -65,7 +71,13 @@ internal fun InputTextField(
|
|||||||
LocalTextToolbar provides UnittoTextToolbar(
|
LocalTextToolbar provides UnittoTextToolbar(
|
||||||
view = LocalView.current,
|
view = LocalView.current,
|
||||||
copyCallback = ::copyToClipboard,
|
copyCallback = ::copyToClipboard,
|
||||||
pasteCallback = { pasteCallback(clipboardManager.getText()?.text ?: "") },
|
pasteCallback = {
|
||||||
|
pasteCallback(
|
||||||
|
Formatter.toSeparator(
|
||||||
|
clipboardManager.getText()?.text ?: "", Separator.COMMA
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
cutCallback = { copyToClipboard(); cutCallback() }
|
cutCallback = { copyToClipboard(); cutCallback() }
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -38,6 +38,7 @@ 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
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
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.Formatter
|
import com.sadellie.unitto.core.ui.Formatter
|
||||||
@ -88,6 +89,7 @@ internal fun TopScreenPart(
|
|||||||
targetValue = if (swapped) 0f else 180f,
|
targetValue = if (swapped) 0f else 180f,
|
||||||
animationSpec = tween(easing = FastOutSlowInEasing)
|
animationSpec = tween(easing = FastOutSlowInEasing)
|
||||||
)
|
)
|
||||||
|
val mContext = LocalContext.current
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@ -115,6 +117,7 @@ internal fun TopScreenPart(
|
|||||||
converterMode == ConverterMode.BASE -> outputValue.uppercase()
|
converterMode == ConverterMode.BASE -> outputValue.uppercase()
|
||||||
formatTime and (unitTo?.group == UnitGroup.TIME) -> {
|
formatTime and (unitTo?.group == UnitGroup.TIME) -> {
|
||||||
Formatter.formatTime(
|
Formatter.formatTime(
|
||||||
|
context = mContext,
|
||||||
input = calculatedValue ?: inputValue,
|
input = calculatedValue ?: inputValue,
|
||||||
basicUnit = unitFrom?.basicUnit
|
basicUnit = unitFrom?.basicUnit
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user