Files
luna-tracker/app/src/main/java/it/danieleverducci/lunatracker/StatisticsActivity.kt
Maximilian von Heyden b6110c2cbb 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-08 09:33:36 +01:00

208 lines
7.7 KiB
Kotlin

package it.danieleverducci.lunatracker
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.Spinner
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import com.thegrizzlylabs.sardineandroid.impl.SardineException
import it.danieleverducci.lunatracker.entities.Logbook
import it.danieleverducci.lunatracker.entities.LunaEvent
import it.danieleverducci.lunatracker.fragments.DailySummaryFragment
import it.danieleverducci.lunatracker.fragments.DiaperStatsFragment
import it.danieleverducci.lunatracker.fragments.FeedingStatsFragment
import it.danieleverducci.lunatracker.fragments.GrowthStatsFragment
import it.danieleverducci.lunatracker.fragments.SleepStatsFragment
import it.danieleverducci.lunatracker.repository.FileLogbookRepository
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
import it.danieleverducci.lunatracker.repository.LogbookLoadedListener
import it.danieleverducci.lunatracker.repository.LogbookRepository
import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
import okio.IOException
import org.json.JSONException
import utils.StatisticsCalculator
class StatisticsActivity : AppCompatActivity() {
companion object {
const val TAG = "StatisticsActivity"
const val EXTRA_LOGBOOK_NAME = "logbook_name"
}
private lateinit var viewPager: ViewPager2
private lateinit var tabLayout: TabLayout
private lateinit var periodSpinner: Spinner
private var events: List<LunaEvent> = emptyList()
private var selectedPeriod: Int = 7 // Default 7 days
private var logbookName: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_statistics)
// Back button
findViewById<ImageView>(R.id.button_back).setOnClickListener {
finish()
}
// Title with logbook name
logbookName = intent.getStringExtra(EXTRA_LOGBOOK_NAME) ?: ""
if (logbookName.isNotEmpty()) {
findViewById<TextView>(R.id.statistics_title).text =
"${getString(R.string.statistics_title)} - $logbookName"
}
// Period spinner
periodSpinner = findViewById(R.id.period_spinner)
val periods = arrayOf(
getString(R.string.stats_period_7days),
getString(R.string.stats_period_14days),
getString(R.string.stats_period_30days)
)
val periodAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, periods)
periodAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
periodSpinner.adapter = periodAdapter
periodSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
selectedPeriod = when (position) {
0 -> 7
1 -> 14
2 -> 30
else -> 7
}
notifyFragmentsOfPeriodChange()
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
// ViewPager and TabLayout
viewPager = findViewById(R.id.view_pager)
tabLayout = findViewById(R.id.tab_layout)
// Load events
loadEvents()
}
private fun loadEvents() {
val settingsRepo = LocalSettingsRepository(this)
val repository: LogbookRepository = when (settingsRepo.loadDataRepository()) {
LocalSettingsRepository.DATA_REPO.WEBDAV -> {
val credentials = settingsRepo.loadWebdavCredentials()
if (credentials != null) {
WebDAVLogbookRepository(credentials[0], credentials[1], credentials[2])
} else {
FileLogbookRepository()
}
}
LocalSettingsRepository.DATA_REPO.LOCAL_FILE -> FileLogbookRepository()
}
repository.loadLogbook(this, logbookName, object : LogbookLoadedListener {
override fun onLogbookLoaded(logbook: Logbook) {
runOnUiThread {
events = logbook.logs
setupViewPager()
}
}
override fun onIOError(error: IOException) {
Log.e(TAG, "IO error loading logbook", error)
runOnUiThread {
events = emptyList()
setupViewPager()
}
}
override fun onWebDAVError(error: SardineException) {
Log.e(TAG, "WebDAV error loading logbook", error)
runOnUiThread {
events = emptyList()
setupViewPager()
}
}
override fun onJSONError(error: JSONException) {
Log.e(TAG, "JSON error loading logbook", error)
runOnUiThread {
events = emptyList()
setupViewPager()
}
}
override fun onError(error: Exception) {
Log.e(TAG, "Error loading logbook", error)
runOnUiThread {
events = emptyList()
setupViewPager()
}
}
})
}
private fun setupViewPager() {
val adapter = StatisticsPagerAdapter(this)
viewPager.adapter = adapter
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = when (position) {
0 -> getString(R.string.stats_tab_today)
1 -> getString(R.string.stats_tab_feeding)
2 -> getString(R.string.stats_tab_diapers)
3 -> getString(R.string.stats_tab_sleep)
4 -> getString(R.string.stats_tab_growth)
else -> ""
}
}.attach()
}
private fun notifyFragmentsOfPeriodChange() {
if (events.isEmpty()) return
// Force fragment refresh by recreating adapter
val currentItem = viewPager.currentItem
viewPager.adapter = StatisticsPagerAdapter(this)
viewPager.setCurrentItem(currentItem, false)
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = when (position) {
0 -> getString(R.string.stats_tab_today)
1 -> getString(R.string.stats_tab_feeding)
2 -> getString(R.string.stats_tab_diapers)
3 -> getString(R.string.stats_tab_sleep)
4 -> getString(R.string.stats_tab_growth)
else -> ""
}
}.attach()
}
fun getEvents(): List<LunaEvent> = events
fun getSelectedPeriod(): Int = selectedPeriod
fun getCalculator(): StatisticsCalculator = StatisticsCalculator(events)
private inner class StatisticsPagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
override fun getItemCount(): Int = 5
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> DailySummaryFragment()
1 -> FeedingStatsFragment()
2 -> DiaperStatsFragment()
3 -> SleepStatsFragment()
4 -> GrowthStatsFragment()
else -> DailySummaryFragment()
}
}
}
}