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
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user