From 51b18f622e7843f264ba2b31d72b00115b6fbc3e Mon Sep 17 00:00:00 2001 From: AkYML Date: Tue, 11 Jul 2023 17:06:02 +0700 Subject: [PATCH 1/5] update ios screen --- .../kotlin/co/yml/kmm/charts/StartScreen.kt | 3 +- .../co/yml/kmm/charts/CommonMainScreen.kt | 66 +++ .../yml/kmm/charts/common/utils/DataUtils.kt | 21 + .../charts/ui/combinedchart/CombinedChart.kt | 378 ++++++++++++++++++ .../combinedchart/model/CombinedChartData.kt | 39 ++ .../yml/kmm/charts/ui/linechart/LineChart.kt | 2 +- .../co/yml/kmm/charts/StartScreenIOS.kt | 3 +- .../CombinedLineAndBarChartActivity.kt | 96 +---- iosApp/iosApp/ContentView.swift | 20 + 9 files changed, 534 insertions(+), 94 deletions(-) create mode 100644 KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/combinedchart/CombinedChart.kt create mode 100644 KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/combinedchart/model/CombinedChartData.kt diff --git a/KMMYCharts/src/androidMain/kotlin/co/yml/kmm/charts/StartScreen.kt b/KMMYCharts/src/androidMain/kotlin/co/yml/kmm/charts/StartScreen.kt index df2e9dfb..b9303a09 100644 --- a/KMMYCharts/src/androidMain/kotlin/co/yml/kmm/charts/StartScreen.kt +++ b/KMMYCharts/src/androidMain/kotlin/co/yml/kmm/charts/StartScreen.kt @@ -10,6 +10,7 @@ fun ChartScreen(chartType: Int) { 3 -> LineChartScreen() 4 -> PieChartScreen() 5 -> DonutPieChartScreen() - 6-> BubbleChartWithGrid() + 6 -> BubbleChartWithGrid() + 7 -> BarWithLineChart() } } \ No newline at end of file diff --git a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/CommonMainScreen.kt b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/CommonMainScreen.kt index 5eb13f14..b2a5c907 100644 --- a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/CommonMainScreen.kt +++ b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/CommonMainScreen.kt @@ -32,6 +32,8 @@ import co.yml.kmm.charts.ui.barchart.models.GroupBarChartData import co.yml.kmm.charts.ui.barchart.models.GroupSeparatorConfig import co.yml.kmm.charts.ui.bubblechart.BubbleChart import co.yml.kmm.charts.ui.bubblechart.model.BubbleChartData +import co.yml.kmm.charts.ui.combinedchart.CombinedChart +import co.yml.kmm.charts.ui.combinedchart.model.CombinedChartData import co.yml.kmm.charts.ui.linechart.LineChart import co.yml.kmm.charts.ui.linechart.model.* import co.yml.kmm.charts.ui.piechart.charts.DonutPieChart @@ -463,3 +465,67 @@ internal fun BubbleChartWithGrid() { ) } + +@Composable +internal fun BarWithLineChart() { + val maxRange = 100 + val groupBarData = DataUtils.getGroupBarChartData(50, 100, 3) + val yStepSize = 10 + val xAxisData = AxisData.Builder() + .axisStepSize(30.dp) + .bottomPadding(5.dp) + .labelData { index -> index.toString() } + .build() + val yAxisData = AxisData.Builder() + .steps(yStepSize) + .labelAndAxisLinePadding(20.dp) + .axisOffset(20.dp) + .labelData { index -> (index * (maxRange / yStepSize)).toString() } + .build() + val linePlotData = LinePlotData( + lines = listOf( + Line( + DataUtils.getLineChartData(50, maxRange = 100), + lineStyle = LineStyle(color = Color.Blue), + intersectionPoint = IntersectionPoint(), + selectionHighlightPoint = SelectionHighlightPoint(), + selectionHighlightPopUp = SelectionHighlightPopUp() + ), + Line( + DataUtils.getLineChartData(50, maxRange = 100), + lineStyle = LineStyle(color = Color.Black), + intersectionPoint = IntersectionPoint(), + selectionHighlightPoint = SelectionHighlightPoint(), + selectionHighlightPopUp = SelectionHighlightPopUp() + ) + ) + ) + val colorPaletteList = DataUtils.getColorPaletteList(3) + val legendsConfig = LegendsConfig( + legendLabelList = DataUtils.getLegendsLabelData(colorPaletteList), + gridColumnCount = 3 + ) + val barPlotData = BarPlotData( + groupBarList = groupBarData, + barStyle = BarStyle(barWidth = 35.dp), + barColorPaletteList = colorPaletteList + ) + val combinedChartData = CombinedChartData( + combinedPlotDataList = listOf(barPlotData, linePlotData), + xAxisData = xAxisData, + yAxisData = yAxisData + ) + Column( + Modifier + .height(500.dp) + ) { + CombinedChart( + modifier = Modifier + .height(400.dp), + combinedChartData = combinedChartData + ) + Legends( + legendsConfig = legendsConfig + ) + } +} diff --git a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/common/utils/DataUtils.kt b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/common/utils/DataUtils.kt index 535dfd48..114d251c 100644 --- a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/common/utils/DataUtils.kt +++ b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/common/utils/DataUtils.kt @@ -164,6 +164,27 @@ object DataUtils { return list } + + /** + * Returns list of points + * @param listSize: Size of total number of points needed. + * @param start: X values to start from. ex: 50 to 100 + * @param maxRange: Max range of Y values + */ + fun getLineChartData(listSize: Int, start: Int = 0, maxRange: Int): List { + val list = arrayListOf() + for (index in 0 until listSize) { + list.add( + Point( + index.toFloat(), + (start until maxRange).random().toFloat() + ) + ) + } + return list + } + + } diff --git a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/combinedchart/CombinedChart.kt b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/combinedchart/CombinedChart.kt new file mode 100644 index 00000000..88eb7ffa --- /dev/null +++ b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/combinedchart/CombinedChart.kt @@ -0,0 +1,378 @@ +@file:OptIn(ExperimentalMaterialApi::class, ExperimentalTextApi::class) + +package co.yml.kmm.charts.ui.combinedchart + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.ExperimentalTextApi +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.unit.dp +import co.yml.kmm.charts.axis.XAxis +import co.yml.kmm.charts.axis.YAxis +import co.yml.kmm.charts.chartcontainer.container.ScrollableCanvasContainer +import co.yml.kmm.charts.common.extensions.RowClip +import co.yml.kmm.charts.common.extensions.getMaxElementInYAxis +import co.yml.kmm.charts.common.extensions.isNotNull +import co.yml.kmm.charts.common.extensions.isPointTapped +import co.yml.kmm.charts.common.extensions.isTapped +import co.yml.kmm.charts.common.model.PlotData +import co.yml.kmm.charts.common.model.PlotType +import co.yml.kmm.charts.common.model.Point +import co.yml.kmm.charts.ui.barchart.drawUnderScrollMask +import co.yml.kmm.charts.ui.barchart.getGroupBarDrawOffset +import co.yml.kmm.charts.ui.barchart.highlightGroupBar +import co.yml.kmm.charts.ui.barchart.models.BarData +import co.yml.kmm.charts.ui.barchart.models.BarPlotData +import co.yml.kmm.charts.ui.combinedchart.model.CombinedChartData +import co.yml.kmm.charts.ui.linechart.drawHighLightOnSelectedPoint +import co.yml.kmm.charts.ui.linechart.drawHighlightText +import co.yml.kmm.charts.ui.linechart.drawShadowUnderLineAndIntersectionPoint +import co.yml.kmm.charts.ui.linechart.drawStraightOrCubicLine +import co.yml.kmm.charts.ui.linechart.getCubicPoints +import co.yml.kmm.charts.ui.linechart.getMappingPointsToGraph +import co.yml.kmm.charts.ui.linechart.getMaxScrollDistance +import co.yml.kmm.charts.ui.linechart.model.LinePlotData +import kotlinx.coroutines.launch + +/** + * + * CombinedChart compose method for drawing combined line and bar charts. + * @param modifier: All modifier related properties + * @param combinedChartData : All data needed to draw combined chart. [CombinedChartData] Data + * class to save all params related to combined line and bar chart. + */ +@OptIn(ExperimentalTextApi::class) +@Composable +internal fun CombinedChart(modifier: Modifier, combinedChartData: CombinedChartData) { + val accessibilitySheetState = + rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden) + val scope = rememberCoroutineScope() + val textMeasure = rememberTextMeasurer() + + Surface(modifier) { + with(combinedChartData) { + var xOffset by remember { mutableStateOf(0f) } + var isTapped by remember { mutableStateOf(false) } + var columnWidth by remember { mutableStateOf(0f) } + var rowHeight by remember { mutableStateOf(0f) } + val paddingRight = paddingEnd + val linePlotData: LinePlotData = + getDataFromType(combinedPlotDataList, PlotType.Line) as? LinePlotData + ?: LinePlotData.default() + val barPlotData: BarPlotData = + getDataFromType(combinedPlotDataList, PlotType.Bar) as? BarPlotData + ?: BarPlotData.default() + val linePoints: List = + linePlotData.lines.flatMap { line -> line.dataPoints.map { it } } + val barPoints = barPlotData.groupBarList.flatMap { bar -> bar.barList.map { it } } + val bgColor = MaterialTheme.colors.surface + val xMin = + minOf(if(linePoints.isEmpty()) 0.0f else linePoints.minOf { it.x }, (barPlotData.groupBarList.size).toFloat()) + val xMax = + maxOf(if(linePoints.isEmpty()) 0.0f else linePoints.maxOf { it.x }, (barPlotData.groupBarList.size).toFloat()) + val yMin = minOf(if(linePoints.isEmpty()) 0.0f else linePoints.minOf { it.y }, if(barPoints.isEmpty())0.0f else barPoints.minOf { it.point.y }) + val yMax = maxOf(if(linePoints.isEmpty()) 0.0f else linePoints.maxOf { it.y }, if(barPoints.isEmpty())0.0f else barPoints.maxOf { it.point.y }) + val requiredSteps = + maxOf( + if(linePlotData.lines.isEmpty()) 0 else linePlotData.lines.map { it.dataPoints.size - 1 }.maxOf { it }, + if(barPlotData.groupBarList.isEmpty()) 0 else barPlotData.groupBarList.size + ) + val xAxisData = xAxisData.copy( + axisStepSize = if(barPlotData.groupBarList.isEmpty()) 30.dp else((barPlotData.barStyle.barWidth * barPlotData.groupingSize) + + barPlotData.barStyle.paddingBetweenBars), + steps = requiredSteps, + startDrawPadding = LocalDensity.current.run { columnWidth.toDp() }, + shouldDrawAxisLineTillEnd = true + ) + val yAxisData = + yAxisData.copy( + axisBottomPadding = LocalDensity.current.run { rowHeight.toDp() }, + axisTopPadding = paddingTop + ) + val maxElementInYAxis = getMaxElementInYAxis(yMax, yAxisData.steps) + var identifiedBarPoint by remember { mutableStateOf(BarData(Point(0f, 0f))) } + var identifiedPoint by remember { mutableStateOf(Point(0f, 0f)) } + var tapOffset by remember { mutableStateOf(Offset(0f, 0f)) } + + ScrollableCanvasContainer( + modifier = modifier + .semantics { + contentDescription = accessibilityConfig.chartDescription + }, + containerBackgroundColor = backgroundColor, + isPinchZoomEnabled = isZoomAllowed, + calculateMaxDistance = { xZoom -> + xOffset = + ((barPlotData.barStyle.barWidth.toPx() * barPlotData.groupingSize) + + barPlotData.barStyle.paddingBetweenBars.toPx()) * xZoom + getMaxScrollDistance( + columnWidth, + xMax, + xMin, + xOffset, + 0f, + paddingRight.toPx(), + size.width + ) + }, + drawXAndYAxis = { scrollOffset, xZoom -> + val axisPoints = mutableListOf() + for (index in 0 until xMax.toInt()) { + axisPoints.add(Point(index.toFloat(), 0f)) + } + XAxis( + xAxisData = xAxisData, + modifier = Modifier + .align(Alignment.BottomStart) + .fillMaxWidth() + .wrapContentHeight() + .clip( + RowClip( + columnWidth, + paddingRight + ) + ) + .onGloballyPositioned { + rowHeight = it.size.height.toFloat() + }, + xStart = columnWidth + LocalDensity.current.run { + (barPlotData.barStyle.barWidth.toPx() * barPlotData.groupingSize) / 2 + horizontalExtraSpace.toPx() + }, + scrollOffset = scrollOffset, + zoomScale = xZoom, + chartData = if(barPoints.isEmpty()) linePoints else axisPoints, + axisStart = columnWidth + ) + YAxis( + modifier = Modifier + .align(Alignment.TopStart) + .fillMaxHeight() + .wrapContentWidth() + .onGloballyPositioned { + columnWidth = it.size.width.toFloat() + }, + yAxisData = yAxisData + ) + }, + onDraw = { scrollOffset, xZoom -> + val yBottom = size.height - rowHeight + val yOffset = + ((yBottom - yAxisData.axisTopPadding.toPx()) / maxElementInYAxis) + xOffset = if(barPoints.isEmpty())xAxisData.axisStepSize.toPx() * xZoom else + ((barPlotData.barStyle.barWidth.toPx() * barPlotData.groupingSize) + + barPlotData.barStyle.paddingBetweenBars.toPx()) * xZoom + val xLeft = + columnWidth + horizontalExtraSpace.toPx() + val barTapLocks = mutableMapOf>() + val linePointLocks = mutableMapOf>() + + for (plotData in combinedPlotDataList) { + when (plotData) { + is LinePlotData -> { + // Draw line chart + val xStartPosition = + columnWidth + horizontalExtraSpace.toPx() + + ((barPlotData.barStyle.barWidth.toPx() * barPlotData.groupingSize) / 2) + plotData.lines.forEach { line -> + val pointsData = getMappingPointsToGraph( + line.dataPoints, + xMin, + xOffset, + xStartPosition, + scrollOffset, + yBottom, + yMin, + yOffset + ) + val (cubicPoints1, cubicPoints2) = getCubicPoints(pointsData) + + // Draw cubic line using the points and form a line graph + val cubicPath = + drawStraightOrCubicLine( + pointsData, + cubicPoints1, + cubicPoints2, + line.lineStyle + ) + + // Draw area under curve + drawShadowUnderLineAndIntersectionPoint( + cubicPath, + pointsData, + yBottom, + line + ) + + pointsData.forEachIndexed { index, point -> + if (isTapped && point.isPointTapped( + tapOffset, + tapPadding.toPx() + ) + ) { + // Dealing with only one line graph hence tapPointLocks[0] + linePointLocks[0] = line.dataPoints[index] to point + } + } + if (isTapped && linePointLocks.isNotEmpty()) { + drawHighLightOnSelectedPoint( + linePointLocks, + columnWidth, + paddingRight, + yBottom, + line.selectionHighlightPoint?.copy( + isHighlightLineRequired = false + ) + ) + if (line.selectionHighlightPopUp != null) { + val x = + linePointLocks.values.firstOrNull()?.second?.x + if (x != null) identifiedPoint = + linePointLocks.values.map { it.first }.first() + val selectedOffset = + linePointLocks.values.firstOrNull()?.second + if (selectedOffset.isNotNull()) { + drawHighlightText( + identifiedPoint, + selectedOffset ?: Offset(0f, 0f), + line.selectionHighlightPopUp, + textMeasure + ) + } + } + } + } + } + is BarPlotData -> { + // Draw bar graph + plotData.groupBarList.forEachIndexed { index, groupBarData -> + var insideOffset = 0f + groupBarData.barList.forEachIndexed { subIndex, individualBar -> + val drawOffset = getGroupBarDrawOffset( + index, individualBar.point.y, xOffset, xLeft, + scrollOffset, yBottom, yOffset, 0f + ) + val height = yBottom - drawOffset.y + + val individualOffset = + Offset(drawOffset.x + insideOffset, drawOffset.y) + + // drawing each individual bars + drawGroupBarGraph( + plotData, + individualOffset, + height, + subIndex + ) + insideOffset += plotData.barStyle.barWidth.toPx() + + val middleOffset = + Offset( + individualOffset.x + plotData.barStyle.barWidth.toPx() / 2, + drawOffset.y + ) + // store the tap points for selection + if (isTapped && middleOffset.isTapped( + tapOffset, + plotData.barStyle.barWidth.toPx(), + yBottom, + tapPadding.toPx() + ) + ) { + barTapLocks[0] = individualBar to individualOffset + } + } + } + } + } + } + if (isTapped && linePointLocks.isEmpty() && + barPlotData.barStyle.selectionHighlightData != null + ) { + // highlighting the selected bar and showing the data points + identifiedBarPoint = highlightGroupBar( + barTapLocks, + true, + identifiedBarPoint, + barPlotData.barStyle.selectionHighlightData, + isTapped, + columnWidth, + yBottom, + paddingRight, + yOffset, + barPlotData.barStyle.barWidth, + textMeasure + ) + } + drawUnderScrollMask(columnWidth, paddingRight, bgColor) + }, + onPointClicked = { offset: Offset, _: Float -> + isTapped = true + tapOffset = offset + }, + onScroll = { + isTapped = false + } + ) + + } + } +} + +/** + * Returns data for given plot type from the combinedPlotDataList + * @param combinedPlotDataList : List of combined plot data + * @param type: Type of plot of with data is to be returned. + */ +private fun getDataFromType(combinedPlotDataList: List, type: PlotType): PlotData? { + return when (type) { + is PlotType.Line -> combinedPlotDataList.filterIsInstance().firstOrNull() + is PlotType.Bar -> combinedPlotDataList.filterIsInstance().firstOrNull() + else -> null // Handle if required in future. + } +} + +/** + * + * Used to draw the individual bars + * @param barPlotData : all meta data related to the bar graph + * @param drawOffset: topLeft offset for the drawing the bar + * @param height : height of the bar graph + * @param subIndex : Index of the bar + */ +private fun DrawScope.drawGroupBarGraph( + barPlotData: BarPlotData, drawOffset: Offset, + height: Float, + subIndex: Int +) { + val color = if (subIndex < barPlotData.barColorPaletteList.size) { + barPlotData.barColorPaletteList[subIndex] + } else Color.Transparent + with(barPlotData.barStyle) { + drawRoundRect( + color = color, + topLeft = drawOffset, + size = Size(barWidth.toPx(), height), + cornerRadius = CornerRadius( + cornerRadius.toPx(), + cornerRadius.toPx() + ), + style = barDrawStyle, + blendMode = barBlendMode + ) + } +} diff --git a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/combinedchart/model/CombinedChartData.kt b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/combinedchart/model/CombinedChartData.kt new file mode 100644 index 00000000..69bb9dc4 --- /dev/null +++ b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/combinedchart/model/CombinedChartData.kt @@ -0,0 +1,39 @@ +package co.yml.kmm.charts.ui.combinedchart.model + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import co.yml.kmm.charts.axis.AxisData +import co.yml.kmm.charts.common.model.AccessibilityConfig +import co.yml.kmm.charts.common.model.PlotData + +/** + * + * CombinedLineAndBarGraphData data class that contains all params user need to define to draw a bar and line graph. + * @param combinedPlotDataList: Defines list of plot data's to be drawn and order of graph drawing is maintained as + * per the list order. Distinct plot data's are only allowed. + * @param xAxisData: All the configurations related to X-Axis to be defined here in [AxisData] + * @param yAxisData: All the configurations related to Y-Axis to be defined here in [AxisData] + * @param paddingTop: Padding from the top of the canvas to start of the graph container. + * @param bottomPadding: Padding from the bottom of the canvas to bottom of the graph container. + * @param containerPaddingEnd: Container inside padding end after the last point of the graph. + * @param backgroundColor: Background color of the Y & X components., + * @param isZoomAllowed: True if zoom in for all vertical graph components is allowed else false. + * @param tapPadding: Tap padding offset for selected point. + * @param horizontalExtraSpace: Extra padding at the end of the canvas container. + * @param accessibilityConfig: Configs related to accessibility service defined here in [AccessibilityConfig] + */ +data class CombinedChartData( + val combinedPlotDataList: List, + val xAxisData: AxisData = AxisData.Builder().build(), + val yAxisData: AxisData = AxisData.Builder().build(), + val paddingTop: Dp = 30.dp, + val bottomPadding: Dp = 10.dp, + val paddingEnd: Dp = 10.dp, + val horizontalExtraSpace: Dp = 10.dp, + val containerPaddingEnd: Dp = 15.dp, + val backgroundColor: Color = Color.White, + val tapPadding: Dp = 10.dp, + val isZoomAllowed: Boolean = true, + val accessibilityConfig: AccessibilityConfig = AccessibilityConfig() +) diff --git a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/linechart/LineChart.kt b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/linechart/LineChart.kt index ca8a36bc..86f2ecfc 100644 --- a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/linechart/LineChart.kt +++ b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/linechart/LineChart.kt @@ -101,7 +101,7 @@ internal fun LineChart(modifier: Modifier, lineChartData: LineChartData) { xMax, xMin, xOffset, - paddingRight.toPx(), + paddingRight.toSp().toPx(), size.width, containerPaddingEnd.toPx() ) diff --git a/KMMYCharts/src/iosMain/kotlin/co/yml/kmm/charts/StartScreenIOS.kt b/KMMYCharts/src/iosMain/kotlin/co/yml/kmm/charts/StartScreenIOS.kt index 7ab57c2f..dfdada27 100644 --- a/KMMYCharts/src/iosMain/kotlin/co/yml/kmm/charts/StartScreenIOS.kt +++ b/KMMYCharts/src/iosMain/kotlin/co/yml/kmm/charts/StartScreenIOS.kt @@ -10,6 +10,7 @@ internal fun StartScreenIOS(chartType: Int) { 3 -> LineChartScreen() 4 -> PieChartScreen() 5 -> DonutPieChartScreen() - 6-> BubbleChartWithGrid() + 6 -> BubbleChartWithGrid() + 7 -> BarWithLineChart() } } \ No newline at end of file diff --git a/androidApp/src/main/java/co/yml/ycharts/app/presentation/CombinedLineAndBarChartActivity.kt b/androidApp/src/main/java/co/yml/ycharts/app/presentation/CombinedLineAndBarChartActivity.kt index 1531243d..4f484cd9 100644 --- a/androidApp/src/main/java/co/yml/ycharts/app/presentation/CombinedLineAndBarChartActivity.kt +++ b/androidApp/src/main/java/co/yml/ycharts/app/presentation/CombinedLineAndBarChartActivity.kt @@ -6,32 +6,15 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import co.yml.charts.axis.AxisData +import co.yml.kmm.charts.ChartScreen +import co.yml.ycharts.app.R import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton import co.yml.ycharts.app.ui.theme.YChartsTheme -import co.yml.charts.ui.barchart.models.BarPlotData -import co.yml.charts.ui.barchart.models.BarStyle -import co.yml.charts.ui.combinedchart.CombinedChart -import co.yml.charts.ui.combinedchart.model.CombinedChartData -import co.yml.charts.ui.linechart.model.IntersectionPoint -import co.yml.charts.ui.linechart.model.Line -import co.yml.charts.ui.linechart.model.LinePlotData -import co.yml.charts.ui.linechart.model.LineStyle -import co.yml.charts.ui.linechart.model.SelectionHighlightPoint -import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp -import co.yml.charts.common.components.Legends -import co.yml.charts.common.model.LegendsConfig -import co.yml.charts.common.utils.DataUtils -import co.yml.ycharts.app.R class CombinedLineAndBarChartActivity : ComponentActivity() { @@ -51,17 +34,11 @@ class CombinedLineAndBarChartActivity : ComponentActivity() { { Box( modifier = Modifier + .padding(it) .fillMaxSize() - .padding(it), - contentAlignment = Alignment.TopCenter ) { - LazyColumn(content = { - items(1) { item -> - when (item) { - 0 -> BarWithLineChart() - } - } - }) + Spacer(modifier = Modifier.height(20.dp)) + ChartScreen(chartType = 7) } } } @@ -69,66 +46,3 @@ class CombinedLineAndBarChartActivity : ComponentActivity() { } } -@Composable -fun BarWithLineChart() { - val maxRange = 100 - val groupBarData = DataUtils.getGroupBarChartData(50, 100, 3) - val yStepSize = 10 - val xAxisData = AxisData.Builder() - .axisStepSize(30.dp) - .bottomPadding(5.dp) - .labelData { index -> index.toString() } - .build() - val yAxisData = AxisData.Builder() - .steps(yStepSize) - .labelAndAxisLinePadding(20.dp) - .axisOffset(20.dp) - .labelData { index -> (index * (maxRange / yStepSize)).toString() } - .build() - val linePlotData = LinePlotData( - lines = listOf( - Line( - DataUtils.getLineChartData(50, maxRange = 100), - lineStyle = LineStyle(color = Color.Blue), - intersectionPoint = IntersectionPoint(), - selectionHighlightPoint = SelectionHighlightPoint(), - selectionHighlightPopUp = SelectionHighlightPopUp() - ), - Line( - DataUtils.getLineChartData(50, maxRange = 100), - lineStyle = LineStyle(color = Color.Black), - intersectionPoint = IntersectionPoint(), - selectionHighlightPoint = SelectionHighlightPoint(), - selectionHighlightPopUp = SelectionHighlightPopUp() - ) - ) - ) - val colorPaletteList = DataUtils.getColorPaletteList(3) - val legendsConfig = LegendsConfig( - legendLabelList = DataUtils.getLegendsLabelData(colorPaletteList), - gridColumnCount = 3 - ) - val barPlotData = BarPlotData( - groupBarList = groupBarData, - barStyle = BarStyle(barWidth = 35.dp), - barColorPaletteList = colorPaletteList - ) - val combinedChartData = CombinedChartData( - combinedPlotDataList = listOf(barPlotData, linePlotData), - xAxisData = xAxisData, - yAxisData = yAxisData - ) - Column( - Modifier - .height(500.dp) - ) { - CombinedChart( - modifier = Modifier - .height(400.dp), - combinedChartData = combinedChartData - ) - Legends( - legendsConfig = legendsConfig - ) - } -} diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index 1b83f4a6..732ea604 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -16,6 +16,8 @@ struct ContentView: View { @State private var isPieChartPresented = false @State private var isDonutChartPresented = false @State private var isBubbleChartPresented = false + @State private var isCombinedChartPresented = false + @@ -112,6 +114,24 @@ struct ContentView: View { .navigationTitle("Bubble Chart") }.frame(maxWidth: .infinity).padding() + + Button(action: { + self.isCombinedChartPresented = true + }) { + Text("Combined Chart") + .padding() + .frame(maxWidth: .infinity) + .font(.body) + .foregroundColor(.white) + .background(Color.black) + + }.navigationDestination(isPresented: $isCombinedChartPresented) { + MainView(chartType: 7) + .navigationTitle("Combined Chart") + }.frame(maxWidth: .infinity).padding() + + + } } } From ac1483b1a2b965c6097b1459fcc4d8f4e54def8b Mon Sep 17 00:00:00 2001 From: AkYML Date: Tue, 11 Jul 2023 17:10:09 +0700 Subject: [PATCH 2/5] Update BubbleChart.kt --- .../kotlin/co/yml/kmm/charts/ui/bubblechart/BubbleChart.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/bubblechart/BubbleChart.kt b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/bubblechart/BubbleChart.kt index bd6767c7..02d2d723 100644 --- a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/bubblechart/BubbleChart.kt +++ b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/bubblechart/BubbleChart.kt @@ -55,7 +55,7 @@ import co.yml.kmm.charts.ui.linechart.model.SelectionHighlightPopUp */ @OptIn(ExperimentalMaterialApi::class) @Composable -fun BubbleChart(modifier: Modifier, bubbleChartData: BubbleChartData) { +internal fun BubbleChart(modifier: Modifier, bubbleChartData: BubbleChartData) { val accessibilitySheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden) val scope = rememberCoroutineScope() From f028b6f07ff4d5e395e29588d20a0a7558367a8c Mon Sep 17 00:00:00 2001 From: Anup <61045629+anup350@users.noreply.github.com> Date: Thu, 13 Jul 2023 11:17:34 +0530 Subject: [PATCH 3/5] Initial commit --- .gitignore | 24 ++++++++++++++++++++++++ README.md | 2 ++ 2 files changed, 26 insertions(+) create mode 100644 .gitignore create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..524f0963 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* diff --git a/README.md b/README.md new file mode 100644 index 00000000..cf6be574 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# kmm-ycharts +Cross platform repository From 6165157854089efa12b45a2141ea0dedb6a6bfc0 Mon Sep 17 00:00:00 2001 From: AkYML Date: Thu, 13 Jul 2023 17:32:03 +0700 Subject: [PATCH 4/5] remove sp conversion --- .../kotlin/co/yml/kmm/charts/ui/linechart/LineChart.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/linechart/LineChart.kt b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/linechart/LineChart.kt index 86f2ecfc..ca8a36bc 100644 --- a/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/linechart/LineChart.kt +++ b/KMMYCharts/src/commonMain/kotlin/co/yml/kmm/charts/ui/linechart/LineChart.kt @@ -101,7 +101,7 @@ internal fun LineChart(modifier: Modifier, lineChartData: LineChartData) { xMax, xMin, xOffset, - paddingRight.toSp().toPx(), + paddingRight.toPx(), size.width, containerPaddingEnd.toPx() ) From e802cc5ba608444929b5a45e98d6cbe6be984bca Mon Sep 17 00:00:00 2001 From: Deepak KK Date: Mon, 17 Jul 2023 12:25:27 +0530 Subject: [PATCH 5/5] Code refactores, Removed unsed module --- YChartsLib/build.gradle.kts | 146 ----- YChartsLib/proguard-rules.pro | 21 - .../java/co/yml/charts/YChartsTest.kt | 71 --- .../yml/charts/piechart/DonutPieChartTest.kt | 73 --- .../co/yml/charts/piechart/PieChartTest.kt | 67 --- YChartsLib/src/main/AndroidManifest.xml | 2 - .../main/java/co/yml/charts/axis/AxisData.kt | 170 ------ .../main/java/co/yml/charts/axis/Gravity.kt | 8 - .../src/main/java/co/yml/charts/axis/XAxis.kt | 198 ------- .../src/main/java/co/yml/charts/axis/YAxis.kt | 192 ------- .../container/ScrollableCanvasContainer.kt | 121 ----- .../gestures/GestureExtensions.kt | 56 -- .../charts/common/components/ItemDivider.kt | 25 - .../yml/charts/common/components/Legends.kt | 64 --- .../AccessibilityBottomSheetDailog.kt | 64 --- .../components/accessibility/BarInfo.kt | 49 -- .../accessibility/CombinedChartInfo.kt | 81 --- .../components/accessibility/GroupBarInfo.kt | 56 -- .../components/accessibility/LinePointInfo.kt | 51 -- .../components/accessibility/SliceInfo.kt | 42 -- .../charts/common/extensions/Extensions.kt | 251 --------- .../common/model/AccessibilityConfig.kt | 25 - .../yml/charts/common/model/LegendsConfig.kt | 38 -- .../co/yml/charts/common/model/PlotData.kt | 19 - .../java/co/yml/charts/common/model/Point.kt | 16 - .../yml/charts/common/utils/ChartConstants.kt | 9 - .../co/yml/charts/common/utils/DataUtils.kt | 228 -------- .../co/yml/charts/ui/barchart/BarChart.kt | 422 --------------- .../yml/charts/ui/barchart/GroupBarChart.kt | 446 --------------- .../charts/ui/barchart/models/BarChartData.kt | 59 -- .../yml/charts/ui/barchart/models/BarData.kt | 21 - .../charts/ui/barchart/models/BarPlotData.kt | 23 - .../yml/charts/ui/barchart/models/GroupBar.kt | 11 - .../ui/barchart/models/GroupBarChartData.kt | 38 -- .../barchart/models/GroupSeparatorConfig.kt | 21 - .../barchart/models/SelectionHighlightData.kt | 165 ------ .../charts/ui/combinedchart/CombinedChart.kt | 439 --------------- .../combinedchart/model/CombinedChartData.kt | 39 -- .../co/yml/charts/ui/linechart/LineChart.kt | 506 ------------------ .../ui/linechart/LineChartExtensions.kt | 38 -- .../charts/ui/linechart/model/GridLines.kt | 57 -- .../ui/linechart/model/IntersectionPoint.kt | 44 -- .../ui/linechart/model/LineChartData.kt | 59 -- .../charts/ui/linechart/model/LinePlotData.kt | 18 - .../charts/ui/linechart/model/LineStyle.kt | 52 -- .../model/SelectionHighlightPoint.kt | 61 --- .../model/SelectionHighlightPopUp.kt | 92 ---- .../ui/linechart/model/ShadowUnderLine.kt | 34 -- .../co/yml/charts/ui/piechart/ChartWrapper.kt | 52 -- .../charts/ui/piechart/PieChartConstants.kt | 13 - .../charts/ui/piechart/charts/DonuPieChart.kt | 206 ------- .../yml/charts/ui/piechart/charts/DrawPie.kt | 47 -- .../yml/charts/ui/piechart/charts/PieChart.kt | 229 -------- .../ui/piechart/models/PieChartConfig.kt | 62 --- .../charts/ui/piechart/models/PieChartData.kt | 35 -- .../charts/ui/piechart/utils/PieChartUtils.kt | 87 --- .../yml/charts/axis/XGraphExtensionsTest.kt | 60 --- .../yml/charts/axis/YChartsExtensionsTest.kt | 48 -- .../ScrollableCanvasContainerTest.kt | 48 -- .../yml/charts/extensions/ExtensionsTest.kt | 66 --- .../co/yml/charts/ui/bargraph/BarChartTest.kt | 95 ---- .../ui/linegraph/LineChartExtensionsTest.kt | 58 -- .../yml/charts/ui/linegraph/LineChartTest.kt | 192 ------- .../co/yml/charts/utils/PieChartUtilsTest.kt | 70 --- androidApp/build.gradle.kts | 1 - .../app/presentation/BarChartActivity.kt | 84 +-- .../CombinedLineAndBarChartActivity.kt | 92 +--- .../app/presentation/LineChartActivity.kt | 173 ------ settings.gradle.kts | 5 +- 69 files changed, 6 insertions(+), 6505 deletions(-) delete mode 100644 YChartsLib/build.gradle.kts delete mode 100644 YChartsLib/proguard-rules.pro delete mode 100644 YChartsLib/src/androidTest/java/co/yml/charts/YChartsTest.kt delete mode 100644 YChartsLib/src/androidTest/java/co/yml/charts/piechart/DonutPieChartTest.kt delete mode 100644 YChartsLib/src/androidTest/java/co/yml/charts/piechart/PieChartTest.kt delete mode 100644 YChartsLib/src/main/AndroidManifest.xml delete mode 100644 YChartsLib/src/main/java/co/yml/charts/axis/AxisData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/axis/Gravity.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/axis/XAxis.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/axis/YAxis.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/chartcontainer/container/ScrollableCanvasContainer.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/chartcontainer/gestures/GestureExtensions.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/components/ItemDivider.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/components/Legends.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/AccessibilityBottomSheetDailog.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/BarInfo.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/CombinedChartInfo.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/GroupBarInfo.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/LinePointInfo.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/SliceInfo.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/extensions/Extensions.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/model/AccessibilityConfig.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/model/LegendsConfig.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/model/PlotData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/model/Point.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/utils/ChartConstants.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/common/utils/DataUtils.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/barchart/BarChart.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/barchart/GroupBarChart.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarChartData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarPlotData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupBar.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupBarChartData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupSeparatorConfig.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/SelectionHighlightData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/combinedchart/CombinedChart.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/combinedchart/model/CombinedChartData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/LineChart.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/LineChartExtensions.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/GridLines.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/IntersectionPoint.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LineChartData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LinePlotData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LineStyle.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/SelectionHighlightPoint.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/SelectionHighlightPopUp.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/ShadowUnderLine.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/piechart/ChartWrapper.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/piechart/PieChartConstants.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/DonuPieChart.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/DrawPie.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/PieChart.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/piechart/models/PieChartConfig.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/piechart/models/PieChartData.kt delete mode 100644 YChartsLib/src/main/java/co/yml/charts/ui/piechart/utils/PieChartUtils.kt delete mode 100644 YChartsLib/src/test/java/co/yml/charts/axis/XGraphExtensionsTest.kt delete mode 100644 YChartsLib/src/test/java/co/yml/charts/axis/YChartsExtensionsTest.kt delete mode 100644 YChartsLib/src/test/java/co/yml/charts/chartcontainer/ScrollableCanvasContainerTest.kt delete mode 100644 YChartsLib/src/test/java/co/yml/charts/extensions/ExtensionsTest.kt delete mode 100644 YChartsLib/src/test/java/co/yml/charts/ui/bargraph/BarChartTest.kt delete mode 100644 YChartsLib/src/test/java/co/yml/charts/ui/linegraph/LineChartExtensionsTest.kt delete mode 100644 YChartsLib/src/test/java/co/yml/charts/ui/linegraph/LineChartTest.kt delete mode 100644 YChartsLib/src/test/java/co/yml/charts/utils/PieChartUtilsTest.kt diff --git a/YChartsLib/build.gradle.kts b/YChartsLib/build.gradle.kts deleted file mode 100644 index 45d6cdd7..00000000 --- a/YChartsLib/build.gradle.kts +++ /dev/null @@ -1,146 +0,0 @@ -plugins { - id("com.android.library") - id("org.jetbrains.kotlin.android") - id("maven-publish") - id("signing") - id("org.jetbrains.dokka") -} - -android { - compileSdk = 33 - - defaultConfig { - minSdk = 26 - targetSdk = 33 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = "1.8" - freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" - } - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = co.ycharts.dependency.Version.KOTLIN_COMPILER_EXT - } - packagingOptions { - resources { - exclude("/META-INF/{AL2.0,LGPL2.1}") - } - } -} - -dependencies { - - implementation(co.ycharts.dependency.YChartDependency.CORE_KTX) - implementation(co.ycharts.dependency.YChartDependency.APPCOMPAT) - implementation(co.ycharts.dependency.YChartDependency.MATERIAL) - implementation(co.ycharts.dependency.YChartDependency.MATERIAL_3) - implementation(co.ycharts.dependency.YChartDependency.COMPOSE_UI) - implementation(co.ycharts.dependency.YChartDependency.COMPOSE_ACTIVITY) - implementation(co.ycharts.dependency.YChartDependency.COMPOSE_MATERIAL) - implementation(co.ycharts.dependency.YChartDependency.COMPOSE_TOOLING_PREVIEW) - testImplementation(co.ycharts.dependency.YChartDependency.JUNIT) - testImplementation(co.ycharts.dependency.YChartDependency.MOCKK) - androidTestImplementation(co.ycharts.dependency.YChartDependency.COMPOSE_JUNIT) - debugImplementation(co.ycharts.dependency.YChartDependency.COMPOSE_UI_TEST_MANIFEST) - androidTestImplementation(co.ycharts.dependency.YChartDependency.TEST_EXTN) - androidTestImplementation(co.ycharts.dependency.YChartDependency.ESPRESSO_CORE) -} -val dokkaOutputDir = "$buildDir/dokka" - -tasks.dokkaHtml { - outputDirectory.set(file(dokkaOutputDir)) -} - -val deleteDokkaOutputDir by tasks.register("deleteDokkaOutputDirectory") { - delete(dokkaOutputDir) -} -val javadocJar = tasks.register("javadocJar") { - dependsOn(deleteDokkaOutputDir, tasks.dokkaHtml) - archiveClassifier.set("javadoc") - from(dokkaOutputDir) -} -publishing { - repositories { - maven { - name = "YCharts" - setUrl("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2") - credentials { - username = project.findProperty("mavenCentralUsername")?.toString() ?: System.getenv("MAVEN_USERNAME") - password = project.findProperty("mavenCentralPassword")?.toString() ?: System.getenv("MAVEN_PASSWORD") - } - } - } - publications { - register("release") { - groupId = "co.yml" - artifactId = "ycharts" - version = "1.0.1" - afterEvaluate { - from(components["release"]) - } - artifact(javadocJar) - pom { - name.set("YCharts") - description.set("YCharts is a light and extensible chart library for Jetpack Compose system.") - url.set("https://github.com/yml-org/YCharts") - licenses { - license { - name.set("The Apache License, Version 2.0") - url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") - distribution.set("http://www.apache.org/licenses/LICENSE-2.0.txt") - } - } - developers { - developer { - id.set("dkk009") - name.set("Deepak KK") - url.set("https://github.com/dkk009") - } - developer { - id.set("preetham1316") - name.set("Preetham Ivan Dsouza") - url.set("https://github.com/preetham1316") - } - developer { - id.set("kikoso") - name.set("Enrique López Mañas") - url.set("https://github.com/kikoso") - } - } - scm { - url.set("https://github.com/yml-org/YCharts") - connection.set("scm:git:git://github.com/yml-org/YCharts.git") - developerConnection.set("scm:git:ssh://git@github.com:yml-org/YCharts.git") - } - } - } - } -} - - -signing { - useInMemoryPgpKeys( - project.findProperty("signing.keyId")?.toString() ?: System.getenv("SIGNINGKEY"), - project.findProperty("signing.InMemoryKey")?.toString() ?: System.getenv("MEMORY_KEY"), - project.findProperty("signing.password")?.toString()?:System.getenv("SIGNINGPASSWORD") - ) - sign(publishing.publications) -} diff --git a/YChartsLib/proguard-rules.pro b/YChartsLib/proguard-rules.pro deleted file mode 100644 index 2f9dc5a4..00000000 --- a/YChartsLib/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle.kts. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/YChartsLib/src/androidTest/java/co/yml/charts/YChartsTest.kt b/YChartsLib/src/androidTest/java/co/yml/charts/YChartsTest.kt deleted file mode 100644 index 97abda03..00000000 --- a/YChartsLib/src/androidTest/java/co/yml/charts/YChartsTest.kt +++ /dev/null @@ -1,71 +0,0 @@ -package co.yml.charts - -import androidx.compose.foundation.layout.height -import androidx.compose.ui.Modifier -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertIsNotDisplayed -import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import co.yml.charts.axis.AxisData -import co.yml.charts.axis.Gravity -import co.yml.charts.axis.YAxis -import org.junit.Ignore -import org.junit.Rule - -class YChartsTest { - - @get:Rule - val composeTestRule = createComposeRule() - - @Ignore("Need to fix as nodes are not visible for components drawn inside canvas") - fun whenGraphAxisIsRenderedYaxisLabelShouldBeDisplayed(): Unit = - with(composeTestRule) { - // Given - val axisData = AxisData.Builder() - .steps(DEFAULT_AXIS_STEP_SIZE) - .axisPosition(Gravity.LEFT) - .axisLabelFontSize(AXIS_LABEL_FONT_SIZE.sp) - .labelData { index -> index.toString() } - .build() - - // When - setContent { - YAxis(modifier = Modifier.height(AXIS_HEIGHT.dp), yAxisData = axisData) - } - - // Then - onNode(hasText("8")).assertIsDisplayed() - } - - @Ignore("Need to fix as nodes are not visible for components drawn inside canvas") - fun whenGivenYaxisLineNotRequiredIsConfiguredShouldNotBeVisible(): Unit = - with(composeTestRule) { - // Given - val axisData = co.yml.charts.axis.AxisData.Builder() - .axisPosition(Gravity.LEFT) - .axisLabelFontSize(AXIS_LABEL_FONT_SIZE.sp) - .axisConfig(co.yml.charts.axis.AxisConfig(isAxisLineRequired = false)) - .labelData { index -> index.toString() } - .build() - - // When - setContent { - YAxis( - modifier = Modifier.height(AXIS_HEIGHT.dp), - yAxisData = axisData - ) - } - - // Then - composeTestRule.onNodeWithText("8").assertIsNotDisplayed() - } - - companion object { - private const val AXIS_HEIGHT = 250 - private const val AXIS_LABEL_FONT_SIZE = 14 - private const val DEFAULT_AXIS_STEP_SIZE = 10 - } -} diff --git a/YChartsLib/src/androidTest/java/co/yml/charts/piechart/DonutPieChartTest.kt b/YChartsLib/src/androidTest/java/co/yml/charts/piechart/DonutPieChartTest.kt deleted file mode 100644 index bbc5a822..00000000 --- a/YChartsLib/src/androidTest/java/co/yml/charts/piechart/DonutPieChartTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -package co.yml.charts.piechart - -import androidx.compose.foundation.layout.Column -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithText -import co.yml.charts.common.components.Legends -import co.yml.charts.common.model.PlotType -import co.yml.charts.common.utils.DataUtils -import co.yml.charts.ui.piechart.charts.DonutPieChart -import co.yml.charts.ui.piechart.models.PieChartConfig -import co.yml.charts.ui.piechart.models.PieChartData - -import org.junit.Rule -import org.junit.Test - - -class DonutPieChartTest { - @get:Rule - val composeTestRule = createComposeRule() - - private val pieChartConfig = PieChartConfig( - percentVisible = false, - strokeWidth = 120f, - percentColor = Color.Black - ) - - private val pieChartData = PieChartData( - slices = listOf( - PieChartData.Slice("A", 15f, Color(0xFF58BDFF)), - PieChartData.Slice("B", 35f, Color(0xFF125B7F)), - PieChartData.Slice("C", 40f, Color(0xFF092D40)), - ), - plotType = PlotType.Donut - ) - - - @OptIn(ExperimentalMaterialApi::class) - @Test - fun whenIsLegendVisibleIsTrueLegendLabelsAreVisible() { - composeTestRule.setContent { - Column(modifier = Modifier) { - Legends(legendsConfig = DataUtils.getLegendsConfigFromPieChartData(pieChartData, 3)) - DonutPieChart( - modifier = Modifier, - pieChartData = pieChartData, - pieChartConfig = pieChartConfig - ) - } - } - composeTestRule.onNodeWithText("A").assertIsDisplayed() - composeTestRule.onNodeWithText("B").assertIsDisplayed() - composeTestRule.onNodeWithText("C").assertIsDisplayed() - } - - @OptIn(ExperimentalMaterialApi::class) - @Test - fun whenIsLegendVisibleIsFalseNoLegendLabelsAreShown() { - composeTestRule.setContent { - DonutPieChart( - modifier = Modifier, pieChartData = pieChartData, - pieChartConfig = pieChartConfig - ) - } - composeTestRule.onNodeWithText("A").assertDoesNotExist() - composeTestRule.onNodeWithText("B").assertDoesNotExist() - composeTestRule.onNodeWithText("C").assertDoesNotExist() - } -} - diff --git a/YChartsLib/src/androidTest/java/co/yml/charts/piechart/PieChartTest.kt b/YChartsLib/src/androidTest/java/co/yml/charts/piechart/PieChartTest.kt deleted file mode 100644 index ed821bec..00000000 --- a/YChartsLib/src/androidTest/java/co/yml/charts/piechart/PieChartTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -package co.yml.charts.piechart - -import androidx.compose.foundation.layout.Column -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithText -import co.yml.charts.common.components.Legends -import co.yml.charts.common.model.PlotType -import co.yml.charts.common.utils.DataUtils -import co.yml.charts.ui.piechart.charts.PieChart -import co.yml.charts.ui.piechart.models.PieChartConfig -import co.yml.charts.ui.piechart.models.PieChartData -import org.junit.Rule -import org.junit.Test - - -class PieChartTest { - @get:Rule - val composeTestRule = createComposeRule() - - private val pieChartConfig = PieChartConfig( - percentVisible = false, - strokeWidth = 120f, - percentColor = Color.Black - ) - private val pieChartData = PieChartData( - slices = listOf( - PieChartData.Slice("A", 15f, Color(0xFF58BDFF)), - PieChartData.Slice("B", 35f, Color(0xFF125B7F)), - PieChartData.Slice("C", 40f, Color(0xFF092D40)), - ), plotType = PlotType.Pie - ) - - - @Test - fun whenIsLegendVisibleIsTrueLegendLabelsAreVisible() { - composeTestRule.setContent { - Column(modifier = Modifier) { - Legends(legendsConfig = DataUtils.getLegendsConfigFromPieChartData(pieChartData, 3)) - PieChart( - modifier = Modifier, - pieChartData = pieChartData, - pieChartConfig = pieChartConfig - ) - } - - } - composeTestRule.onNodeWithText("A").assertIsDisplayed() - composeTestRule.onNodeWithText("B").assertIsDisplayed() - composeTestRule.onNodeWithText("C").assertIsDisplayed() - } - - @Test - fun whenIsLegendVisibleIsFalseNoLegendLabelsAreShown() { - composeTestRule.setContent { - PieChart( - modifier = Modifier, pieChartData = pieChartData, - pieChartConfig = pieChartConfig - ) - } - composeTestRule.onNodeWithText("A").assertDoesNotExist() - composeTestRule.onNodeWithText("B").assertDoesNotExist() - composeTestRule.onNodeWithText("C").assertDoesNotExist() - } -} diff --git a/YChartsLib/src/main/AndroidManifest.xml b/YChartsLib/src/main/AndroidManifest.xml deleted file mode 100644 index 045215d0..00000000 --- a/YChartsLib/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/YChartsLib/src/main/java/co/yml/charts/axis/AxisData.kt b/YChartsLib/src/main/java/co/yml/charts/axis/AxisData.kt deleted file mode 100644 index 35d70f13..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/axis/AxisData.kt +++ /dev/null @@ -1,170 +0,0 @@ -package co.yml.charts.axis - -import android.graphics.Typeface -import android.text.TextUtils -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp - -/** - * - * YAxis data class params used in drawing yAxis in any graph. - * @param steps: No of step for label segmentation - * @param axisBottomPadding: Axis Label offset bottom padding - * @param labelData(Int)-> String: lambda method for providing labels, @param Int will be the index - * given for each level in Axis - * @param axisLabelFontSize: Font size of axis label data - * @param axisPos :Gravity of axis - * @param labelAndAxisLinePadding: Text label padding from Axis - * @param axisOffset: Drawing offset for axis. - * @param axisLineThickness: Thickness of yAxis line - * @param axisTopPadding: Axis top padding - * @param axisStartPadding: Axis start padding. - * @param indicatorLineWidth: Indicator width on Y axis line for showing points - * @param axisConfig: All config related param to toggle the elements while drawing graph - * @param axisStepSize: Size of each Axis step in Dp - * @param axisLineColor Color of the Y or X axis - * @param axisLabelColor Color of the Y or X axis labels - * @param backgroundColor Background color of the Y or X components - * @param typeface The type of font style - * @param axisBottomPadding: Axis bottom padding, - * @param axisLabelAngle: Angle for the axis labels - * @param startDrawPadding: Padding between Axis and first point on the Axis - * @param shouldDrawAxisLineTillEnd : Boolean to draw axis line till end. - * @param axisLabelDescription: Description to describe axis value for accessibility service - */ -data class AxisData( - val steps: Int, - val labelData: (Int) -> String, - val axisPos: Gravity, - val labelAndAxisLinePadding: Dp, - val axisOffset: Dp, - val axisTopPadding: Dp, - val axisBottomPadding: Dp, - val axisStartPadding: Dp, - val axisStepSize: Dp, - val axisLabelAngle: Float, - val axisLineColor: Color, - val axisLabelColor: Color, - val axisLabelFontSize: TextUnit, - val axisLineThickness: Dp, - val indicatorLineWidth: Dp, - val backgroundColor: Color, - val typeface: Typeface, - val axisConfig: AxisConfig, - val startDrawPadding: Dp, - val shouldDrawAxisLineTillEnd: Boolean, - val axisLabelDescription: (String) -> String -) { - class Builder { - private var steps: Int = 1 - private var labelData: (Int) -> String = { _ -> "" } - private var axisPos: Gravity = Gravity.LEFT - private var labelAndAxisLinePadding: Dp = 20.dp - private var axisStartPadding: Dp = 10.dp - private var axisOffset: Dp = 10.dp - private var axisTopPadding: Dp = 20.dp - private var axisBottomPadding: Dp = 10.dp - private var axisStepSize: Dp = 30.dp - private var axisConfig = AxisConfig() - private var indicatorLineWidth: Dp = 5.dp - private var backgroundColor: Color = Color.Transparent - private var typeface: Typeface = Typeface.DEFAULT - private var axisLineColor: Color = Color.Black - private var axisLabelFontSize: TextUnit = 14.sp - private var axisLineThickness: Dp = 2.dp - private var axisLabelColor: Color = Color.Black - private var startDrawPadding: Dp = 0.dp - private var axisLabelAngle: Float = 0f - private var shouldDrawAxisLineTillEnd: Boolean = false - private var axisLabelDescription: (String) -> String = { label -> "X Axis label $label" } - - fun steps(count: Int) = apply { this.steps = count } - - fun axisOffset(offset: Dp) = apply { this.axisOffset = offset } - - fun labelAndAxisLinePadding(padding: Dp) = apply { this.labelAndAxisLinePadding = padding } - - fun axisStepSize(size: Dp) = apply { this.axisStepSize = size } - - fun labelData(labelData: (Int) -> String) = apply { this.labelData = labelData } - - fun axisLineColor(lineColor: Color) = apply { this.axisLineColor = lineColor } - - fun axisLabelFontSize(fontSize: TextUnit) = apply { this.axisLabelFontSize = fontSize } - - fun axisPosition(pos: Gravity) = apply { this.axisPos = pos } - - fun axisLineThickness(thickness: Dp) = apply { this.axisLineThickness = thickness } - - fun topPadding(padding: Dp) = apply { this.axisTopPadding = padding } - - fun startPadding(padding: Dp) = apply { this.axisStartPadding = padding } - - fun bottomPadding(padding: Dp) = apply { this.axisBottomPadding = padding } - - fun indicatorLineWidth(lineWidth: Dp) = apply { this.indicatorLineWidth = lineWidth } - - fun backgroundColor(color: Color) = apply { this.backgroundColor = color } - - fun typeFace(typeface: Typeface) = apply { this.typeface = typeface } - - fun axisConfig(config: AxisConfig) = apply { this.axisConfig = config } - - fun axisLabelColor(color: Color) = apply { this.axisLabelColor = color } - - fun startDrawPadding(padding: Dp) = - apply { this.startDrawPadding = padding } - - fun axisLabelAngle(angle: Float) = apply { this.axisLabelAngle = angle } - - fun shouldDrawAxisLineTillEnd(flag: Boolean) = - apply { this.shouldDrawAxisLineTillEnd = flag } - - fun axisLabelDescription(description: (String) -> String) = - apply { this.axisLabelDescription = description } - - - fun build() = co.yml.charts.axis.AxisData( - steps, - labelData, - axisPos, - labelAndAxisLinePadding, - axisOffset, - axisTopPadding, - axisBottomPadding, - axisStartPadding, - axisStepSize, - axisLabelAngle, - axisLineColor, - axisLabelColor, - axisLabelFontSize, - axisLineThickness, - indicatorLineWidth, - backgroundColor, - typeface, - axisConfig, - startDrawPadding, - shouldDrawAxisLineTillEnd, - axisLabelDescription - ) - } -} - -/** - * - * AxisConfig data class used to mention all config related param required to draw graph. - * @param isAxisLineRequired : true if should show the axis and points on the line else false - * @param shouldEllipsizeAxisLabel : true if should ellipsize the axis label at end else false - * @param minTextWidthToEllipsize : minimum width of the axis label post which label will be ellipsized - * @param ellipsizeAt : position at which the label will be truncated or ellipsized - - */ -data class AxisConfig( - val isAxisLineRequired: Boolean = true, - val shouldEllipsizeAxisLabel: Boolean = false, - val minTextWidthToEllipsize: Dp = 40.dp, - val ellipsizeAt: TextUtils.TruncateAt = TextUtils.TruncateAt.END -) diff --git a/YChartsLib/src/main/java/co/yml/charts/axis/Gravity.kt b/YChartsLib/src/main/java/co/yml/charts/axis/Gravity.kt deleted file mode 100644 index 290da7fa..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/axis/Gravity.kt +++ /dev/null @@ -1,8 +0,0 @@ -package co.yml.charts.axis - -/** - * To specify gravity positions - */ -enum class Gravity { - TOP, LEFT, RIGHT, BOTTOM -} diff --git a/YChartsLib/src/main/java/co/yml/charts/axis/XAxis.kt b/YChartsLib/src/main/java/co/yml/charts/axis/XAxis.kt deleted file mode 100644 index fea5fc24..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/axis/XAxis.kt +++ /dev/null @@ -1,198 +0,0 @@ -package co.yml.charts.axis - -import android.graphics.Paint -import android.text.TextPaint -import android.text.TextUtils -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.nativeCanvas -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.core.graphics.withRotation -import co.yml.charts.common.extensions.getTextHeight -import co.yml.charts.common.extensions.getTextWidth -import co.yml.charts.common.model.Point -import kotlin.math.ceil - -/** - * - * XAxis compose method used for drawing xAxis in any given graph. - * @param xAxisData : All data needed to draw Yaxis - * @see co.yml.charts.axis.AxisData Data class to save all params related to axis - * @param modifier : All modifier related property. - * @param xStart : Start position of xAxis Points. - * @param scrollOffset : Offset of delta scrolled position. - * @param zoomScale : Scale at which zoom transformation being applied. - * @param chartData : List of data points used in the graph. - */ -@Composable -fun XAxis( - xAxisData: AxisData, - modifier: Modifier, - xStart: Float, - scrollOffset: Float, - zoomScale: Float, - chartData: List -) { - with(xAxisData) { - var xAxisHeight by remember { mutableStateOf(0.dp) } - Row(modifier = modifier.clipToBounds()) { - Canvas( - modifier = modifier - .fillMaxWidth() - .height(xAxisHeight) - .background(backgroundColor) - ) { - val (_, _, xAxisScale) = getXAxisScale(chartData, steps) - var xPos = xStart - scrollOffset - - // used in the case of barchart - if (startDrawPadding != 0.dp) { - drawLine( - axisLineColor, - Offset(startDrawPadding.toPx(), 0f), - Offset(xPos, 0f), - strokeWidth = axisLineThickness.toPx() - ) - } - - for (index in 0..steps) { - xAxisHeight = drawXAxisLabel( - xAxisData, - index, - xAxisScale, - xPos - ) - drawAxisLineWithPointers( - xPos, - xAxisData, - zoomScale, - xAxisScale, - index != steps - ) - xPos += ((axisStepSize.toPx() * (zoomScale * xAxisScale))) - } - } - } - } -} - -private fun DrawScope.drawAxisLineWithPointers( - xPos: Float, - axisData: AxisData, - zoomScale: Float, - xAxisScale: Float, - canDrawEndLine: Boolean // Added check to avoid drawing an extra line post the last point -) { - with(axisData) { - if (axisConfig.isAxisLineRequired) { - if (canDrawEndLine) { - val axisStepWidth = (axisStepSize.toPx() * (zoomScale * xAxisScale)) - drawLine( - axisLineColor, - Offset(xPos, 0f), - if (shouldDrawAxisLineTillEnd) { - Offset((xPos + (axisStepWidth / 2) + axisStepWidth), 0f) - } else { - Offset(xPos + axisStepWidth, 0f) - }, strokeWidth = axisLineThickness.toPx() - ) - } - drawLine( - axisLineColor, - Offset(xPos, 0f), - Offset(xPos, indicatorLineWidth.toPx()), - strokeWidth = axisLineThickness.toPx() - ) - } - } -} - -private fun DrawScope.drawXAxisLabel( - axisData: AxisData, - index: Int, - xAxisScale: Float, - xPos: Float -): Dp = with(axisData) { - val calculatedXAxisHeight: Dp - val xAxisTextPaint = TextPaint().apply { - textSize = axisLabelFontSize.toPx() - color = axisLabelColor.toArgb() - textAlign = Paint.Align.LEFT - typeface = axisData.typeface - } - val xLabel = labelData((index * xAxisScale).toInt()) - val labelHeight = xLabel.getTextHeight(xAxisTextPaint) - val labelWidth = xLabel.getTextWidth(xAxisTextPaint) - calculatedXAxisHeight = - if (axisConfig.isAxisLineRequired) { - labelHeight.toDp() + axisLineThickness + - indicatorLineWidth + labelAndAxisLinePadding + axisBottomPadding - } else labelHeight.toDp() + labelAndAxisLinePadding - val ellipsizedText = TextUtils.ellipsize( - xLabel, - xAxisTextPaint, - axisStepSize.toPx(), - axisConfig.ellipsizeAt - ) - drawContext.canvas.nativeCanvas.apply { - val x = xPos - (labelWidth / 2) - val y = labelHeight / 2 + indicatorLineWidth.toPx() + labelAndAxisLinePadding.toPx() - withRotation(axisLabelAngle, x, y) { - drawText( - if (axisConfig.shouldEllipsizeAxisLabel) ellipsizedText.toString() else xLabel, - x, - y, - xAxisTextPaint - ) - } - } - calculatedXAxisHeight -} - -/** - * Returns triple of Xmax, Xmin & scale for given list of points and steps - * @param points: List of points in axis - * @param steps: Total steps in axis - */ -fun getXAxisScale( - points: List, - steps: Int, -): Triple { - val xMin = points.takeIf { it.isNotEmpty() }?.minOf { it.x } ?: 0f - val xMax = points.takeIf { it.isNotEmpty() }?.maxOf { it.x } ?: 0f - val totalSteps = (xMax - xMin) - val temp = totalSteps / steps - val scale = ceil(temp) - return Triple(xMin, xMax, scale) -} - -@Preview(showBackground = true) -@Composable -private fun XAxisPreview() { - val axisData = AxisData.Builder() - .labelAndAxisLinePadding(10.dp) - .axisPosition(Gravity.BOTTOM) - .axisLabelFontSize(14.sp) - .labelData { index -> index.toString() } - .build() - XAxis( - modifier = Modifier.height(40.dp), - xAxisData = axisData, - xStart = 0f, - scrollOffset = 0f, - zoomScale = 1f, - chartData = listOf() - ) -} diff --git a/YChartsLib/src/main/java/co/yml/charts/axis/YAxis.kt b/YChartsLib/src/main/java/co/yml/charts/axis/YAxis.kt deleted file mode 100644 index b51d8c71..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/axis/YAxis.kt +++ /dev/null @@ -1,192 +0,0 @@ -package co.yml.charts.axis - -import android.graphics.Paint -import android.text.TextPaint -import android.text.TextUtils -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.nativeCanvas -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import co.yml.charts.common.extensions.getTextHeight -import co.yml.charts.common.extensions.getTextWidth - -/** - * - * YAxis compose method used for drawing yAxis in any given graph. - * @param modifier : All modifier related property. - * @param yAxisData : All data needed to draw Yaxis. - * @see co.yml.charts.axis Data class to save all params related to Yaxis - */ -@Composable -fun YAxis(modifier: Modifier, yAxisData: AxisData) { - with(yAxisData) { - var yAxisWidth by remember { mutableStateOf(0.dp) } - val isRightAligned = axisPos == Gravity.RIGHT - Column(modifier = modifier.clipToBounds()) { - val steps = steps + 1 - Canvas( - modifier = modifier - .clipToBounds() - .width(yAxisWidth) - .background(backgroundColor) - ) { - val (yAxisHeight, segmentHeight) = getAxisInitValues( - yAxisData, - size.height, - axisBottomPadding.toPx(), - axisTopPadding.toPx() - ) - for (index in 0 until steps) { - // Drawing the axis labels - yAxisWidth = drawAxisLabel( - index, - yAxisData, - yAxisWidth, - isRightAligned, - yAxisHeight, - segmentHeight - ) - drawAxisLineWithPointers( - yAxisData, - index, - steps, - isRightAligned, - yAxisWidth, - yAxisHeight, - segmentHeight - ) - } - } - } - } -} - -fun getAxisInitValues( - axisData: AxisData, - canvasHeight: Float, - bottomPadding: Float, - topPadding: Float -): Pair = with(axisData) { - val yAxisHeight = canvasHeight - bottomPadding - // Minus the top padding to avoid cropping at the top - val segmentHeight = (yAxisHeight - topPadding) / axisData.steps - Pair(yAxisHeight, segmentHeight) -} - - -private fun DrawScope.drawAxisLineWithPointers( - axisData: AxisData, - index: Int, - totalSteps: Int, - isRightAligned: Boolean, - yAxisWidth: Dp, - yAxisHeight: Float, - segmentHeight: Float -) { - with(axisData) { - if (axisConfig.isAxisLineRequired) { - // Draw line only until reqYLabelsQuo -1 else will be a extra line at top with no label - if (index != (totalSteps - 1)) { - //Draw Yaxis line - drawLine( - start = Offset( - x = if (isRightAligned) 0.dp.toPx() else yAxisWidth.toPx(), - y = yAxisHeight - (segmentHeight * index) - ), - end = Offset( - x = if (isRightAligned) 0.dp.toPx() else yAxisWidth.toPx(), - y = yAxisHeight - (segmentHeight * (index + 1)) - ), - color = axisLineColor, strokeWidth = axisLineThickness.toPx() - ) - } - - //Draw pointer lines on Yaxis - drawLine( - start = Offset( - x = if (isRightAligned) 0.dp.toPx() else { - yAxisWidth.toPx() - indicatorLineWidth.toPx() - }, - y = yAxisHeight - (segmentHeight * index) - ), - end = Offset( - x = if (isRightAligned) indicatorLineWidth.toPx() else yAxisWidth.toPx(), - y = yAxisHeight - (segmentHeight * index) - ), - color = axisLineColor, strokeWidth = axisLineThickness.toPx() - ) - } - } -} - - -private fun DrawScope.drawAxisLabel( - index: Int, - axisData: AxisData, - yAxisWidth: Dp, - isRightAligned: Boolean, - yAxisHeight: Float, - segmentHeight: Float -): Dp = with(axisData) { - var calculatedYAxisWidth = yAxisWidth - val yAxisTextPaint = TextPaint().apply { - textSize = axisLabelFontSize.toPx() - color = axisLabelColor.toArgb() - textAlign = if (isRightAligned) Paint.Align.RIGHT else Paint.Align.LEFT - typeface = axisData.typeface - } - val yAxisLabel = labelData(index) - val measuredWidth = yAxisLabel.getTextWidth(yAxisTextPaint) - val height: Int = yAxisLabel.getTextHeight(yAxisTextPaint) - if (measuredWidth > calculatedYAxisWidth.toPx()) { - val width = - if (axisConfig.shouldEllipsizeAxisLabel) { - axisConfig.minTextWidthToEllipsize - } else measuredWidth.toDp() - calculatedYAxisWidth = - width + labelAndAxisLinePadding + axisOffset - } - val ellipsizedText = TextUtils.ellipsize( - yAxisLabel, - yAxisTextPaint, - axisConfig.minTextWidthToEllipsize.toPx(), - axisConfig.ellipsizeAt - ) - drawContext.canvas.nativeCanvas.apply { - drawText( - if (axisConfig.shouldEllipsizeAxisLabel) ellipsizedText.toString() else yAxisLabel, - if (isRightAligned) calculatedYAxisWidth.toPx() - labelAndAxisLinePadding.toPx() else { - axisStartPadding.toPx() - }, - yAxisHeight + height / 2 - ((segmentHeight * index)), - yAxisTextPaint - ) - } - return calculatedYAxisWidth -} - -@Preview(showBackground = true) -@Composable -private fun YAxisPreview() { - val yAxisData = AxisData.Builder() - .steps(5) - .bottomPadding(10.dp) - .axisPosition(Gravity.LEFT) - .axisLabelFontSize(14.sp) - .labelData { index -> index.toString() } - .build() - YAxis(modifier = Modifier.height(300.dp), yAxisData = yAxisData) -} - diff --git a/YChartsLib/src/main/java/co/yml/charts/chartcontainer/container/ScrollableCanvasContainer.kt b/YChartsLib/src/main/java/co/yml/charts/chartcontainer/container/ScrollableCanvasContainer.kt deleted file mode 100644 index dc65a35c..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/chartcontainer/container/ScrollableCanvasContainer.kt +++ /dev/null @@ -1,121 +0,0 @@ -package co.yml.charts.chartcontainer.container - -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.gestures.rememberScrollableState -import androidx.compose.foundation.gestures.scrollable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.unit.LayoutDirection -import co.yml.charts.chartcontainer.gestures.detectZoomGesture - -/** - * - * ScrollableCanvasContainer is a container used to draw any graph which supports scroll, - * zoom & tap or drag gestures. - * @param modifier : All modifier related property. - * @param calculateMaxDistance: callback to calculate the maximum scrolling distance. - * @param onDraw: Draw any canvas inside the onDraw scope using the input params in the lambda fxn - * @param drawXAndYAxis: Draw the X and Y axis along with the drawing area. - * @param containerBackgroundColor: Background color of the whole container. - * @param isPinchZoomEnabled: True if user can zoom in and out else false - * @param layoutDirection: Used to define the direction of scroll. - * @param onPointClicked: Callback for tap detected along with offset for tap. - * @param onScroll: Callback when user starts scrolling the graph. - * @param onZoomInAndOut: Callback when user starts zoomIn and Out w.r.t to the graph - */ - -@Composable -fun ScrollableCanvasContainer( - modifier: Modifier, - calculateMaxDistance: DrawScope.(Float) -> Float, - onDraw: DrawScope.(Float, Float) -> Unit, - drawXAndYAxis: @Composable BoxScope.(Float, Float) -> Unit, - containerBackgroundColor: Color = Color.White, - layoutDirection: LayoutDirection = LayoutDirection.Ltr, - onPointClicked: (Offset, Float) -> Unit = { _, _ -> }, - isPinchZoomEnabled: Boolean = true, - onScroll: () -> Unit = {}, - onZoomInAndOut: () -> Unit = {} -) { - val scrollOffset = remember { mutableStateOf(0f) } - val maxScrollOffset = remember { mutableStateOf(0f) } - val xZoom = remember { mutableStateOf(1f) } - val scrollState = rememberScrollableState { delta -> - scrollOffset.value -= delta - scrollOffset.value = checkAndGetMaxScrollOffset( - scrollOffset.value, - maxScrollOffset.value - ) - delta - } - - if (scrollState.isScrollInProgress){ - onScroll() - } - - CompositionLocalProvider( - LocalLayoutDirection provides layoutDirection, - ) { - Box( - modifier = modifier.clipToBounds(), - ) { - Canvas(modifier = Modifier - .align(Alignment.Center) - .fillMaxHeight() - .fillMaxWidth() - .background(containerBackgroundColor) - .scrollable( - state = scrollState, Orientation.Horizontal, enabled = true - ) - .pointerInput(Unit) { - detectTapGestures(onTap = { - onPointClicked(it, scrollOffset.value) - }) - } - .pointerInput(Unit) { - detectZoomGesture( - isZoomAllowed = isPinchZoomEnabled, - onZoom = { zoom -> - xZoom.value *= zoom - onZoomInAndOut() - } - ) - }, - onDraw = { - maxScrollOffset.value = calculateMaxDistance(xZoom.value) - onDraw(scrollOffset.value, xZoom.value) - }) - drawXAndYAxis(scrollOffset.value, xZoom.value) - } - } -} - -/** - * Returns the scroll state within the start and computed max scrollOffset & filters invalid scroll states. - * @param currentScrollOffset: Current scroll offset when user trying to scroll the canvas. - * @param computedMaxScrollOffset: Maximum calculated scroll offset for given data set. - */ -fun checkAndGetMaxScrollOffset(currentScrollOffset: Float, computedMaxScrollOffset: Float): Float { - return when { - currentScrollOffset < 0f -> 0f - currentScrollOffset > computedMaxScrollOffset -> computedMaxScrollOffset - else -> currentScrollOffset - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/chartcontainer/gestures/GestureExtensions.kt b/YChartsLib/src/main/java/co/yml/charts/chartcontainer/gestures/GestureExtensions.kt deleted file mode 100644 index beb48a0b..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/chartcontainer/gestures/GestureExtensions.kt +++ /dev/null @@ -1,56 +0,0 @@ -package co.yml.charts.chartcontainer.gestures - -import androidx.compose.foundation.gestures.awaitFirstDown -import androidx.compose.foundation.gestures.calculateCentroidSize -import androidx.compose.foundation.gestures.calculateZoom -import androidx.compose.foundation.gestures.forEachGesture -import androidx.compose.ui.input.pointer.PointerInputScope -import androidx.compose.ui.input.pointer.positionChanged -import kotlin.math.abs - -/** - * Gesture support to detect and filter pointer scopes to give a zoom start callback - * @param isZoomAllowed: True if user is allowed to zoom. - * @param onZoom: Callback when zoom gesture is detected. - */ -internal suspend fun PointerInputScope.detectZoomGesture( - isZoomAllowed: Boolean = true, - onZoom: (zoom: Float) -> Unit -) { - if (isZoomAllowed) { - forEachGesture { - awaitPointerEventScope { - awaitFirstDown(requireUnconsumed = false) - } - awaitPointerEventScope { - var zoom = 1f - var pastTouchSlop = false - val touchSlop = viewConfiguration.touchSlop - - do { - val event = awaitPointerEvent() - val canceled = event.changes.any { it.isConsumed } - if (event.changes.size == 1) break - else if (event.changes.size == 2) { - if (isZoomAllowed) { - if (!canceled) { - val zoomChange = event.calculateZoom() - if (!pastTouchSlop) { - zoom *= zoomChange - val centroidSize = - event.calculateCentroidSize(useCurrent = false) - val zoomMotion = abs(1 - zoom) * centroidSize - if (zoomMotion > touchSlop) pastTouchSlop = true - } - if (pastTouchSlop) { - if (zoomChange != 1f) onZoom(zoomChange) - event.changes.forEach { if (it.positionChanged()) it.consume() } - } - } - } - } else break - } while (!canceled && event.changes.any { it.pressed }) - } - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/components/ItemDivider.kt b/YChartsLib/src/main/java/co/yml/charts/common/components/ItemDivider.kt deleted file mode 100644 index eb6681b8..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/components/ItemDivider.kt +++ /dev/null @@ -1,25 +0,0 @@ -package co.yml.charts.common.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp - -/** - * Draws a horizontal line as a divider for given thickness and color - * @param thickness: Defines the thickness of the divider. - * @param dividerColor: Defines the color of the divider. - */ -@Composable -fun ItemDivider(thickness: Dp, dividerColor: Color = Color.Black) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(thickness) - .background(dividerColor) - ) -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/components/Legends.kt b/YChartsLib/src/main/java/co/yml/charts/common/components/Legends.kt deleted file mode 100644 index b6522cb0..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/components/Legends.kt +++ /dev/null @@ -1,64 +0,0 @@ -package co.yml.charts.common.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.items -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import co.yml.charts.common.model.LegendLabel -import co.yml.charts.common.model.LegendsConfig - -/** - * Renders the list of legends in a grid format for given given grid column count - * @param modifier: Defines the arrangements of ui compositions. - * @param legendsConfig: Defines the configurations required for rendering legends in [LegendsConfig] - */ -@Composable -fun Legends(modifier: Modifier = Modifier, legendsConfig: LegendsConfig) { - with(legendsConfig) { - if (legendLabelList.size > 1) { - LazyVerticalGrid( - modifier = modifier - .fillMaxWidth() - .padding( - horizontal = gridPaddingHorizontal, - vertical = gridPaddingVertical - ), - verticalArrangement = Arrangement.spacedBy(10.dp), - horizontalArrangement = Arrangement.spacedBy(10.dp), - columns = GridCells.Fixed(gridColumnCount) - ) { - items(legendLabelList) { - Legend(legendsConfig, it) - } - } - } - } -} - -@Composable -private fun Legend(config: LegendsConfig, legendLabel: LegendLabel) { - Row( - horizontalArrangement = config.legendsArrangement, - verticalAlignment = Alignment.CenterVertically - ) { - Box( - modifier = Modifier - .background(legendLabel.color) - .size(config.colorBoxSize) - ) - Spacer(modifier = Modifier.padding(config.spaceBWLabelAndColorBox)) - Text( - text = legendLabel.name, - style = config.textStyle, - overflow = TextOverflow.Ellipsis - ) - } -} - diff --git a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/AccessibilityBottomSheetDailog.kt b/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/AccessibilityBottomSheetDailog.kt deleted file mode 100644 index 04706121..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/AccessibilityBottomSheetDailog.kt +++ /dev/null @@ -1,64 +0,0 @@ -package co.yml.charts.common.components.accessibility - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import kotlinx.coroutines.launch - -/** - * Bottom sheet to show items in vertical list view with a close button at top - */ -@ExperimentalMaterialApi -@Composable -fun AccessibilityBottomSheetDialog( - content: @Composable ColumnScope.() -> Unit, - modifier: Modifier = Modifier, - backgroundColor: Color, - popUpTopRightButtonTitle: String, - popUpTopRightButtonDescription: String, - sheetState: ModalBottomSheetState -) { - val composeScope = rememberCoroutineScope() - ModalBottomSheetLayout( - sheetContent = { - Box( - modifier = modifier - .background(backgroundColor) - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier.fillMaxWidth() - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(10.dp), - horizontalArrangement = Arrangement.End - ) { - Button(onClick = { - composeScope.launch { sheetState.hide() } - }) { - Text( - text = popUpTopRightButtonTitle, - modifier = Modifier.semantics { - contentDescription = popUpTopRightButtonDescription - }) - } - } - content() - } - } - }, - sheetState = sheetState, - sheetBackgroundColor = Color.White, - sheetElevation = 0.dp - ) {} -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/BarInfo.kt b/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/BarInfo.kt deleted file mode 100644 index ff19dbdc..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/BarInfo.kt +++ /dev/null @@ -1,49 +0,0 @@ -package co.yml.charts.common.components.accessibility - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp - -/** - * Composable to display each bar item for given bar chart - * @param axisLabelDescription: Axis label description - * @param barDescription: Bar description - */ -@Composable -fun BarInfo( - axisLabelDescription: String, - barDescription: String, barColor: Color -) { - // Merge elements below for accessibility purposes - Row(modifier = Modifier - .padding(start = 10.dp, end = 10.dp) - .clickable { } - .semantics(mergeDescendants = true) {}, verticalAlignment = Alignment.CenterVertically - ) { - Text(axisLabelDescription, fontSize = 12.sp) - Spacer(modifier = Modifier.width(10.dp)) - Column( - modifier = Modifier - .weight(1f) - .padding(10.dp) - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Box( - modifier = Modifier - .padding(5.dp) - .background(barColor) - .size(20.dp) - ) - Text(barDescription, fontSize = 12.sp) - } - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/CombinedChartInfo.kt b/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/CombinedChartInfo.kt deleted file mode 100644 index ca97aa26..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/CombinedChartInfo.kt +++ /dev/null @@ -1,81 +0,0 @@ -package co.yml.charts.common.components.accessibility - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CornerSize -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import co.yml.charts.ui.barchart.models.GroupBar -import co.yml.charts.common.components.ItemDivider -import co.yml.charts.common.model.Point - -/** - * Composable to display each combined chart item for given combined chart. - * @param pointsList: List of points in each item. - * @param lineColor: List of colors of lines as per line chart. - * @param groupBar: Details of each group bar. - * @param axisLabelDescription: Axis label description. - * @param barColorPaletteList: List of each bar colors for a given group bar. - * @param dividerColor: Divider color between each point items. - */ -@Composable -fun CombinedChartInfo( - pointsList: List, - lineColor: List, - groupBar: GroupBar?, - axisLabelDescription: String, - barColorPaletteList: List, - dividerColor: Color -) { - // Merge elements below for accessibility purposes - Row(modifier = Modifier - .padding(start = 10.dp, end = 10.dp) - .clickable { } - .semantics(mergeDescendants = true) {}, verticalAlignment = Alignment.CenterVertically - ) { - Text(axisLabelDescription, fontSize = 12.sp) - Spacer(modifier = Modifier.width(10.dp)) - Column( - modifier = Modifier - .weight(1f) - .padding(5.dp) - ) { - pointsList.forEachIndexed { index, point -> - Row(verticalAlignment = Alignment.CenterVertically) { - Box( - modifier = Modifier - .padding(5.dp) - .background( - color = lineColor[index], - shape = RoundedCornerShape(corner = CornerSize(10.dp)) - ) - .size(10.dp) - ) - Text(point.description, fontSize = 12.sp) - } - Spacer(modifier = Modifier.height(5.dp)) - } - ItemDivider(thickness = 1.dp, dividerColor = dividerColor) - groupBar?.barList?.forEachIndexed { index, value -> - Row(verticalAlignment = Alignment.CenterVertically) { - Box( - modifier = Modifier - .padding(5.dp) - .background(barColorPaletteList[index]) - .size(20.dp) - ) - Text(value.description, fontSize = 12.sp) - } - Spacer(modifier = Modifier.height(5.dp)) - } - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/GroupBarInfo.kt b/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/GroupBarInfo.kt deleted file mode 100644 index 1ec84a9f..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/GroupBarInfo.kt +++ /dev/null @@ -1,56 +0,0 @@ -package co.yml.charts.common.components.accessibility - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import co.yml.charts.ui.barchart.models.GroupBar - - -/** - * Composable to display each group bar item for given group bar chart. - * @param axisLabelDescription: Axis label description. - * @param groupBar: Details of each group bar. - * @param barColorPaletteList: List of each bar colors for a given group bar. - */ -@Composable -fun GroupBarInfo( - groupBar: GroupBar, - axisLabelDescription: String, - barColorPaletteList: List -) { - // Merge elements below for accessibility purposes - Row(modifier = Modifier - .padding(start = 10.dp, end = 10.dp) - .clickable { } - .semantics(mergeDescendants = true) {}, verticalAlignment = Alignment.CenterVertically - ) { - Text(axisLabelDescription, fontSize = 12.sp) - Spacer(modifier = Modifier.width(10.dp)) - Column( - modifier = Modifier - .weight(1f) - .padding(5.dp) - ) { - groupBar.barList.forEachIndexed { index, value -> - Row(verticalAlignment = Alignment.CenterVertically) { - Box( - modifier = Modifier - .padding(5.dp) - .background(barColorPaletteList[index]) - .size(20.dp) - ) - Text(value.description, fontSize = 12.sp) - } - Spacer(modifier = Modifier.height(5.dp)) - } - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/LinePointInfo.kt b/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/LinePointInfo.kt deleted file mode 100644 index 61a27201..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/LinePointInfo.kt +++ /dev/null @@ -1,51 +0,0 @@ -package co.yml.charts.common.components.accessibility - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp - -/** - * Composable to display each line point data for given line chart. - * @param axisLabelDescription: Axis label description. - * @param pointDescription: Details of each point on the line. - * @param lineColor: Color of each line. - */ -@Composable -fun LinePointInfo( - axisLabelDescription: String, - pointDescription: String, - lineColor: Color -) { - // Merge elements below for accessibility purposes - Row(modifier = Modifier - .padding(start = 10.dp, end = 10.dp) - .clickable { } - .semantics(mergeDescendants = true) {}, verticalAlignment = Alignment.CenterVertically - ) { - Text(axisLabelDescription, fontSize = 12.sp) - Spacer(modifier = Modifier.width(5.dp)) - Column( - modifier = Modifier - .weight(1f) - .padding(5.dp) - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Box( - modifier = Modifier - .padding(5.dp) - .background(lineColor) - .size(20.dp) - ) - Text(pointDescription, fontSize = 12.sp) - } - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/SliceInfo.kt b/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/SliceInfo.kt deleted file mode 100644 index 205590e6..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/components/accessibility/SliceInfo.kt +++ /dev/null @@ -1,42 +0,0 @@ -package co.yml.charts.common.components.accessibility - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import co.yml.charts.ui.piechart.models.PieChartData - -/** - * Composable to display each slice for a given pie chart. - * @param slice: Details of each slice in pie/ donut chart. - * @param slicePercentage: Percentage of each slice. - */ -@Composable -fun SliceInfo(slice: PieChartData.Slice, slicePercentage: Int) { - // Merge elements below for accessibility purposes - Row(modifier = Modifier - .clickable { } - .semantics(mergeDescendants = true) {}, - verticalAlignment = Alignment.CenterVertically - ) { - Box( - modifier = Modifier - .padding(5.dp) - .background(slice.color) - .size(30.dp) - ) - Column( - modifier = Modifier - .weight(1f) - .padding(top = 10.dp, bottom = 10.dp) - ) { - Text(slice.sliceDescription(slicePercentage), fontSize = 12.sp) - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/extensions/Extensions.kt b/YChartsLib/src/main/java/co/yml/charts/common/extensions/Extensions.kt deleted file mode 100644 index 7eb3085b..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/extensions/Extensions.kt +++ /dev/null @@ -1,251 +0,0 @@ -package co.yml.charts.common.extensions - -import android.accessibilityservice.AccessibilityServiceInfo -import android.content.Context -import android.graphics.Paint -import android.graphics.Rect -import android.text.TextPaint -import android.view.accessibility.AccessibilityManager -import androidx.compose.runtime.* -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Outline -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.LayoutDirection -import co.yml.charts.common.model.Point -import co.yml.charts.ui.linechart.model.GridLines -import java.text.DecimalFormat - - -/** -return the width of text in canvas drawn text - */ -fun String.getTextWidth(paint: Paint): Float { - return paint.measureText(this) -} - -/** -return the height of text in canvas drawn text - */ -fun String.getTextHeight(paint: Paint): Int { - val bounds = Rect() - paint.getTextBounds( - this, - 0, - this.length, - bounds - ) - return bounds.height() -} - -/** -return the shape that is used to mask a particular area for given leftPadding & rightPadding - */ -internal class RowClip( - private val leftPadding: Float, - private val rightPadding: Dp, - private val topPadding: Float = 0f -) : Shape { - override fun createOutline( - size: Size, - layoutDirection: LayoutDirection, - density: Density - ): Outline { - return Outline.Rectangle( - androidx.compose.ui.geometry.Rect( - leftPadding, - topPadding, - size.width - rightPadding.value * density.density, - size.height - ) - ) - } -} - -/** - * Returns true or false if the object is null - */ -fun Any?.isNotNull() = this != null - - -/** - * Returns the background rect for the highlighted text. - * @param x : X point. - * @param y: Y point. - * @param text: Text to be drawn inside the background. - * @param paint: Background paint. - */ -fun getTextBackgroundRect( - x: Float, - y: Float, - text: String, - paint: TextPaint -): Rect { - val fontMetrics = paint.fontMetrics - val textLength = paint.measureText(text) - return Rect( - (x - (textLength / 2)).toInt(), - (y + fontMetrics.top).toInt(), - (x + (textLength / 2)).toInt(), - (y + fontMetrics.bottom).toInt() - ) -} - -/** - * Returns the maximum and minimum points of X axis. - * @param points: List of the points to be drawn. - */ -fun getXMaxAndMinPoints( - points: List, -): Pair { - val xMin = points.minOf { it.x } - val xMax = points.maxOf { it.x } - return Pair(xMin, xMax) -} - - -/** - * Returns the maximum and minimum points of Y axis - * @param points List of points - */ -fun getYMaxAndMinPoints( - points: List, -): Pair { - if (points.isEmpty()) - return Pair(0f, 0f) - val xMin = points.minOf { it.y } - val xMax = points.maxOf { it.y } - return Pair(xMin, xMax) -} - -/** - * Returns the maximum value of Y axis - * @param yMax Maximum value in the Y axis - * @param yStepSize size of one step in the Y axis - */ -fun getMaxElementInYAxis(yMax: Float, yStepSize: Int): Int { - var reqYLabelsQuo = - (yMax / yStepSize) - val reqYLabelsRem = yMax.rem(yStepSize) - if (reqYLabelsRem > 0f) { - reqYLabelsQuo += 1 - } - return reqYLabelsQuo.toInt() * yStepSize -} - - -/** - * Return true if the point is locked - * @param dragOffset Tapped offset - * @param xOffset in the X axis - */ -fun Offset.isDragLocked(dragOffset: Float, xOffset: Float) = - ((dragOffset) > x - xOffset / 2) && ((dragOffset) < x + xOffset / 2) - - -/** - * Return true if the point is selected - * @param tapOffset Tapped offset - * @param xOffset in the X axis - * @param bottom bottom Value - */ -fun Offset.isTapped(tapOffset: Offset, xOffset: Float, bottom: Float, tapPadding: Float) = - ((tapOffset.x) > x - (xOffset + tapPadding) / 2) && ((tapOffset.x) < x + (xOffset + tapPadding) / 2) && - ((tapOffset.plus(Offset(0f, tapPadding))).y > y) && ((tapOffset.y) < bottom) - - -/** - * Returns true if the tapped point is withing the given boundries else false - * @param tapOffset Tapped offset - * @param tapPadding plus or minus padding from the point or clickable padding - */ -fun Offset.isPointTapped(tapOffset: Offset, tapPadding: Float) = - ((tapOffset.x) > x - tapPadding) && ((tapOffset.x) < x + tapPadding) && - ((tapOffset.plus(Offset(0f, tapPadding))).y > y) && - ((tapOffset.minus(Offset(0f, tapPadding))).y < y) - - -/*** - * Returns converted single precision string from float value - */ -fun Float.formatToSinglePrecision(): String = DecimalFormat("#.#").format(this) - - -/** - * - * DrawScope.drawGridLines is the extension method used to draw the grid lines on any graph - * @param yBottom : Bottom value for Y-Axis - * @param top: Top value for Y axis - * @param xLeft: Total left padding in X-Axis. - * @param paddingRight : Total right padding. - * @param scrollOffset : Total scroll offset. - * @param verticalPointsSize : Total points in the X-Axis. - * @param xZoom : Total zoom offset. - * @param xAxisScale: Scale of each point in X-Axis. - * @param ySteps : Number of steps in y-Axis. - * @param xAxisStepSize: The size of each X-Axis step. - * @param gridLines: Data class to handle styling related to grid lines. - */ -fun DrawScope.drawGridLines( - yBottom: Float, - top: Float, - xLeft: Float, - paddingRight: Dp, - scrollOffset: Float, - verticalPointsSize: Int, - xZoom: Float, - xAxisScale: Float, - ySteps: Int, - xAxisStepSize: Dp, - gridLines: GridLines -) { - val availableHeight = yBottom - top - val steps = ySteps + 1 // Considering 0 as step - val gridOffset = availableHeight / if (steps > 1) steps - 1 else 1 - // Should start from 1 as we don't consider the XAxis - if (gridLines.enableHorizontalLines) { - (1 until steps).forEach { - val y = yBottom - (it * gridOffset) - gridLines.drawHorizontalLines(this, xLeft, y, size.width - paddingRight.toPx()) - } - } - if (gridLines.enableVerticalLines) { - var xPos = xLeft - scrollOffset - (0 until verticalPointsSize).forEach { _ -> - gridLines.drawVerticalLines(this, xPos, yBottom, top) - xPos += ((xAxisStepSize.toPx() * (xZoom * xAxisScale))) - } - } -} - -/** - * Returns true if accessibility services are enabled else false - */ -@Composable -internal fun Context.collectIsTalkbackEnabledAsState(): State { - val accessibilityManager = - this.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager? - fun isTalkbackEnabled(): Boolean { - val accessibilityServiceInfoList = - accessibilityManager?.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN) - return accessibilityServiceInfoList?.any { - it.resolveInfo.serviceInfo.processName.equals(TALKBACK_PACKAGE_NAME) - } ?: false - } - - val talkbackEnabled = remember { mutableStateOf(isTalkbackEnabled()) } - val accessibilityManagerEnabled = accessibilityManager?.isEnabled ?: false - var accessibilityEnabled by remember { mutableStateOf(accessibilityManagerEnabled) } - - accessibilityManager?.addAccessibilityStateChangeListener { accessibilityEnabled = it } - - LaunchedEffect(accessibilityEnabled) { - talkbackEnabled.value = if (accessibilityEnabled) isTalkbackEnabled() else false - } - return talkbackEnabled -} - -private const val TALKBACK_PACKAGE_NAME = "com.google.android.marvin.talkback" diff --git a/YChartsLib/src/main/java/co/yml/charts/common/model/AccessibilityConfig.kt b/YChartsLib/src/main/java/co/yml/charts/common/model/AccessibilityConfig.kt deleted file mode 100644 index f7c9fe8a..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/model/AccessibilityConfig.kt +++ /dev/null @@ -1,25 +0,0 @@ -package co.yml.charts.common.model - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import co.yml.charts.common.utils.ChartConstants - -/** - * AccessibilityConfig is a data class to configure all params needed for accessibility service. - * @param shouldHandleBackWhenTalkBackPopUpShown: True by default to dismiss the accessibility dialog when back pressed else false. - * @param chartDescription: Description used by accessibility service when tapped on the chart. - * @param popUpTopRightButtonTitle: Title of the button visible on the accessibility popUp at the top right. - * @param popUpTopRightButtonDescription: Content description of the button visible on the accessibility popUp at the top right. - * @param dividerColor: Defines the color for the divider used in the accessibility popUp - * @param dividerThickness: Defines the thickness for the divider in Dp used in the accessibility popUp - */ -data class AccessibilityConfig( - val chartDescription: String = ChartConstants.CHART_DESCRIPTION, - val shouldHandleBackWhenTalkBackPopUpShown: Boolean = true, - val popUpTopRightButtonTitle: String = ChartConstants.POPUP_TOP_RIGHT_BUTTON_TITLE, - val popUpTopRightButtonDescription: String = ChartConstants.POPUP_TOP_RIGHT_BUTTON_DESCRIPTION, - val dividerColor: Color = Color.Gray, - val dividerThickness: Dp = 2.dp -) - diff --git a/YChartsLib/src/main/java/co/yml/charts/common/model/LegendsConfig.kt b/YChartsLib/src/main/java/co/yml/charts/common/model/LegendsConfig.kt deleted file mode 100644 index 27f1a61e..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/model/LegendsConfig.kt +++ /dev/null @@ -1,38 +0,0 @@ -package co.yml.charts.common.model - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -/** - * LegendsConfig data class params used in config label in graph. - * @param legendLabelList: stackLabelList is used to show labels with colors - * @param gridColumnCount : Column Count for stackLabel grid - * @param gridPaddingHorizontal : Horizontal padding for stackLabel grid - * @param gridPaddingVertical : Vertical padding for stackLabel grid - * @param spaceBWLabelAndColorBox: Space between Label and ColorBox for stackLabel grid item - * @param colorBoxSize: Blend mode for the groupSeparator - * @param textStyle: TextStyle for label - * */ -data class LegendsConfig( - val legendLabelList: List, - val gridColumnCount: Int = 1, - val gridPaddingHorizontal: Dp = 8.dp, - val gridPaddingVertical: Dp = 8.dp, - val colorBoxSize: Dp = 25.dp, - val textStyle: TextStyle = TextStyle(), - val spaceBWLabelAndColorBox: Dp = 8.dp, - val legendsArrangement: Arrangement.Horizontal = Arrangement.Center -) - -/** - * LegendLabel data class params used in drawing label in graph. - * @param color : Color of label. - * @param name : Name of label. - * */ -data class LegendLabel( - val color: Color, - val name: String, -) diff --git a/YChartsLib/src/main/java/co/yml/charts/common/model/PlotData.kt b/YChartsLib/src/main/java/co/yml/charts/common/model/PlotData.kt deleted file mode 100644 index 050d0028..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/model/PlotData.kt +++ /dev/null @@ -1,19 +0,0 @@ -package co.yml.charts.common.model - -/** - * PlotData is a base class to all the types of available graphs - * @param plotType: Type of the graph given in [PlotType] - */ -interface PlotData { - val plotType: PlotType -} - -/** - * PlotType is a sealed class to define the types of supported graphs/plots - */ -sealed interface PlotType { - object Line : PlotType - object Bar : PlotType - object Pie : PlotType - object Donut : PlotType -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/model/Point.kt b/YChartsLib/src/main/java/co/yml/charts/common/model/Point.kt deleted file mode 100644 index 40cc512b..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/model/Point.kt +++ /dev/null @@ -1,16 +0,0 @@ -package co.yml.charts.common.model - -/** - * - * Point data class is used for holding the point on the graph. - * @param x: x co-ordinate value in the graph - * @param y: y co-ordinate value in the graph - * @param description: Description given to describe the value of the point for accessibility service - * Used this class over PointF as its a java class and unable to mock the same - */ -data class Point( - val x: Float, - val y: Float, - val description: String = "Value of point is ${String.format("%.2f", y)}" -) - diff --git a/YChartsLib/src/main/java/co/yml/charts/common/utils/ChartConstants.kt b/YChartsLib/src/main/java/co/yml/charts/common/utils/ChartConstants.kt deleted file mode 100644 index 9e162599..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/utils/ChartConstants.kt +++ /dev/null @@ -1,9 +0,0 @@ -package co.yml.charts.common.utils - -object ChartConstants { - const val DEFAULT_YAXIS_BOTTOM_PADDING = 10 - const val DEFAULT_DETECT_DRAG_TIME_OUT = 100L - const val CHART_DESCRIPTION = "Double tap to know the chart in detail" - const val POPUP_TOP_RIGHT_BUTTON_TITLE = "Close" - const val POPUP_TOP_RIGHT_BUTTON_DESCRIPTION = "Tap to close the dialog" -} diff --git a/YChartsLib/src/main/java/co/yml/charts/common/utils/DataUtils.kt b/YChartsLib/src/main/java/co/yml/charts/common/utils/DataUtils.kt deleted file mode 100644 index 3df30ad9..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/common/utils/DataUtils.kt +++ /dev/null @@ -1,228 +0,0 @@ -package co.yml.charts.common.utils - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import co.yml.charts.ui.barchart.models.BarData -import co.yml.charts.ui.barchart.models.GroupBar -import co.yml.charts.ui.piechart.models.PieChartData -import co.yml.charts.common.model.LegendLabel -import co.yml.charts.common.model.LegendsConfig -import co.yml.charts.common.model.PlotType -import co.yml.charts.common.model.Point -import kotlin.random.Random - -object DataUtils { - /** - * Returns list of points - * @param listSize: Size of total number of points needed. - * @param start: X values to start from. ex: 50 to 100 - * @param maxRange: Max range of Y values - */ - fun getLineChartData(listSize: Int, start: Int = 0, maxRange: Int): List { - val list = arrayListOf() - for (index in 0 until listSize) { - list.add( - Point( - index.toFloat(), - (start until maxRange).random().toFloat() - ) - ) - } - return list - } - - /** - * Return the sample bar chart data - * @param listSize Size of the list - * @param maxRange Maximum range for the values - */ - fun getBarChartData(listSize: Int, maxRange: Int): List { - val list = arrayListOf() - for (index in 0 until listSize) { - list.add( - BarData( - Point( - index.toFloat(), - "%.2f".format(Random.nextDouble(1.0, maxRange.toDouble())).toFloat() - ), - Color( - Random.nextInt(256), Random.nextInt(256), Random.nextInt(256) - ), - "Bar$index" - ) - ) - } - return list - } - - /** - * Return the sample gradient bar chart data - * @param listSize Size of the list - * @param maxRange Maximum range for the values - */ - fun getGradientBarChartData(listSize: Int, maxRange: Int): List { - val list = arrayListOf() - for (index in 0 until listSize) { - list.add( - BarData( - point = Point( - index.toFloat(), - "%.2f".format(Random.nextDouble(1.0, maxRange.toDouble())).toFloat() - ), - gradientColorList = listOf( - Color( - Random.nextInt(256), Random.nextInt(256), Random.nextInt(256) - ), - Color( - Random.nextInt(256), Random.nextInt(256), Random.nextInt(256) - ), - Color( - Random.nextInt(256), Random.nextInt(256), Random.nextInt(256) - ), - Color( - Random.nextInt(256), Random.nextInt(256), Random.nextInt(256) - ) - ), - label = "Bar$index" - ) - ) - } - return list - } - - /** - * Returns sample pie chart data - */ - fun getPieChartData(): PieChartData { - return PieChartData( - slices = listOf( - PieChartData.Slice("SciFi", 15f, Color(0xFF333333)), - PieChartData.Slice("Comedy", 15f, Color(0xFF666a86)), - PieChartData.Slice("Drama", 10f, Color(0xFF95B8D1)), - PieChartData.Slice("Romance", 10f, Color(0xFFE8DDB5)), - PieChartData.Slice("Action", 20f, Color(0xFFEDAFB8)), - PieChartData.Slice("Thriller", 100f, Color(0xFFF94892)), - PieChartData.Slice("Western", 10f, Color(0xFFA675A1)), - PieChartData.Slice("Fantasy", 10f, Color(0xFF8F3985)), - ), - plotType = PlotType.Pie - ) - } - - /** - * Returns sample pie chart data - */ - fun getPieChartData2(): PieChartData { - return PieChartData( - slices = listOf( - PieChartData.Slice("Android", 30f, Color(0xFF002B5B)), - PieChartData.Slice("iOS", 30f, Color(0xFF2B4865)), - PieChartData.Slice("Windows", 15f, Color(0xFF256D85)), - PieChartData.Slice("Other", 25f, Color(0xFF806D85)), - ), - plotType = PlotType.Pie - ) - } - - /** - * Returns sample donut chart data - */ - fun getDonutChartData(): PieChartData { - return PieChartData( - slices = listOf( - PieChartData.Slice("HP", 15f, Color(0xFF5F0A87)), - PieChartData.Slice("Dell", 30f, Color(0xFF20BF55)), - PieChartData.Slice("Lenovo", 10f, Color(0xFFA40606)), - PieChartData.Slice("Asus", 15f, Color(0xFFF53844)), - PieChartData.Slice("Acer", 10f, Color(0xFFEC9F05)), - PieChartData.Slice("Apple", 30f, Color(0xFF009FFD)), - ), - plotType = PlotType.Donut - ) - } - - /** - * Returns the sample gradient bar chart data. - * @param listSize Size of the list - * @param maxRange Maximum range for the values - * @param barSize size of bars in one group - */ - fun getGroupBarChartData(listSize: Int, maxRange: Int, barSize: Int): List { - val list = mutableListOf() - for (index in 0 until listSize) { - val barList = mutableListOf() - for (i in 0 until barSize) { - val barValue = "%.2f".format(Random.nextDouble(1.0, maxRange.toDouble())).toFloat() - barList.add( - BarData( - Point( - i.toFloat(), - barValue - ), - label = "B$i", - description = "Bar at $i with label B$i has value ${ - String.format( - "%.2f", barValue - ) - }" - ) - ) - } - list.add(GroupBar(index.toString(), barList)) - } - return list - } - - /** - * Returns the sample stackLabelList data - * @param colorPaletteList color list for each legend - */ - fun getLegendsLabelData(colorPaletteList: List): List { - val legendLabelList = mutableListOf() - for (index in colorPaletteList.indices) { - legendLabelList.add( - LegendLabel( - colorPaletteList[index], - "B$index" - ) - ) - } - return legendLabelList - } - - /** - * Returns the sample colors list for given size - * @param listSize: Size of the colors list. - */ - fun getColorPaletteList(listSize: Int): List { - val colorList = mutableListOf() - - for (index in 0 until listSize) { - colorList.add( - Color( - (0 until 256).random(), (0 until 256).random(), (0 until 256).random() - ) - ) - } - return colorList - } - - /** - * Returns the legends config for given pie chart data - * @param pieChartData: Pie chart details. - * @param gridSize: Legends grid size. - */ - fun getLegendsConfigFromPieChartData(pieChartData: PieChartData, gridSize: Int): LegendsConfig { - val legendsList = mutableListOf() - pieChartData.slices.forEach { slice -> - legendsList.add(LegendLabel(slice.color, slice.label)) - } - return LegendsConfig( - legendLabelList = legendsList, - gridColumnCount = gridSize, - legendsArrangement = Arrangement.Start, - textStyle = TextStyle() - ) - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/BarChart.kt b/YChartsLib/src/main/java/co/yml/charts/ui/barchart/BarChart.kt deleted file mode 100644 index 490e969f..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/BarChart.kt +++ /dev/null @@ -1,422 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class) - -package co.yml.charts.ui.barchart - - -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.geometry.CornerRadius -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.drawscope.DrawScope -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import co.yml.charts.axis.XAxis -import co.yml.charts.axis.YAxis -import co.yml.charts.chartcontainer.container.ScrollableCanvasContainer -import co.yml.charts.ui.barchart.models.BarChartData -import co.yml.charts.ui.barchart.models.BarData -import co.yml.charts.ui.barchart.models.BarStyle -import co.yml.charts.ui.barchart.models.SelectionHighlightData -import co.yml.charts.common.components.ItemDivider -import co.yml.charts.common.components.accessibility.AccessibilityBottomSheetDialog -import co.yml.charts.common.components.accessibility.BarInfo -import co.yml.charts.common.extensions.RowClip -import co.yml.charts.common.extensions.collectIsTalkbackEnabledAsState -import co.yml.charts.common.extensions.getMaxElementInYAxis -import co.yml.charts.common.extensions.getXMaxAndMinPoints -import co.yml.charts.common.extensions.getYMaxAndMinPoints -import co.yml.charts.common.extensions.isTapped -import co.yml.charts.common.model.Point -import co.yml.charts.common.utils.ChartConstants.DEFAULT_YAXIS_BOTTOM_PADDING -import kotlinx.coroutines.launch - - -/** - * - * [BarChart] compose method for drawing bar chart. - * @param modifier: All modifier related properties - * @param barChartData : All data needed to Bar Chart - * @see [BarChartData] Data class to save all params related to Bar Chart - */ -@OptIn(ExperimentalMaterialApi::class) -@Composable -fun BarChart(modifier: Modifier, barChartData: BarChartData) { - val accessibilitySheetState = - rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden) - val scope = rememberCoroutineScope() - val isTalkBackEnabled by LocalContext.current.collectIsTalkbackEnabledAsState() - - if (accessibilitySheetState.isVisible && isTalkBackEnabled - && barChartData.accessibilityConfig.shouldHandleBackWhenTalkBackPopUpShown - ) { - BackHandler { - scope.launch { - accessibilitySheetState.hide() - } - } - } - Surface(modifier) { - with(barChartData) { - var barHighlightVisibility by remember { mutableStateOf(false) } - var identifiedPoint by remember { mutableStateOf(BarData(Point(0f, 0f))) } - var xOffset by remember { mutableStateOf(0f) } - var tapOffset by remember { mutableStateOf(Offset(0f, 0f)) } - var isTapped by remember { mutableStateOf(false) } - var columnWidth by remember { mutableStateOf(0f) } - var horizontalGap by remember { mutableStateOf(0f) } - var rowHeight by remember { mutableStateOf(0f) } - val paddingRight = paddingEnd - val points = chartData.map { it.point } - val bgColor = MaterialTheme.colors.surface - - val (xMin, xMax) = getXMaxAndMinPoints(points) - val (_, yMax) = getYMaxAndMinPoints(points) - - val xAxisData = xAxisData.copy(axisStepSize = barStyle.barWidth + barStyle.paddingBetweenBars, - steps = chartData.size - 1, - startDrawPadding = LocalDensity.current.run { columnWidth.toDp() }) - val yAxisData = yAxisData.copy(axisBottomPadding = LocalDensity.current.run { rowHeight.toDp() }) - val maxElementInYAxis = getMaxElementInYAxis(yMax, yAxisData.steps) - - if (!showXAxis) { - rowHeight = LocalDensity.current.run { DEFAULT_YAXIS_BOTTOM_PADDING.dp.toPx() } - } - - ScrollableCanvasContainer(modifier = modifier - .semantics { - contentDescription = barChartData.accessibilityConfig.chartDescription - } - .clickable { - if (isTalkBackEnabled) { - scope.launch { - accessibilitySheetState.animateTo( - ModalBottomSheetValue.Expanded - ) - } - } - }, - containerBackgroundColor = backgroundColor, - calculateMaxDistance = { xZoom -> - horizontalGap = horizontalExtraSpace.toPx() - val xLeft = columnWidth + horizontalGap - xOffset = - (barStyle.barWidth.toPx() + barStyle.paddingBetweenBars.toPx()) * xZoom - getMaxScrollDistance( - columnWidth, xMax, xMin, xOffset, xLeft, paddingRight.toPx(), size.width - ) - }, - onDraw = { scrollOffset, xZoom -> - val yBottom = size.height - rowHeight - val yOffset = ((yBottom - yAxisData.axisTopPadding.toPx()) / maxElementInYAxis) - xOffset = - ((barStyle.barWidth).toPx() + barStyle.paddingBetweenBars.toPx()) * xZoom - val xLeft = columnWidth + horizontalGap + barStyle.barWidth.toPx() / 2 - val dragLocks = mutableMapOf>() - - // Draw bar lines - chartData.forEachIndexed { _, barData -> - val drawOffset = getDrawOffset( - barData.point, xMin, xOffset, xLeft, scrollOffset, yBottom, yOffset, 0f - ) - val height = yBottom - drawOffset.y - - // drawing each individual bars - drawBarGraph(barChartData.barStyle, barData, drawOffset, height) - - val middleOffset = - Offset(drawOffset.x + barStyle.barWidth.toPx() / 2, drawOffset.y) - // store the tap points for selection - if (isTapped && middleOffset.isTapped( - tapOffset, barStyle.barWidth.toPx(), yBottom, tapPadding.toPx() - ) - ) { - dragLocks[0] = barData to drawOffset - } - } - - drawUnderScrollMask(columnWidth, paddingRight, bgColor) - - if (barStyle.selectionHighlightData != null) { - // highlighting the selected bar and showing the data points - identifiedPoint = highlightBar( - dragLocks, - barHighlightVisibility, - identifiedPoint, - barChartData.barStyle, - isTapped, - columnWidth, - yBottom, - paddingRight, - yOffset - ) - } - }, - drawXAndYAxis = { scrollOffset, xZoom -> - if (showXAxis) { - XAxis( - xAxisData = xAxisData, - modifier = Modifier - .align(Alignment.BottomStart) - .fillMaxWidth() - .wrapContentHeight() - .clip( - RowClip( - columnWidth, paddingRight - ) - ) - .onGloballyPositioned { - rowHeight = it.size.height.toFloat() - }, - xStart = columnWidth + horizontalGap + LocalDensity.current.run { - (barStyle.barWidth.toPx()) - }, - scrollOffset = scrollOffset, - zoomScale = xZoom, - chartData = points - ) - } - - if (showYAxis) { - YAxis( - modifier = Modifier - .align(Alignment.TopStart) - .fillMaxHeight() - .wrapContentWidth() - .onGloballyPositioned { - columnWidth = it.size.width.toFloat() - }, yAxisData = yAxisData - ) - } - }, onPointClicked = { offset: Offset, _: Float -> - isTapped = true - barHighlightVisibility = true - tapOffset = offset - }, onScroll = { - isTapped = false - barHighlightVisibility = false - }) - } - if (isTalkBackEnabled) { - with(barChartData) { - AccessibilityBottomSheetDialog( - modifier = Modifier.fillMaxSize(), - backgroundColor = Color.White, - content = { - LazyColumn { - items(chartData.size) { index -> - Column { - BarInfo( - xAxisData.axisLabelDescription( - xAxisData.labelData( - index - ) - ), - chartData[index].description, - chartData[index].color - ) - ItemDivider( - thickness = accessibilityConfig.dividerThickness, - dividerColor = accessibilityConfig.dividerColor - ) - } - } - } - }, - popUpTopRightButtonTitle = accessibilityConfig.popUpTopRightButtonTitle, - popUpTopRightButtonDescription = accessibilityConfig.popUpTopRightButtonDescription, - sheetState = accessibilitySheetState - ) - } - } - } -} - -/** - * - * Used to draw the highlighted text - * @param identifiedPoint : Selected points - * @param selectedOffset: Offset selected - * @param barWidth: Width of single bar - * @param highlightData: Data for the highlight section - */ -private fun DrawScope.drawHighlightText( - identifiedPoint: BarData, selectedOffset: Offset, barWidth: Dp, highlightData: SelectionHighlightData -) { - val centerPointOfBar = selectedOffset.x + barWidth.toPx() / 2 - // Drawing the highlighted background and text - highlightData.drawPopUp(this, selectedOffset, identifiedPoint, centerPointOfBar) -} - -/** - * - * Used to draw the individual bars - * @param barStyle : all meta data related to the bar styling - * @param barData : data related to a single bar - * @param drawOffset: top left offset for the drawing the bar - * @param height : height of the bar graph - */ -fun DrawScope.drawBarGraph( - barStyle: BarStyle, barData: BarData, drawOffset: Offset, height: Float -) = with(barStyle) { - // Draw bar lines - if (isGradientEnabled) { - val brush = Brush.verticalGradient( - colors = barData.gradientColorList - ) - drawRoundRect( - brush = brush, topLeft = drawOffset, size = Size(barWidth.toPx(), height), cornerRadius = CornerRadius( - cornerRadius.toPx(), cornerRadius.toPx() - ), style = barDrawStyle, blendMode = barBlendMode - ) - } else { - drawRoundRect( - color = barData.color, - topLeft = drawOffset, - size = Size(barWidth.toPx(), height), - cornerRadius = CornerRadius( - cornerRadius.toPx(), cornerRadius.toPx() - ), - style = barDrawStyle, - blendMode = barBlendMode - ) - } -} - -/** - * - * returns the max scrollable distance based on the points to be drawn along with padding etc. - * @param columnWidth : Width of the Y-Axis. - * @param xMax : Max X-Axis value. - * @param xMin: Min X-Axis value. - * @param xOffset: Total distance between two X-Axis points. - * @param xLeft: Total Left padding. - * @param paddingRight : Padding at the end of the canvas. - * @param canvasWidth : Total available canvas width. - */ -fun getMaxScrollDistance( - columnWidth: Float, - xMax: Float, - xMin: Float, - xOffset: Float, - xLeft: Float, - paddingRight: Float, - canvasWidth: Float -): Float { - val xLastPoint = (xMax - xMin) * xOffset + xLeft + columnWidth + paddingRight - return if (xLastPoint > canvasWidth) { - xLastPoint - canvasWidth - } else 0f -} - -/** - * - * returns identified point and displaying the data points and highlighted bar . - * @param dragLocks : Mutable map of BarData and drawOffset. - * @param barHighlightVisibility : Flag to control the visibility of highlighted text. - * @param identifiedPoint: selected bar data. - * @param barStyle: Data related to the bar graph styling. - * @param isDragging : Boolean flag for the dragging status. - * @param columnWidth : Width of the Y axis. - * @param yBottom : Bottom padding. - * @param paddingRight : Right padding. - * @param yOffset : Distance between two y points. - * @param shouldShowHighlightPopUp : Default true but if highlight popup not required make it false - */ -fun DrawScope.highlightBar( - dragLocks: MutableMap>, - barHighlightVisibility: Boolean, - identifiedPoint: BarData, - barStyle: BarStyle, - isDragging: Boolean, - columnWidth: Float, - yBottom: Float, - paddingRight: Dp, - yOffset: Float, - shouldShowHighlightPopUp: Boolean = true -): BarData { - var mutableIdentifiedPoint: BarData = identifiedPoint - // Handle the show the selected bar - if (isDragging) { - val x = dragLocks.values.firstOrNull()?.second?.x - if (x != null) { - mutableIdentifiedPoint = dragLocks.values.map { it.first }.first() - } - - // Draw highlight bar on selection - if (barStyle.selectionHighlightData?.isHighlightBarRequired == true) { - dragLocks.values.firstOrNull()?.let { (barData, location) -> - val (xPoint, yPoint) = location - if (xPoint >= columnWidth && xPoint <= size.width - paddingRight.toPx()) { - val y1 = yBottom - ((barData.point.y - 0) * yOffset) - barStyle.selectionHighlightData.drawHighlightBar( - this, xPoint, yPoint, barStyle.barWidth.toPx(), (yBottom - y1) - ) - } - } - } - } - if (shouldShowHighlightPopUp) { - val selectedOffset = dragLocks.values.firstOrNull()?.second - if (barHighlightVisibility && selectedOffset != null && barStyle.selectionHighlightData != null) { - drawHighlightText( - mutableIdentifiedPoint, selectedOffset, barStyle.barWidth, barStyle.selectionHighlightData - ) - } - } - return mutableIdentifiedPoint -} - -/** - * - * DrawScope.drawUnderScrollMask extension method used for drawing a rectangular mask to make graph scrollable under the YAxis. - * @param columnWidth : Width of the rectangular mask here width of Y Axis is used. - * @param paddingRight : Padding given at the end of the graph. - * @param bgColor : Background of the rectangular mask. - */ -fun DrawScope.drawUnderScrollMask(columnWidth: Float, paddingRight: Dp, bgColor: Color) { - // Draw column to make graph look scrollable under Yaxis - drawRect( - bgColor, Offset(0f, 0f), Size(columnWidth, size.height) - ) - // Draw right padding - drawRect( - bgColor, Offset(size.width - paddingRight.toPx(), 0f), Size(paddingRight.toPx(), size.height) - ) -} - -/** - * returns the draw offset for bar graph. - * @param point : bar point - * @param xMin: Minimum value on the x axis - * @param yMin: Minimum value on the y axis - * @param xOffset: Distance between bars - * @param yOffset: Distance between y axis points - * @param xLeft: X starting point of bar graph - * @param scrollOffset: Scroll offset - * @param yBottom: Y starting point of bar graph - */ -fun getDrawOffset( - point: Point, xMin: Float, xOffset: Float, - xLeft: Float, scrollOffset: Float, yBottom: Float, yOffset: Float, yMin: Float -): Offset { - val (x, y) = point - val x1 = ((x - xMin) * xOffset) + xLeft - scrollOffset - val y1 = yBottom - ((y - yMin) * yOffset) - return Offset(x1, y1) -} - diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/GroupBarChart.kt b/YChartsLib/src/main/java/co/yml/charts/ui/barchart/GroupBarChart.kt deleted file mode 100644 index 8def0da9..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/GroupBarChart.kt +++ /dev/null @@ -1,446 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class) - -package co.yml.charts.ui.barchart - - -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import co.yml.charts.axis.XAxis -import co.yml.charts.axis.YAxis -import co.yml.charts.chartcontainer.container.ScrollableCanvasContainer -import co.yml.charts.ui.barchart.models.BarData -import co.yml.charts.ui.barchart.models.GroupBarChartData -import co.yml.charts.ui.barchart.models.SelectionHighlightData -import co.yml.charts.common.components.ItemDivider -import co.yml.charts.common.components.accessibility.AccessibilityBottomSheetDialog -import co.yml.charts.common.components.accessibility.GroupBarInfo -import co.yml.charts.common.extensions.RowClip -import co.yml.charts.common.extensions.collectIsTalkbackEnabledAsState -import co.yml.charts.common.extensions.getMaxElementInYAxis -import co.yml.charts.common.extensions.isTapped -import co.yml.charts.common.model.Point -import co.yml.charts.common.utils.ChartConstants.DEFAULT_YAXIS_BOTTOM_PADDING -import kotlinx.coroutines.launch - - -/** - * - * [GroupBarChart] compose method for drawing group bar chart. - * @param modifier: All modifier related properties - * @param groupBarChartData : All data needed to group bar chart - * @see [GroupBarChartData] Data class to save all params related to Bar Chart - */ -@Composable -fun GroupBarChart(modifier: Modifier, groupBarChartData: GroupBarChartData) { - val accessibilitySheetState = - rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden) - val scope = rememberCoroutineScope() - val isTalkBackEnabled by LocalContext.current.collectIsTalkbackEnabledAsState() - if (accessibilitySheetState.isVisible && isTalkBackEnabled - && groupBarChartData.accessibilityConfig.shouldHandleBackWhenTalkBackPopUpShown - ) { - BackHandler { - scope.launch { - accessibilitySheetState.hide() - } - } - } - Surface(modifier.fillMaxSize()) { - with(groupBarChartData.barPlotData) { - var visibility by remember { mutableStateOf(false) } - var identifiedPoint by remember { mutableStateOf(BarData(Point(0f, 0f))) } - var xOffset by remember { mutableStateOf(0f) } - var tapOffset by remember { mutableStateOf(Offset(0f, 0f)) } - var isTapped by remember { mutableStateOf(false) } - var columnWidth by remember { mutableStateOf(0f) } - var horizontalGap by remember { mutableStateOf(0f) } - var rowHeight by remember { mutableStateOf(0f) } - val paddingRight = groupBarChartData.paddingEnd - val valueList = groupBarList.map { it.yMax } - val bgColor = MaterialTheme.colors.surface - - val xMax = groupBarList.size - val yMax = valueList.maxOrNull() ?: 0f - val xAxisData = - groupBarChartData.xAxisData.copy(axisStepSize = ((barStyle.barWidth * groupingSize) + barStyle.paddingBetweenBars), - shouldDrawAxisLineTillEnd = true, - steps = groupBarList.size - 1, - startDrawPadding = LocalDensity.current.run { columnWidth.toDp() }) - val yAxisData = - groupBarChartData.yAxisData.copy(axisBottomPadding = LocalDensity.current.run { rowHeight.toDp() }) - - val maxElementInYAxis = getMaxElementInYAxis(yMax, yAxisData.steps) - - if (!groupBarChartData.showXAxis) { - rowHeight = LocalDensity.current.run { DEFAULT_YAXIS_BOTTOM_PADDING.dp.toPx() } - } - ScrollableCanvasContainer(modifier = modifier - .semantics { - contentDescription = groupBarChartData.accessibilityConfig.chartDescription - } - .clickable { - if (isTalkBackEnabled) { - scope.launch { - accessibilitySheetState.animateTo( - ModalBottomSheetValue.Expanded - ) - } - } - }, - containerBackgroundColor = groupBarChartData.backgroundColor, - calculateMaxDistance = { xZoom -> - horizontalGap = groupBarChartData.horizontalExtraSpace.toPx() - val xLeft = columnWidth + horizontalGap - xOffset = - ((barStyle.barWidth.toPx() * groupingSize) + barStyle.paddingBetweenBars.toPx()) * xZoom - getMaxScrollDistance( - columnWidth, - xMax.toFloat(), - 0f, - xOffset, - xLeft, - paddingRight.toPx(), - size.width - ) - }, - onDraw = { scrollOffset, xZoom -> - val yBottom = size.height - rowHeight - val yOffset = ((yBottom - yAxisData.axisTopPadding.toPx()) / maxElementInYAxis) - xOffset = - ((barStyle.barWidth.toPx() * groupingSize) + barStyle.paddingBetweenBars.toPx()) * xZoom - val xLeft = columnWidth + horizontalGap - val dragLocks = mutableMapOf>() - - // Draw bar lines - groupBarList.forEachIndexed { index, groupBarData -> - var insideOffset = 0f - - groupBarData.barList.forEachIndexed { subIndex, individualBar -> - val drawOffset = getGroupBarDrawOffset( - index, - individualBar.point.y, - xOffset, - xLeft, - scrollOffset, - yBottom, - yOffset, - 0f - ) - val height = yBottom - drawOffset.y - - val individualOffset = Offset(drawOffset.x + insideOffset, drawOffset.y) - - // drawing each individual bars - drawGroupBarGraph( - groupBarChartData, individualOffset, height, subIndex - ) - insideOffset += barStyle.barWidth.toPx() - - val middleOffset = Offset( - individualOffset.x + barStyle.barWidth.toPx() / 2, drawOffset.y - ) - // store the tap points for selection - if (isTapped && middleOffset.isTapped( - tapOffset, - barStyle.barWidth.toPx(), - yBottom, - groupBarChartData.tapPadding.toPx() - ) - ) { - dragLocks[0] = individualBar to individualOffset - } - } - - if (groupBarChartData.groupSeparatorConfig.showSeparator && index != groupBarList.size - 1) { - // drawing each Group Separator bars - val yOffset2 = (yBottom - yAxisData.axisTopPadding.toPx()) - val height = yBottom - yAxisData.axisTopPadding.toPx() - val drawOffset2 = getGroupBarDrawOffset( - index, - rowHeight, - xOffset, - xLeft, - scrollOffset, - yBottom, - yOffset2, - 0f - ) - val xOffset2 = - (drawOffset2.x + insideOffset + (barStyle.paddingBetweenBars.toPx() / 2) - groupBarChartData.groupSeparatorConfig.separatorWidth.toPx() / 2) - val individualOffset = Offset(xOffset2, yAxisData.axisTopPadding.toPx()) - - drawGroupSeparator( - individualOffset, - height, - groupBarChartData.groupSeparatorConfig.separatorWidth.toPx(), - groupBarChartData.groupSeparatorConfig.separatorColor, - groupBarChartData - ) - } - } - drawUnderScrollMask(columnWidth, paddingRight, bgColor) - - if (barStyle.selectionHighlightData != null) { - // highlighting the selected bar and showing the data points - identifiedPoint = highlightGroupBar( - dragLocks, - visibility, - identifiedPoint, - barStyle.selectionHighlightData, - isTapped, - columnWidth, - yBottom, - paddingRight, - yOffset, - barStyle.barWidth - ) - } - }, - drawXAndYAxis = { scrollOffset, xZoom -> - val points = mutableListOf() - for (index in groupBarList.indices) { - points.add(Point(index.toFloat(), 0f)) - } - - if (groupBarChartData.showXAxis) { - XAxis( - xAxisData = xAxisData, - modifier = Modifier - .align(Alignment.BottomStart) - .fillMaxWidth() - .wrapContentHeight() - .clip( - RowClip( - columnWidth, paddingRight - ) - ) - .onGloballyPositioned { - rowHeight = it.size.height.toFloat() - }, - xStart = columnWidth + horizontalGap + LocalDensity.current.run { - (barStyle.barWidth.toPx() * groupingSize) / 2 - }, - scrollOffset = scrollOffset, - zoomScale = xZoom, - chartData = points, - ) - } - - if (groupBarChartData.showYAxis) { - YAxis( - modifier = Modifier - .align(Alignment.TopStart) - .fillMaxHeight() - .wrapContentWidth() - .onGloballyPositioned { - columnWidth = it.size.width.toFloat() - }, - yAxisData = yAxisData, - ) - } - }, - onPointClicked = { offset: Offset, _: Float -> - isTapped = true - visibility = true - tapOffset = offset - }, - onScroll = { - isTapped = false - visibility = false - }) - } - if (isTalkBackEnabled) { - with(groupBarChartData) { - AccessibilityBottomSheetDialog( - modifier = Modifier.fillMaxSize(), backgroundColor = Color.White, content = { - LazyColumn { - items(barPlotData.groupBarList.size) { index -> - Column { - GroupBarInfo( - barPlotData.groupBarList[index], - xAxisData.axisLabelDescription( - xAxisData.labelData(index) - ), - barPlotData.barColorPaletteList - ) - ItemDivider( - thickness = accessibilityConfig.dividerThickness, - dividerColor = accessibilityConfig.dividerColor - ) - } - } - } - }, - popUpTopRightButtonTitle = accessibilityConfig.popUpTopRightButtonTitle, - popUpTopRightButtonDescription = accessibilityConfig.popUpTopRightButtonDescription, - sheetState = accessibilitySheetState - ) - } - } - } -} - -/** - * - * Used to draw the highlighted text - * @param identifiedPoint : Selected points - * @param selectedOffset: Offset selected - * @param barWidth: Width of single bar - * @param highlightData: Data for the highlight section - */ -private fun DrawScope.drawGroupHighlightText( - identifiedPoint: BarData, - selectedOffset: Offset, - barWidth: Dp, - highlightData: SelectionHighlightData -) { - val centerPointOfBar = selectedOffset.x + barWidth.toPx() / 2 - // Drawing the highlighted background and text - highlightData.drawGroupBarPopUp(this, selectedOffset, identifiedPoint, centerPointOfBar) -} - - -/** - * - * Used to draw the individual bars - * @param barGraphData : all meta data related to the bar graph - * @param drawOffset: topLeft offset for the drawing the bar - * @param height : height of the bar graph - * @param subIndex : Index of the bar - */ -private fun DrawScope.drawGroupBarGraph( - barGraphData: GroupBarChartData, drawOffset: Offset, height: Float, subIndex: Int -) { - with(barGraphData.barPlotData) { - val color = barColorPaletteList[subIndex] - drawRoundRect( - color = color, - topLeft = drawOffset, - size = Size(barGraphData.barPlotData.barStyle.barWidth.toPx(), height), - cornerRadius = CornerRadius( - barStyle.cornerRadius.toPx(), barStyle.cornerRadius.toPx() - ), - style = barStyle.barDrawStyle, - blendMode = barStyle.barBlendMode - ) - } -} - - -/** - * - * returns identified point and displaying the data points and highlighted bar . - * @param dragLocks : Mutable map of BarData and drawOffset. - * @param visibility : Flag to control the visibility of highlighted text. - * @param identifiedPoint: selected bar data. - * @param selectionHighlightData: Data related to the bar graph highlight. - * @param isDragging : Boolean flag for the dragging status. - * @param columnWidth : Width of the Y axis. - * @param yBottom : Bottom padding. - * @param paddingRight : Right padding. - * @param yOffset : Distance between two y points. - * @param barWidth : Width of each bar. - */ -fun DrawScope.highlightGroupBar( - dragLocks: MutableMap>, - visibility: Boolean, - identifiedPoint: BarData, - selectionHighlightData: SelectionHighlightData?, - isDragging: Boolean, - columnWidth: Float, - yBottom: Float, - paddingRight: Dp, - yOffset: Float, - barWidth: Dp, -): BarData { - var mutableIdentifiedPoint: BarData = identifiedPoint - // Handle the show the selected bar - if (isDragging) { - val x = dragLocks.values.firstOrNull()?.second?.x - if (x != null) { - mutableIdentifiedPoint = dragLocks.values.map { it.first }.first() - } - - // Draw highlight bar on selection - if (selectionHighlightData?.isHighlightBarRequired == true) { - dragLocks.values.firstOrNull()?.let { (barData, location) -> - val (xPoint, yPoint) = location - if (xPoint >= columnWidth && xPoint <= size.width - paddingRight.toPx()) { - val y1 = yBottom - ((barData.point.y - 0) * yOffset) - selectionHighlightData.drawHighlightBar( - this, xPoint, yPoint, barWidth.toPx(), yBottom - y1 - ) - } - } - } - } - - val selectedOffset = dragLocks.values.firstOrNull()?.second - if (visibility && selectedOffset != null && selectionHighlightData != null) { - drawGroupHighlightText( - mutableIdentifiedPoint, selectedOffset, barWidth, selectionHighlightData - ) - } - return mutableIdentifiedPoint -} - -/** - * returns the draw offset for bar graph. - * @param yMin: Minimum value on the y axis - * @param xOffset: Distance between bars - * @param yOffset: Distance between y axis points - * @param xLeft: X starting point of bar graph - * @param scrollOffset: Scroll offset - * @param yBottom: Y starting point of bar graph - */ -fun getGroupBarDrawOffset( - x: Int, y: Float, xOffset: Float, - xLeft: Float, scrollOffset: Float, yBottom: Float, yOffset: Float, yMin: Float -): Offset { - val x1 = (x * xOffset) + xLeft - scrollOffset - val y1 = yBottom - ((y - yMin) * yOffset) - return Offset(x1, y1) -} - -/** - * - * Used to draw the group separator - * @param barGraphData: [GroupBarChartData] - * @param drawOffset: topLeft offset for the drawing the separator - * @param height : height of the separator - * @param width : width of the separator - * @param color : color of the separator - */ -private fun DrawScope.drawGroupSeparator( - drawOffset: Offset, - height: Float, - width: Float, - color: Color, - barGraphData: GroupBarChartData, -) { - drawRoundRect( - color = color, - topLeft = drawOffset, - size = Size(width, height), - blendMode = barGraphData.groupSeparatorConfig.separatorBlendMode - ) -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarChartData.kt b/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarChartData.kt deleted file mode 100644 index 462a3902..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarChartData.kt +++ /dev/null @@ -1,59 +0,0 @@ -package co.yml.charts.ui.barchart.models - -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.DrawStyle -import androidx.compose.ui.graphics.drawscope.Fill -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import co.yml.charts.axis.AxisData -import co.yml.charts.common.model.AccessibilityConfig - - -/** - * BarGraph data class params used in drawing bar graph. - * @param chartData : List of BarData - * @param xAxisData: All the configurations related to X-Axis to be defined here in [AxisData] - * @param yAxisData: All the configurations related to Y-Axis to be defined here in [AxisData] - * @param horizontalExtraSpace: Extra space added in the horizontal axis - * @param paddingEnd: End Padding - * @param paddingTop: Top Padding - * @param tapPadding: Extra padding area for tapping - * @param showXAxis: Boolean Flag to enable/disable X axis - * @param showYAxis: Boolean Flag to enable/disable Y axis - * @param accessibilityConfig: Configs related to accessibility service defined here in [AccessibilityConfig] - */ -data class BarChartData( - val chartData: List, - val xAxisData: AxisData = AxisData.Builder().build(), - val yAxisData: AxisData = AxisData.Builder().build(), - val backgroundColor: Color = Color.White, - val horizontalExtraSpace: Dp = 0.dp, - val barStyle: BarStyle = BarStyle(), - val paddingEnd: Dp = 10.dp, - val paddingTop: Dp = 0.dp, - val tapPadding: Dp = 10.dp, - val showYAxis: Boolean = true, - val showXAxis: Boolean = true, - val accessibilityConfig: AccessibilityConfig = AccessibilityConfig() -) - -/** - * BarStyle data class adds styling related to each bar in the bar graph - * @param barWidth: Width of a bar - * @param cornerRadius: Corner radius for the bars - * @param paddingBetweenBars: Space between adjacent bars - * @param isGradientEnabled: Boolean Flag to enable/disable gradient bars - * @param barBlendMode: Blend mode for the bars - * @param barDrawStyle: Draw style for the bars - */ -data class BarStyle( - val barWidth: Dp = 30.dp, - val cornerRadius: Dp = 4.dp, - val paddingBetweenBars: Dp = 15.dp, - val isGradientEnabled: Boolean = false, - val barBlendMode: BlendMode = DrawScope.DefaultBlendMode, - val barDrawStyle: DrawStyle = Fill, - val selectionHighlightData: SelectionHighlightData? = SelectionHighlightData() -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarData.kt b/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarData.kt deleted file mode 100644 index 523f7a36..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarData.kt +++ /dev/null @@ -1,21 +0,0 @@ -package co.yml.charts.ui.barchart.models - -import androidx.compose.ui.graphics.Color -import co.yml.charts.common.model.Point - - -/** - * Data class for individual bar. - * @param point : Axis point - * @param color: Color of a bar - * @param label: label of a bar - * @param gradientColorList: Color list for the gradient bar - * @param description: Description to describe bar value for accessibility service. - */ -data class BarData( - val point: Point, - val color: Color = Color.Red, - val label: String = "", - val gradientColorList: List = listOf(Color.Red, Color.Blue), - val description: String = "Value of bar $label is value ${String.format("%.2f", point.y)}" -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarPlotData.kt b/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarPlotData.kt deleted file mode 100644 index b7e5b5d7..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/BarPlotData.kt +++ /dev/null @@ -1,23 +0,0 @@ -package co.yml.charts.ui.barchart.models - -import androidx.compose.ui.graphics.Color -import co.yml.charts.common.model.PlotData -import co.yml.charts.common.model.PlotType - -/** - * BarPlotData is a data class that holds bar graph related data and styling components - * @param plotType : Defines the type of plot/graph - * @param groupBarList : Data related to the bar point. - * @param barStyle : Styling related to the bars. - */ -data class BarPlotData( - override val plotType: PlotType = PlotType.Bar, - val groupBarList: List, - val groupingSize: Int = groupBarList.firstOrNull()?.barList?.size ?: 1, - val barColorPaletteList: List = listOf(), - val barStyle: BarStyle = BarStyle(), -) : PlotData { - companion object { - fun default() = BarPlotData(groupBarList = listOf()) - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupBar.kt b/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupBar.kt deleted file mode 100644 index b5269062..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupBar.kt +++ /dev/null @@ -1,11 +0,0 @@ -package co.yml.charts.ui.barchart.models - -/** - * Data class for individual group bar. - * @param label: Label of a bar - * @param barList: List of individual bars inside a group bar with each bar data of type [BarData] - */ -data class GroupBar(val label: String, val barList: List) { - val yMax: Float - get() = this.barList.map { it.point }.maxOf { it.y } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupBarChartData.kt b/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupBarChartData.kt deleted file mode 100644 index 552a63a4..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupBarChartData.kt +++ /dev/null @@ -1,38 +0,0 @@ -package co.yml.charts.ui.barchart.models - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import co.yml.charts.axis.AxisData -import co.yml.charts.common.model.AccessibilityConfig - -/** - * GroupBarGraph data class params used in drawing bar graph. - * @param barPlotData : Group bar plot data. - * @param xAxisData: All the configurations related to X-Axis to be defined here in [AxisData] - * @param yAxisData: All the configurations related to Y-Axis to be defined here in [AxisData] - * @param horizontalExtraSpace: Extra space added in the horizontal axis. - * @param backgroundColor: Background color of the graph. - * @param paddingEnd: End Padding. - * @param paddingTop: Top Padding. - * @param showXAxis: Boolean Flag to enable/disable X axis. - * @param showYAxis: Boolean Flag to enable/disable Y axis. - * @param tapPadding: Tap padding offset. - * @param groupSeparatorConfig : All config related to the GroupSeparator. - * @param accessibilityConfig: Configs related to accessibility service defined here in [AccessibilityConfig] - */ - -data class GroupBarChartData( - val barPlotData: BarPlotData, - val xAxisData: AxisData = AxisData.Builder().build(), - val yAxisData: AxisData = AxisData.Builder().build(), - val backgroundColor: Color = Color.White, - val horizontalExtraSpace: Dp = 10.dp, - val paddingEnd: Dp = 10.dp, - val paddingTop: Dp = 0.dp, - val showYAxis: Boolean = true, - val showXAxis: Boolean = true, - val tapPadding: Dp = 10.dp, - val groupSeparatorConfig: GroupSeparatorConfig = GroupSeparatorConfig(), - val accessibilityConfig: AccessibilityConfig = AccessibilityConfig() -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupSeparatorConfig.kt b/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupSeparatorConfig.kt deleted file mode 100644 index ccb0206b..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/GroupSeparatorConfig.kt +++ /dev/null @@ -1,21 +0,0 @@ -package co.yml.charts.ui.barchart.models - -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -/** - * GroupSeparatorConfig data class params used in config groupSeparator. - * @param separatorWidth : width of the groupSeparator - * @param separatorColor : color of the groupSeparator - * @param showSeparator : Boolean Flag to show/hide groupSeparator - * @param separatorBlendMode: Blend mode for the groupSeparator - * */ -data class GroupSeparatorConfig( - val separatorWidth: Dp = 1.dp, - val separatorColor: Color = Color.Gray, - val showSeparator: Boolean = true, - val separatorBlendMode: BlendMode = DrawScope.DefaultBlendMode, -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/SelectionHighlightData.kt b/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/SelectionHighlightData.kt deleted file mode 100644 index 02960b0d..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/barchart/models/SelectionHighlightData.kt +++ /dev/null @@ -1,165 +0,0 @@ -package co.yml.charts.ui.barchart.models - -import android.graphics.Paint -import android.graphics.Typeface -import android.text.TextPaint -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.* -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.DrawStyle -import androidx.compose.ui.graphics.drawscope.Fill -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import co.yml.charts.common.extensions.getTextBackgroundRect - -/** - * Used to customise the highlighted text and the bar - * - * @param highlightTextOffset: Padding between the highlighted bar and the text - * @param highlightTextSize: Text size of the highlighted bar text - * @param highlightTextColor: Text color of the highlighted bar text - * @param highlightTextBackgroundColor: Background color of the highlight background - * @param highlightTextBackgroundAlpha: Alpha for the highlighted text background - * @param highlightTextTypeface: Typeface of the highlighted bar text - * @param highlightBarCornerRadius: Corner radius of the highlighted bar - * @param highlightBarColor: Color of the highlighted bar - * @param highlightBarStrokeWidth: Stroke width of the highlighted bar - * @param highlightPopUpCornerRadius: Corner radius of the highlighted background - * @param backgroundColorFilter: ColorFilter to apply to the color when drawn into the destination. - * @param backgroundBlendMode:Blending algorithm to be applied to the path when it is drawn. - * @param backgroundStyle: Whether or not the path is stroked or filled in. - * @param isHighlightBarRequired: Boolean flag to enable disable highlight - * @param popUpLabel : The text that can be shown on the popup given 2 input params x and y values - * @param drawPopUp: Override this to change the default background implementation. You are provided - * with the selected offset, x, y values, center point of bar. - * @param drawHighlightBar: draw override this to change the default drawRoundRect implementation. You are provided - * with the actual point x, y, height, width. - * @param groupBarPopUpLabel: Popup label for selected bar in group bar chart. - * @param drawGroupBarPopUp: draw override this to change the default popup implementation. You are provided - * with the actual point x, y, height, width. - */ -data class SelectionHighlightData( - //highlight text - val highlightTextOffset: Dp = 15.dp, - val highlightTextSize: TextUnit = 12.sp, - val highlightTextColor: Color = Color.Black, - val highlightTextBackgroundColor: Color = Color.Yellow, - val highlightTextBackgroundAlpha: Float = 0.7f, - val highlightTextTypeface: Typeface = Typeface.DEFAULT, - // highlight bar - val highlightBarCornerRadius: Dp = 2.dp, - val highlightBarColor: Color = Color.Black, - val highlightBarStrokeWidth: Dp = 2.dp, - - val highlightPopUpCornerRadius: CornerRadius = CornerRadius(5f), - val backgroundColorFilter: ColorFilter? = null, - val backgroundBlendMode: BlendMode = DrawScope.DefaultBlendMode, - val backgroundStyle: DrawStyle = Fill, - val highlightLabelAlignment: Paint.Align = Paint.Align.CENTER, - val isHighlightBarRequired: Boolean = true, - val popUpLabel: (Float, Float) -> (String) = { x, y -> - val xLabel = "x : ${x.toInt()} " - val yLabel = "y : ${String.format("%.2f", y)}" - "$xLabel $yLabel" - }, - - val drawPopUp: DrawScope.(Offset, BarData, Float) -> Unit = { selectedOffset, identifiedPoint, centerPointOfBar -> - val highlightTextPaint = TextPaint().apply { - textSize = highlightTextSize.toPx() - color = highlightTextColor.toArgb() - textAlign = highlightLabelAlignment - typeface = highlightTextTypeface - } - val label = popUpLabel(identifiedPoint.point.x, identifiedPoint.point.y) - drawContext.canvas.nativeCanvas.apply { - val background = getTextBackgroundRect( - centerPointOfBar, - selectedOffset.y, - label, - highlightTextPaint - ) - drawRoundRect( - color = highlightTextBackgroundColor, - topLeft = Offset( - background.left.toFloat(), - background.top.toFloat() - highlightTextOffset.toPx() - ), - size = Size(background.width().toFloat(), background.height().toFloat()), - alpha = highlightTextBackgroundAlpha, - cornerRadius = highlightPopUpCornerRadius, - colorFilter = backgroundColorFilter, - blendMode = backgroundBlendMode, - style = backgroundStyle - ) - drawText( - label, - centerPointOfBar, - selectedOffset.y - highlightTextOffset.toPx(), - highlightTextPaint - ) - } - }, - - val drawHighlightBar: DrawScope.(Float, Float, Float, Float) -> Unit = { x, y, width, height -> - drawRoundRect( - color = highlightBarColor, - topLeft = Offset(x, y), - size = Size(width, height), - cornerRadius = CornerRadius( - highlightBarCornerRadius.toPx(), - highlightBarCornerRadius.toPx() - ), - style = Stroke(width = highlightBarStrokeWidth.toPx()) - ) - }, - - val groupBarPopUpLabel: (String, Float) -> (String) = { name, value -> - val xLabel = "Name : $name " - val yLabel = "Value : ${String.format("%.2f", value)}" - "$xLabel $yLabel" - }, - - - val drawGroupBarPopUp: DrawScope.(Offset, BarData, Float) -> Unit = { selectedOffset, identifiedPoint, centerPointOfBar -> - val highlightTextPaint = TextPaint().apply { - textSize = highlightTextSize.toPx() - color = highlightTextColor.toArgb() - textAlign = highlightLabelAlignment - typeface = highlightTextTypeface - } - val xLabel = "B${identifiedPoint.point.x.toInt()}" - val label = groupBarPopUpLabel(xLabel, identifiedPoint.point.y) - drawContext.canvas.nativeCanvas.apply { - val background = getTextBackgroundRect( - centerPointOfBar, - selectedOffset.y, - label, - highlightTextPaint - ) - drawRoundRect( - color = highlightTextBackgroundColor, - topLeft = Offset( - background.left.toFloat(), - background.top.toFloat() - highlightTextOffset.toPx() - ), - size = Size(background.width().toFloat(), background.height().toFloat()), - alpha = highlightTextBackgroundAlpha, - cornerRadius = highlightPopUpCornerRadius, - colorFilter = backgroundColorFilter, - blendMode = backgroundBlendMode, - style = backgroundStyle - ) - drawText( - label, - centerPointOfBar, - selectedOffset.y - highlightTextOffset.toPx(), - highlightTextPaint - ) - } - } -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/combinedchart/CombinedChart.kt b/YChartsLib/src/main/java/co/yml/charts/ui/combinedchart/CombinedChart.kt deleted file mode 100644 index 73781aed..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/combinedchart/CombinedChart.kt +++ /dev/null @@ -1,439 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class) - -package co.yml.charts.ui.combinedchart - -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import co.yml.charts.axis.XAxis -import co.yml.charts.axis.YAxis -import co.yml.charts.chartcontainer.container.ScrollableCanvasContainer -import co.yml.charts.ui.barchart.drawUnderScrollMask -import co.yml.charts.ui.barchart.getGroupBarDrawOffset -import co.yml.charts.ui.barchart.getMaxScrollDistance -import co.yml.charts.ui.barchart.highlightGroupBar -import co.yml.charts.ui.barchart.models.BarData -import co.yml.charts.ui.barchart.models.BarPlotData -import co.yml.charts.ui.barchart.models.GroupBar -import co.yml.charts.ui.combinedchart.model.CombinedChartData -import co.yml.charts.ui.linechart.drawHighLightOnSelectedPoint -import co.yml.charts.ui.linechart.drawHighlightText -import co.yml.charts.ui.linechart.drawShadowUnderLineAndIntersectionPoint -import co.yml.charts.ui.linechart.drawStraightOrCubicLine -import co.yml.charts.ui.linechart.getCubicPoints -import co.yml.charts.ui.linechart.getMappingPointsToGraph -import co.yml.charts.ui.linechart.model.LinePlotData -import co.yml.charts.common.components.ItemDivider -import co.yml.charts.common.components.accessibility.AccessibilityBottomSheetDialog -import co.yml.charts.common.components.accessibility.CombinedChartInfo -import co.yml.charts.common.extensions.RowClip -import co.yml.charts.common.extensions.collectIsTalkbackEnabledAsState -import co.yml.charts.common.extensions.getMaxElementInYAxis -import co.yml.charts.common.extensions.isNotNull -import co.yml.charts.common.extensions.isPointTapped -import co.yml.charts.common.extensions.isTapped -import co.yml.charts.common.model.PlotData -import co.yml.charts.common.model.PlotType -import co.yml.charts.common.model.Point -import kotlinx.coroutines.launch - -/** - * - * CombinedChart compose method for drawing combined line and bar charts. - * @param modifier: All modifier related properties - * @param combinedChartData : All data needed to draw combined chart. [CombinedChartData] Data - * class to save all params related to combined line and bar chart. - */ -@OptIn(ExperimentalMaterialApi::class) -@Composable -fun CombinedChart(modifier: Modifier, combinedChartData: CombinedChartData) { - val accessibilitySheetState = - rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden) - val scope = rememberCoroutineScope() - val isTalkBackEnabled by LocalContext.current.collectIsTalkbackEnabledAsState() - if (accessibilitySheetState.isVisible && isTalkBackEnabled - && combinedChartData.accessibilityConfig.shouldHandleBackWhenTalkBackPopUpShown - ) { - BackHandler { - scope.launch { - accessibilitySheetState.hide() - } - } - } - Surface(modifier) { - with(combinedChartData) { - var xOffset by remember { mutableStateOf(0f) } - var isTapped by remember { mutableStateOf(false) } - var columnWidth by remember { mutableStateOf(0f) } - var rowHeight by remember { mutableStateOf(0f) } - val paddingRight = paddingEnd - val linePlotData: LinePlotData = - getDataFromType(combinedPlotDataList, PlotType.Line) as? LinePlotData - ?: LinePlotData.default() - val barPlotData: BarPlotData = - getDataFromType(combinedPlotDataList, PlotType.Bar) as? BarPlotData - ?: BarPlotData.default() - val linePoints: List = - linePlotData.lines.flatMap { line -> line.dataPoints.map { it } } - val barPoints = barPlotData.groupBarList.flatMap { bar -> bar.barList.map { it } } - val bgColor = MaterialTheme.colors.surface - val xMin = - minOf(if(linePoints.isEmpty()) 0.0f else linePoints.minOf { it.x }, (barPlotData.groupBarList.size).toFloat()) - val xMax = - maxOf(if(linePoints.isEmpty()) 0.0f else linePoints.maxOf { it.x }, (barPlotData.groupBarList.size).toFloat()) - val yMin = minOf(if(linePoints.isEmpty()) 0.0f else linePoints.minOf { it.y }, if(barPoints.isEmpty())0.0f else barPoints.minOf { it.point.y }) - val yMax = maxOf(if(linePoints.isEmpty()) 0.0f else linePoints.maxOf { it.y }, if(barPoints.isEmpty())0.0f else barPoints.maxOf { it.point.y }) - val requiredSteps = - maxOf( - if(linePlotData.lines.isEmpty()) 0 else linePlotData.lines.map { it.dataPoints.size - 1 }.maxOf { it }, - if(barPlotData.groupBarList.isEmpty()) 0 else barPlotData.groupBarList.size - ) - val xAxisData = xAxisData.copy( - axisStepSize = if(barPlotData.groupBarList.isEmpty()) 30.dp else((barPlotData.barStyle.barWidth * barPlotData.groupingSize) + - barPlotData.barStyle.paddingBetweenBars), - steps = requiredSteps, - startDrawPadding = LocalDensity.current.run { columnWidth.toDp() }, - shouldDrawAxisLineTillEnd = true - ) - val yAxisData = - yAxisData.copy( - axisBottomPadding = LocalDensity.current.run { rowHeight.toDp() }, - axisTopPadding = paddingTop - ) - val maxElementInYAxis = getMaxElementInYAxis(yMax, yAxisData.steps) - var identifiedBarPoint by remember { mutableStateOf(BarData(Point(0f, 0f))) } - var identifiedPoint by remember { mutableStateOf(Point(0f, 0f)) } - var tapOffset by remember { mutableStateOf(Offset(0f, 0f)) } - - ScrollableCanvasContainer( - modifier = modifier - .semantics { - contentDescription = accessibilityConfig.chartDescription - } - .clickable { - if (isTalkBackEnabled) { - scope.launch { - accessibilitySheetState.animateTo( - ModalBottomSheetValue.Expanded - ) - } - } - }, - containerBackgroundColor = backgroundColor, - isPinchZoomEnabled = isZoomAllowed, - calculateMaxDistance = { xZoom -> - xOffset = - ((barPlotData.barStyle.barWidth.toPx() * barPlotData.groupingSize) + - barPlotData.barStyle.paddingBetweenBars.toPx()) * xZoom - getMaxScrollDistance( - columnWidth, - xMax, - xMin, - xOffset, - 0f, - paddingRight.toPx(), - size.width - ) - }, - drawXAndYAxis = { scrollOffset, xZoom -> - val axisPoints = mutableListOf() - for (index in 0 until xMax.toInt()) { - axisPoints.add(Point(index.toFloat(), 0f)) - } - XAxis( - xAxisData = xAxisData, - modifier = Modifier - .align(Alignment.BottomStart) - .fillMaxWidth() - .wrapContentHeight() - .clip( - RowClip( - columnWidth, - paddingRight - ) - ) - .onGloballyPositioned { - rowHeight = it.size.height.toFloat() - }, - xStart = columnWidth + LocalDensity.current.run { - (barPlotData.barStyle.barWidth.toPx() * barPlotData.groupingSize) / 2 + horizontalExtraSpace.toPx() - }, - scrollOffset = scrollOffset, - zoomScale = xZoom, - chartData = if(barPoints.isEmpty()) linePoints else axisPoints - ) - YAxis( - modifier = Modifier - .align(Alignment.TopStart) - .fillMaxHeight() - .wrapContentWidth() - .onGloballyPositioned { - columnWidth = it.size.width.toFloat() - }, - yAxisData = yAxisData - ) - }, - onDraw = { scrollOffset, xZoom -> - val yBottom = size.height - rowHeight - val yOffset = - ((yBottom - yAxisData.axisTopPadding.toPx()) / maxElementInYAxis) - xOffset = if(barPoints.isEmpty())xAxisData.axisStepSize.toPx() * xZoom else - ((barPlotData.barStyle.barWidth.toPx() * barPlotData.groupingSize) + - barPlotData.barStyle.paddingBetweenBars.toPx()) * xZoom - val xLeft = - columnWidth + horizontalExtraSpace.toPx() - val barTapLocks = mutableMapOf>() - val linePointLocks = mutableMapOf>() - - for (plotData in combinedPlotDataList) { - when (plotData) { - is LinePlotData -> { - // Draw line chart - val xStartPosition = - columnWidth + horizontalExtraSpace.toPx() + - ((barPlotData.barStyle.barWidth.toPx() * barPlotData.groupingSize) / 2) - plotData.lines.forEach { line -> - val pointsData = getMappingPointsToGraph( - line.dataPoints, - xMin, - xOffset, - xStartPosition, - scrollOffset, - yBottom, - yMin, - yOffset - ) - val (cubicPoints1, cubicPoints2) = getCubicPoints(pointsData) - - // Draw cubic line using the points and form a line graph - val cubicPath = - drawStraightOrCubicLine( - pointsData, - cubicPoints1, - cubicPoints2, - line.lineStyle - ) - - // Draw area under curve - drawShadowUnderLineAndIntersectionPoint( - cubicPath, - pointsData, - yBottom, - line - ) - - pointsData.forEachIndexed { index, point -> - if (isTapped && point.isPointTapped( - tapOffset, - tapPadding.toPx() - ) - ) { - // Dealing with only one line graph hence tapPointLocks[0] - linePointLocks[0] = line.dataPoints[index] to point - } - } - if (isTapped && linePointLocks.isNotEmpty()) { - drawHighLightOnSelectedPoint( - linePointLocks, - columnWidth, - paddingRight, - yBottom, - line.selectionHighlightPoint?.copy( - isHighlightLineRequired = false - ) - ) - if (line.selectionHighlightPopUp != null) { - val x = - linePointLocks.values.firstOrNull()?.second?.x - if (x != null) identifiedPoint = - linePointLocks.values.map { it.first }.first() - val selectedOffset = - linePointLocks.values.firstOrNull()?.second - if (selectedOffset.isNotNull()) { - drawHighlightText( - identifiedPoint, - selectedOffset ?: Offset(0f, 0f), - line.selectionHighlightPopUp - ) - } - } - } - } - } - is BarPlotData -> { - // Draw bar graph - plotData.groupBarList.forEachIndexed { index, groupBarData -> - var insideOffset = 0f - groupBarData.barList.forEachIndexed { subIndex, individualBar -> - val drawOffset = getGroupBarDrawOffset( - index, individualBar.point.y, xOffset, xLeft, - scrollOffset, yBottom, yOffset, 0f - ) - val height = yBottom - drawOffset.y - - val individualOffset = - Offset(drawOffset.x + insideOffset, drawOffset.y) - - // drawing each individual bars - drawGroupBarGraph( - plotData, - individualOffset, - height, - subIndex - ) - insideOffset += plotData.barStyle.barWidth.toPx() - - val middleOffset = - Offset( - individualOffset.x + plotData.barStyle.barWidth.toPx() / 2, - drawOffset.y - ) - // store the tap points for selection - if (isTapped && middleOffset.isTapped( - tapOffset, - plotData.barStyle.barWidth.toPx(), - yBottom, - tapPadding.toPx() - ) - ) { - barTapLocks[0] = individualBar to individualOffset - } - } - } - } - } - } - if (isTapped && linePointLocks.isEmpty() && - barPlotData.barStyle.selectionHighlightData != null - ) { - // highlighting the selected bar and showing the data points - identifiedBarPoint = highlightGroupBar( - barTapLocks, - true, - identifiedBarPoint, - barPlotData.barStyle.selectionHighlightData, - isTapped, - columnWidth, - yBottom, - paddingRight, - yOffset, - barPlotData.barStyle.barWidth - ) - } - drawUnderScrollMask(columnWidth, paddingRight, bgColor) - }, - onPointClicked = { offset: Offset, _: Float -> - isTapped = true - tapOffset = offset - }, - onScroll = { - isTapped = false - } - ) - if (isTalkBackEnabled) { - AccessibilityBottomSheetDialog( - modifier = Modifier.fillMaxSize(), backgroundColor = Color.White, content = { - LazyColumn { - items(xMax.toInt()) { index -> - Column { - val lineData: MutableList = mutableListOf() - val lineColors: MutableList = mutableListOf() - linePlotData.lines.forEachIndexed { _, line -> - lineColors.add(line.lineStyle.color) - line.dataPoints - line.dataPoints.forEachIndexed { pointIndex, point -> - if (pointIndex == index) { - lineData.add(point) - } - } - } - val groupBarData: GroupBar? = - barPlotData.groupBarList.filterIndexed { barIndex, _ -> barIndex == index } - .firstOrNull() - CombinedChartInfo( - pointsList = lineData, - lineColor = lineColors, - groupBar = groupBarData, - axisLabelDescription = xAxisData.axisLabelDescription( - xAxisData.labelData(index) - ), - barColorPaletteList = barPlotData.barColorPaletteList, - dividerColor = accessibilityConfig.dividerColor - ) - ItemDivider( - thickness = accessibilityConfig.dividerThickness, - dividerColor = accessibilityConfig.dividerColor - ) - } - } - } - }, - popUpTopRightButtonTitle = accessibilityConfig.popUpTopRightButtonTitle, - popUpTopRightButtonDescription = accessibilityConfig.popUpTopRightButtonDescription, - sheetState = accessibilitySheetState - ) - } - } - } -} - -/** - * Returns data for given plot type from the combinedPlotDataList - * @param combinedPlotDataList : List of combined plot data - * @param type: Type of plot of with data is to be returned. - */ -private fun getDataFromType(combinedPlotDataList: List, type: PlotType): PlotData? { - return when (type) { - is PlotType.Line -> combinedPlotDataList.filterIsInstance().firstOrNull() - is PlotType.Bar -> combinedPlotDataList.filterIsInstance().firstOrNull() - else -> null // Handle if required in future. - } -} - -/** - * - * Used to draw the individual bars - * @param barPlotData : all meta data related to the bar graph - * @param drawOffset: topLeft offset for the drawing the bar - * @param height : height of the bar graph - * @param subIndex : Index of the bar - */ -private fun DrawScope.drawGroupBarGraph( - barPlotData: BarPlotData, drawOffset: Offset, - height: Float, - subIndex: Int -) { - val color = if (subIndex < barPlotData.barColorPaletteList.size) { - barPlotData.barColorPaletteList[subIndex] - } else Color.Transparent - with(barPlotData.barStyle) { - drawRoundRect( - color = color, - topLeft = drawOffset, - size = Size(barWidth.toPx(), height), - cornerRadius = CornerRadius( - cornerRadius.toPx(), - cornerRadius.toPx() - ), - style = barDrawStyle, - blendMode = barBlendMode - ) - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/combinedchart/model/CombinedChartData.kt b/YChartsLib/src/main/java/co/yml/charts/ui/combinedchart/model/CombinedChartData.kt deleted file mode 100644 index 39584c15..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/combinedchart/model/CombinedChartData.kt +++ /dev/null @@ -1,39 +0,0 @@ -package co.yml.charts.ui.combinedchart.model - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import co.yml.charts.axis.AxisData -import co.yml.charts.common.model.AccessibilityConfig -import co.yml.charts.common.model.PlotData - -/** - * - * CombinedLineAndBarGraphData data class that contains all params user need to define to draw a bar and line graph. - * @param combinedPlotDataList: Defines list of plot data's to be drawn and order of graph drawing is maintained as - * per the list order. Distinct plot data's are only allowed. - * @param xAxisData: All the configurations related to X-Axis to be defined here in [AxisData] - * @param yAxisData: All the configurations related to Y-Axis to be defined here in [AxisData] - * @param paddingTop: Padding from the top of the canvas to start of the graph container. - * @param bottomPadding: Padding from the bottom of the canvas to bottom of the graph container. - * @param containerPaddingEnd: Container inside padding end after the last point of the graph. - * @param backgroundColor: Background color of the Y & X components., - * @param isZoomAllowed: True if zoom in for all vertical graph components is allowed else false. - * @param tapPadding: Tap padding offset for selected point. - * @param horizontalExtraSpace: Extra padding at the end of the canvas container. - * @param accessibilityConfig: Configs related to accessibility service defined here in [AccessibilityConfig] - */ -data class CombinedChartData( - val combinedPlotDataList: List, - val xAxisData: AxisData = AxisData.Builder().build(), - val yAxisData: AxisData = AxisData.Builder().build(), - val paddingTop: Dp = 30.dp, - val bottomPadding: Dp = 10.dp, - val paddingEnd: Dp = 10.dp, - val horizontalExtraSpace: Dp = 10.dp, - val containerPaddingEnd: Dp = 15.dp, - val backgroundColor: Color = Color.White, - val tapPadding: Dp = 10.dp, - val isZoomAllowed: Boolean = true, - val accessibilityConfig: AccessibilityConfig = AccessibilityConfig() -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/LineChart.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/LineChart.kt deleted file mode 100644 index 421d1e94..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/LineChart.kt +++ /dev/null @@ -1,506 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class) - -package co.yml.charts.ui.linechart - -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.graphics.PathEffect -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.DrawStyle -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.Dp -import co.yml.charts.axis.XAxis -import co.yml.charts.axis.YAxis -import co.yml.charts.axis.getXAxisScale -import co.yml.charts.chartcontainer.container.ScrollableCanvasContainer -import co.yml.charts.ui.linechart.model.IntersectionPoint -import co.yml.charts.ui.linechart.model.Line -import co.yml.charts.ui.linechart.model.LineChartData -import co.yml.charts.ui.linechart.model.LineStyle -import co.yml.charts.ui.linechart.model.LineType -import co.yml.charts.ui.linechart.model.SelectionHighlightPoint -import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp -import co.yml.charts.common.components.ItemDivider -import co.yml.charts.common.components.accessibility.AccessibilityBottomSheetDialog -import co.yml.charts.common.components.accessibility.LinePointInfo -import co.yml.charts.common.extensions.RowClip -import co.yml.charts.common.extensions.collectIsTalkbackEnabledAsState -import co.yml.charts.common.extensions.drawGridLines -import co.yml.charts.common.extensions.isNotNull -import co.yml.charts.common.model.Point -import kotlinx.coroutines.launch - -/** - * - * [LineChart] compose method used for drawing a Line Chart. - * @param modifier :All modifier related property. - * Data class [LineChartData] to save all params needed to draw the line chart. - * @param lineChartData : Add data related to line chart. - */ -@Composable -fun LineChart(modifier: Modifier, lineChartData: LineChartData) { - val accessibilitySheetState = - rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden) - val scope = rememberCoroutineScope() - val isTalkBackEnabled by LocalContext.current.collectIsTalkbackEnabledAsState() - if (accessibilitySheetState.isVisible && isTalkBackEnabled - && lineChartData.accessibilityConfig.shouldHandleBackWhenTalkBackPopUpShown - ) { - BackHandler { - scope.launch { - accessibilitySheetState.hide() - } - } - } - Surface(modifier = modifier) { - with(lineChartData) { - var columnWidth by remember { mutableStateOf(0f) } - var rowHeight by remember { mutableStateOf(0f) } - var xOffset by remember { mutableStateOf(0f) } - val bgColor = MaterialTheme.colors.surface - var isTapped by remember { mutableStateOf(false) } - var tapOffset by remember { mutableStateOf(Offset(0f, 0f)) } - var selectionTextVisibility by remember { mutableStateOf(false) } - var identifiedPoint by remember { mutableStateOf(Point(0f, 0f)) } - val line = linePlotData.lines.first() - // Update must required values - val xAxisData = xAxisData.copy(axisBottomPadding = bottomPadding) - val yAxisData = yAxisData.copy( - axisBottomPadding = LocalDensity.current.run { rowHeight.toDp() }, - axisTopPadding = paddingTop - ) - - val (xMin, xMax, xAxisScale) = getXAxisScale(line.dataPoints, xAxisData.steps) - val (yMin, _, yAxisScale) = getYAxisScale(line.dataPoints, yAxisData.steps) - val maxElementInYAxis = getMaxElementInYAxis(yAxisScale, yAxisData.steps) - - ScrollableCanvasContainer(modifier = modifier - .semantics { - contentDescription = lineChartData.accessibilityConfig.chartDescription - } - .clickable { - if (isTalkBackEnabled) { - scope.launch { - accessibilitySheetState.animateTo( - ModalBottomSheetValue.Expanded - ) - } - } - }, - calculateMaxDistance = { xZoom -> - xOffset = xAxisData.axisStepSize.toPx() * xZoom - getMaxScrollDistance( - columnWidth, - xMax, - xMin, - xOffset, - paddingRight.toPx(), - size.width, - containerPaddingEnd.toPx() - ) - }, - containerBackgroundColor = backgroundColor, - isPinchZoomEnabled = isZoomAllowed, - drawXAndYAxis = { scrollOffset, xZoom -> - YAxis( - modifier = Modifier - .fillMaxHeight() - .onGloballyPositioned { - columnWidth = it.size.width.toFloat() - }, yAxisData = yAxisData - ) - XAxis(xAxisData = xAxisData, - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() - .align(Alignment.BottomStart) - .onGloballyPositioned { - rowHeight = it.size.height.toFloat() - } - .clip( - RowClip( - columnWidth, paddingRight - ) - ), - xStart = columnWidth, - scrollOffset = scrollOffset, - zoomScale = xZoom, - chartData = line.dataPoints) - }, - onDraw = { scrollOffset, xZoom -> - val yBottom = size.height - rowHeight - val yOffset = ((yBottom - paddingTop.toPx()) / maxElementInYAxis) - xOffset = xAxisData.axisStepSize.toPx() * xZoom - val xLeft = columnWidth // To add extra space if needed - val pointsData = getMappingPointsToGraph( - line.dataPoints, xMin, xOffset, xLeft, scrollOffset, yBottom, yMin, yOffset - ) - val (cubicPoints1, cubicPoints2) = getCubicPoints(pointsData) - val tapPointLocks = mutableMapOf>() - - // Draw guide lines - gridLines?.let { - drawGridLines( - yBottom, - yAxisData.axisTopPadding.toPx(), - xLeft, - paddingRight, - scrollOffset, - pointsData.size, - xZoom, - xAxisScale, - yAxisData.steps, - xAxisData.axisStepSize, - it - ) - } - - // Draw cubic line using the points and form a line graph - val cubicPath = drawStraightOrCubicLine( - pointsData, cubicPoints1, cubicPoints2, line.lineStyle - ) - - // Draw Lines and Points and AreaUnderLine - // Draw area under curve - drawShadowUnderLineAndIntersectionPoint( - cubicPath, pointsData, yBottom, line - ) - - // Draw column to make graph look scrollable under Yaxis - drawUnderScrollMask(columnWidth, paddingRight, bgColor) - - pointsData.forEachIndexed { index, point -> - if (isTapped && point.isTapped(tapOffset.x, xOffset)) { - // Dealing with only one line graph hence tapPointLocks[0] - tapPointLocks[0] = line.dataPoints[index] to point - } - } - - val selectedOffset = tapPointLocks.values.firstOrNull()?.second - if (selectionTextVisibility && selectedOffset.isNotNull()) { - drawHighlightText( - identifiedPoint, - selectedOffset ?: Offset(0f, 0f), - line.selectionHighlightPopUp - ) - } - if (isTapped) { - val x = tapPointLocks.values.firstOrNull()?.second?.x - if (x != null) identifiedPoint = - tapPointLocks.values.map { it.first }.first() - drawHighLightOnSelectedPoint( - tapPointLocks, - columnWidth, - paddingRight, - yBottom, - line.selectionHighlightPoint - ) - } - }, - onPointClicked = { offset: Offset, _: Float -> - isTapped = true - selectionTextVisibility = true - tapOffset = offset - }, - onScroll = { - isTapped = false - selectionTextVisibility = false - }, - onZoomInAndOut = { - isTapped = false - selectionTextVisibility = false - }) - } - if (isTalkBackEnabled) { - with(lineChartData) { - AccessibilityBottomSheetDialog( - modifier = Modifier.fillMaxSize(), - backgroundColor = Color.White, - content = { - val linePoints = linePlotData.lines.firstOrNull()?.dataPoints - LazyColumn { - items(linePoints?.size ?: 0) { index -> - Column { - LinePointInfo( - xAxisData.axisLabelDescription( - xAxisData.labelData( - index - ) - ), - linePoints?.get(index)?.description ?: "", - linePlotData.lines.firstOrNull()?.lineStyle?.color - ?: Color.Transparent - ) - ItemDivider( - thickness = accessibilityConfig.dividerThickness, - dividerColor = accessibilityConfig.dividerColor - ) - } - } - } - }, - popUpTopRightButtonTitle = accessibilityConfig.popUpTopRightButtonTitle, - popUpTopRightButtonDescription = accessibilityConfig.popUpTopRightButtonDescription, - sheetState = accessibilitySheetState - ) - } - } - } -} - -/** - * - * returns the list of transformed points supported to be drawn on the container using the input points . - * @param lineChartPoints :Input data points - * @param xMin: Min X-Axis value. - * @param xOffset : Total distance between two X-Axis points. - * @param xLeft: Total left padding in X-Axis. - * @param scrollOffset : Total scrolled offset. - * @param yBottom : Bottom start offset for X-Axis. - * @param yMin : Min Y-Axis value. - * @param yOffset : Distance between two Y-Axis points. - */ -fun getMappingPointsToGraph( - lineChartPoints: List, - xMin: Float, - xOffset: Float, - xLeft: Float, - scrollOffset: Float, - yBottom: Float, - yMin: Float, - yOffset: Float, -): MutableList { - val pointsData = mutableListOf() - lineChartPoints.forEachIndexed { _, point -> - val (x, y) = point - val x1 = ((x - xMin) * xOffset) + xLeft - scrollOffset - val y1 = yBottom - ((y - yMin) * yOffset) - pointsData.add(Offset(x1, y1)) - } - return pointsData -} - -/** - * - * returns the max scrollable distance based on the points to be drawn along with padding etc. - * @param columnWidth : Width of the Y-Axis. - * @param xMax : Max X-Axis value. - * @param xMin: Min X-Axis value. - * @param xOffset: Total distance between two X-Axis points. - * @param paddingRight : Padding at the end of the canvas. - * @param canvasWidth : Total available canvas width. - * @param containerPaddingEnd : Container inside padding end after the last point of the graph. - */ -fun getMaxScrollDistance( - columnWidth: Float, - xMax: Float, - xMin: Float, - xOffset: Float, - paddingRight: Float, - canvasWidth: Float, - containerPaddingEnd: Float = 0f -): Float { - val xLastPoint = (xMax - xMin) * xOffset + columnWidth + paddingRight + containerPaddingEnd - return if (xLastPoint > canvasWidth) { - xLastPoint - canvasWidth - } else 0f -} - -/** - * - * DrawScope.drawStraightOrCubicLine extension method used for drawing a straight/cubic line for a given Point(x,y). - * @param pointsData : List of points to be drawn on the canvas - * @param cubicPoints1 : List of average left side values for a given Point(x,y). - * @param cubicPoints2 : List of average right side values for a given Point(x,y). - * @param lineStyle : All styles related to the path are included in [LineStyle]. - */ -fun DrawScope.drawStraightOrCubicLine( - pointsData: MutableList, - cubicPoints1: MutableList, - cubicPoints2: MutableList, - lineStyle: LineStyle -): Path { - val path = Path() - path.moveTo(pointsData.first().x, pointsData.first().y) - for (i in 1 until pointsData.size) { - when (lineStyle.lineType) { - is LineType.Straight -> { - path.lineTo(pointsData[i].x, pointsData[i].y) - } - is LineType.SmoothCurve -> { - path.cubicTo( - cubicPoints1[i - 1].x, - cubicPoints1[i - 1].y, - cubicPoints2[i - 1].x, - cubicPoints2[i - 1].y, - pointsData[i].x, - pointsData[i].y - ) - } - } - } - with(lineStyle) { - drawPath( - path, - color = color, - style = getDrawStyleForPath(lineStyle.lineType, lineStyle), - alpha = alpha, - colorFilter = colorFilter, - blendMode = blendMode - ) - } - return path -} - -/** - * - * Returns the Drawstyle for the path. - * @param lineType : Type of the line [LineType] - * @param lineStyle : The style for the path [lineStyle] - */ -private fun getDrawStyleForPath( - lineType: LineType, lineStyle: LineStyle -): DrawStyle = if (lineType.isDotted) Stroke( - width = lineStyle.width, pathEffect = PathEffect.dashPathEffect(lineType.intervals) -) else lineStyle.style - - -/** - * - * DrawScope.drawPointOnLine extension method used for drawing a circle/mark on a line for a given Point(x,y). - * @param offset : Point at which circle/mark has to be drawn. - */ -private fun DrawScope.drawPointOnLine(offset: Offset, intersectionPoint: IntersectionPoint?) { - intersectionPoint?.draw?.let { it(this, offset) } -} - -/** - * - * DrawScope.drawUnderScrollMask extension method used for drawing a rectangular mask to make graph scrollable under the YAxis. - * @param columnWidth : Width of the rectangular mask here width of Y Axis is used. - * @param paddingRight : Padding given at the end of the graph. - * @param bgColor : Background of the rectangular mask. - */ -private fun DrawScope.drawUnderScrollMask(columnWidth: Float, paddingRight: Dp, bgColor: Color) { - drawRect( - bgColor, Offset(0f, 0f), Size(columnWidth, size.height) - ) - drawRect( - bgColor, - Offset(size.width - paddingRight.toPx(), 0f), - Size(paddingRight.toPx(), size.height) - ) -} - -/** - * - * DrawScope.drawShadowUnderLineAndIntersectionPoint extension method used for drawing a - * shadow below the line graph points and also drawing intersection points on the line graph. - * @param cubicPath : Path used to draw the shadow - * @param pointsData : List of the points on the Line graph. - * @param yBottom : Offset of X-Axis starting position i.e shade to be drawn until. - * @param line : line on which shadow & intersectionPoints has to be drawn. - */ -fun DrawScope.drawShadowUnderLineAndIntersectionPoint( - cubicPath: Path, pointsData: MutableList, yBottom: Float, line: Line -) { - if (line.shadowUnderLine.isNotNull()) { - cubicPath.lineTo(pointsData.last().x, yBottom) - cubicPath.lineTo(pointsData.first().x, yBottom) - line.shadowUnderLine?.draw?.let { it(this, cubicPath) } - } - if (line.intersectionPoint.isNotNull()) { - pointsData.forEach { offset -> - drawPointOnLine(offset, line.intersectionPoint) - } - } -} - - -/** - * - * getCubicPoints method provides left and right average value for a given point to get a smooth curve. - * @param pointsData : List of the points on the Line graph. - */ -fun getCubicPoints(pointsData: List): Pair, MutableList> { - val cubicPoints1 = mutableListOf() - val cubicPoints2 = mutableListOf() - - for (i in 1 until pointsData.size) { - cubicPoints1.add( - Offset( - (pointsData[i].x + pointsData[i - 1].x) / 2, pointsData[i - 1].y - ) - ) - cubicPoints2.add( - Offset( - (pointsData[i].x + pointsData[i - 1].x) / 2, pointsData[i].y - ) - ) - } - return Pair(cubicPoints1, cubicPoints2) -} - -/** - * Used to draw the highlighted text - * @param identifiedPoint : Selected points - * @param selectedOffset: Offset selected - * @param selectionHighlightPopUp : Data class with all styling related info [SelectionHighlightPopUp] - */ -fun DrawScope.drawHighlightText( - identifiedPoint: Point, - selectedOffset: Offset, - selectionHighlightPopUp: SelectionHighlightPopUp? -) { - selectionHighlightPopUp?.run { - draw(selectedOffset, identifiedPoint) - } -} - -/** - * - * DrawScope.drawHighLightOnSelectedPoint extension method used for drawing and highlight the selected - * point when dragging. - * @param dragLocks : List of points to be drawn on the canvas and that can be selected. - * @param columnWidth : Width of the Axis in the left or right. - * @param paddingRight : Padding given to the right side of the canvas. - * @param yBottom : Start position from below of the canvas. - * @param selectionHighlightPoint : Data class to define all the styles to be drawn in [SelectionHighlightPoint] - */ -fun DrawScope.drawHighLightOnSelectedPoint( - dragLocks: MutableMap>, - columnWidth: Float, - paddingRight: Dp, - yBottom: Float, - selectionHighlightPoint: SelectionHighlightPoint? -) { - if (selectionHighlightPoint.isNotNull()) { - dragLocks.values.firstOrNull()?.let { (_, location) -> - val (x, y) = location - if (x >= columnWidth && x <= size.width - paddingRight.toPx()) { - selectionHighlightPoint?.draw?.let { it(this, Offset(x, y)) } - if (selectionHighlightPoint?.isHighlightLineRequired == true) { - selectionHighlightPoint.drawHighlightLine( - this, Offset(x, yBottom), Offset(x, y) - ) - } - } - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/LineChartExtensions.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/LineChartExtensions.kt deleted file mode 100644 index 8bf3b81a..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/LineChartExtensions.kt +++ /dev/null @@ -1,38 +0,0 @@ -package co.yml.charts.ui.linechart - -import androidx.compose.ui.geometry.Offset -import co.yml.charts.common.model.Point - -/** - * returns total offset for given no of steps and offset . - * @param offset: Distance of each step. - * @param steps: No of steps in Y-Axis. - */ -fun getMaxElementInYAxis(offset: Float, steps: Int): Float { - return (if (steps > 1) steps - 1 else 1) * offset -} - -/** - * returns Triple value with minY,maxY and scale of each Y-Axis step. - * @param points: List of points to be drawn. - * @param steps: Count of steps in the Y-Axis. - */ -fun getYAxisScale( - points: List, - steps: Int -): Triple { - val yMin = points.takeIf { it.isNotEmpty() }?.minOf { it.y } ?: 0f - val yMax = points.takeIf { it.isNotEmpty() }?.maxOf { it.y } ?: 0f - val totalSteps = (yMax - yMin) - val scale = - totalSteps / if (steps > 1) (steps - 1) else 1 // First step starts from 0 by default - return Triple(yMin, yMax, scale) -} - -/** - * Returns true if the given tap offset is selected point or not else false - * @param tapOffset: Offset of the tapped point. - * @param xOffset: Distance between two points in X-Axis. - */ -fun Offset.isTapped(tapOffset: Float, xOffset: Float) = - ((tapOffset) > x - xOffset / 2) && ((tapOffset) < x + xOffset / 2) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/GridLines.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/GridLines.kt deleted file mode 100644 index 5fa759e6..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/GridLines.kt +++ /dev/null @@ -1,57 +0,0 @@ -package co.yml.charts.ui.linechart.model - -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.PathEffect -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -/** - * Represents the grid lines for any graph - * @param color: Defines the color of the grid lines. - * @param lineWidth: Defines the width of the lines. - * @param pathEffect optional effect or pattern to apply to the line. - * @param alpha Opacity to be applied to the path from 0.0f to 1.0f representing. - * fully transparent to fully opaque respectively. - * @param colorFilter ColorFilter to apply to the [color] when drawn into the destination. - * @param blendMode Blending algorithm to be applied to the path when it is drawn. - * @param enableHorizontalLines False to disable horizontal line else true. - * @param enableVerticalLines False to disable vertical lines else true. - * @param drawHorizontalLines Draw param used to draw the horizontal lines with given input params. - * @param drawVerticalLines Draw param used to draw the vertical lines with given input params. - */ -data class GridLines( - val color: Color = Color.LightGray, - val lineWidth: Dp = 1.dp, - val pathEffect: PathEffect? = null, - val alpha: Float = 1.0f, - val colorFilter: ColorFilter? = null, - val blendMode: BlendMode = DrawScope.DefaultBlendMode, - val enableHorizontalLines: Boolean = true, - val enableVerticalLines: Boolean = true, - val drawHorizontalLines: DrawScope.(Float, Float, Float) -> Unit = { xStart, y, xEnd -> - drawLine( - color = color, - start = Offset(xStart, y), - end = Offset(xEnd, y), - strokeWidth = lineWidth.toPx(), - pathEffect = pathEffect, - colorFilter = colorFilter, - blendMode = blendMode - ) - }, - val drawVerticalLines: DrawScope.(Float, Float, Float) -> Unit = { x, yStart, yEnd -> - drawLine( - color = color, - start = Offset(x, yStart), - end = Offset(x, yEnd), - strokeWidth = lineWidth.toPx(), - pathEffect = pathEffect, - colorFilter = colorFilter, - blendMode = blendMode - ) - } -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/IntersectionPoint.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/IntersectionPoint.kt deleted file mode 100644 index af7a1aa2..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/IntersectionPoint.kt +++ /dev/null @@ -1,44 +0,0 @@ -package co.yml.charts.ui.linechart.model - -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.DrawStyle -import androidx.compose.ui.graphics.drawscope.Fill -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -/** - * Represents a point on the line graph - * - * @param color The color or fill to be applied to the circle - * @param radius The radius of the circle - * @param alpha Opacity to be applied to the circle from 0.0f to 1.0f representing - * fully transparent to fully opaque respectively - * @param style Whether or not the circle is stroked or filled in - * @param colorFilter ColorFilter to apply to the [color] when drawn into the destination - * @param blendMode Blending algorithm to be applied to the brush - * @param draw override this to change the default drawCircle implementation. You are provided - * with the actual [Point] that represents the intersection. - */ -data class IntersectionPoint( - val color: Color = Color.Black, - val radius: Dp = 6.dp, - val alpha: Float = 1.0f, - val style: DrawStyle = Fill, - val colorFilter: ColorFilter? = null, - val blendMode: BlendMode = DrawScope.DefaultBlendMode, - val draw: DrawScope.(Offset) -> Unit = { center -> - drawCircle( - color, - radius.toPx(), - center, - alpha, - style, - colorFilter, - blendMode - ) - } -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LineChartData.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LineChartData.kt deleted file mode 100644 index 54087912..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LineChartData.kt +++ /dev/null @@ -1,59 +0,0 @@ -package co.yml.charts.ui.linechart.model - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import co.yml.charts.axis.AxisData -import co.yml.charts.common.model.AccessibilityConfig -import co.yml.charts.common.model.Point - - -/** - * - * LineGraphData data class that contains all params user need to define to draw a line graph. - * @param linePlotData: The path to be drawn on the graph represented by a line. - * @param xAxisData: All the configurations related to X-Axis to be defined here in [AxisData] - * @param yAxisData: All the configurations related to Y-Axis to be defined here in [AxisData] - * @param isZoomAllowed: True if zoom in for all vertical graph components is allowed else false. - * @param paddingTop: Padding from the top of the canvas to start of the graph container. - * @param paddingRight: Padding from the end of the canvas to end of the graph container. - * @param bottomPadding: Padding from the bottom of the canvas to bottom of the graph container. - * @param containerPaddingEnd: Container inside padding end after the last point of the graph. - * @param backgroundColor: Background color of the Y & X components, - * @param gridLines: This enables graph to draw horizontal and vertical grid lines - * @param accessibilityConfig: Configs related to accessibility service defined here in [AccessibilityConfig] - */ -data class LineChartData( - val linePlotData: LinePlotData, - val xAxisData: AxisData = AxisData.Builder().build(), - val yAxisData: AxisData = AxisData.Builder().build(), - val isZoomAllowed: Boolean = true, - val paddingTop: Dp = 30.dp, - val bottomPadding: Dp = 10.dp, - val paddingRight: Dp = 10.dp, - val containerPaddingEnd: Dp = 15.dp, - val backgroundColor: Color = Color.White, - val gridLines: GridLines? = null, - val accessibilityConfig: AccessibilityConfig = AccessibilityConfig() -) - -/** - * Represent a Line in the [co.yml.charts.ui.linechart] - * - * @param dataPoints list of points [Point] in the line - * @param lineStyle Adds styling options in [LineStyle] to the line path drawn. - * @param intersectionPoint drawing logic to draw the point itself in [IntersectionPoint]. - * If null, the point is not drawn. - * @param selectionHighlightPoint drawing logic to draw the highlight at the point when it is selected - * in [SelectionHighlightPoint] If null, the point won't be highlighted on selection - * @param shadowUnderLine drawing logic for the section under the line in [ShadowUnderLine]. - * @param selectionHighlightPopUp All prams related to selection popup to be added here in [SelectionHighlightPopUp] - */ -data class Line( - val dataPoints: List, - val lineStyle: LineStyle = LineStyle(), - val intersectionPoint: IntersectionPoint? = null, - val selectionHighlightPoint: SelectionHighlightPoint? = null, - val shadowUnderLine: ShadowUnderLine? = null, - val selectionHighlightPopUp: SelectionHighlightPopUp? = null -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LinePlotData.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LinePlotData.kt deleted file mode 100644 index f9e6accd..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LinePlotData.kt +++ /dev/null @@ -1,18 +0,0 @@ -package co.yml.charts.ui.linechart.model - -import co.yml.charts.common.model.PlotData -import co.yml.charts.common.model.PlotType -/** - * LinePlotData is a data class that holds line graph related data and styling components - * @param plotType : Defines the type of plot/graph - * @param lines : Data related to the list of lines to be drawn. - */ -data class LinePlotData( - override val plotType: PlotType = PlotType.Line, - val lines: List -) : PlotData { - companion object { - fun default() = - LinePlotData(lines = listOf()) - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LineStyle.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LineStyle.kt deleted file mode 100644 index 764e7514..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/LineStyle.kt +++ /dev/null @@ -1,52 +0,0 @@ -package co.yml.charts.ui.linechart.model - -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.drawscope.DrawScope.Companion.DefaultBlendMode -import androidx.compose.ui.graphics.drawscope.DrawStyle -import androidx.compose.ui.graphics.drawscope.Stroke - -/** - * Handles styling for the path drawn in the line graph - * - * @param color Defines the color of the path or line. - * @param width Defines the width of the path/line stroke. - * @param alpha Defines the alpha of the path/line. - * @param style Defines if the path/line is filled or stroke. - * @param colorFilter ColorFilter to apply to the [color] when drawn into the destination. - * @param blendMode All prams related to selection popup to be added here in [SelectionHighlightPopUp] - */ -data class LineStyle( - val lineType: LineType = LineType.SmoothCurve(isDotted = false), - val color: Color = Color.Black, - val width: Float = 8f, - val alpha: Float = 1.0f, - val style: DrawStyle = Stroke(width = width), - val colorFilter: ColorFilter? = null, - val blendMode: BlendMode = DefaultBlendMode -) - -/** - * Represent different types of line to be drawn - * @see LineType.Straight Draws straight lines with no curves, just a connection from - * one point to another. - * @see LineType.SmoothCurve Draws curved lines using cubic paths - * It has @param isDotted true if line has to be dotted else false. - * Also @param intervals Array of "on" and "off" distances for the dashed line segments - * phase - Pixel offset into the intervals array - */ -sealed class LineType { - abstract val isDotted: Boolean - abstract var intervals: FloatArray - - data class Straight( - override val isDotted: Boolean = false, - override var intervals: FloatArray = floatArrayOf(30f, 10f) - ) : LineType() - - data class SmoothCurve( - override val isDotted: Boolean = false, - override var intervals: FloatArray = floatArrayOf(30f, 10f) - ) : LineType() -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/SelectionHighlightPoint.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/SelectionHighlightPoint.kt deleted file mode 100644 index b939ea8f..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/SelectionHighlightPoint.kt +++ /dev/null @@ -1,61 +0,0 @@ -package co.yml.charts.ui.linechart.model - -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.PathEffect -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.DrawStyle -import androidx.compose.ui.graphics.drawscope.Fill -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - - -/** - * Represents the point when it is selected on the line graph - * - * @param color The color or fill to be applied to the circle - * @param radius The radius of the circle - * @param alpha Opacity to be applied to the circle from 0.0f to 1.0f representing - * fully transparent to fully opaque respectively - * @param style Whether or not the circle is stroked or filled in - * @param colorFilter ColorFilter to apply to the [color] when drawn into the destination - * @param blendMode Blending algorithm to be applied to the brush - * @param draw override this to change the default drawCircle implementation. You are provided - * with the actual point [Offset]. - */ -data class SelectionHighlightPoint( - val color: Color = Color.Red, - val radius: Dp = 6.dp, - val alpha: Float = 1.0f, - val style: DrawStyle = Fill, - val colorFilter: ColorFilter? = null, - val blendMode: BlendMode = DrawScope.DefaultBlendMode, - val draw: DrawScope.(Offset) -> Unit = { point -> - drawCircle( - color, - radius.toPx(), - point, - alpha, - style, - colorFilter, - blendMode - ) - }, - val isHighlightLineRequired: Boolean = true, - val drawHighlightLine: DrawScope.(Offset, Offset) -> Unit = { start, end -> - drawLine( - color, - start, - end, - (radius / 2).toPx(), - Stroke.DefaultCap, - PathEffect.dashPathEffect(floatArrayOf(40f, 20f)), - alpha, - colorFilter, - blendMode - ) - } -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/SelectionHighlightPopUp.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/SelectionHighlightPopUp.kt deleted file mode 100644 index afdd44e2..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/SelectionHighlightPopUp.kt +++ /dev/null @@ -1,92 +0,0 @@ -package co.yml.charts.ui.linechart.model - -import android.graphics.Paint -import android.graphics.Typeface -import android.text.TextPaint -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.* -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.DrawStyle -import androidx.compose.ui.graphics.drawscope.Fill -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import co.yml.charts.common.extensions.getTextBackgroundRect -import co.yml.charts.common.model.Point - -/** - * SelectionHighlightPopUp is a data class used to draw the pop on the given selected point on a line - * to identify the dimensions of the selected point.All the styling related params are included here - * @param backgroundColor : Defines the background color of the popup. - * @param backgroundAlpha : Defines the alpha of the background color. - * @param backgroundCornerRadius : Defines the corner radius of the background. - * @param backgroundColorFilter : ColorFilter to apply to the color when drawn into the destination. - * @param backgroundBlendMode :Blending algorithm to be applied to the path when it is drawn. - * @param backgroundStyle : Whether or not the path is stroked or filled in. - * @param labelSize : The size of the popUp label in [TextUnit]. - * @param labelColor : The color of the label text. - * @param labelAlignment : The alignment of the label text. - * @param labelTypeface : The style of the label text. - * @param paddingBetweenPopUpAndPoint : The padding between the anchor position/ popup - * start position and the selected point on the line. - * @param popUpLabel : The text that can be shown on the popup given 2 input params x and y values - * @param draw : Draw the popUp marker on the selected point with 2 inputs [Offset] i.e selectedPoint - * and [Point] i.e the input data w.r.t selected point - */ -data class SelectionHighlightPopUp( - val backgroundColor: Color = Color.White, - val backgroundAlpha: Float = 0.7f, - val backgroundCornerRadius: CornerRadius = CornerRadius(5f), - val backgroundColorFilter: ColorFilter? = null, - val backgroundBlendMode: BlendMode = DrawScope.DefaultBlendMode, - val backgroundStyle: DrawStyle = Fill, - val paddingBetweenPopUpAndPoint: Dp = 20.dp, - val labelSize: TextUnit = 14.sp, - val labelColor: Color = Color.Black, - val labelAlignment: Paint.Align = Paint.Align.CENTER, - val labelTypeface: Typeface = Typeface.DEFAULT, - val popUpLabel: (Float, Float) -> (String) = { x, y -> - val xLabel = "x : ${x.toInt()} " - val yLabel = "y : ${String.format("%.2f", y)}" - "$xLabel $yLabel" - }, - val draw: DrawScope.(Offset, Point) -> Unit = { selectedOffset, identifiedPoint -> - val highlightTextPaint = TextPaint().apply { - textSize = labelSize.toPx() - color = labelColor.toArgb() - textAlign = labelAlignment - typeface = labelTypeface - } - val label = popUpLabel(identifiedPoint.x, identifiedPoint.y) - drawContext.canvas.nativeCanvas.apply { - val background = getTextBackgroundRect( - selectedOffset.x, - selectedOffset.y, - label, - highlightTextPaint - ) - drawRoundRect( - color = backgroundColor, - topLeft = Offset( - background.left.toFloat(), - background.top.toFloat() - paddingBetweenPopUpAndPoint.toPx() - ), - size = Size(background.width().toFloat(), background.height().toFloat()), - alpha = backgroundAlpha, - cornerRadius = backgroundCornerRadius, - colorFilter = backgroundColorFilter, - blendMode = backgroundBlendMode, - style = backgroundStyle - ) - drawText( - label, - selectedOffset.x, - selectedOffset.y - paddingBetweenPopUpAndPoint.toPx(), - highlightTextPaint - ) - } - } -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/ShadowUnderLine.kt b/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/ShadowUnderLine.kt deleted file mode 100644 index f67c8619..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/linechart/model/ShadowUnderLine.kt +++ /dev/null @@ -1,34 +0,0 @@ -package co.yml.charts.ui.linechart.model - -import androidx.compose.ui.graphics.* -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.DrawStyle -import androidx.compose.ui.graphics.drawscope.Fill - -/** - * Controls the drawing behaviour of the area/section under the line. - * - * @param color Color to be applied to the path - * @param alpha Opacity to be applied to the path from 0.0f to 1.0f representing - * fully transparent to fully opaque respectively - * @param style Whether or not the path is stroked or filled in - * @param colorFilter ColorFilter to apply to the [color] when drawn into the destination - * @param blendMode Blending algorithm to be applied to the path when it is drawn - * @param draw override this to change the default drawPath implementation. You are provided with - * the [Path] of the line - */ -data class ShadowUnderLine( - val color: Color = Color.Black, - val brush: Brush? = null, - val alpha: Float = 0.1f, - val style: DrawStyle = Fill, - val colorFilter: ColorFilter? = null, - val blendMode: BlendMode = DrawScope.DefaultBlendMode, - val draw: DrawScope.(Path) -> Unit = { path -> - if (brush != null) { - drawPath(path, brush, alpha, style, colorFilter, blendMode) - } else { - drawPath(path, color, alpha, style, colorFilter, blendMode) - } - } -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/ChartWrapper.kt b/YChartsLib/src/main/java/co/yml/charts/ui/piechart/ChartWrapper.kt deleted file mode 100644 index 96fdd198..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/ChartWrapper.kt +++ /dev/null @@ -1,52 +0,0 @@ -package co.yml.charts.ui.piechart - -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import co.yml.charts.common.model.PlotType -import co.yml.charts.ui.piechart.charts.DonutPieChart -import co.yml.charts.ui.piechart.charts.PieChart -import co.yml.charts.ui.piechart.models.PieChartConfig -import co.yml.charts.ui.piechart.models.PieChartData - -@OptIn(ExperimentalMaterialApi::class) -object ChartWrapper { - - /** - * Wrapper compose method for drawing Pie Chart and Donut chart. - * @param modifier : All modifier related property - * @param plotType: Type of the chart (Pie or Donut) - * @param pieChartData: data list for the pie chart - * @param onSliceClick: Callback when any slice is clicked. - */ - - @Composable - fun DrawChart( - modifier: Modifier, - plotType: PlotType, - pieChartData: PieChartData, - pieChartConfig: PieChartConfig, - onSliceClick: (PieChartData.Slice) -> Unit = {} - ) { - when (plotType) { - is PlotType.Pie -> { - PieChart( - modifier = modifier, - pieChartData = pieChartData, - pieChartConfig = pieChartConfig, - onSliceClick = onSliceClick - ) - } - is PlotType.Donut -> { - DonutPieChart( - modifier = modifier, - pieChartData = pieChartData, - pieChartConfig = pieChartConfig, - onSliceClick = onSliceClick - ) - } - else -> { // T0DO Handle if required for other types - } - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/PieChartConstants.kt b/YChartsLib/src/main/java/co/yml/charts/ui/piechart/PieChartConstants.kt deleted file mode 100644 index f3b739ea..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/PieChartConstants.kt +++ /dev/null @@ -1,13 +0,0 @@ -package co.yml.charts.ui.piechart - - -object PieChartConstants { - const val DEFAULT_PADDING = 10 - const val DEFAULT_START_ANGLE = 270f - const val DEFAULT_STROKE_WIDTH = 100f - const val DEFAULT_SLICE_LABEL_TEXT_SIZE = 12 - const val MINIMUM_PERCENTAGE_FOR_SLICE_LABELS = 5 - const val TOTAL_ANGLE = 360 - const val ONE_HUNDRED = 100 - const val DESCRIPTION = "Double tap to know the chart in detail" -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/DonuPieChart.kt b/YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/DonuPieChart.kt deleted file mode 100644 index f7337a49..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/DonuPieChart.kt +++ /dev/null @@ -1,206 +0,0 @@ -package co.yml.charts.ui.piechart.charts - -import android.graphics.Paint -import androidx.activity.compose.BackHandler -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.tween -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.clickable -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.Surface -import androidx.compose.material.rememberModalBottomSheetState -import androidx.compose.runtime.* -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.nativeCanvas -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import co.yml.charts.ui.piechart.models.PieChartConfig -import co.yml.charts.ui.piechart.models.PieChartData -import co.yml.charts.ui.piechart.utils.convertTouchEventPointToAngle -import co.yml.charts.ui.piechart.utils.proportion -import co.yml.charts.ui.piechart.utils.sweepAngles -import co.yml.charts.common.components.accessibility.AccessibilityBottomSheetDialog -import co.yml.charts.common.components.accessibility.SliceInfo -import co.yml.charts.common.extensions.collectIsTalkbackEnabledAsState -import co.yml.charts.common.model.PlotType -import kotlinx.coroutines.launch -import kotlin.math.roundToInt - - -/** - * Compose function for Drawing Donut chart - * @param modifier : All modifier related property - * @param pieChartData: data list for the pie chart - * @param pieChartConfig: configuration for the pie chart - * @param onSliceClick(pieChartData.Slice)->Unit: The event that captures the click - */ -@ExperimentalMaterialApi -@Composable -fun DonutPieChart( - modifier: Modifier, - pieChartData: PieChartData, - pieChartConfig: PieChartConfig, - onSliceClick: (PieChartData.Slice) -> Unit = {} -) { - // Sum of all the values - val sumOfValues = pieChartData.totalLength - - // Calculate each proportion value - val proportions = pieChartData.slices.proportion(sumOfValues) - - // Convert each proportions to angle - val sweepAngles = proportions.sweepAngles() - - val progressSize = mutableListOf() - progressSize.add(sweepAngles.first()) - - for (x in 1 until sweepAngles.size) { - progressSize.add(sweepAngles[x] + progressSize[x - 1]) - } - - var activePie by rememberSaveable { - mutableStateOf(-1) - } - val accessibilitySheetState = - rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden) - val scope = rememberCoroutineScope() - val isTalkBackEnabled by LocalContext.current.collectIsTalkbackEnabledAsState() - if (accessibilitySheetState.isVisible && isTalkBackEnabled - && pieChartConfig.accessibilityConfig.shouldHandleBackWhenTalkBackPopUpShown - ) { - BackHandler { - scope.launch { - accessibilitySheetState.hide() - } - } - } - Surface( - modifier = modifier - ) { - BoxWithConstraints( - modifier = modifier - .aspectRatio(1f) - .semantics { - contentDescription = pieChartConfig.accessibilityConfig.chartDescription - } - .clickable { - if (isTalkBackEnabled) { - scope.launch { - accessibilitySheetState.animateTo( - ModalBottomSheetValue.Expanded - ) - } - } - }) { - - val sideSize = Integer.min(constraints.maxWidth, constraints.maxHeight) - val padding = (sideSize * pieChartConfig.chartPadding) / 100f - val size = Size(sideSize.toFloat() - padding, sideSize.toFloat() - padding) - - val pathPortion = remember { - Animatable(initialValue = 0f) - } - - if (pieChartConfig.isAnimationEnable) { - LaunchedEffect(key1 = Unit) { - pathPortion.animateTo( - 1f, animationSpec = tween(pieChartConfig.animationDuration) - ) - } - } - - Canvas( - modifier = Modifier - .width(sideSize.dp) - .height(sideSize.dp) - .pointerInput(true) { - - detectTapGestures { - val clickedAngle = convertTouchEventPointToAngle( - sideSize.toFloat(), - sideSize.toFloat(), - it.x, - it.y - ) - progressSize.forEachIndexed { index, item -> - if (clickedAngle <= item) { - if (activePie != index) - activePie = index - onSliceClick(pieChartData.slices[index]) - return@detectTapGestures - } - } - } - } - - ) { - - var sAngle = pieChartConfig.startAngle - - sweepAngles.forEachIndexed { index, arcProgress -> - drawPie( - color = pieChartData.slices[index].color, - startAngle = sAngle, - arcProgress = if (pieChartConfig.isAnimationEnable) - arcProgress * pathPortion.value else arcProgress, - size = size, - padding = padding, - isDonut = pieChartData.plotType == PlotType.Donut, - strokeWidth = pieChartConfig.strokeWidth, - isActive = activePie == index, - pieChartConfig = pieChartConfig - ) - sAngle += arcProgress - } - - if (activePie != -1 && pieChartConfig.percentVisible) - drawContext.canvas.nativeCanvas.apply { - val fontSize = pieChartConfig.percentageFontSize.toPx() - drawText( - "${proportions[activePie].roundToInt()}%", - (sideSize / 2) + fontSize / 4, (sideSize / 2) + fontSize / 3, - Paint().apply { - color = pieChartConfig.percentColor.toArgb() - textSize = fontSize - textAlign = Paint.Align.CENTER - typeface = pieChartConfig.percentageTypeface - - } - ) - } - } - } - if (isTalkBackEnabled) { - with(pieChartConfig) { - AccessibilityBottomSheetDialog( - modifier = Modifier.fillMaxSize(), - backgroundColor = Color.White, - content = { - LazyColumn { - items(pieChartData.slices.size) { index -> - SliceInfo( - pieChartData.slices[index], - proportions[index].roundToInt() - ) - } - } - }, - popUpTopRightButtonTitle = accessibilityConfig.popUpTopRightButtonTitle, - popUpTopRightButtonDescription = accessibilityConfig.popUpTopRightButtonDescription, - sheetState = accessibilitySheetState - ) - } - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/DrawPie.kt b/YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/DrawPie.kt deleted file mode 100644 index 64cbfebc..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/DrawPie.kt +++ /dev/null @@ -1,47 +0,0 @@ -package co.yml.charts.ui.piechart.charts - -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.Fill -import androidx.compose.ui.graphics.drawscope.Stroke -import co.yml.charts.ui.piechart.models.PieChartConfig - -/** - * Extension for drawing arcs - * @param color : Color for slice - * @param startAngle : StartAngle for the slice, from where to start draw - * @param arcProgress : Process of slice - * @param size : Size of the chart - * @param strokeWidth : StrokeWidth for the pie chart - * @param padding : Padding from top left - * @param isDonut : DonutPieChart or PieChart to indicate - * @param isActive : DonutPieChart zoom slice if IsActive - */ - -fun DrawScope.drawPie( - color: Color, - startAngle: Float, - arcProgress: Float, - size: Size, - strokeWidth: Float = 100f, - padding: Float, - isDonut: Boolean = false, - isActive: Boolean = false, - pieChartConfig: PieChartConfig -) { - drawArc( - color = color, - startAngle = startAngle, - sweepAngle = arcProgress, - useCenter = !isDonut, - size = size, - alpha = if (isActive) pieChartConfig.activeSliceAlpha else pieChartConfig.inActiveSliceAlpha, - style = if (isDonut) Stroke( - width = if (isActive) (strokeWidth + 20f) else strokeWidth, - ) else Fill, - - topLeft = Offset(padding / 2, padding / 2) - ) -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/PieChart.kt b/YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/PieChart.kt deleted file mode 100644 index 2165d4e0..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/charts/PieChart.kt +++ /dev/null @@ -1,229 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class) - -package co.yml.charts.ui.piechart.charts - -import android.graphics.Paint -import android.text.TextPaint -import android.text.TextUtils -import androidx.activity.compose.BackHandler -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.tween -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.clickable -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.Surface -import androidx.compose.material.rememberModalBottomSheetState -import androidx.compose.runtime.* -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas -import androidx.compose.ui.graphics.nativeCanvas -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import androidx.core.graphics.withRotation -import co.yml.charts.ui.piechart.PieChartConstants.MINIMUM_PERCENTAGE_FOR_SLICE_LABELS -import co.yml.charts.ui.piechart.models.PieChartConfig -import co.yml.charts.ui.piechart.models.PieChartData -import co.yml.charts.ui.piechart.utils.convertTouchEventPointToAngle -import co.yml.charts.ui.piechart.utils.getSliceCenterPoints -import co.yml.charts.ui.piechart.utils.proportion -import co.yml.charts.ui.piechart.utils.sweepAngles -import co.yml.charts.common.components.accessibility.AccessibilityBottomSheetDialog -import co.yml.charts.common.components.accessibility.SliceInfo -import co.yml.charts.common.extensions.collectIsTalkbackEnabledAsState -import co.yml.charts.common.extensions.getTextHeight -import co.yml.charts.common.model.PlotType -import kotlinx.coroutines.launch -import kotlin.math.abs -import kotlin.math.roundToInt - - -/** - * Compose function used to Draw the Pie Chart. - * @param modifier : All modifier related property - * @param pieChartData: data list for the pie chart - * @param pieChartConfig: configuration for the pie chart - * @param onSliceClick(pieChartData.Slice)->Unit: The event that captures the click - */ -@Composable -fun PieChart( - modifier: Modifier, - pieChartData: PieChartData, - pieChartConfig: PieChartConfig, - onSliceClick: (PieChartData.Slice) -> Unit = {} -) { - // Sum of all the values - val sumOfValues = pieChartData.totalLength - - // Calculate each proportion value - val proportions = pieChartData.slices.proportion(sumOfValues) - - // Convert each proportions to angle - val sweepAngles = proportions.sweepAngles() - - val progressSize = mutableListOf() - progressSize.add(sweepAngles.first()) - - for (x in 1 until sweepAngles.size) { - progressSize.add(sweepAngles[x] + progressSize[x - 1]) - } - - var activePie by rememberSaveable { - mutableStateOf(-1) - } - val accessibilitySheetState = - rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden) - val scope = rememberCoroutineScope() - val isTalkBackEnabled by LocalContext.current.collectIsTalkbackEnabledAsState() - if (accessibilitySheetState.isVisible && isTalkBackEnabled && pieChartConfig.accessibilityConfig.shouldHandleBackWhenTalkBackPopUpShown) { - BackHandler { - scope.launch { - accessibilitySheetState.hide() - } - } - } - Surface( - modifier = modifier.fillMaxWidth() - ) { - BoxWithConstraints( - modifier = modifier - .aspectRatio(1f) - .semantics { - contentDescription = pieChartConfig.accessibilityConfig.chartDescription - } - .clickable { - if (isTalkBackEnabled) { - scope.launch { - accessibilitySheetState.animateTo( - ModalBottomSheetValue.Expanded - ) - } - } - }, - ) { - - val sideSize = Integer.min(constraints.maxWidth, constraints.maxHeight) - val padding = (sideSize * pieChartConfig.chartPadding) / 100f - val size = Size(sideSize.toFloat() - padding, sideSize.toFloat() - padding) - - val pathPortion = remember { - Animatable(initialValue = 0f) - } - if (pieChartConfig.isAnimationEnable) { - LaunchedEffect(key1 = Unit) { - pathPortion.animateTo( - 1f, animationSpec = tween(pieChartConfig.animationDuration) - ) - } - } - Canvas(modifier = Modifier - .width(sideSize.dp) - .height(sideSize.dp) - .pointerInput(true) { - - detectTapGestures { - val clickedAngle = convertTouchEventPointToAngle( - sideSize.toFloat(), sideSize.toFloat(), it.x, it.y - ) - progressSize.forEachIndexed { index, item -> - if (clickedAngle <= item) { - if (activePie != index) activePie = index - onSliceClick(pieChartData.slices[index]) - return@detectTapGestures - } - } - } - }) { - - var sAngle = pieChartConfig.startAngle - - val sliceLabelPaint = TextPaint().apply { - isAntiAlias = true - textSize = pieChartConfig.sliceLabelTextSize.toPx() - textAlign = Paint.Align.CENTER - color = pieChartConfig.sliceLabelTextColor.toArgb() - typeface = pieChartConfig.sliceLabelTypeface - } - - sweepAngles.forEachIndexed { index, arcProgress -> - drawPie( - color = pieChartData.slices[index].color, - startAngle = sAngle, - arcProgress = if (pieChartConfig.isAnimationEnable) arcProgress * pathPortion.value else arcProgress, - size = size, - padding = padding, - isDonut = (pieChartData.plotType == PlotType.Pie).not(), - isActive = activePie == index, - pieChartConfig = pieChartConfig - ) - - // if percentage is less than 5 width of slice will be very small - if (pieChartConfig.showSliceLabels && proportions[index] >= MINIMUM_PERCENTAGE_FOR_SLICE_LABELS) { - - val (arcCenter, x, y) = getSliceCenterPoints(sAngle, arcProgress, size, padding) - - // find the height of text - val height = pieChartData.slices[index].label.getTextHeight(sliceLabelPaint) - - var label = pieChartData.slices[index].label - - val ellipsizedText by lazy { - TextUtils.ellipsize( - label, - sliceLabelPaint, - pieChartConfig.sliceMinTextWidthToEllipsize.toPx(), - pieChartConfig.sliceLabelEllipsizeAt - ).toString() - } - - drawIntoCanvas { - it.nativeCanvas.withRotation( - arcCenter, x, y - ) { - if (pieChartConfig.percentVisible) { - label = "$label ${proportions[index].roundToInt()}%" - } - it.nativeCanvas.drawText( - if (pieChartConfig.isEllipsizeEnabled) ellipsizedText - else label, x, y + abs(height) / 2, sliceLabelPaint - ) - } - } - } - - sAngle += arcProgress - } - } - } - if (isTalkBackEnabled) { - with(pieChartConfig) { - AccessibilityBottomSheetDialog( - modifier = Modifier.fillMaxSize(), - backgroundColor = Color.White, - content = { - LazyColumn { - items(pieChartData.slices.size) { index -> - SliceInfo( - pieChartData.slices[index], proportions[index].roundToInt() - ) - } - } - }, - popUpTopRightButtonTitle = accessibilityConfig.popUpTopRightButtonTitle, - popUpTopRightButtonDescription = accessibilityConfig.popUpTopRightButtonDescription, - sheetState = accessibilitySheetState - ) - } - } - } -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/models/PieChartConfig.kt b/YChartsLib/src/main/java/co/yml/charts/ui/piechart/models/PieChartConfig.kt deleted file mode 100644 index 69c6c6da..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/models/PieChartConfig.kt +++ /dev/null @@ -1,62 +0,0 @@ -package co.yml.charts.ui.piechart.models - -import android.graphics.Typeface -import android.text.TextUtils -import androidx.annotation.IntRange -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import co.yml.charts.ui.piechart.PieChartConstants -import co.yml.charts.ui.piechart.PieChartConstants.DEFAULT_PADDING -import co.yml.charts.ui.piechart.PieChartConstants.DEFAULT_SLICE_LABEL_TEXT_SIZE -import co.yml.charts.ui.piechart.PieChartConstants.DEFAULT_START_ANGLE -import co.yml.charts.ui.piechart.PieChartConstants.DEFAULT_STROKE_WIDTH -import co.yml.charts.common.model.AccessibilityConfig - -/** - * PieChartConfig data class used to mention all config related param required to draw PieChart. - * @param startAngle: Starting angle - * @param showSliceLabels: Control the labels visibility - * @param sliceLabelTextSize: Text size of the labels - * @param sliceLabelTextColor: Text color of the labels - * @param sliceLabelTypeface: Typeface of the labels - * @param isAnimationEnable: Boolean Flag for enabling animation - * @param animationDuration: Duration of animation - * @param strokeWidth: Stroke width of Donut Chart - * @param percentageFontSize: Percentage text font size - * @param percentageTypeface: Percentage text typeface - * @param percentVisible: Percentage text visibility - * @param percentColor: Percentage text color - * @param activeSliceAlpha: Opacity of the active slice - * @param inActiveSliceAlpha: Opacity of the inactive slice - * @param isEllipsizeEnabled: Boolean flag for enabling ellipsize - * @param sliceMinTextWidthToEllipsize: Minimum width of the label post which label will be ellipsized - * @param sliceLabelEllipsizeAt: Position at which the label will be truncated or ellipsized - * @param chartPadding: Padding for the Pie chart/Donut Chart - * @param accessibilityConfig: Configs related to accessibility service defined here in [AccessibilityConfig] - */ -data class PieChartConfig( - val startAngle: Float = DEFAULT_START_ANGLE, - val showSliceLabels: Boolean = true, - val sliceLabelTextSize: TextUnit = DEFAULT_SLICE_LABEL_TEXT_SIZE.sp, - val sliceLabelTextColor: Color = Color.White, - val sliceLabelTypeface: Typeface = Typeface.DEFAULT, - val isAnimationEnable: Boolean = false, - @IntRange(from = 1) val animationDuration: Int = 500, - val strokeWidth: Float = DEFAULT_STROKE_WIDTH, - val percentageFontSize: TextUnit = 24.sp, - val percentageTypeface: Typeface = Typeface.DEFAULT, - val percentVisible: Boolean = false, - val percentColor: Color = Color.White, - val activeSliceAlpha: Float = .8f, - val inActiveSliceAlpha: Float = 1f, - val isEllipsizeEnabled: Boolean = false, - val sliceMinTextWidthToEllipsize: Dp = 80.dp, - val sliceLabelEllipsizeAt: TextUtils.TruncateAt = TextUtils.TruncateAt.END, - val chartPadding: Int = DEFAULT_PADDING, - val accessibilityConfig: AccessibilityConfig = AccessibilityConfig( - chartDescription = PieChartConstants.DESCRIPTION - ) -) diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/models/PieChartData.kt b/YChartsLib/src/main/java/co/yml/charts/ui/piechart/models/PieChartData.kt deleted file mode 100644 index 1afa3476..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/models/PieChartData.kt +++ /dev/null @@ -1,35 +0,0 @@ -package co.yml.charts.ui.piechart.models - -import androidx.compose.ui.graphics.Color -import co.yml.charts.common.model.PlotData -import co.yml.charts.common.model.PlotType -import co.yml.charts.ui.piechart.models.PieChartData.Slice -import co.yml.charts.ui.piechart.utils.sum - -/** - * PieChartData is a data class to mention all the data needed to draw slices in the pie/donut chart. - * @param slices: Defines the list of slices [Slice] to be drawn. - * @param plotType: Defines the type of the chart. - */ -data class PieChartData(val slices: List, override val plotType: PlotType) : PlotData { - val totalLength: Float - get() { - return slices.sum() - } - - /** - * Slice is data class to mention data of each arc in a 360 degree chart. - * @param label: Name of the arc. - * @param value: Value of the arc. - * @param color: Color of the arc. - * @param sliceDescription: Description of the arc for accessibility service. - */ - data class Slice( - val label: String, - val value: Float, - val color: Color, - val sliceDescription: (Int) -> String = { slicePercentage -> - "Slice name : $label \nPercentage : $slicePercentage %" - } - ) -} diff --git a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/utils/PieChartUtils.kt b/YChartsLib/src/main/java/co/yml/charts/ui/piechart/utils/PieChartUtils.kt deleted file mode 100644 index 839600ad..00000000 --- a/YChartsLib/src/main/java/co/yml/charts/ui/piechart/utils/PieChartUtils.kt +++ /dev/null @@ -1,87 +0,0 @@ -package co.yml.charts.ui.piechart.utils - -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.geometry.center -import co.yml.charts.ui.piechart.PieChartConstants.ONE_HUNDRED -import co.yml.charts.ui.piechart.PieChartConstants.TOTAL_ANGLE -import co.yml.charts.ui.piechart.models.PieChartData -import kotlin.math.atan2 -import kotlin.math.cos -import kotlin.math.sin - -/** - * Returns the angle for given touch point - * @param width: Width of the chart. - * @param height: Height of the chart. - * @param xPos: X offset of the tap point. - * @param yPos: Y offset of the tap point. - */ -fun convertTouchEventPointToAngle( - width: Float, - height: Float, - xPos: Float, - yPos: Float -): Double { - val x = xPos - (width * 0.5f) - val y = yPos - (height * 0.5f) - - var angle = Math.toDegrees(atan2(y.toDouble(), x.toDouble()) + Math.PI / 2) - angle = if (angle < 0) angle + 360 else angle - return angle -} - -/** - * Returns the sum of all the arc values - */ -fun List.sum(): Float { - return this.map { it.value }.sum() -} - - -/** - * Returns the center points of the slice - * @param sAngle Start angle of the point - * @param arcProgress Progress angle of the point - * @param size Size of the canvas - * @param padding padding of the canvas - */ -fun getSliceCenterPoints(sAngle: Float, arcProgress: Float, size: Size, padding: Float): - Triple { - val arcCenter = sAngle + (arcProgress / 2) - // Middle point radius is half of the radius of the pie chart - val pointRadius = size.width / 4 - - /* Calculate the x & y co-ordinates to show the label/percentage tex - * find points using angle and radius - *https://en.wikipedia.org/wiki/Polar_coordinate_system#Converting_between_polar_and_Cartesian_coordinates - * */ - val x = - (pointRadius * cos(Math.toRadians(arcCenter.toDouble()))) + - size.center.x + padding / 2 - val y = - (pointRadius * sin(Math.toRadians(arcCenter.toDouble()))) + - size.center.y + padding / 2 - - return Triple(arcCenter,x.toFloat(),y.toFloat()) -} - - -/** - * Returns the calculated proportion value of each arc - * @param total: Total of the the slices. - */ -fun List.proportion(total:Float): List { - return this.map { - it.value * ONE_HUNDRED / total - } -} - -/** - * Returns the list of sweep angles - */ -fun List.sweepAngles(): List { - return this.map { - TOTAL_ANGLE * it / ONE_HUNDRED - } -} - diff --git a/YChartsLib/src/test/java/co/yml/charts/axis/XGraphExtensionsTest.kt b/YChartsLib/src/test/java/co/yml/charts/axis/XGraphExtensionsTest.kt deleted file mode 100644 index 2d6d6a0e..00000000 --- a/YChartsLib/src/test/java/co/yml/charts/axis/XGraphExtensionsTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package co.yml.charts.axis - -import co.yml.charts.common.model.Point -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test - -class XGraphExtensionsTest { - - @Test - fun `Given list of points exact xMin and xMax value should be calculated`() { - // Given - val chartData = listOf( - Point(0f, 10f), Point(1f, 30f), Point(2f, 15f), Point(3f, 50f) - ) - val steps = 2 - - // When - val (xMin, xMax, scale) = getXAxisScale(chartData, steps) - - //Then - assertEquals(xMin.toInt(), 0) - assertEquals(xMax.toInt(), 3) - } - - @Test - fun `Given list of points when drawing axis using the scale the value should be withing xMin and xMax`() { - // Given - val chartData = listOf( - Point(0f, 10f), Point(1f, 30f), Point(2f, 15f), Point(3f, 50f) - ) - val steps = 2 - - // When - val (xMin, xMax, scale) = getXAxisScale(chartData, steps) - - //Then - for (i in 0 until steps) { - val value = i * scale - assertTrue(value >= xMin) - assertTrue(value <= xMax) - } - } - - @Test - fun `Given empty list of points all the values should be zero`() { - // Given - val chartData = listOf() - val steps = 2 - - // When - val (xMin, xMax, scale) = getXAxisScale(chartData, steps) - - //Then - assertEquals(xMin.toInt(), 0) - assertEquals(xMax.toInt(), 0) - assertEquals(scale.toInt(), 0) - } - -} diff --git a/YChartsLib/src/test/java/co/yml/charts/axis/YChartsExtensionsTest.kt b/YChartsLib/src/test/java/co/yml/charts/axis/YChartsExtensionsTest.kt deleted file mode 100644 index 9d4aa562..00000000 --- a/YChartsLib/src/test/java/co/yml/charts/axis/YChartsExtensionsTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package co.yml.charts.axis - -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.mockk -import org.junit.Assert.assertTrue -import org.junit.Test - -class YChartsExtensionsTest { - - @MockK - private val axisData: AxisData = mockk() - - @Test - fun `When getAxisInitValues extension is invoked should not return values lesser than zero`() { - // Given - every { axisData.steps }.returns(5) - - // When - val values = getAxisInitValues( - axisData, - 687f, // 250.dp.toPx() - 20f, - 20f - ) - - // Then - assertTrue(values.first > 0) - assertTrue(values.second > 0) - } - - @Test - fun `Given yAxisData segmentHeight should be less than yaxisHeight `() { - // Given - every { axisData.steps }.returns(5) - - // When - val values = getAxisInitValues( - axisData, - 687f, // 250.dp.toPx() - 20f, - 20f - ) - - // Then - assertTrue(values.second < values.first) - } -} diff --git a/YChartsLib/src/test/java/co/yml/charts/chartcontainer/ScrollableCanvasContainerTest.kt b/YChartsLib/src/test/java/co/yml/charts/chartcontainer/ScrollableCanvasContainerTest.kt deleted file mode 100644 index bd784a1a..00000000 --- a/YChartsLib/src/test/java/co/yml/charts/chartcontainer/ScrollableCanvasContainerTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package co.yml.charts.chartcontainer - -import co.yml.charts.chartcontainer.container.checkAndGetMaxScrollOffset -import org.junit.Assert.assertEquals -import org.junit.Test - -class ScrollableCanvasContainerTest { - - @Test - fun `Given negative scroll offset canvas should not scroll`() { - // Given - val scrollOffset = -20f - val maxScrollOffset = 200f - - // When - val calculatedScrollOffset = checkAndGetMaxScrollOffset(scrollOffset, maxScrollOffset) - - // Then - assertEquals(calculatedScrollOffset, 0f) - } - - @Test - fun `Given scroll offset canvas should support scroll if within maxScrollOffset`() { - // Given - val scrollOffset = 20f - val maxScrollOffset = 200f - - // When - val calculatedScrollOffset = checkAndGetMaxScrollOffset(scrollOffset, maxScrollOffset) - - // Then - assertEquals(calculatedScrollOffset, 20f) - } - - - @Test - fun `Given scroll offset more then maxScrollOffset then scroll offset should be reset to same`() { - // Given - val scrollOffset = 220f - val maxScrollOffset = 200f - - // When - val calculatedScrollOffset = checkAndGetMaxScrollOffset(scrollOffset, maxScrollOffset) - - // Then - assertEquals(calculatedScrollOffset, maxScrollOffset) - } -} diff --git a/YChartsLib/src/test/java/co/yml/charts/extensions/ExtensionsTest.kt b/YChartsLib/src/test/java/co/yml/charts/extensions/ExtensionsTest.kt deleted file mode 100644 index b417a9d7..00000000 --- a/YChartsLib/src/test/java/co/yml/charts/extensions/ExtensionsTest.kt +++ /dev/null @@ -1,66 +0,0 @@ -package co.yml.charts.extensions - -import androidx.compose.ui.geometry.Offset -import co.yml.charts.common.extensions.getMaxElementInYAxis -import co.yml.charts.common.extensions.getXMaxAndMinPoints -import co.yml.charts.common.extensions.getYMaxAndMinPoints -import co.yml.charts.common.extensions.isDragLocked -import co.yml.charts.common.model.Point -import org.junit.Assert.* -import org.junit.Test - -class ExtensionsTest{ - @Test - fun `Find proper maximum and minimum X values from the given points`() { - val pointList = listOf( - Point(0f, 10f), Point(1f, 20f), Point(2f, 30f), Point(3f, 40f) - ) - - val (xMin, xMax) = getXMaxAndMinPoints(pointList) - - assertEquals(xMin, 0f) - assertEquals(xMax, 3f) - } - - @Test - fun `Find proper maximum and minimum Y values from the given points`() { - val pointList = listOf( - Point(0f, 10f), Point(1f, 20f), Point(2f, 30f), Point(3f, 40f) - ) - - val (yMin, yMax) = getYMaxAndMinPoints(pointList) - - assertEquals(yMin, 10f) - assertEquals(yMax, 40f) - } - - @Test - fun `Given empty list maximum and minimum points should be zero`() { - val pointList = emptyList() - - val (yMin, yMax) = getYMaxAndMinPoints(pointList) - - assertEquals(yMin, 0f) - assertEquals(yMax, 0f) - } - - @Test - fun `Find the maximum Y point from the steps size and max value`() { - val stepSize = 10 - val maxValue = 45f - - val maximumYValue = getMaxElementInYAxis(maxValue,stepSize) - - assertEquals(maximumYValue, 50) - } - - @Test - fun `Given a point and proper drag lock status should be calculated`() { - val offset = Offset(30f,20f) - val dragOffsetX = 25f - val xOffset = 20f - - val isDragLocked = offset.isDragLocked(dragOffsetX, xOffset) - assertEquals(isDragLocked, true) - } -} diff --git a/YChartsLib/src/test/java/co/yml/charts/ui/bargraph/BarChartTest.kt b/YChartsLib/src/test/java/co/yml/charts/ui/bargraph/BarChartTest.kt deleted file mode 100644 index 3943082e..00000000 --- a/YChartsLib/src/test/java/co/yml/charts/ui/bargraph/BarChartTest.kt +++ /dev/null @@ -1,95 +0,0 @@ -package co.yml.charts.ui.bargraph - -import co.yml.charts.ui.barchart.getDrawOffset -import co.yml.charts.ui.barchart.getMaxScrollDistance -import co.yml.charts.common.model.Point -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test - -class BarChartTest{ - @Test - fun `Given a container with input values valid scroll offset should be returned`() { - // Given - val columnWidth = 104f - val xMax = 100f - val xMin = 0f - val xOffset = 200f - val xLeft = 10f - val paddingRight = 40f - val canvasWidth = 500f - - // When - val scrollDistance = getMaxScrollDistance( - columnWidth, - xMax, - xMin, - xOffset, - xLeft, - paddingRight, - canvasWidth - ) - - assertTrue(scrollDistance > 0) - } - - - @Test - fun `Given a total drawing width lesser than container width container should not be able to scrolled`() { - // Given - val columnWidth = 20f - val xMax = 20f - val xMin = 0f - val xOffset = 20f - val xLeft = 10f - val paddingRight = 40f - val canvasWidth = 500f - - // When - val scrollDistance = getMaxScrollDistance( - columnWidth, - xMax, - xMin, - xOffset, - xLeft, - paddingRight, - canvasWidth - ) - - assertEquals(scrollDistance, 0f) - } - - @Test - fun `Given a point drawOffset should be positive`() { - val point = Point(100f, 20f) - val drawOffset = getDrawOffset( - point = point, - xMin = 0f, - xOffset = 10f, - xLeft = 20f, - scrollOffset = 50f, - yBottom = 1500f, - yOffset = 20f, - yMin = 0f - ) - assertTrue(drawOffset.x > 0 && drawOffset.y > 0) - } - - - @Test - fun `Given scroll offset,xleft,xmin are zero drawOffset should be product of xOffset and xValue`() { - val point = Point(1f, 20f) - val xOffset = 20f - val drawOffset = getDrawOffset( - point = point, - xMin = 0f, - xOffset = xOffset, - xLeft = 0f, - scrollOffset = 0f, - yBottom = 1500f, - yOffset = 20f, - yMin = 0f - ) - assertEquals(drawOffset.x, xOffset*point.x) - } -} diff --git a/YChartsLib/src/test/java/co/yml/charts/ui/linegraph/LineChartExtensionsTest.kt b/YChartsLib/src/test/java/co/yml/charts/ui/linegraph/LineChartExtensionsTest.kt deleted file mode 100644 index d4b3c6b8..00000000 --- a/YChartsLib/src/test/java/co/yml/charts/ui/linegraph/LineChartExtensionsTest.kt +++ /dev/null @@ -1,58 +0,0 @@ -package co.yml.charts.ui.linegraph - -import co.yml.charts.ui.linechart.getYAxisScale -import co.yml.charts.common.model.Point -import org.junit.Assert -import org.junit.Test - -class LineChartExtensionsTest { - @Test - fun `Given list of points exact yMin and yMax value should be calculated`() { - // Given - val chartData = listOf( - Point(0f, 10f), Point(1f, 30f), Point(2f, 15f), Point(3f, 50f) - ) - val steps = 2 - - // When - val (yMin, yMax, scale) = getYAxisScale(chartData, steps) - - //Then - Assert.assertEquals(yMin.toInt(), 10) - Assert.assertEquals(yMax.toInt(), 50) - } - - @Test - fun `Given list of points when drawing axis using the scale the value should be withing yMin and yMax`() { - // Given - val chartData = listOf( - Point(0f, 10f), Point(1f, 30f), Point(2f, 15f), Point(3f, 50f) - ) - val steps = 2 - - // When - val (yMin, yMax, scale) = getYAxisScale(chartData, steps) - - //Then - for (i in 1 until steps) { - val value = i * scale - Assert.assertTrue(value >= yMin) - Assert.assertTrue(value <= yMax) - } - } - - @Test - fun `Given empty list of points all the values should be zero`() { - // Given - val chartData = listOf() - val steps = 2 - - // When - val (yMin, yMax, scale) = getYAxisScale(chartData, steps) - - //Then - Assert.assertEquals(yMin.toInt(), 0) - Assert.assertEquals(yMax.toInt(), 0) - Assert.assertEquals(scale.toInt(), 0) - } -} diff --git a/YChartsLib/src/test/java/co/yml/charts/ui/linegraph/LineChartTest.kt b/YChartsLib/src/test/java/co/yml/charts/ui/linegraph/LineChartTest.kt deleted file mode 100644 index 4b936042..00000000 --- a/YChartsLib/src/test/java/co/yml/charts/ui/linegraph/LineChartTest.kt +++ /dev/null @@ -1,192 +0,0 @@ -package co.yml.charts.ui.linegraph - -import androidx.compose.ui.geometry.Offset -import co.yml.charts.ui.linechart.getCubicPoints -import co.yml.charts.ui.linechart.getMappingPointsToGraph -import co.yml.charts.ui.linechart.getMaxScrollDistance -import co.yml.charts.common.model.Point -import org.junit.Assert -import org.junit.Test - -class LineChartTest { - - @Test - fun `Given few points data n-1 size of cubicPoints1 & cubicPoints2 should be returned`() { - // Given - val pointsData = listOf(Offset(1f, 30f), Offset(2f, 10f), Offset(3f, 55f)) - - // When - val pairCubicPoints = getCubicPoints(pointsData) - - //Then - Assert.assertEquals(pointsData.size - 1, pairCubicPoints.first.size) - Assert.assertEquals(pointsData.size - 1, pairCubicPoints.second.size) - } - - @Test - fun `Given two points calculated cubicPoints1 value should be returned`() { - // Given - val pointsData = listOf(Offset(1f, 30f), Offset(2f, 10f)) - - // When - val pairCubicPoints = getCubicPoints(pointsData) - - //Then - val expectedCubic1Point = Offset( - (pointsData[1].x + pointsData.first().x) / 2, - pointsData.first().y - ) - - Assert.assertEquals(expectedCubic1Point, pairCubicPoints.first.first()) - } - - @Test - fun `Given two points calculated cubicPoints2 value should be returned`() { - // Given - val pointsData = listOf(Offset(1f, 30f), Offset(2f, 10f)) - - // When - val pairCubicPoints = getCubicPoints(pointsData) - - //Then - val expectedCubic1Point = Offset( - (pointsData[1].x + pointsData.first().x) / 2, - pointsData[1].y - ) - - Assert.assertEquals(expectedCubic1Point, pairCubicPoints.second.first()) - } - - @Test - fun `Given a container with input values valid scroll offset should be returned`() { - // Given - val columnWidth = 104f - val xMax = 100f - val xMin = 0f - val xOffset = 200f - val paddingRight = 40f - val canvasWidth = 500f - - // When - val scrollDistance = getMaxScrollDistance( - columnWidth, - xMax, - xMin, - xOffset, - paddingRight, - canvasWidth - ) - - Assert.assertTrue(scrollDistance > 0) - } - - @Test - fun `Given a total drawing width lesser than container width container should not be able to scrolled`() { - // Given - val columnWidth = 20f - val xMax = 20f - val xMin = 0f - val xOffset = 20f - val paddingRight = 40f - val canvasWidth = 500f - - // When - val scrollDistance = getMaxScrollDistance( - columnWidth, - xMax, - xMin, - xOffset, - paddingRight, - canvasWidth - ) - - Assert.assertEquals(scrollDistance, 0f) - } - - @Test - fun `Given input points size the transformed list size should be same`() { - // Given - val pointsData = listOf(Point(1f, 30f), Point(2f, 10f), Point(3f, 55f)) - val xLeft = 20f - val scrollOffset = 0f - val xMin = 0f - val xOffset = 20f - val yBottom = 40f - val yMin = 0f - val yOffset = 20f - - //When - val transformedPointsList = getMappingPointsToGraph( - pointsData, xMin, - xOffset, - xLeft, - scrollOffset, - yBottom, - yMin, - yOffset - ) - - // Then - Assert.assertEquals(pointsData.size, transformedPointsList.size) - - } - - @Test - fun `Given a point x and transformed point x to w_r_t container should be same `() { - // Given - val pointData = listOf(Point(1f, 30f)) - val xLeft = 20f - val scrollOffset = 0f - val xMin = 0f - val xOffset = 20f - val yBottom = 40f - val yMin = 0f - val yOffset = 20f - - //When - val transformedXPoint = getMappingPointsToGraph( - pointData, xMin, - xOffset, - xLeft, - scrollOffset, - yBottom, - yMin, - yOffset - ).first().x - - //Then - val expectedXPoint = ((pointData.first().x - xMin) * xOffset) + xLeft - scrollOffset - Assert.assertEquals(expectedXPoint, transformedXPoint) - } - - @Test - fun `Given a point y and transformed point y to w_r_t container should be same `() { - // Given - val pointData = listOf(Point(1f, 30f)) - val xLeft = 20f - val scrollOffset = 0f - val xMin = 0f - val xOffset = 20f - val yBottom = 40f - val yMin = 0f - val yOffset = 20f - - //When - val transformedYPoint = getMappingPointsToGraph( - pointData, xMin, - xOffset, - xLeft, - scrollOffset, - yBottom, - yMin, - yOffset - ).first().y - - //Then - val expectedYPoint = yBottom - ((pointData.first().y - yMin) * yOffset) - Assert.assertEquals(expectedYPoint, transformedYPoint) - } - -} - - diff --git a/YChartsLib/src/test/java/co/yml/charts/utils/PieChartUtilsTest.kt b/YChartsLib/src/test/java/co/yml/charts/utils/PieChartUtilsTest.kt deleted file mode 100644 index ae6b236a..00000000 --- a/YChartsLib/src/test/java/co/yml/charts/utils/PieChartUtilsTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package co.yml.charts.utils - -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import co.yml.charts.ui.piechart.PieChartConstants.DEFAULT_PADDING -import co.yml.charts.ui.piechart.PieChartConstants.DEFAULT_START_ANGLE -import co.yml.charts.ui.piechart.models.PieChartData -import co.yml.charts.ui.piechart.utils.convertTouchEventPointToAngle -import co.yml.charts.ui.piechart.utils.getSliceCenterPoints -import co.yml.charts.ui.piechart.utils.proportion -import co.yml.charts.ui.piechart.utils.sum -import co.yml.charts.ui.piechart.utils.sweepAngles -import org.junit.Assert.* -import org.junit.Test - -class PieChartUtilsTest { - - private val sliceList = listOf( - PieChartData.Slice("A", 10f, Color.Cyan), - PieChartData.Slice("B", 20f, Color.Red), - PieChartData.Slice("C", 40f, Color.Black), - PieChartData.Slice("D", 30f, Color.Blue) - ) - - - @Test - fun `Slice total calculation is correct`() { - val sum = sliceList.sum() - assertEquals(100f, sum) - } - - @Test - fun `Proportion calculation is correct`() { - val sum = sliceList.sum() - val proportions = sliceList.proportion(sum) - assertEquals(10f, proportions.first()) - } - - @Test - fun `Sweep angle calculation is correct`() { - val sum = sliceList.sum() - val proportions = sliceList.proportion(sum) - val sweepAngles = proportions.sweepAngles() - assertEquals(36f, sweepAngles.first()) - } - - @Test - fun `Arch center calculation is correct and x and y are positive`() { - - val sideSize = 200 - val padding = (sideSize * DEFAULT_PADDING) / 100f - val size = Size(sideSize.toFloat() - padding, sideSize.toFloat() - padding) - - val (arcCenter, x, y) = getSliceCenterPoints( - DEFAULT_START_ANGLE, - 30f, - size, - padding - ) - assertEquals(285f, arcCenter) - assertTrue(x > 0) - assertTrue(y > 0) - } - - @Test - fun `Calculated angle is in the correct range`() { - val angle = convertTouchEventPointToAngle(200f, 200f, 10f, 10f) - assertTrue(angle in 0.0..360.0) - } -} diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 0b1cd25a..9cdd3adc 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -49,7 +49,6 @@ android { dependencies { implementation(project(mapOf("path" to ":KMMYCharts"))) - implementation(project(mapOf("path" to ":YChartsLib"))) implementation(co.ycharts.dependency.YChartDependency.CORE_KTX) implementation(co.ycharts.dependency.YChartDependency.COMPOSE_UI) implementation(co.ycharts.dependency.YChartDependency.COMPOSE_MATERIAL) diff --git a/androidApp/src/main/java/co/yml/ycharts/app/presentation/BarChartActivity.kt b/androidApp/src/main/java/co/yml/ycharts/app/presentation/BarChartActivity.kt index d5dba961..7011c702 100644 --- a/androidApp/src/main/java/co/yml/ycharts/app/presentation/BarChartActivity.kt +++ b/androidApp/src/main/java/co/yml/ycharts/app/presentation/BarChartActivity.kt @@ -5,25 +5,15 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton -import co.yml.ycharts.app.ui.theme.YChartsTheme -import co.yml.charts.axis.AxisData -import co.yml.charts.ui.barchart.BarChart -import co.yml.charts.ui.barchart.models.BarChartData -import co.yml.charts.ui.barchart.models.BarStyle -import co.yml.charts.ui.barchart.models.SelectionHighlightData -import co.yml.charts.common.utils.DataUtils import co.yml.kmm.charts.ChartScreen import co.yml.ycharts.app.R +import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton +import co.yml.ycharts.app.ui.theme.YChartsTheme class BarChartActivity : ComponentActivity() { @@ -63,74 +53,4 @@ class BarChartActivity : ComponentActivity() { } } -@Composable -private fun BarChart1() { - val maxRange = 50 - val barData = DataUtils.getBarChartData(50, maxRange) - val yStepSize = 10 - - val xAxisData = AxisData.Builder() - .axisStepSize(30.dp) - .steps(barData.size - 1) - .bottomPadding(40.dp) - .axisLabelAngle(20f) - .labelData { index -> barData[index].label } - .build() - val yAxisData = AxisData.Builder() - .steps(yStepSize) - .labelAndAxisLinePadding(20.dp) - .axisOffset(20.dp) - .labelData { index -> (index * (maxRange / yStepSize)).toString() } - .build() - val barChartData = BarChartData( - chartData = barData, - xAxisData = xAxisData, - yAxisData = yAxisData, - barStyle = BarStyle( - paddingBetweenBars = 20.dp, - barWidth = 25.dp - ), - showYAxis = true, - showXAxis = true, - horizontalExtraSpace = 10.dp, - ) - BarChart(modifier = Modifier.height(350.dp), barChartData = barChartData) -} - -@Composable -private fun BarChart2() { - val maxRange = 100 - val barData = DataUtils.getGradientBarChartData(50, 100) - val yStepSize = 10 - val xAxisData = AxisData.Builder() - .axisStepSize(30.dp) - .steps(barData.size - 1) - .bottomPadding(40.dp) - .axisLabelAngle(20f) - .labelData { index -> barData[index].label } - .build() - val yAxisData = AxisData.Builder() - .steps(yStepSize) - .labelAndAxisLinePadding(20.dp) - .axisOffset(20.dp) - .labelData { index -> (index * (maxRange / yStepSize)).toString() } - .build() - val barChartData = BarChartData( - chartData = barData, - xAxisData = xAxisData, - yAxisData = yAxisData, - barStyle = BarStyle(paddingBetweenBars = 20.dp, - barWidth = 35.dp, - isGradientEnabled = true, - selectionHighlightData = SelectionHighlightData( - highlightBarColor = Color.Red, - highlightTextBackgroundColor = Color.Green, - popUpLabel = { _, y -> " Value : $y " } - )), - showYAxis = true, - showXAxis = true, - horizontalExtraSpace = 20.dp - ) - BarChart(modifier = Modifier.height(350.dp), barChartData = barChartData) -} diff --git a/androidApp/src/main/java/co/yml/ycharts/app/presentation/CombinedLineAndBarChartActivity.kt b/androidApp/src/main/java/co/yml/ycharts/app/presentation/CombinedLineAndBarChartActivity.kt index 1531243d..cdc39031 100644 --- a/androidApp/src/main/java/co/yml/ycharts/app/presentation/CombinedLineAndBarChartActivity.kt +++ b/androidApp/src/main/java/co/yml/ycharts/app/presentation/CombinedLineAndBarChartActivity.kt @@ -6,32 +6,15 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import co.yml.charts.axis.AxisData +import co.yml.kmm.charts.ChartScreen +import co.yml.ycharts.app.R import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton import co.yml.ycharts.app.ui.theme.YChartsTheme -import co.yml.charts.ui.barchart.models.BarPlotData -import co.yml.charts.ui.barchart.models.BarStyle -import co.yml.charts.ui.combinedchart.CombinedChart -import co.yml.charts.ui.combinedchart.model.CombinedChartData -import co.yml.charts.ui.linechart.model.IntersectionPoint -import co.yml.charts.ui.linechart.model.Line -import co.yml.charts.ui.linechart.model.LinePlotData -import co.yml.charts.ui.linechart.model.LineStyle -import co.yml.charts.ui.linechart.model.SelectionHighlightPoint -import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp -import co.yml.charts.common.components.Legends -import co.yml.charts.common.model.LegendsConfig -import co.yml.charts.common.utils.DataUtils -import co.yml.ycharts.app.R class CombinedLineAndBarChartActivity : ComponentActivity() { @@ -55,13 +38,7 @@ class CombinedLineAndBarChartActivity : ComponentActivity() { .padding(it), contentAlignment = Alignment.TopCenter ) { - LazyColumn(content = { - items(1) { item -> - when (item) { - 0 -> BarWithLineChart() - } - } - }) + ChartScreen(chartType = 1) } } } @@ -69,66 +46,3 @@ class CombinedLineAndBarChartActivity : ComponentActivity() { } } -@Composable -fun BarWithLineChart() { - val maxRange = 100 - val groupBarData = DataUtils.getGroupBarChartData(50, 100, 3) - val yStepSize = 10 - val xAxisData = AxisData.Builder() - .axisStepSize(30.dp) - .bottomPadding(5.dp) - .labelData { index -> index.toString() } - .build() - val yAxisData = AxisData.Builder() - .steps(yStepSize) - .labelAndAxisLinePadding(20.dp) - .axisOffset(20.dp) - .labelData { index -> (index * (maxRange / yStepSize)).toString() } - .build() - val linePlotData = LinePlotData( - lines = listOf( - Line( - DataUtils.getLineChartData(50, maxRange = 100), - lineStyle = LineStyle(color = Color.Blue), - intersectionPoint = IntersectionPoint(), - selectionHighlightPoint = SelectionHighlightPoint(), - selectionHighlightPopUp = SelectionHighlightPopUp() - ), - Line( - DataUtils.getLineChartData(50, maxRange = 100), - lineStyle = LineStyle(color = Color.Black), - intersectionPoint = IntersectionPoint(), - selectionHighlightPoint = SelectionHighlightPoint(), - selectionHighlightPopUp = SelectionHighlightPopUp() - ) - ) - ) - val colorPaletteList = DataUtils.getColorPaletteList(3) - val legendsConfig = LegendsConfig( - legendLabelList = DataUtils.getLegendsLabelData(colorPaletteList), - gridColumnCount = 3 - ) - val barPlotData = BarPlotData( - groupBarList = groupBarData, - barStyle = BarStyle(barWidth = 35.dp), - barColorPaletteList = colorPaletteList - ) - val combinedChartData = CombinedChartData( - combinedPlotDataList = listOf(barPlotData, linePlotData), - xAxisData = xAxisData, - yAxisData = yAxisData - ) - Column( - Modifier - .height(500.dp) - ) { - CombinedChart( - modifier = Modifier - .height(400.dp), - combinedChartData = combinedChartData - ) - Legends( - legendsConfig = legendsConfig - ) - } -} diff --git a/androidApp/src/main/java/co/yml/ycharts/app/presentation/LineChartActivity.kt b/androidApp/src/main/java/co/yml/ycharts/app/presentation/LineChartActivity.kt index f4fd3d7f..c2b040e9 100644 --- a/androidApp/src/main/java/co/yml/ycharts/app/presentation/LineChartActivity.kt +++ b/androidApp/src/main/java/co/yml/ycharts/app/presentation/LineChartActivity.kt @@ -1,36 +1,14 @@ package co.yml.ycharts.app.presentation -import android.graphics.Typeface import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import co.yml.charts.axis.AxisData -import co.yml.charts.common.extensions.formatToSinglePrecision -import co.yml.charts.common.model.Point -import co.yml.charts.ui.linechart.LineChart -import co.yml.charts.ui.linechart.model.GridLines -import co.yml.charts.ui.linechart.model.IntersectionPoint -import co.yml.charts.ui.linechart.model.Line -import co.yml.charts.ui.linechart.model.LineChartData -import co.yml.charts.ui.linechart.model.LinePlotData -import co.yml.charts.ui.linechart.model.LineStyle -import co.yml.charts.ui.linechart.model.LineType -import co.yml.charts.ui.linechart.model.SelectionHighlightPoint -import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp -import co.yml.charts.ui.linechart.model.ShadowUnderLine import co.yml.kmm.charts.ChartScreen import co.yml.ycharts.app.R import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton @@ -59,154 +37,3 @@ class LineChartActivity : ComponentActivity() { } } } - -@Composable -private fun LineGraph1(pointsData: List) { - val steps = 5 - val xAxisData = AxisData.Builder() - .axisStepSize(30.dp) - .steps(pointsData.size - 1) - .labelData { i -> i.toString() } - .labelAndAxisLinePadding(15.dp) - .build() - val yAxisData = AxisData.Builder() - .steps(steps) - .labelAndAxisLinePadding(20.dp) - .labelData { i -> - // Add yMin to get the negative axis values to the scale - val yMin = pointsData.minOf { it.y } - val yMax = pointsData.maxOf { it.y } - val yScale = (yMax - yMin) / steps - ((i * yScale) + yMin).formatToSinglePrecision() - }.build() - val data = LineChartData( - linePlotData = LinePlotData( - lines = listOf( - Line( - dataPoints = pointsData, - LineStyle(), - IntersectionPoint(), - SelectionHighlightPoint(), - ShadowUnderLine(), - SelectionHighlightPopUp() - ) - ) - ), - xAxisData = xAxisData, - yAxisData = yAxisData, - gridLines = GridLines() - ) - LineChart( - modifier = Modifier - .fillMaxWidth() - .height(300.dp), - lineChartData = data - ) -} - -@Composable -private fun LineGraph2(pointsData: List) { - val xAxisData = AxisData.Builder() - .axisStepSize(40.dp) - .steps(pointsData.size - 1) - .labelData { i -> (1900 + i).toString() } - .axisLabelAngle(20f) - .labelAndAxisLinePadding(15.dp) - .axisLabelColor(Color.Blue) - .axisLineColor(Color.DarkGray) - .typeFace(Typeface.DEFAULT_BOLD) - .build() - val yAxisData = AxisData.Builder() - .steps(10) - .labelData { i -> "${(i * 20)}k" } - .labelAndAxisLinePadding(30.dp) - .axisLabelColor(Color.Blue) - .axisLineColor(Color.DarkGray) - .typeFace(Typeface.DEFAULT_BOLD) - .build() - val data = LineChartData( - linePlotData = LinePlotData( - lines = listOf( - Line( - dataPoints = pointsData, - lineStyle = LineStyle(lineType = LineType.Straight(), color = Color.Blue), - intersectionPoint = IntersectionPoint(color = Color.Red), - selectionHighlightPopUp = SelectionHighlightPopUp(popUpLabel = { x, y -> - val xLabel = "x : ${(1900 + x).toInt()} " - val yLabel = "y : ${String.format("%.2f", y)}" - "$xLabel $yLabel" - }) - ) - ) - ), - xAxisData = xAxisData, - yAxisData = yAxisData - ) - LineChart( - modifier = Modifier - .fillMaxWidth() - .height(300.dp), - lineChartData = data - ) -} - -@Composable -private fun LineGraph3(pointsData: List) { - val steps = 10 - val xAxisData = AxisData.Builder() - .axisStepSize(40.dp) - .steps(pointsData.size - 1) - .labelData { i -> i.toString() } - .labelAndAxisLinePadding(15.dp) - .axisLineColor(Color.Red) - .build() - val yAxisData = AxisData.Builder() - .steps(steps) - .labelData { i -> - val yMin = pointsData.minOf { it.y } - val yMax = pointsData.maxOf { it.y } - val yScale = (yMax - yMin) / steps - ((i * yScale) + yMin).formatToSinglePrecision() - } - .axisLineColor(Color.Red) - .labelAndAxisLinePadding(20.dp) - .build() - val data = LineChartData( - linePlotData = LinePlotData( - lines = listOf( - Line( - dataPoints = pointsData, - lineStyle = LineStyle( - lineType = LineType.SmoothCurve(isDotted = true), - color = Color.Green - ), - shadowUnderLine = ShadowUnderLine( - brush = Brush.verticalGradient( - listOf( - Color.Green, - Color.Transparent - ) - ), alpha = 0.3f - ), - selectionHighlightPoint = SelectionHighlightPoint( - color = Color.Green - ), - selectionHighlightPopUp = SelectionHighlightPopUp( - backgroundColor = Color.Black, - backgroundStyle = Stroke(2f), - labelColor = Color.Red, - labelTypeface = Typeface.DEFAULT_BOLD - ) - ) - ) - ), - xAxisData = xAxisData, - yAxisData = yAxisData - ) - LineChart( - modifier = Modifier - .fillMaxWidth() - .height(300.dp), - lineChartData = data - ) -} diff --git a/settings.gradle.kts b/settings.gradle.kts index 339d68af..3023d0f2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,11 +12,8 @@ dependencyResolutionManagement { mavenCentral() } } -rootProject.name = "YCharts" +rootProject.name = "KMM-YCharts" include( ":androidApp", - ":YChartsLib", - ":experiments:chartcontainer", - ":experiments:piechartcontainer", ":KMMYCharts" )