16 Commits

Author SHA1 Message Date
a4b5ae7cd0 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-01 23:00:52 +01:00
bed3350113 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-01 23:00:52 +01:00
758f37b510 StatisticsActivity: rework all statistics
Improve the overall code.
2026-02-01 23:00:48 +01:00
c8c678b294 NumericUtils: remove possible trailing whitespace 2026-01-23 06:46:11 +01:00
45e798ac3b MainActivity: do not switch logbook on reload 2026-01-23 06:46:11 +01:00
a06264091b LunaEvent: reorganize event text getters
Use method names that better reflect
the use of the returned text.
2026-01-23 06:46:11 +01:00
1e82c94d83 MainAcitivty: add dynamic header setting
The setting allows to build the menu and
popup list to be populated by the frequency
of events that has been created.
This also makes the 'no breastfeeding' setting irrelevant.
2026-01-23 06:46:11 +01:00
85567cce77 LunaEvent: use enum class for event types
This helps to have compile errors when some
case it not handled while adding a new type.
The enum class can also be interated over
to create a complete drop down list.
2026-01-23 06:46:11 +01:00
80a51ea8ef MainActivity: increase bottle volume to 340ml
This is the maximum amount found in sold bottles.
2026-01-23 06:46:11 +01:00
28679a4a66 gradle: use uniform implementation directive for sardine-android 2026-01-23 06:46:11 +01:00
f73d3562a9 gradle: avoid inclusion of apk signing blobs
See https://android.izzysoft.de/articles/named/iod-scan-apkchecks?lang=en#blobs
2026-01-23 06:46:11 +01:00
8a2932b1e7 gradle: set compileSDK/targetSdk to 36 2026-01-23 06:46:11 +01:00
2af8989777 StatisticsActivity: add statistics for bottle and sleep events 2026-01-23 06:46:11 +01:00
b417fe48a6 MainActivity: show save button if any values has changed 2026-01-23 06:46:11 +01:00
a887d9f29f MainActivity: use unique templates for notes 2026-01-23 06:46:11 +01:00
44748506ff LunaEvent: add sleep event 2026-01-23 06:46:07 +01:00
3 changed files with 195 additions and 96 deletions

View File

