6 Commits

Author SHA1 Message Date
1763a9cfd0 MainActivity: generate dynamic menu from last two weeks 2026-02-19 11:05:00 +01:00
3779e7e34d LunaEvent: rework sleep event
Make the UI more flexible and
slightly easier to understand.
2026-02-19 11:05:00 +01:00
9a1f489b8b MainActivity: rename datepicker 2026-02-19 11:05:00 +01:00
dd02bbce65 layout: replace menu icon with utf8 character
The three dot menu icosn looks odd when stretched
due to the dynamic menu feature. Thus replace it
with the hamburger menu character that looks better
when scaled.
2026-02-19 11:05:00 +01:00
e6ac11d335 MainActivity: sort events before saving
Also replace notifyItemInserted since it does not
call the adapter to redraw the row striping when
a new event is added.
2026-02-19 11:05:00 +01:00
e1e8832f51 StatisticsActivity: rework all statistics
Improve the overall code.
2026-02-19 11:04:57 +01:00

View File

@@ -181,14 +181,11 @@ class StatisticsActivity : AppCompatActivity() {
val values = ArrayList<BarEntry>() val values = ArrayList<BarEntry>()
val stack = ArrayList(List(state.endSpan - state.startSpan + 1) { IntArray(24 * 60 * 60 / SLEEP_PATTERN_GRANULARITY) }) val stack = ArrayList(List(state.endSpan - state.startSpan + 1) { IntArray(24 * 60 * 60 / SLEEP_PATTERN_GRANULARITY) })
Log.d(TAG, "stack.size: ${stack.size}, array.size: ${stack[0].size}, dayCounter.daysWithData.size: ${state.dayCounter.daysWithData.size}")
fun stackValuePattern(index: Int, spanBegin: Long, spanEnd: Long, begin: Long, end: Long) { fun stackValuePattern(index: Int, spanBegin: Long, spanEnd: Long, begin: Long, end: Long) {
val beginDays = unixToDays(begin) val beginDays = unixToDays(begin)
val endDays = unixToDays(end) val endDays = unixToDays(end)
var mid = begin var mid = begin
//Log.d(TAG, "stackValuePattern: ${beginDays}..${endDays}")
for (i in beginDays..endDays) { for (i in beginDays..endDays) {
// i is the days/weeks/months since unix epoch // i is the days/weeks/months since unix epoch
val dayBegin = daysToUnix(i) val dayBegin = daysToUnix(i)
@@ -201,7 +198,6 @@ class StatisticsActivity : AppCompatActivity() {
assert(sleepBegin <= sleepEnd) assert(sleepBegin <= sleepEnd)
val iBegin = (sleepBegin - dayBegin) / SLEEP_PATTERN_GRANULARITY val iBegin = (sleepBegin - dayBegin) / SLEEP_PATTERN_GRANULARITY
val iEnd = iBegin + (sleepEnd - sleepBegin) / SLEEP_PATTERN_GRANULARITY val iEnd = iBegin + (sleepEnd - sleepBegin) / SLEEP_PATTERN_GRANULARITY
//Log.d(TAG, "index: $index, iBegin: $iBegin, iEnd: $iEnd, dayBegin: ${Date(dayBegin * 1000)}, dayEnd: ${Date(dayEnd * 1000)}, sleepBegin: ${Date(sleepBegin * 1000)}, sleepEnd: ${Date(sleepEnd * 1000)}")
for (j in iBegin..<iEnd) { for (j in iBegin..<iEnd) {
stack[index][j.toInt()] += 1 stack[index][j.toInt()] += 1
} }
@@ -219,17 +215,14 @@ class StatisticsActivity : AppCompatActivity() {
val endIndex = unixToSpan(endUnix) val endIndex = unixToSpan(endUnix)
var mid = startUnix var mid = startUnix
//Log.d(TAG, "startUnix: ${Date(startUnix * 1000)}, endUnix: ${Date(endUnix * 1000)}, begIndex: $begIndex, endIndex: $endIndex (index diff: ${endIndex - begIndex})")
for (i in begIndex..endIndex) { for (i in begIndex..endIndex) {
// i is the days/weeks/months since unix epoch // i is the days/weeks/months since unix epoch
val spanBegin = spanToUnix(i) val spanBegin = spanToUnix(i)
val spanEnd = spanToUnix(i + 1) val spanEnd = spanToUnix(i + 1)
//Log.d(TAG, "mid: ${Date(mid * 1000)}, spanBegin: ${Date(spanBegin * 1000)}, spanEnd: ${Date(spanEnd * 1000)}, beginUnix: ${Date(startUnix * 1000)} endUnix: ${Date(endUnix * 1000)}")
val sleepBegin = max(mid, spanBegin) val sleepBegin = max(mid, spanBegin)
val sleepEnd = min(endUnix, spanEnd) val sleepEnd = min(endUnix, spanEnd)
val index = i - state.startSpan val index = i - state.startSpan
val duration = sleepEnd - sleepBegin val duration = sleepEnd - sleepBegin
//Log.d(TAG, "[$index] sleepBegin: ${Date(sleepBegin * 1000)}, sleepEnd: ${Date(sleepEnd * 1000)}, ${DateUtils.formatTimeDuration(this, duration)}")
state.dayCounter.setDaysWithData(sleepBegin, sleepEnd) state.dayCounter.setDaysWithData(sleepBegin, sleepEnd)
stackValuePattern(index, spanBegin, spanEnd, sleepBegin, sleepEnd) stackValuePattern(index, spanBegin, spanEnd, sleepBegin, sleepEnd)
@@ -256,7 +249,6 @@ class StatisticsActivity : AppCompatActivity() {
for ((index, dayArray) in stack.withIndex()) { for ((index, dayArray) in stack.withIndex()) {
val daysWithData = state.dayCounter.countDaysWithData(spanToUnix(state.startSpan + index), spanToUnix(state.startSpan + index + 1)) val daysWithData = state.dayCounter.countDaysWithData(spanToUnix(state.startSpan + index), spanToUnix(state.startSpan + index + 1))
//Log.d(TAG, "index: $index: daysWithData: $daysWithData, dayArray: ${dayArray.joinToString { it.toString() }}")
val vals = ArrayList<Float>() val vals = ArrayList<Float>()
var prevIndex = -1 // time slot index var prevIndex = -1 // time slot index
@@ -278,14 +270,10 @@ class StatisticsActivity : AppCompatActivity() {
allColors.add(mapColor(prevValue, daysWithData)) allColors.add(mapColor(prevValue, daysWithData))
} }
//Log.d(TAG, "Range $index, vals: ${vals.joinToString { it.toInt().toString() }}") //, allColors: ${allColors.joinToString { it.toString() }}")
assert(values.size == index) assert(values.size == index)
values.add(BarEntry(values.size.toFloat(), vals.toFloatArray())) values.add(BarEntry(values.size.toFloat(), vals.toFloatArray()))
} }
//Log.d(TAG, "daysWithData: ${state.dayCounter.daysWithData.joinToString()}")
barChart.setOnChartValueSelectedListener(object : OnChartValueSelectedListener { barChart.setOnChartValueSelectedListener(object : OnChartValueSelectedListener {
override fun onValueSelected(e: Entry?, h: Highlight?) { override fun onValueSelected(e: Entry?, h: Highlight?) {
if (e == null || h == null) { if (e == null || h == null) {
@@ -356,8 +344,6 @@ class StatisticsActivity : AppCompatActivity() {
set1.isHighlightEnabled = true set1.isHighlightEnabled = true
set1.setDrawIcons(false) set1.setDrawIcons(false)
//Log.d(TAG, "showSleepPatternBarGraphSlotted; barChart.xAxis.labelCount: ${barChart.xAxis.labelCount}, barChart.visibleXRange: ${barChart.visibleXRange}, barChart.xAxis.isCenterAxisLabelsEnabled: ${barChart.xAxis.isCenterAxisLabelsEnabled}, barChart.isAutoScaleMinMaxEnabled: ${barChart.isAutoScaleMinMaxEnabled}, barChart.isScaleXEnabled: ${barChart.isScaleXEnabled}, barChart.isScaleYEnabled: ${barChart.isScaleYEnabled}")
val data = BarData(set1) val data = BarData(set1)
data.setValueTextSize(12f) data.setValueTextSize(12f)
@@ -411,17 +397,14 @@ class StatisticsActivity : AppCompatActivity() {
val endIndex = unixToSpan(endUnix) val endIndex = unixToSpan(endUnix)
var mid = startUnix var mid = startUnix
//Log.d(TAG, "startUnix: ${Date(startUnix * 1000)}, endUnix: ${Date(endUnix * 1000)}, begIndex: $begIndex, endIndex: $endIndex (index diff: ${endIndex - begIndex})")
for (i in begIndex..endIndex) { for (i in begIndex..endIndex) {
// i is the days/weeks/months since unix epoch // i is the days/weeks/months since unix epoch
val spanBegin = spanToUnix(i) val spanBegin = spanToUnix(i)
val spanEnd = spanToUnix(i + 1) val spanEnd = spanToUnix(i + 1)
//Log.d(TAG, "mid: ${Date(mid * 1000)}, spanBegin: ${Date(spanBegin * 1000)}, spanEnd: ${Date(spanEnd * 1000)}, beginUnix: ${Date(startUnix * 1000)} endUnix: ${Date(endUnix * 1000)}")
val sleepBegin = max(mid, spanBegin) val sleepBegin = max(mid, spanBegin)
val sleepEnd = min(endUnix, spanEnd) val sleepEnd = min(endUnix, spanEnd)
val index = i - state.startSpan val index = i - state.startSpan
val duration = sleepEnd - sleepBegin val duration = sleepEnd - sleepBegin
//Log.d(TAG, "[$index] sleepBegin: ${Date(sleepBegin * 1000)}, sleepEnd: ${Date(sleepEnd * 1000)}, ${DateUtils.formatTimeDuration(this, duration)}")
state.dayCounter.setDaysWithData(sleepBegin, sleepEnd) state.dayCounter.setDaysWithData(sleepBegin, sleepEnd)
stackValuePattern(index, spanBegin, spanEnd, sleepBegin, sleepEnd) stackValuePattern(index, spanBegin, spanEnd, sleepBegin, sleepEnd)
@@ -474,13 +457,10 @@ class StatisticsActivity : AppCompatActivity() {
override fun onNothingSelected() {} override fun onNothingSelected() {}
}) })
//Log.d(TAG, "showSleepPatternBarGraphDaily: values.size: ${values.size}, barChart.xAxis.labelCount: ${barChart.xAxis.labelCount}")
val data = BarData(set1) val data = BarData(set1)
data.setValueTextSize(12f) data.setValueTextSize(12f)
//Log.d(TAG, "showSleepPatternBarGraphDaily: new barChart.xAxis.labelCount: ${barChart.xAxis.labelCount}")
val valueCount = min(values.size, 24) val valueCount = min(values.size, 24)
barChart.setData(data) barChart.setData(data)
barChart.legend.isEnabled = false barChart.legend.isEnabled = false
@@ -519,18 +499,14 @@ class StatisticsActivity : AppCompatActivity() {
val endIndex = unixToSpan(endUnix) val endIndex = unixToSpan(endUnix)
var mid = startUnix var mid = startUnix
//Log.d(TAG, "beginIndex: $begIndex, endIndex: $endIndex, startUnix: ${Date(startUnix * 1000)} ($startUnix), endUnix: ${Date(endUnix * 1000)} ($endUnix)")
//Log.d(TAG, "startUnix: ${Date(startUnix * 1000)}, endUnix: ${Date(endUnix * 1000)}, begIndex: $begIndex, endIndex: $endIndex (index diff: ${endIndex - begIndex})")
for (i in begIndex..endIndex) { for (i in begIndex..endIndex) {
// i is the days/weeks/months since unix epoch // i is the days/weeks/months since unix epoch
val spanBegin = spanToUnix(i) val spanBegin = spanToUnix(i)
val spanEnd = spanToUnix(i + 1) val spanEnd = spanToUnix(i + 1)
//Log.d(TAG, "i: $i, mid: ${Date(mid * 1000)}, spanBegin: ${Date(spanBegin * 1000)}, spanEnd: ${Date(spanEnd * 1000)}, beginUnix: ${Date(startUnix * 1000)} endUnix: ${Date(endUnix * 1000)}")
val sleepBegin = max(mid, spanBegin) val sleepBegin = max(mid, spanBegin)
val sleepEnd = min(endUnix, spanEnd) val sleepEnd = min(endUnix, spanEnd)
val index = i - state.startSpan val index = i - state.startSpan
val duration = sleepEnd - sleepBegin val duration = sleepEnd - sleepBegin
//Log.d(TAG, "[$index] sleepBegin: ${Date(sleepBegin * 1000)}, sleepEnd: ${Date(sleepEnd * 1000)}, ${DateUtils.formatTimeDuration(this, duration)}")
state.dayCounter.setDaysWithData(sleepBegin, sleepEnd) state.dayCounter.setDaysWithData(sleepBegin, sleepEnd)
@@ -613,7 +589,6 @@ class StatisticsActivity : AppCompatActivity() {
for (index in values.indices) { for (index in values.indices) {
val daysWithData = state.dayCounter.countDaysWithData(spanToUnix(state.startSpan + index), spanToUnix(state.startSpan + index + 1)) val daysWithData = state.dayCounter.countDaysWithData(spanToUnix(state.startSpan + index), spanToUnix(state.startSpan + index + 1))
//Log.d(TAG, "values[$index].y: ${values[index].y}, daysWithData: $daysWithData")
if (daysWithData == 0) { if (daysWithData == 0) {
assert(values[index].y == 0F) assert(values[index].y == 0F)
} else { } else {
@@ -664,7 +639,6 @@ class StatisticsActivity : AppCompatActivity() {
fun countDaysWithData(beginUnix: Long, endUnix: Long): Int { fun countDaysWithData(beginUnix: Long, endUnix: Long): Int {
val beginDays = unixToDays(beginUnix) val beginDays = unixToDays(beginUnix)
val endDays = unixToDays(endUnix) val endDays = unixToDays(endUnix)
//Log.d(TAG, "countDaysWithData: beginDays: $beginDays, endDays: $endDays, ${Date(beginUnix * 1000)} - ${Date(endUnix * 1000)}")
var count = 0 var count = 0
for (i in (beginDays - startDays)..<(endDays - startDays)) { for (i in (beginDays - startDays)..<(endDays - startDays)) {
//Log.d(TAG, "countDaysWithData: i: $i, size: ${daysWithData.size}") //Log.d(TAG, "countDaysWithData: i: $i, size: ${daysWithData.size}")
@@ -694,7 +668,6 @@ class StatisticsActivity : AppCompatActivity() {
barChart.notifyDataSetChanged() barChart.notifyDataSetChanged()
barChart.clear() barChart.clear()
barChart.invalidate() barChart.invalidate()
//Log.d(TAG, "resetBarChart; barChart.xAxis.labelCount: ${barChart.xAxis.labelCount}, barChart.visibleXRange: ${barChart.visibleXRange}, barChart.xAxis.isCenterAxisLabelsEnabled: ${barChart.xAxis.isCenterAxisLabelsEnabled}, barChart.isAutoScaleMinMaxEnabled: ${barChart.isAutoScaleMinMaxEnabled}, barChart.isScaleXEnabled: ${barChart.isScaleXEnabled}, barChart.isScaleYEnabled: ${barChart.isScaleYEnabled}")
val type = when (graphTypeSelection) { val type = when (graphTypeSelection) {
GraphType.BOTTLE_EVENTS, GraphType.BOTTLE_EVENTS,
@@ -729,8 +702,6 @@ class StatisticsActivity : AppCompatActivity() {
val endDays = unixToDays(spanToUnix(endSpan + 1)) // until end of next span val endDays = unixToDays(spanToUnix(endSpan + 1)) // until end of next span
val dayCounter = DayCounter(startDays, endDays) val dayCounter = DayCounter(startDays, endDays)
//Log.d(TAG, "startUnix: ${Date(1000 * startUnix)}, endUnix: ${Date(1000 * endUnix)}, startSpan: ${Date(1000 * spanToUnix(startSpan))}, endSpan: ${Date(1000 * spanToUnix(endSpan))}, startDaysUnix: ${Date(daysToUnix(startDays) * 1000)}. endDaysUnix: ${Date(daysToUnix(endDays) * 1000)}")
// print dates // print dates
barChart.xAxis.valueFormatter = object: ValueFormatter() { barChart.xAxis.valueFormatter = object: ValueFormatter() {
override fun getFormattedValue(value: Float): String { override fun getFormattedValue(value: Float): String {
@@ -744,8 +715,6 @@ class StatisticsActivity : AppCompatActivity() {
val week = dateTime.get(Calendar.WEEK_OF_YEAR) val week = dateTime.get(Calendar.WEEK_OF_YEAR)
val day = dateTime.get(Calendar.DAY_OF_MONTH) val day = dateTime.get(Calendar.DAY_OF_MONTH)
//Log.d(TAG, "index: $index, unixSeconds: ${Date(1000 * unixSeconds)}, day: $day, week: $week, month: $month, year: $year")
// Adjust years if the first week of a year starts in the previous year. // Adjust years if the first week of a year starts in the previous year.
val years = if (month == 12 && week == 1) { val years = if (month == 12 && week == 1) {
year + 1 year + 1
@@ -797,8 +766,6 @@ class StatisticsActivity : AppCompatActivity() {
val spinnerAdapter = val spinnerAdapter =
ArrayAdapter.createFromResource(this, arrayId, R.layout.statistics_spinner_item) ArrayAdapter.createFromResource(this, arrayId, R.layout.statistics_spinner_item)
//Log.d(TAG, "spinner ${arrayValues.indexOf(currentValue)} (${arrayValues.joinToString { it }})")
spinner.adapter = spinnerAdapter spinner.adapter = spinnerAdapter
spinner.setSelection(arrayValues.indexOf(currentValue)) spinner.setSelection(arrayValues.indexOf(currentValue))
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {