Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Switch to Vico for charts #68

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,12 @@ dependencies {
// Relative time calculation
implementation 'org.ocpsoft.prettytime:prettytime:5.0.0.Final'

// Charts
// Charts: MPAndroidChart
implementation 'com.github.PhilJay:MPAndroidChart:3.1.0'

// Charts: Vico
implementation("com.patrykandpatrick.vico:compose:2.0.0-alpha.19")

// Testing
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
Expand Down
73 changes: 69 additions & 4 deletions app/src/main/java/ch/coredump/watertemp/activities/MapActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package ch.coredump.watertemp.activities

import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Typeface
import android.os.Bundle
import android.util.Log
import android.util.SparseArray
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
import androidx.collection.mutableFloatListOf
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
Expand All @@ -17,7 +19,7 @@ import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
Expand Down Expand Up @@ -65,8 +67,30 @@ import com.mapbox.mapboxsdk.maps.Style
import com.mapbox.mapboxsdk.plugins.annotation.Symbol
import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions
import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost
import com.patrykandpatrick.vico.compose.cartesian.axis.rememberBottomAxis
import com.patrykandpatrick.vico.compose.cartesian.axis.rememberStartAxis
import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLineCartesianLayer
import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLineSpec
import com.patrykandpatrick.vico.compose.cartesian.rememberCartesianChart
import com.patrykandpatrick.vico.compose.cartesian.rememberVicoScrollState
import com.patrykandpatrick.vico.compose.cartesian.rememberVicoZoomState
import com.patrykandpatrick.vico.compose.common.component.rememberShapeComponent
import com.patrykandpatrick.vico.compose.common.component.rememberTextComponent
import com.patrykandpatrick.vico.compose.common.of
import com.patrykandpatrick.vico.compose.common.shader.color
import com.patrykandpatrick.vico.core.cartesian.Zoom
import com.patrykandpatrick.vico.core.cartesian.data.AxisValueOverrider
import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer
import com.patrykandpatrick.vico.core.cartesian.data.lineSeries
import com.patrykandpatrick.vico.core.common.Dimensions
import com.patrykandpatrick.vico.core.common.component.TextComponent
import com.patrykandpatrick.vico.core.common.shader.DynamicShader
import com.patrykandpatrick.vico.core.common.shape.Shape
import com.skydoves.landscapist.glide.GlideImage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.ocpsoft.prettytime.PrettyTime
import retrofit2.Call
import retrofit2.Callback
Expand All @@ -75,6 +99,7 @@ import java.time.Duration
import java.time.Instant
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import kotlin.random.Random

// Marker image names
private const val MARKER_DEFAULT = "marker_default"
Expand Down Expand Up @@ -533,7 +558,6 @@ class MapActivity : ComponentActivity() {

// State: Bottom sheet scaffold
val scaffoldState = rememberBottomSheetScaffoldState(
DrawerState(DrawerValue.Closed),
rememberBottomSheetState(BottomSheetValue.Collapsed)
)

Expand Down Expand Up @@ -786,6 +810,7 @@ class MapActivity : ComponentActivity() {
fun SensorDetails(viewModel: SensorBottomSheetViewModel) {
val sensor by viewModel.sensor.collectAsState()
val measurements by viewModel.measurements.collectAsState()
val chartModelProducer = viewModel.modelProducer.collectAsState(CartesianChartModelProducer.build())

Column() {
sensor?.let { sensor ->
Expand All @@ -807,7 +832,29 @@ class MapActivity : ComponentActivity() {
style = MaterialTheme.typography.body1.plus(TextStyle(fontStyle = Italic))
)
} else {
TemperatureChart(
TemperatureChartNew(
chartModelProducer.value,
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
)
}
}
}
Box(
modifier = Modifier.height(144.dp)
) {
if (measurements == null) {
LoadingDataText()
}
measurements?.let { measurements ->
if (measurements.isEmpty()) {
Text(
stringResource(R.string.chart_no_data),
style = MaterialTheme.typography.body1.plus(TextStyle(fontStyle = Italic))
)
} else {
TemperatureChartOld(
measurements,
modifier = Modifier
.fillMaxWidth()
Expand Down Expand Up @@ -874,7 +921,7 @@ class MapActivity : ComponentActivity() {
}

@Composable
fun TemperatureChart(measurements: List<Measurement>, modifier: Modifier) {
fun TemperatureChartOld(measurements: List<Measurement>, modifier: Modifier) {
AndroidView(
modifier = modifier,
factory = { context -> LineChart(context).apply {
Expand Down Expand Up @@ -926,6 +973,24 @@ class MapActivity : ComponentActivity() {
)
}

@Composable
fun TemperatureChartNew(chartModelProducer: CartesianChartModelProducer, modifier: Modifier) {
CartesianChartHost(
chart = rememberCartesianChart(
rememberLineCartesianLayer(
lines = listOf(rememberLineSpec(DynamicShader.color(Color(0xffa485e0)))),
axisValueOverrider = AxisValueOverrider.adaptiveYValues(yFraction = 1f, round = true),
),
startAxis = rememberStartAxis(),
bottomAxis = rememberBottomAxis(guideline = null),
),
modelProducer = chartModelProducer,
modifier = modifier,
scrollState = rememberVicoScrollState(),
zoomState = rememberVicoZoomState(initialZoom = remember { Zoom.Content }, zoomEnabled = true),
)
}

@Composable
fun LoadingDataText() {
Text(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package ch.coredump.watertemp.ui.viewmodels

import android.util.Log
import androidx.compose.runtime.remember
import androidx.lifecycle.ViewModel
import ch.coredump.watertemp.rest.models.ApiMeasurement
import ch.coredump.watertemp.rest.models.ApiSensor
import ch.coredump.watertemp.rest.models.ApiSensorDetails
import ch.coredump.watertemp.rest.models.ApiSponsor
import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer
import com.patrykandpatrick.vico.core.cartesian.data.lineSeries
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import java.time.ZonedDateTime

data class Measurement(
Expand Down Expand Up @@ -69,6 +76,24 @@ class SensorBottomSheetViewModel : ViewModel() {
private val _showBottomSheet = MutableStateFlow(false)
val showBottomSheet = _showBottomSheet.asStateFlow()

// Chart model producer
val modelProducer = measurements.map {
val producer = CartesianChartModelProducer.build()
Log.i("ViewModel", "run")
it?.let {
Log.i("ViewModel", "Updating value")
producer.tryRunTransaction {
lineSeries {
series(
it.map { measurement -> measurement.timestamp.toInstant().toEpochMilli() },
it.map { measurement -> measurement.temperature },
)
}
}
}
producer
}

/**
* Overwrite the current sensor and reset associated measurements.
*/
Expand Down