@@ -468,8 +468,10 @@ class MainActivity : AppCompatActivity() {
if (hideDurationButtons) { if (hideDurationButtons) {
durationButtons.visibility = View.GONE durationButtons.visibility = View.GONE
d.setMessage(getString(R.string.log_sleep_dialog_description_start))
} else { } else {
durationButtons.visibility = View.VISIBLE durationButtons.visibility = View.VISIBLE
d.setMessage(event.getDialogMessage(this))
fun adjust(minutes: Int) { fun adjust(minutes: Int) {
duration += minutes * 60 duration += minutes * 60
@@ -514,6 +516,7 @@ class MainActivity : AppCompatActivity() {
} }
fun addAmountEvent(event: LunaEvent) { fun addAmountEvent(event: LunaEvent) {
setToPreviousQuantity(event)
askAmountValue(event, true) { saveEvent(event) } askAmountValue(event, true) { saveEvent(event) }
} }
@@ -530,7 +533,7 @@ class MainActivity : AppCompatActivity() {
R.array.AmountLabels, R.array.AmountLabels,
android.R.layout.simple_spinner_dropdown_item android.R.layout.simple_spinner_dropdown_item
) )
// set pre-selected item and ensure the quantity to index is in bounds
spinner.setSelection(event.quantity.coerceIn(0, spinner.count - 1)) spinner.setSelection(event.quantity.coerceIn(0, spinner.count - 1))
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker) val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)

View File

@@ -12,6 +12,7 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.toColorInt import androidx.core.graphics.toColorInt
import com.github.mikephil.charting.charts.BarChart import com.github.mikephil.charting.charts.BarChart
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.BarData import com.github.mikephil.charting.data.BarData
import com.github.mikephil.charting.data.BarDataSet import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.BarEntry import com.github.mikephil.charting.data.BarEntry
@@ -31,7 +32,7 @@ import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
class StatisticsActivity : AppCompatActivity() { class StatisticsActivity : AppCompatActivity() {
var lastToastShown = 0L var lastToastShown = 0L
lateinit var barChart: BarChart lateinit var barChart: BarChart
@@ -82,10 +83,12 @@ class StatisticsActivity : AppCompatActivity() {
barChart.axisRight.setDrawGridLines(false) barChart.axisRight.setDrawGridLines(false)
barChart.axisRight.setDrawLabels(false) barChart.axisRight.setDrawLabels(false)
//barChart.xAxis.setDrawGridLines(true)
barChart.xAxis.setDrawLabels(true) barChart.xAxis.setDrawLabels(true)
barChart.xAxis.setDrawAxisLine(false) barChart.xAxis.setDrawAxisLine(false)
barChart.isScaleXEnabled = false
barChart.isScaleYEnabled = true
graphTypeSpinner = findViewById(R.id.graph_type_selection) graphTypeSpinner = findViewById(R.id.graph_type_selection)
timeRangeSpinner = findViewById(R.id.time_range_selection) timeRangeSpinner = findViewById(R.id.time_range_selection)
@@ -136,20 +139,54 @@ class StatisticsActivity : AppCompatActivity() {
} }
} }
fun resetBarChart() {
barChart.fitScreen()
barChart.data?.clearValues()
barChart.xAxis.valueFormatter = null
barChart.notifyDataSetChanged()
barChart.clear()
barChart.invalidate()
/*
barChart.setBackgroundColor(Color.WHITE)
//barChart.description.text = logbookName
barChart.setDrawValueAboveBar(false)
barChart.axisLeft.setAxisMinimum(0F)
barChart.axisLeft.setDrawGridLines(false)
barChart.axisLeft.setDrawLabels(false)
barChart.axisRight.setDrawGridLines(false)
barChart.axisRight.setDrawLabels(false)
//barChart.xAxis.setDrawGridLines(true)
barChart.xAxis.setDrawLabels(true)
barChart.xAxis.setDrawAxisLine(false)
barChart.xAxis.setCenterAxisLabels(true)
barChart.setScaleEnabled(false)
//barChart.isScaleXEnabled = false
//barChart.isScaleYEnabled = true
*/
// for debugging
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}")
}
fun showMedicineBarGraph(state: GraphState) { fun showMedicineBarGraph(state: GraphState) {
val values = HashMap<String, ArrayList<BarEntry>>() val values = HashMap<String, ArrayList<BarEntry>>()
val days = state.endSpan - state.startSpan + 1
for (event in state.events) { for (event in state.events) {
val index = unixToSpan(event.time) - state.startSpan val index = unixToSpan(event.time) - state.startSpan
val key = event.notes.trim().lowercase() val key = event.notes.trim().lowercase()
val array = values.getOrPut(key) { val array = values.getOrPut(key) {
ArrayList(List(state.endSpan - state.startSpan + 1) { BarEntry(it.toFloat(), 0F) }) // create initial array with 0
ArrayList(List(days) { BarEntry(it.toFloat(), 0F) })
} }
array[index].y += 1F array[index].y += 1F
} }
Log.d(TAG, "values.size: ${values.size}") Log.d(TAG, "values.size: ${values.size}, days: $days")
for ((key, value) in values) { for ((key, value) in values) {
Log.d(TAG, "key: $key, value.size: ${value.size} ,value: ${value.joinToString { it.y.toLong().toString() }}") Log.d(TAG, "key: $key, value.size: ${value.size} ,value: ${value.joinToString { it.y.toLong().toString() }}")
} }
@@ -168,19 +205,18 @@ class StatisticsActivity : AppCompatActivity() {
} }
val sets = arrayListOf<IBarDataSet>() val sets = arrayListOf<IBarDataSet>()
for ((key, value) in values.entries) { for ((key, array) in values.entries) {
if (key.startsWith("v")) { val description = shorten(key)
val description = shorten(key) Log.d(TAG, "key: $key")
val barDataSet = BarDataSet(value, description) val barDataSet = BarDataSet(array, description)
barDataSet.color = chooseColor(key) barDataSet.color = chooseColor(key)
sets.add(barDataSet) sets.add(barDataSet)
}
} }
val data = BarData(sets) val data = BarData(sets)
//data.groupBars(0F, 0.2F, 0.1F); //data.groupBars(0F, 0.2F, 0.1F);
data.setValueTextSize(12f) //data.setValueTextSize(12f)
data.barWidth = 1F //data.barWidth = 1F
//data.groupBars(0F, 1F, 1F) //data.groupBars(0F, 1F, 1F)
data.setValueFormatter(object : ValueFormatter() { data.setValueFormatter(object : ValueFormatter() {
@@ -193,22 +229,31 @@ class StatisticsActivity : AppCompatActivity() {
} }
}) })
barChart.setScaleEnabled(true) barChart.setOnChartValueSelectedListener(object : OnChartValueSelectedListener {
barChart.legend.isEnabled = true override fun onValueSelected(e: Entry?, h: Highlight?) {
Log.d(TAG, "onValueSelected ${e == null} ${h == null}")
if (e == null || h == null) {
return
}
//barChart.xAxis.setLabelCount(min(values.size, 24), false); val index = e.x.toInt()
//val maxCount = min(maxIndex, 30) // values.size if (index !in 0..values.size) {
//Log.d(TAG, "maxCount: $maxCount") return
barChart.setVisibleXRangeMaximum(20F) //maxCount.toFloat()) // show max 24 entries }
barChart.xAxis.setLabelCount(30, true)
//barChart.xAxis.isEnabled = false
barChart.xAxis.setCenterAxisLabels(true)
barChart.setScaleEnabled(false)
//barChart.axisLeft.isSLEEP_PATTERN_GRANULARITYEnabled = true Log.d(TAG, "index: $index")
//barChart.axisLeft.setSLEEP_PATTERN_GRANULARITY(0.8F) }
override fun onNothingSelected() {}
})
data.setValueTextSize(12f)
barChart.setData(data) barChart.setData(data)
barChart.legend.isEnabled = true
val valueCount = min(days, 24)
barChart.setVisibleXRangeMaximum(valueCount.toFloat())
barChart.xAxis.setLabelCount(valueCount)
barChart.xAxis.setCenterAxisLabels(false)
barChart.invalidate() barChart.invalidate()
} }
@@ -273,7 +318,7 @@ 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)}") //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
} }
@@ -311,13 +356,11 @@ class StatisticsActivity : AppCompatActivity() {
} }
fun mapColor(occurrences: Int, maxOccurrences: Int): Int { fun mapColor(occurrences: Int, maxOccurrences: Int): Int {
//Log.d(TAG, "$occurrences <= $maxOccurrences") // occurrences: number of reported sleeps in a specific time slot
// occurrences: number of reported sleeps in a specific time slice
// maxOccurrences: maximum number of days with data that can contribute to maxOccurrences // maxOccurrences: maximum number of days with data that can contribute to maxOccurrences
assert(maxOccurrences > 0) assert(maxOccurrences > 0)
assert(occurrences <= maxOccurrences) assert(occurrences <= maxOccurrences)
// map to color // map to color
val q = occurrences.toFloat() / maxOccurrences.toFloat() val q = occurrences.toFloat() / maxOccurrences.toFloat()
val i = q * (SLEEP_PATTERN_COLORS.size - 1).toFloat() val i = q * (SLEEP_PATTERN_COLORS.size - 1).toFloat()
@@ -333,8 +376,8 @@ class StatisticsActivity : AppCompatActivity() {
//Log.d(TAG, "index: $index: daysWithData: $daysWithData, dayArray: ${dayArray.joinToString { it.toString() }}") //Log.d(TAG, "index: $index: daysWithData: $daysWithData, dayArray: ${dayArray.joinToString { it.toString() }}")
val vals = ArrayList<Float>() val vals = ArrayList<Float>()
var prevIndex = -1 // time slice index var prevIndex = -1 // time slot index
var prevValue = -1 // number of entries we have found for time slice var prevValue = -1 // number of entries we have found for time slot
for ((i, v) in dayArray.withIndex()) { for ((i, v) in dayArray.withIndex()) {
if (i == 0) { if (i == 0) {
prevIndex = i prevIndex = i
@@ -354,6 +397,7 @@ class StatisticsActivity : AppCompatActivity() {
//Log.d(TAG, "Range $index, vals: ${vals.joinToString { it.toInt().toString() }}") //, allColors: ${allColors.joinToString { it.toString() }}") //Log.d(TAG, "Range $index, vals: ${vals.joinToString { it.toInt().toString() }}") //, allColors: ${allColors.joinToString { it.toString() }}")
Log.d(TAG, "index: ${index.toFloat()}")
values.add(BarEntry(index.toFloat(), vals.toFloatArray())) values.add(BarEntry(index.toFloat(), vals.toFloatArray()))
} }
@@ -434,17 +478,46 @@ class StatisticsActivity : AppCompatActivity() {
set1.isHighlightEnabled = true set1.isHighlightEnabled = true
set1.setDrawIcons(false) set1.setDrawIcons(false)
barChart.legend.isEnabled = false
barChart.setScaleEnabled(false) //barChart.legend.isEnabled = false
barChart.xAxis.setLabelCount(min(values.size, 24)) /*
val valueCount = min(values.size, 24)
barChart.setVisibleXRangeMaximum(valueCount.toFloat())
barChart.xAxis.setLabelCount(valueCount)
*/
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}")
//barChart.minimumWidth
//barChart.isAutoScaleMinMaxEnabled = true
//barChart.setScaleEnabled(true)
//barChart.fitScreen()
//debugBarValues(values)
//barChart.xAxis.setLabelCount(min(values.size, 24))
data.setValueTextSize(12f) data.setValueTextSize(12f)
barChart.setData(data) barChart.setData(data)
Log.d(TAG, "xChartMax: ${barChart.xChartMax}")
barChart.centerViewTo(barChart.xChartMax, 0F, YAxis.AxisDependency.RIGHT)
// does not work quite right yet
barChart.legend.isEnabled = false
val valueCount = min(values.size, 24)
barChart.setVisibleXRangeMaximum(valueCount.toFloat())
barChart.xAxis.setLabelCount(valueCount)
barChart.xAxis.setCenterAxisLabels(false)
barChart.invalidate() barChart.invalidate()
//barChart.moveViewToX(77F) //values.lastOrNull()!!.x)
} }
// Sleep pattern bars that do not use time slots. // Sleep pattern bars that do not use time slots.
// This is useful/nicer for bars that only represent data of a singlur days. // This is useful/nicer for bars that only represent data of a singular days.
fun showSleepPatternBarGraphDaily(state: GraphState) { fun showSleepPatternBarGraphDaily(state: GraphState) {
val ranges = toSleepRanges(state.events) val ranges = toSleepRanges(state.events)
val values = ArrayList(List(state.endSpan - state.startSpan + 1) { BarEntry(it.toFloat(), FloatArray(0)) }) val values = ArrayList(List(state.endSpan - state.startSpan + 1) { BarEntry(it.toFloat(), FloatArray(0)) })
@@ -549,13 +622,25 @@ class StatisticsActivity : AppCompatActivity() {
override fun onNothingSelected() {} override fun onNothingSelected() {}
}) })
Log.d(TAG, "showSleepPatternBarGraphDaily: values.size: ${values.size}, barChart.xAxis.labelCount: ${barChart.xAxis.labelCount}")
set1.setDrawIcons(false) set1.setDrawIcons(false)
barChart.legend.isEnabled = false //barChart.legend.isEnabled = false
barChart.setScaleEnabled(false)
barChart.xAxis.setLabelCount(min(values.size, 24)) //val valueCount = min(values.size, 24)
//barChart.setVisibleXRangeMaximum(valueCount.toFloat())
//barChart.xAxis.setLabelCount(valueCount)
data.setValueTextSize(12f) data.setValueTextSize(12f)
barChart.setData(data) barChart.setData(data)
Log.d(TAG, "showSleepPatternBarGraphDaily: new barChart.xAxis.labelCount: ${barChart.xAxis.labelCount}")
barChart.legend.isEnabled = false
val valueCount = min(values.size, 24)
barChart.setVisibleXRangeMaximum(valueCount.toFloat())
barChart.xAxis.setLabelCount(valueCount)
barChart.xAxis.setCenterAxisLabels(false)
barChart.invalidate() barChart.invalidate()
} }
@@ -601,14 +686,12 @@ class StatisticsActivity : AppCompatActivity() {
} }
} }
if (graphTypeSelection == GraphType.SLEEP_SUM) { 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)) if (daysWithData == 0) {
if (daysWithData == 0) { assert(values[index].y == 0F)
assert(values[index].y == 0F) } else {
} else { values[index].y /= daysWithData
values[index].y /= daysWithData
}
} }
} }
@@ -619,44 +702,44 @@ class StatisticsActivity : AppCompatActivity() {
data.setValueFormatter(object : ValueFormatter() { data.setValueFormatter(object : ValueFormatter() {
override fun getFormattedValue(value: Float): String { override fun getFormattedValue(value: Float): String {
val prefix = if (timeRangeSelection == TimeRange.DAY) { "" } else { "" }
return when (graphTypeSelection) { return when (graphTypeSelection) {
GraphType.SLEEP_EVENTS -> value.toInt().toString() GraphType.SLEEP_EVENTS -> {
prefix + value.toInt().toString()
}
GraphType.SLEEP_SUM -> { GraphType.SLEEP_SUM -> {
val prefix = if (timeRangeSelection == TimeRange.DAY) { "" } else { "" } prefix + NumericUtils(applicationContext).formatEventQuantity(LunaEvent.Type.SLEEP, value.toInt())
return prefix + NumericUtils(applicationContext).formatEventQuantity(LunaEvent.Type.SLEEP, value.toInt())
} }
else -> { else -> {
Log.e(TAG, "unhandled graphTypeSelection $graphTypeSelection") Log.e(TAG, "unhandled graphTypeSelection $graphTypeSelection")
value.toInt().toString() prefix + value.toInt().toString()
} }
} }
} }
}) })
set1.setDrawIcons(false) set1.setDrawIcons(false)
barChart.legend.isEnabled = false
barChart.setScaleEnabled(false)
barChart.xAxis.setLabelCount(min(values.size, 24))
data.setValueTextSize(12f) data.setValueTextSize(12f)
barChart.setData(data) barChart.setData(data)
barChart.legend.isEnabled = false
val valueCount = min(values.size, 24)
barChart.setVisibleXRangeMaximum(valueCount.toFloat())
barChart.xAxis.setLabelCount(valueCount)
barChart.xAxis.setCenterAxisLabels(false)
barChart.invalidate() barChart.invalidate()
} }
fun showBottleBarGraph(state: GraphState) { fun showBottleBarGraph(state: GraphState) {
val values = ArrayList(List(state.endSpan - state.startSpan + 1) { BarEntry(it.toFloat(), 0F) }) val values = ArrayList(List(state.endSpan - state.startSpan + 1) { BarEntry(it.toFloat(), 0F) })
// needed?
for (i in values.indices) {
values[i].x = i.toFloat()
}
for (event in state.events) { for (event in state.events) {
val index = unixToSpan(event.time) - state.startSpan val index = unixToSpan(event.time) - state.startSpan
state.dayCounter.setDaysWithData(event.time, event.time) state.dayCounter.setDaysWithData(event.time, event.time)
// setDaysWithData(sleepBegin, sleepEnd) values[index].x += values.size.toFloat()
if (graphTypeSelection == GraphType.BOTTLE_EVENTS) { if (graphTypeSelection == GraphType.BOTTLE_EVENTS) {
values[index].y += 1F values[index].y += 1F
} else if (graphTypeSelection == GraphType.BOTTLE_SUM) { } else if (graphTypeSelection == GraphType.BOTTLE_SUM) {
@@ -667,15 +750,13 @@ class StatisticsActivity : AppCompatActivity() {
} }
} }
if (graphTypeSelection == GraphType.BOTTLE_SUM) { 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, "index: $index, daysWithData: $daysWithData")
//Log.d(TAG, "index: $index, daysWithData: $daysWithData") if (daysWithData == 0) {
if (daysWithData == 0) { assert(values[index].y == 0F)
assert(values[index].y == 0F) } else {
} else { values[index].y /= daysWithData
values[index].y /= daysWithData
}
} }
} }
@@ -693,37 +774,34 @@ class StatisticsActivity : AppCompatActivity() {
data.setValueFormatter(object : ValueFormatter() { data.setValueFormatter(object : ValueFormatter() {
override fun getFormattedValue(value: Float): String { override fun getFormattedValue(value: Float): String {
val prefix = if (timeRangeSelection == TimeRange.DAY) { "" } else { "" }
//Log.d(TAG, "getFormattedValue ${dataTypeSelectionValue} ${eventTypeSelectionValue}") //Log.d(TAG, "getFormattedValue ${dataTypeSelectionValue} ${eventTypeSelectionValue}")
return when (graphTypeSelection) { return when (graphTypeSelection) {
GraphType.BOTTLE_EVENTS -> value.toInt().toString() GraphType.BOTTLE_EVENTS -> {
prefix + value.toInt().toString()
}
GraphType.BOTTLE_SUM -> { GraphType.BOTTLE_SUM -> {
val prefix = if (timeRangeSelection == TimeRange.DAY) { "" } else { "" } prefix + NumericUtils(applicationContext).formatEventQuantity(LunaEvent.Type.BABY_BOTTLE, value.toInt())
return prefix + NumericUtils(applicationContext).formatEventQuantity(LunaEvent.Type.BABY_BOTTLE, value.toInt())
} }
else -> { else -> {
Log.e(TAG, "unhandled graphTypeSelection") Log.e(TAG, "unhandled graphTypeSelection")
value.toInt().toString() prefix + value.toInt()
} }
} }
} }
}) })
// hm, does not work yet
data.setValueTextSize(12f) data.setValueTextSize(12f)
barChart.setData(data) barChart.setData(data)
barChart.moveViewToX(values.lastOrNull()!!.x) //barChart.legend.isEnabled = false
val valueCount = min(values.size, 24)
val maximumRange = 16F barChart.setVisibleXRangeMaximum(valueCount.toFloat())
//val count = values.size.coerceIn(5, 20) barChart.xAxis.setLabelCount(valueCount)
barChart.setVisibleXRangeMaximum(maximumRange) // show max 24 entries barChart.xAxis.setCenterAxisLabels(false)
barChart.xAxis.setLabelCount(maximumRange.toInt(), true)
//barChart.xAxis.isEnabled = false
barChart.xAxis.setCenterAxisLabels(true)
barChart.setScaleEnabled(false)
barChart.invalidate() barChart.invalidate()
//barChart.moveViewToX(values.lastOrNull()!!.x)
} }
class DayCounter(val startDays: Int, val stopDays: Int) { class DayCounter(val startDays: Int, val stopDays: Int) {
@@ -756,7 +834,7 @@ class StatisticsActivity : AppCompatActivity() {
data class GraphState(val events: List<LunaEvent>, val dayCounter: DayCounter, val startUnix: Long, val endUnix: Long, val startSpan: Int, val endSpan: Int) data class GraphState(val events: List<LunaEvent>, val dayCounter: DayCounter, val startUnix: Long, val endUnix: Long, val startSpan: Int, val endSpan: Int)
// wrapper for comon graph setup // wrapper for common graph setup
fun prepareGraph(type: LunaEvent.Type, callback: (GraphState) -> Unit) { fun prepareGraph(type: LunaEvent.Type, callback: (GraphState) -> Unit) {
val events = MainActivity.allEvents.filter { it.type == type }.sortedBy { it.time } val events = MainActivity.allEvents.filter { it.type == type }.sortedBy { it.time }
@@ -779,7 +857,7 @@ class StatisticsActivity : AppCompatActivity() {
// days when the a day/week/month starts/ends // days when the a day/week/month starts/ends
val startDays = unixToDays(spanToUnix(startSpan)) val startDays = unixToDays(spanToUnix(startSpan))
val endDays = unixToDays(spanToUnix(endSpan + 1)) // until end of next week val endDays = unixToDays(spanToUnix(endSpan + 1)) // until end of next span
val dayCounter = DayCounter(startDays, endDays) val dayCounter = DayCounter(startDays, endDays)
// print dates // print dates
@@ -794,10 +872,22 @@ class StatisticsActivity : AppCompatActivity() {
val month = dateTime.get(Calendar.MONTH) + 1 // month starts at 0 val month = dateTime.get(Calendar.MONTH) + 1 // month starts at 0
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)
// Adjust years if the first week of a year starts in the previous year.
val years = if (month == 12 && week == 1) {
year + 1
} else {
year
}
val days = "%02d".format(day)
val weeks = "%02d".format(week)
val months = "%02d".format(month)
return when (timeRangeSelection) { return when (timeRangeSelection) {
TimeRange.DAY -> "$day/$month/$year" TimeRange.DAY -> "$days/$months/$years"
TimeRange.WEEK -> "$week/$year" TimeRange.WEEK -> "$weeks/$years"
TimeRange.MONTH -> "$month/$year" TimeRange.MONTH -> "$months/$years"
} }
} }
} }
@@ -810,19 +900,23 @@ class StatisticsActivity : AppCompatActivity() {
fun showGraph() { fun showGraph() {
//Log.d(TAG, "showGraph: graphTypeSelection: $graphTypeSelection, timeRangeSelection: $timeRangeSelection") //Log.d(TAG, "showGraph: graphTypeSelection: $graphTypeSelection, timeRangeSelection: $timeRangeSelection")
// test
resetBarChart()
when (graphTypeSelection) { when (graphTypeSelection) {
GraphType.BOTTLE_EVENTS, GraphType.BOTTLE_EVENTS,
GraphType.BOTTLE_SUM -> prepareGraph(LunaEvent.Type.BABY_BOTTLE) { state -> showBottleBarGraph(state) } GraphType.BOTTLE_SUM -> prepareGraph(LunaEvent.Type.BABY_BOTTLE) { state -> showBottleBarGraph(state) }
GraphType.SLEEP_EVENTS, GraphType.SLEEP_EVENTS,
GraphType.SLEEP_SUM -> prepareGraph(LunaEvent.Type.SLEEP) { state -> showSleepBarGraph(state) } GraphType.SLEEP_SUM -> prepareGraph(LunaEvent.Type.SLEEP) { state -> showSleepBarGraph(state) }
GraphType.SLEEP_PATTERN -> prepareGraph(LunaEvent.Type.SLEEP) { state -> GraphType.SLEEP_PATTERN -> prepareGraph(LunaEvent.Type.SLEEP) { state -> showSleepPatternBarGraphSlotted(state) }
if (timeRangeSelection == TimeRange.DAY) { /* if (timeRangeSelection == TimeRange.DAY) {
// specialized pattern bar for daily view // specialized pattern bar for daily view (optional)
showSleepPatternBarGraphDaily(state) showSleepPatternBarGraphDaily(state)
} else { } else {
showSleepPatternBarGraphSlotted(state) showSleepPatternBarGraphSlotted(state)
} }
} }
*/
GraphType.MEDICINE_EVENTS -> prepareGraph(LunaEvent.Type.MEDICINE) { state -> showMedicineBarGraph(state) } GraphType.MEDICINE_EVENTS -> prepareGraph(LunaEvent.Type.MEDICINE) { state -> showMedicineBarGraph(state) }
} }
} }
@@ -914,9 +1008,10 @@ class StatisticsActivity : AppCompatActivity() {
dateTime.time = Date(seconds * 1000) dateTime.time = Date(seconds * 1000)
val years = dateTime.get(Calendar.YEAR) - 1970 val years = dateTime.get(Calendar.YEAR) - 1970
val weeks = dateTime.get(Calendar.WEEK_OF_YEAR) val weeks = dateTime.get(Calendar.WEEK_OF_YEAR)
val month = dateTime.get(Calendar.MONTH) val month = dateTime.get(Calendar.MONTH) + 1 // month starts at 0
// dirty hack to get monotone number of weeks
if (month == 11 && weeks == 1) { if (month == 12 && weeks == 1) {
// The first week if the year might start in the previous year.
return 52 * (years + 1) + weeks return 52 * (years + 1) + weeks
} }

View File

@@ -131,6 +131,7 @@
<string name="log_unknown_dialog_description"></string> <string name="log_unknown_dialog_description"></string>
<string name="log_weight_dialog_description">Insert the weight:</string> <string name="log_weight_dialog_description">Insert the weight:</string>
<string name="log_sleep_dialog_description">Set sleep duration:</string> <string name="log_sleep_dialog_description">Set sleep duration:</string>
<string name="log_sleep_dialog_description_start">Start sleep cycle:</string>
<string name="measurement_unit_liquid_base_metric" translatable="false">ml</string> <string name="measurement_unit_liquid_base_metric" translatable="false">ml</string>
<string name="measurement_unit_weight_base_metric" translatable="false">g</string> <string name="measurement_unit_weight_base_metric" translatable="false">g</string>