|
| 1 | +package com.choidev.latesteffort.core.util.chart |
| 2 | + |
| 3 | +import android.graphics.Typeface |
| 4 | +import androidx.compose.foundation.isSystemInDarkTheme |
| 5 | +import androidx.compose.runtime.Composable |
| 6 | +import androidx.compose.runtime.remember |
| 7 | +import androidx.compose.ui.graphics.Brush |
| 8 | +import androidx.compose.ui.graphics.Color |
| 9 | +import androidx.compose.ui.graphics.toArgb |
| 10 | +import androidx.compose.ui.unit.dp |
| 11 | +import androidx.compose.ui.unit.sp |
| 12 | +import com.choidev.latesteffort.core.util.motion.CachedAccelerometerData |
| 13 | +import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis |
| 14 | +import com.patrykandpatrick.vico.compose.axis.vertical.startAxis |
| 15 | +import com.patrykandpatrick.vico.compose.chart.Chart |
| 16 | +import com.patrykandpatrick.vico.compose.chart.line.lineChart |
| 17 | +import com.patrykandpatrick.vico.compose.component.shape.shader.fromBrush |
| 18 | +import com.patrykandpatrick.vico.compose.component.shapeComponent |
| 19 | +import com.patrykandpatrick.vico.compose.component.textComponent |
| 20 | +import com.patrykandpatrick.vico.compose.dimensions.dimensionsOf |
| 21 | +import com.patrykandpatrick.vico.compose.legend.legendItem |
| 22 | +import com.patrykandpatrick.vico.compose.legend.verticalLegend |
| 23 | +import com.patrykandpatrick.vico.compose.style.ChartStyle |
| 24 | +import com.patrykandpatrick.vico.compose.style.ProvideChartStyle |
| 25 | +import com.patrykandpatrick.vico.compose.style.currentChartStyle |
| 26 | +import com.patrykandpatrick.vico.core.DefaultAlpha |
| 27 | +import com.patrykandpatrick.vico.core.DefaultColors |
| 28 | +import com.patrykandpatrick.vico.core.DefaultDimens |
| 29 | +import com.patrykandpatrick.vico.core.chart.copy |
| 30 | +import com.patrykandpatrick.vico.core.chart.line.LineChart |
| 31 | +import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider |
| 32 | +import com.patrykandpatrick.vico.core.component.shape.LineComponent |
| 33 | +import com.patrykandpatrick.vico.core.component.shape.Shapes |
| 34 | +import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders |
| 35 | +import com.patrykandpatrick.vico.core.entry.entriesOf |
| 36 | +import com.patrykandpatrick.vico.core.entry.entryModelOf |
| 37 | + |
| 38 | +private const val COLOR_1_CODE = 0xffb983ff |
| 39 | +private const val COLOR_2_CODE = 0xff91b1fd |
| 40 | +private const val COLOR_3_CODE = 0xff8fdaff |
| 41 | +private val color1 = Color(COLOR_1_CODE) |
| 42 | +private val color2 = Color(COLOR_2_CODE) |
| 43 | +private val color3 = Color(COLOR_3_CODE) |
| 44 | +private val chartColors = listOf(color1, color2, color3) |
| 45 | + |
| 46 | +private val legendItemLabelTextSize = 12.sp |
| 47 | + |
| 48 | +private val legendStrings = listOf("ACC X", "ACC Y", "ACC Z") |
| 49 | +private val legendItemIconSize = 8.dp |
| 50 | +private val legendItemIconPaddingValue = 10.dp |
| 51 | +private val legendItemSpacing = 4.dp |
| 52 | +private val legendTopPaddingValue = 8.dp |
| 53 | +private val legendPadding = dimensionsOf(top = legendTopPaddingValue) |
| 54 | + |
| 55 | +@Composable |
| 56 | +fun DynamicAccelerometerChart(data: CachedAccelerometerData) { |
| 57 | + val axisValueOverrider = AxisValuesOverrider.fixed(minY = -20f, maxY = 20f) |
| 58 | + |
| 59 | + val chartEntryModel = entryModelOf( |
| 60 | + entriesOf(*data.accelerationX.toNumberPairs()), |
| 61 | + entriesOf(*data.accelerationY.toNumberPairs()), |
| 62 | + entriesOf(*data.accelerationZ.toNumberPairs()), |
| 63 | + ) |
| 64 | + |
| 65 | + ProvideChartStyle(rememberChartStyle(chartColors)) { |
| 66 | + val defaultLines = currentChartStyle.lineChart.lines |
| 67 | + Chart( |
| 68 | + chart = lineChart( |
| 69 | + remember(defaultLines) { |
| 70 | + defaultLines.map { defaultLine -> defaultLine.copy(lineBackgroundShader = null) } |
| 71 | + }, |
| 72 | + axisValuesOverrider = axisValueOverrider |
| 73 | + ), |
| 74 | + model = chartEntryModel, |
| 75 | + startAxis = startAxis(), |
| 76 | + bottomAxis = bottomAxis(), |
| 77 | + legend = rememberLegend() |
| 78 | + ) |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | +@Composable |
| 83 | +private fun rememberLegend() = verticalLegend( |
| 84 | + items = chartColors.mapIndexed { index, chartColor -> |
| 85 | + legendItem( |
| 86 | + icon = shapeComponent(Shapes.pillShape, chartColor), |
| 87 | + label = textComponent( |
| 88 | + color = currentChartStyle.axis.axisLabelColor, |
| 89 | + textSize = legendItemLabelTextSize, |
| 90 | + typeface = Typeface.MONOSPACE, |
| 91 | + ), |
| 92 | + labelText = legendStrings[index], |
| 93 | + ) |
| 94 | + }, |
| 95 | + iconSize = legendItemIconSize, |
| 96 | + iconPadding = legendItemIconPaddingValue, |
| 97 | + spacing = legendItemSpacing, |
| 98 | + padding = legendPadding, |
| 99 | +) |
| 100 | + |
| 101 | +@Composable |
| 102 | +internal fun rememberChartStyle( |
| 103 | + columnChartColors: List<Color>, |
| 104 | + lineChartColors: List<Color> |
| 105 | +): ChartStyle { |
| 106 | + val isSystemInDarkTheme = isSystemInDarkTheme() |
| 107 | + return remember(columnChartColors, lineChartColors, isSystemInDarkTheme) { |
| 108 | + val defaultColors = if (isSystemInDarkTheme) DefaultColors.Dark else DefaultColors.Light |
| 109 | + ChartStyle( |
| 110 | + ChartStyle.Axis( |
| 111 | + axisLabelColor = Color(defaultColors.axisLabelColor), |
| 112 | + axisGuidelineColor = Color(defaultColors.axisGuidelineColor), |
| 113 | + axisLineColor = Color(defaultColors.axisLineColor), |
| 114 | + ), |
| 115 | + ChartStyle.ColumnChart( |
| 116 | + columnChartColors.map { columnChartColor -> |
| 117 | + LineComponent( |
| 118 | + columnChartColor.toArgb(), |
| 119 | + DefaultDimens.COLUMN_WIDTH, |
| 120 | + Shapes.roundedCornerShape(DefaultDimens.COLUMN_ROUNDNESS_PERCENT), |
| 121 | + ) |
| 122 | + }, |
| 123 | + ), |
| 124 | + ChartStyle.LineChart( |
| 125 | + lineChartColors.map { lineChartColor -> |
| 126 | + LineChart.LineSpec( |
| 127 | + lineColor = lineChartColor.toArgb(), |
| 128 | + lineBackgroundShader = DynamicShaders.fromBrush( |
| 129 | + Brush.verticalGradient( |
| 130 | + listOf( |
| 131 | + lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_START), |
| 132 | + lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_END), |
| 133 | + ), |
| 134 | + ), |
| 135 | + ), |
| 136 | + ) |
| 137 | + }, |
| 138 | + ), |
| 139 | + ChartStyle.Marker(), |
| 140 | + Color(defaultColors.elevationOverlayColor), |
| 141 | + ) |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +@Composable |
| 146 | +internal fun rememberChartStyle(chartColors: List<Color>) = |
| 147 | + rememberChartStyle(columnChartColors = chartColors, lineChartColors = chartColors) |
| 148 | + |
| 149 | +private fun List<Float>.toNumberPairs(): Array<Pair<Number, Number>> { |
| 150 | + return this.mapIndexed { index, value -> index to value }.toTypedArray() |
| 151 | +} |
0 commit comments