xref: /aosp_15_r20/frameworks/base/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
<lambda>null2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settingslib.spa.widget.chart
18 
19 import android.view.ViewGroup
20 import android.widget.LinearLayout
21 import androidx.compose.animation.Crossfade
22 import androidx.compose.foundation.layout.Arrangement
23 import androidx.compose.foundation.layout.Column
24 import androidx.compose.foundation.layout.fillMaxWidth
25 import androidx.compose.foundation.layout.height
26 import androidx.compose.foundation.layout.padding
27 import androidx.compose.foundation.layout.wrapContentHeight
28 import androidx.compose.foundation.layout.wrapContentSize
29 import androidx.compose.material3.ColorScheme
30 import androidx.compose.material3.MaterialTheme
31 import androidx.compose.runtime.Composable
32 import androidx.compose.ui.Alignment
33 import androidx.compose.ui.Modifier
34 import androidx.compose.ui.graphics.Color
35 import androidx.compose.ui.graphics.toArgb
36 import androidx.compose.ui.unit.dp
37 import androidx.compose.ui.viewinterop.AndroidView
38 import com.android.settingslib.spa.framework.theme.divider
39 import com.github.mikephil.charting.charts.BarChart
40 import com.github.mikephil.charting.components.XAxis
41 import com.github.mikephil.charting.components.YAxis
42 import com.github.mikephil.charting.components.YAxis.AxisDependency
43 import com.github.mikephil.charting.data.BarData
44 import com.github.mikephil.charting.data.BarDataSet
45 import com.github.mikephil.charting.data.BarEntry
46 import com.github.mikephil.charting.formatter.IAxisValueFormatter
47 
48 /**
49  * The chart settings model for [BarChart].
50  */
51 interface BarChartModel {
52     /**
53      * The chart data list for [BarChart].
54      */
55     val chartDataList: List<BarChartData>
56 
57     /**
58      * The color list for [BarChart].
59      */
60     val colors: List<Color>
61 
62     /**
63      * The label text formatter for x value.
64      */
65     val xValueFormatter: IAxisValueFormatter?
66         get() = null
67 
68     /**
69      * The label text formatter for y value.
70      */
71     val yValueFormatter: IAxisValueFormatter?
72         get() = null
73 
74     /**
75      * The minimum value for y-axis.
76      */
77     val yAxisMinValue: Float
78         get() = 0f
79 
80     /**
81      * The maximum value for y-axis.
82      */
83     val yAxisMaxValue: Float
84         get() = 1f
85 
86     /**
87      * The label count for y-axis.
88      */
89     val yAxisLabelCount: Int
90         get() = 3
91 
92     /** If set to true, touch gestures are enabled on the [BarChart]. */
93     val enableBarchartTouch: Boolean
94         get() = true
95 
96     /** The renderer provider for x-axis. */
97     val xAxisRendererProvider: XAxisRendererProvider?
98         get() = null
99 }
100 
101 data class BarChartData(
102     var x: Float,
103     var y: List<Float>,
104 )
105 
106 @Composable
BarChartnull107 fun BarChart(barChartModel: BarChartModel) {
108     Column(
109         modifier = Modifier
110             .fillMaxWidth()
111             .wrapContentHeight(),
112         horizontalAlignment = Alignment.CenterHorizontally,
113         verticalArrangement = Arrangement.Center
114     ) {
115         Column(
116             modifier = Modifier
117                 .padding(16.dp)
118                 .height(170.dp),
119             horizontalAlignment = Alignment.CenterHorizontally,
120             verticalArrangement = Arrangement.Center
121         ) {
122             val colorScheme = MaterialTheme.colorScheme
123             val labelTextColor = colorScheme.onSurfaceVariant.toArgb()
124             val labelTextSize = MaterialTheme.typography.bodyMedium.fontSize.value
125             Crossfade(
126                 targetState = barChartModel.chartDataList,
127                 label = "chartDataList",
128             ) { barChartData ->
129                 AndroidView(
130                     factory = { context ->
131                         BarChart(context).apply {
132                             layoutParams = LinearLayout.LayoutParams(
133                                 ViewGroup.LayoutParams.MATCH_PARENT,
134                                 ViewGroup.LayoutParams.MATCH_PARENT,
135                             )
136                             description.isEnabled = false
137                             legend.isEnabled = false
138                             extraBottomOffset = 4f
139                             setScaleEnabled(false)
140                             setTouchEnabled(barChartModel.enableBarchartTouch)
141 
142                             xAxis.apply {
143                                 position = XAxis.XAxisPosition.BOTTOM
144                                 setDrawAxisLine(false)
145                                 setDrawGridLines(false)
146                                 textColor = labelTextColor
147                                 textSize = labelTextSize
148                                 valueFormatter = barChartModel.xValueFormatter
149                                 yOffset = 10f
150                             }
151 
152                             barChartModel.xAxisRendererProvider?.let {
153                                 setXAxisRenderer(
154                                     it.provideXAxisRenderer(
155                                         getViewPortHandler(),
156                                         getXAxis(),
157                                         getTransformer(YAxis.AxisDependency.LEFT)
158                                     )
159                                 )
160                             }
161 
162                             axisLeft.apply {
163                                 axisMaximum = barChartModel.yAxisMaxValue
164                                 axisMinimum = barChartModel.yAxisMinValue
165                                 isEnabled = false
166                             }
167 
168                             axisRight.apply {
169                                 axisMaximum = barChartModel.yAxisMaxValue
170                                 axisMinimum = barChartModel.yAxisMinValue
171                                 gridColor = colorScheme.divider.toArgb()
172                                 setDrawAxisLine(false)
173                                 setLabelCount(barChartModel.yAxisLabelCount, true)
174                                 textColor = labelTextColor
175                                 textSize = labelTextSize
176                                 valueFormatter = barChartModel.yValueFormatter
177                                 xOffset = 10f
178                             }
179                         }
180                     },
181                     modifier = Modifier
182                         .wrapContentSize()
183                         .padding(4.dp),
184                     update = { barChart ->
185                         updateBarChartWithData(
186                             chart = barChart,
187                             data = barChartData,
188                             colorList = barChartModel.colors,
189                             colorScheme = colorScheme,
190                         )
191                     }
192                 )
193             }
194         }
195     }
196 }
197 
updateBarChartWithDatanull198 private fun updateBarChartWithData(
199     chart: BarChart,
200     data: List<BarChartData>,
201     colorList: List<Color>,
202     colorScheme: ColorScheme
203 ) {
204     val entries = data.map { item ->
205         BarEntry(item.x, item.y.toFloatArray())
206     }
207 
208     val ds = BarDataSet(entries, "").apply {
209         colors = colorList.map(Color::toArgb)
210         setDrawValues(false)
211         isHighlightEnabled = true
212         highLightColor = colorScheme.primary.toArgb()
213         highLightAlpha = 255
214     }
215     // TODO: Sets round corners for bars.
216 
217     chart.data = BarData(ds)
218     chart.invalidate()
219 }
220