From d75368608f7008d5be62e8ff9a79a77325ef8eca Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Oct 2023 11:45:51 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/core/ui/res/TangemColors.kt | 4 + .../com/tangem/core/ui/res/TangemDimens.kt | 2 + .../com/tangem/core/ui/res/TangemTheme.kt | 12 +- .../ui/components/PriceChangesChart.kt | 121 ++++++++++++++++++ 4 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/components/PriceChangesChart.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemColors.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemColors.kt index f8ad2530e8..e814d46e20 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemColors.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemColors.kt @@ -145,6 +145,7 @@ class TangemColors internal constructor( class Background internal constructor( primary: Color, secondary: Color, + tertiary: Color, plain: Color, action: Color, fade: Color, @@ -153,6 +154,8 @@ class TangemColors internal constructor( private set var secondary by mutableStateOf(secondary) private set + var tertiary by mutableStateOf(tertiary) + private set var plain by mutableStateOf(plain) private set var action by mutableStateOf(action) @@ -163,6 +166,7 @@ class TangemColors internal constructor( fun update(other: Background) { primary = other.primary secondary = other.secondary + tertiary = other.tertiary plain = other.plain action = other.action fade = other.fade diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index bea3f669ff..97bd14d0d1 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -33,6 +33,7 @@ data class TangemDimens internal constructor( val radius18: Dp = 18.dp, val radius24: Dp = 24.dp, val radius28: Dp = 28.dp, + val radius36: Dp = 36.dp, // endregion Radius // region Size val size0: Dp = 0.dp, @@ -71,6 +72,7 @@ data class TangemDimens internal constructor( val size80: Dp = 80.dp, val size84: Dp = 84.dp, val size86: Dp = 86.dp, + val size90: Dp = 90.dp, val size93: Dp = 93.dp, val size96: Dp = 96.dp, val size102: Dp = 102.dp, diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt index a4f6721f5c..92a10122db 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt @@ -116,12 +116,13 @@ private fun lightThemeColors(): TangemColors { background = TangemColors.Background( primary = TangemColorPalette.White, secondary = TangemColorPalette.Light1, + tertiary = TangemColorPalette.Light1, plain = TangemColorPalette.White, - action = TangemColorPalette.Black, + action = TangemColorPalette.White, fade = TangemColorPalette.White, ), control = TangemColors.Control( - checked = TangemColorPalette.Meadow, + checked = TangemColorPalette.Dark6, unchecked = TangemColorPalette.Light2, key = TangemColorPalette.White, ), @@ -168,14 +169,15 @@ private fun darkThemeColors(): TangemColors { background = TangemColors.Background( primary = TangemColorPalette.Dark6, secondary = TangemColorPalette.Black, + tertiary = TangemColorPalette.Dark6, plain = TangemColorPalette.Black, - action = TangemColorPalette.Light4, + action = TangemColorPalette.Dark5, fade = TangemColorPalette.Black, ), control = TangemColors.Control( - checked = TangemColorPalette.Meadow, + checked = TangemColorPalette.Azure, unchecked = TangemColorPalette.Dark4, - key = TangemColorPalette.Light1, + key = TangemColorPalette.White, ), stroke = TangemColors.Stroke( primary = TangemColorPalette.Dark5, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/components/PriceChangesChart.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/components/PriceChangesChart.kt new file mode 100644 index 0000000000..6a7444977d --- /dev/null +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/components/PriceChangesChart.kt @@ -0,0 +1,121 @@ +package com.tangem.managetokens.presentation.managetokens.ui.components + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.res.TangemTheme + +/** + * A chart with a solid line and gradient underneath. It can accept values of any range and number. + * If the last value is bigger or equal to the first, the chart is of accent color, otherwise it's warning color. + * + * @param values a list of float values for a chart. + **/ +@Composable +fun PriceChangesChart(values: List, modifier: Modifier = Modifier) { + Row(modifier = modifier) { + if (values.size < 2) return // escape without drawing when there are not enough points + + val lineColor = if (values.last() >= values.first()) { + TangemTheme.colors.icon.accent + } else { + TangemTheme.colors.icon.warning + } + val gradient = Brush.verticalGradient( + colors = listOf(lineColor.copy(alpha = 0.21f), lineColor.copy(alpha = 0.0f)), + ) + Chart(list = values, lineColor = lineColor, gradient = gradient, modifier = Modifier.weight(1f)) + } +} + +@Composable +private fun Chart(list: List, lineColor: Color, gradient: Brush, modifier: Modifier = Modifier) { + val max = list.max() + val min = list.min() + val zipList: List> = list.zipWithNext() + + for (pair in zipList) { + val fromValuePercentage = getValuePercentageForRange(pair.first, max, min) + val toValuePercentage = getValuePercentageForRange(pair.second, max, min) + + Canvas( + modifier = modifier.fillMaxHeight(), + onDraw = { + val fromPoint = Offset(x = 0f, y = size.height.times(1 - fromValuePercentage)) + val toPoint = Offset(x = size.width, y = size.height.times(1 - toValuePercentage)) + + val path = drawChartLineAndCreatePath(fromPoint = fromPoint, toPoint = toPoint, lineColor = lineColor) + + fillChart( + path = path, + fromPoint = fromPoint, + toPoint = toPoint, + size = size, + gradient = gradient, + ) + }, + ) + } +} + +private fun DrawScope.drawChartLineAndCreatePath(fromPoint: Offset, toPoint: Offset, lineColor: Color): Path { + val path = Path() + path.moveTo(fromPoint.x, fromPoint.y) + path.lineTo(toPoint.x, toPoint.y) + drawPath( + path = path, + color = lineColor, + style = Stroke(width = 1f), + ) + return path +} + +private fun DrawScope.fillChart(path: Path, fromPoint: Offset, toPoint: Offset, size: Size, gradient: Brush) { + path.lineTo(toPoint.x, size.height) + path.lineTo(fromPoint.x, size.height) + path.lineTo(0f, fromPoint.y) + drawPath( + path = path, + brush = gradient, + ) +} + +private fun getValuePercentageForRange(value: Float, max: Float, min: Float): Float { + return if (max == min) { // to draw a straight line when all values are the same + val modifiedMax = max + 1 + val modifiedMin = min - 1 + (value - modifiedMin) / (modifiedMax - modifiedMin) + } else { + (value - min) / (max - min) + } +} + +@Preview(widthDp = 150, heightDp = 150, showBackground = true) +@Composable +private fun Chart_Positive_Preview() { + TangemTheme(isDark = true) { + PriceChangesChart( + listOf(1f, 2f, 4f, 1f, 5f), + ) + } +} + +@Preview(widthDp = 150, heightDp = 150, showBackground = true) +@Composable +private fun Chart_Negative_Preview() { + TangemTheme(isDark = true) { + PriceChangesChart( + listOf(10f, 2f, 4f, 1f, 5f), + ) + } +} \ No newline at end of file