Files
luna-tracker/app/src/main/java/it/danieleverducci/lunatracker/fragments/GrowthStatsFragment.kt
Maximilian von Heyden 2355dd4390 Add sleep tracking, statistics module and backup features
Features:
- Sleep tracking with timer and manual duration input
- Statistics module with 5 tabs (daily summary, feeding, diapers, sleep, growth)
- Export/Import backup functionality in settings
- Complete German, French and Italian translations
2026-01-12 08:27:03 +01:00

140 lines
5.6 KiB
Kotlin

package it.danieleverducci.lunatracker.fragments
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.fragment.app.Fragment
import it.danieleverducci.lunatracker.R
import it.danieleverducci.lunatracker.StatisticsActivity
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class GrowthStatsFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_growth_stats, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateUI(view)
}
private fun updateUI(view: View) {
val activity = activity as? StatisticsActivity ?: return
val calculator = activity.getCalculator()
val weightHistory = calculator.getWeightHistory()
val noDataMessage = view.findViewById<TextView>(R.id.no_data_message)
if (weightHistory.isEmpty()) {
noDataMessage.visibility = View.VISIBLE
view.findViewById<View>(R.id.chart_container).visibility = View.GONE
view.findViewById<View>(R.id.chart_labels).visibility = View.GONE
view.findViewById<TextView>(R.id.current_weight).visibility = View.GONE
view.findViewById<TextView>(R.id.weight_gain_week).visibility = View.GONE
view.findViewById<TextView>(R.id.weight_gain_month).visibility = View.GONE
return
}
noDataMessage.visibility = View.GONE
// Draw weight chart (line chart approximated with bars)
val chartContainer = view.findViewById<LinearLayout>(R.id.chart_container)
val chartLabels = view.findViewById<LinearLayout>(R.id.chart_labels)
chartContainer.removeAllViews()
chartLabels.removeAllViews()
val recentWeights = weightHistory.takeLast(10) // Show last 10 measurements
val minWeight = recentWeights.minOfOrNull { it.weightGrams } ?: 0
val maxWeight = recentWeights.maxOfOrNull { it.weightGrams } ?: 1
val weightRange = (maxWeight - minWeight).coerceAtLeast(100) // At least 100g range
val dateFormat = SimpleDateFormat("d/M", Locale.getDefault())
for (point in recentWeights) {
// Bar container
val barContainer = LinearLayout(requireContext()).apply {
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1f)
orientation = LinearLayout.VERTICAL
gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
setPadding(4, 0, 4, 0)
}
// Calculate relative height (showing weight above minimum)
val relativeWeight = point.weightGrams - minWeight + (weightRange * 0.1).toInt()
val barHeight = (relativeWeight.toFloat() / (weightRange * 1.2) * 100).toInt()
val bar = View(requireContext()).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
(barHeight * resources.displayMetrics.density).toInt()
)
setBackgroundColor(resources.getColor(R.color.accent, null))
}
barContainer.addView(bar)
// Weight value on top
val weightLabel = TextView(requireContext()).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
val kg = point.weightGrams / 1000f
text = String.format(Locale.getDefault(), "%.1f", kg)
textSize = 8f
gravity = Gravity.CENTER
}
barContainer.addView(weightLabel, 0)
chartContainer.addView(barContainer)
// Date label
val label = TextView(requireContext()).apply {
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
text = dateFormat.format(Date(point.time * 1000))
textSize = 10f
gravity = Gravity.CENTER
}
chartLabels.addView(label)
}
// Current weight
val currentWeight = weightHistory.lastOrNull()
if (currentWeight != null) {
val kg = currentWeight.weightGrams / 1000f
view.findViewById<TextView>(R.id.current_weight).text =
getString(R.string.stats_current_weight, String.format(Locale.getDefault(), "%.2f kg", kg))
}
// Weight gain calculations
val gainWeek = calculator.getWeightGainForDays(7)
val gainMonth = calculator.getWeightGainForDays(30)
val weekView = view.findViewById<TextView>(R.id.weight_gain_week)
val monthView = view.findViewById<TextView>(R.id.weight_gain_month)
if (gainWeek != null) {
weekView.text = getString(R.string.stats_weight_gain_week, gainWeek)
weekView.visibility = View.VISIBLE
} else {
weekView.visibility = View.GONE
}
if (gainMonth != null) {
monthView.text = getString(R.string.stats_weight_gain_month, gainMonth)
monthView.visibility = View.VISIBLE
} else {
monthView.visibility = View.GONE
}
}
}