mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-04-23 03:12:14 +00:00
feat: Add average graph and estimation on goal completion (#153)
This commit is contained in:
parent
cd537470ba
commit
4afa432a29
@ -26,12 +26,14 @@ import com.dzeio.openhealth.core.BaseFragment
|
||||
import com.dzeio.openhealth.data.weight.Weight
|
||||
import com.dzeio.openhealth.databinding.FragmentListWeightBinding
|
||||
import com.dzeio.openhealth.units.Units
|
||||
import com.dzeio.openhealth.utils.ChartUtils
|
||||
import com.dzeio.openhealth.utils.PermissionsUtils
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.abs
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ListWeightFragment :
|
||||
@ -131,16 +133,11 @@ class ListWeightFragment :
|
||||
}
|
||||
|
||||
viewModel.goalWeight.observe(viewLifecycleOwner) {
|
||||
binding.chart.yAxis.apply {
|
||||
clearLines()
|
||||
if (it != null) {
|
||||
addLine(
|
||||
it,
|
||||
Line(true, Paint(linePaint).apply { strokeWidth = 4f })
|
||||
)
|
||||
}
|
||||
binding.chart.refresh()
|
||||
val list = viewModel.weights.value
|
||||
if (list == null) {
|
||||
return@observe
|
||||
}
|
||||
updateGraph(list, it)
|
||||
}
|
||||
|
||||
viewModel.weights.observe(viewLifecycleOwner) { list ->
|
||||
@ -154,42 +151,40 @@ class ListWeightFragment :
|
||||
unit.unit,
|
||||
unit.formatToString(it.weight)
|
||||
),
|
||||
it.formatTimestamp(),
|
||||
getString(
|
||||
R.string.weight_item,
|
||||
it.formatTimestamp(),
|
||||
it.bmi ?: 0f,
|
||||
it.totalBodyWater ?: 0f,
|
||||
it.muscles ?: 0f,
|
||||
it.leanBodyMass ?: 0f,
|
||||
it.bodyFat ?: 0f,
|
||||
it.boneMass ?: 0f,
|
||||
it.visceralFat ?: 0f,
|
||||
it.basalMetabolicRate ?: 0,
|
||||
it.totalDailyEnergyExpendure ?: 0
|
||||
),
|
||||
icon = R.drawable.ic_outline_edit_24
|
||||
)
|
||||
}.reversed()
|
||||
)
|
||||
updateGraph(list)
|
||||
updateGraph(list, viewModel.goalWeight.value)
|
||||
}
|
||||
}
|
||||
|
||||
val chart = binding.chart
|
||||
|
||||
val serie = LineSerie(chart).apply {
|
||||
linePaint.color = MaterialColors.getColor(
|
||||
requireView(),
|
||||
com.google.android.material.R.attr.colorPrimary
|
||||
)
|
||||
textPaint.color = MaterialColors.getColor(
|
||||
requireView(),
|
||||
com.google.android.material.R.attr.colorOnPrimary
|
||||
)
|
||||
}
|
||||
|
||||
chart.apply {
|
||||
series = arrayListOf(serie)
|
||||
series = arrayListOf(LineSerie(chart), LineSerie(chart))
|
||||
ChartUtils.materielTheme(this, requireView())
|
||||
(series[1] as LineSerie).linePaint.color = MaterialColors.getColor(
|
||||
requireView(),
|
||||
com.google.android.material.R.attr.colorSecondary
|
||||
)
|
||||
animator.enabled = false
|
||||
|
||||
yAxis.apply {
|
||||
setYMin(null)
|
||||
textLabel.color = MaterialColors.getColor(
|
||||
requireView(),
|
||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||
)
|
||||
linePaint.color = MaterialColors.getColor(
|
||||
requireView(),
|
||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||
)
|
||||
|
||||
onValueFormat = { value -> "${value.toInt()}" }
|
||||
}
|
||||
@ -198,10 +193,6 @@ class ListWeightFragment :
|
||||
// 7 day history
|
||||
dataWidth = 6.048e+8 * 4
|
||||
scrollEnabled = true
|
||||
textPaint.color = MaterialColors.getColor(
|
||||
requireView(),
|
||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||
)
|
||||
textPaint.textSize = 32f
|
||||
onValueFormat = onValueFormat@{
|
||||
val formatter = DateFormat.getDateTimeInstance(
|
||||
@ -232,12 +223,65 @@ class ListWeightFragment :
|
||||
requireActivity().removeMenuProvider(menuProvider)
|
||||
}
|
||||
|
||||
private fun updateGraph(list: List<Weight>) {
|
||||
private fun updateGraph(list: List<Weight>, goal: Float? = null) {
|
||||
val chart = binding.chart
|
||||
val serie = chart.series[0] as LineSerie
|
||||
val avSerie = chart.series[1] as LineSerie
|
||||
|
||||
val entries: ArrayList<Entry> = arrayListOf()
|
||||
|
||||
val previous = 5
|
||||
val next = 5
|
||||
val averageList = arrayListOf<Entry>()
|
||||
for (i in list.indices) {
|
||||
val sub = list.subList((i - previous).coerceAtLeast(0), (i + next).coerceAtMost(list.size - 1))
|
||||
val average = (sub.map { it.weight }.reduceOrNull { acc, it -> it + acc } ?: 0f) / sub.size
|
||||
val it = list[i]
|
||||
averageList.add(
|
||||
Entry(
|
||||
it.timestamp.toDouble(),
|
||||
average
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val direction = averageList.last().y - averageList[averageList.size - 2].y
|
||||
|
||||
chart.yAxis.clearLines()
|
||||
if (goal != null) {
|
||||
chart.yAxis.apply {
|
||||
addLine(
|
||||
goal,
|
||||
Line(true, Paint(linePaint).apply { strokeWidth = 4f })
|
||||
)
|
||||
}
|
||||
|
||||
val dt = averageList.last().x - averageList[list.size - previous].x
|
||||
val timeUntilGoal = ((list.last().weight - goal) / abs(direction)).toLong() * dt
|
||||
|
||||
binding.goalText.text = getString(
|
||||
R.string.days_until_goal_is_achieved,
|
||||
(timeUntilGoal / 1000 / 60 / 60 / 24).toInt()
|
||||
)
|
||||
averageList.add(
|
||||
Entry(
|
||||
(list.last().timestamp + timeUntilGoal),
|
||||
goal
|
||||
)
|
||||
)
|
||||
binding.goalText.visibility = View.VISIBLE
|
||||
} else {
|
||||
averageList.add(
|
||||
Entry(
|
||||
averageList.last().x + chart.xAxis.dataWidth!!,
|
||||
averageList.last().y + direction * 7 * 4
|
||||
)
|
||||
)
|
||||
binding.goalText.visibility = View.GONE
|
||||
}
|
||||
|
||||
avSerie.entries = averageList
|
||||
|
||||
list.forEach {
|
||||
entries.add(
|
||||
Entry(
|
||||
|
@ -64,6 +64,7 @@
|
||||
<string name="import_complete">Import réussi, redémarrage de l\'application</string>
|
||||
<string name="export_complete">Export Réussi!</string>
|
||||
<string name="import_export">Importer/Exporter</string>
|
||||
<string name="days_until_goal_is_achieved">Jours avant d\'atteindre son but: ~%1$d jours</string>
|
||||
<string name="weight_item">Date: %1$s\nBMI: %2$.2f\nEau corporelle: %3$.2f\nMuscles: %4$.2f\nMasse maigre: %5$.2f\nMasse grasse: %6$.2f\nMasse osseuse: %7$.2f\nGraisse viscérale: %8$.2f\nMétabolisme basal: %9$d\nDépense énergétique quotidienne totale: %10$d</string>
|
||||
<string-array name="activity_levels">
|
||||
<item>Cloué au lit</item>
|
||||
|
@ -77,6 +77,7 @@
|
||||
<string name="import_complete">Import successful, Restarting the application</string>
|
||||
<string name="export_complete">Export successful!</string>
|
||||
<string name="import_export">Import/Export</string>
|
||||
<string name="days_until_goal_is_achieved">Days until goal is achieved: ~%1$d days</string>
|
||||
<string name="weight_item">Date: %1$s\nBMI: %2$.2f\nBody water: %3$.2f\nMuscles: %4$.2f\nLean body mass: %5$.2f\nBody fat: %6$.2f\nBone mass: %7$.2f\nVisceral fat: %8$.2f\nBasal metabolic rate: %9$d\nTotal daily energy expendure: %10$d\n</string>
|
||||
<string-array name="activity_levels">
|
||||
<item>Bedridden</item>
|
||||
|
Loading…
x
Reference in New Issue
Block a user