17 Commits

Author SHA1 Message Date
ddcc2fc492 sleep: fix date picker usage
The time the date picker uses needs to be updated.
Also make some variable names more logical.
2026-06-18 14:28:36 +02:00
c75d4ed8a7 activity_main: add horizontal margin to error message 2026-06-18 14:09:51 +02:00
16e5b39d6f notes: do not update quantity
When using the tiny arrows to switch
between older texts, do not change
the quantity field.
2026-06-12 16:10:09 +02:00
7bbf480a7f event: add support for height 2026-06-11 21:36:14 +02:00
f01ea7f571 weight: add local unit to dialog 2026-06-11 21:20:30 +02:00
d6c79431b4 sleep: remove plus/minus buttons
Make the UI simpler.
2026-06-11 20:44:33 +02:00
0566116913 NumberUtils: remove unused numberFormat 2026-06-11 20:43:01 +02:00
56b3418c72 MainActivity: show edit dialog for food and medicine quantity 2026-04-06 22:03:43 +02:00
d76f5cf2ce MainActivity: set unit for bottle content 2026-03-10 23:14:58 +01:00
557a9ab69a DateUtils: make duration rounded up to minutes 2026-03-06 12:33:57 +01:00
620e20aa2e MainActivity: inline nested function for dynamic menu 2026-03-06 12:33:57 +01:00
8687d62bac MainActivity: generate dynamic menu from last two weeks 2026-03-06 12:33:57 +01:00
3ae68ffa7b LunaEvent: rework sleep event
Make the UI more flexible and
slightly easier to understand.
2026-03-06 12:33:53 +01:00
dd4be8dcd7 MainActivity: rename datepicker 2026-02-19 16:41:16 +01:00
cb91922e2a 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 16:41:16 +01:00
24f48cb533 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 16:41:16 +01:00
08022541f1 StatisticsActivity: rework all statistics
Improve the overall code.
2026-02-19 16:41:12 +01:00
15 changed files with 1095 additions and 401 deletions

View File

@@ -148,10 +148,12 @@ class MainActivity : AppCompatActivity() {
val eventTypeStats = mutableMapOf<LunaEvent.Type, Int>() val eventTypeStats = mutableMapOf<LunaEvent.Type, Int>()
if (dynamicMenu) { if (dynamicMenu) {
val sampleSize = 100 // populate frequency map from all events of the last two weeks
// populate frequency map from first 100 events val lastWeekTime = (System.currentTimeMillis() / 1000) - (14 * 24 * 60 * 60)
allEvents.take(sampleSize.coerceAtMost(allEvents.size)).forEach { allEvents.forEach {
eventTypeStats[it.type] = 1 + (eventTypeStats[it.type] ?: 0) if (it.time > lastWeekTime) {
eventTypeStats[it.type] = 1 + (eventTypeStats[it.type] ?: 0)
}
} }
} }
@@ -160,60 +162,55 @@ class MainActivity : AppCompatActivity() {
compareBy({ -1 * (eventTypeStats[it] ?: 0) }, { it.ordinal }) compareBy({ -1 * (eventTypeStats[it] ?: 0) }, { it.ordinal })
).filter { it != LunaEvent.Type.UNKNOWN } ).filter { it != LunaEvent.Type.UNKNOWN }
fun setupMenu(maxButtonCount: Int, sortedEventTypes: List<LunaEvent.Type>): Int {
val row1 = findViewById<View>(R.id.linear_layout_row1)
val row1Button1 = findViewById<TextView>(R.id.button1_row1)
val row1Button2 = findViewById<TextView>(R.id.button2_row1)
val row2 = findViewById<View>(R.id.linear_layout_row2)
val row2Button1 = findViewById<TextView>(R.id.button1_row2)
val row2Button2 = findViewById<TextView>(R.id.button2_row2)
val row2Button3 = findViewById<TextView>(R.id.button3_row2)
val row3 = findViewById<View>(R.id.linear_layout_row3)
val row3Button1 = findViewById<TextView>(R.id.button1_row3)
val row3Button2 = findViewById<TextView>(R.id.button2_row3)
// hide all rows/buttons (except row 3)
for (view in listOf(row1, row1Button1, row1Button2,
row2, row2Button1, row2Button2, row2Button3,
row3, row3Button1, row3Button2)) {
view.visibility = View.GONE
}
row3.visibility = View.VISIBLE
var showCounter = 0
fun show(vararg tvs: TextView) {
for (tv in tvs) {
val type = sortedEventTypes[showCounter]
tv.text = LunaEvent.getHeaderEmoji(applicationContext, type)
tv.setOnClickListener { showCreateDialog(type) }
tv.visibility = View.VISIBLE
// show parent row
(tv.parent as View).visibility = View.VISIBLE
showCounter += 1
}
}
when (maxButtonCount) {
0 -> { } // ignore - show empty row3
1 -> show(row3Button1)
2 -> show(row3Button1, row3Button2)
3 -> show(row1Button1, row3Button1)
4, 5, 6 -> show(row1Button1, row1Button2, row3Button1, row3Button2)
else -> show(row1Button1, row1Button2, row2Button1, row2Button2, row2Button3, row3Button1, row3Button2)
}
return showCounter
}
val usedEventCount = eventTypeStats.count { it.value > 0 } val usedEventCount = eventTypeStats.count { it.value > 0 }
val maxButtonCount = if (dynamicMenu) { usedEventCount } else { 7 } val maxButtonCount = if (dynamicMenu) { usedEventCount } else { 7 }
val eventsShown = setupMenu(maxButtonCount, eventTypesSorted)
val row1 = findViewById<View>(R.id.linear_layout_row1)
val row1Button1 = findViewById<TextView>(R.id.button1_row1)
val row1Button2 = findViewById<TextView>(R.id.button2_row1)
val row2 = findViewById<View>(R.id.linear_layout_row2)
val row2Button1 = findViewById<TextView>(R.id.button1_row2)
val row2Button2 = findViewById<TextView>(R.id.button2_row2)
val row2Button3 = findViewById<TextView>(R.id.button3_row2)
val row3 = findViewById<View>(R.id.linear_layout_row3)
val row3Button1 = findViewById<TextView>(R.id.button1_row3)
val row3Button2 = findViewById<TextView>(R.id.button2_row3)
// hide all rows/buttons (except row 3)
for (view in listOf(row1, row1Button1, row1Button2,
row2, row2Button1, row2Button2, row2Button3,
row3, row3Button1, row3Button2)) {
view.visibility = View.GONE
}
row3.visibility = View.VISIBLE
var showCounter = 0
fun show(vararg tvs: TextView) {
for (tv in tvs) {
val type = eventTypesSorted[showCounter]
tv.text = LunaEvent.getHeaderEmoji(applicationContext, type)
tv.setOnClickListener { showCreateDialog(type) }
tv.visibility = View.VISIBLE
// show parent row
(tv.parent as View).visibility = View.VISIBLE
showCounter += 1
}
}
when (maxButtonCount) {
0 -> { } // ignore - show empty row3
1 -> show(row3Button1)
2 -> show(row3Button1, row3Button2)
3 -> show(row1Button1, row3Button1)
4, 5, 6 -> show(row1Button1, row1Button2, row3Button1, row3Button2)
else -> show(row1Button1, row1Button2, row2Button1, row2Button2, row2Button3, row3Button1, row3Button2)
}
// store left over events for popup menu // store left over events for popup menu
currentPopupItems = eventTypesSorted.subList(eventsShown, eventTypesSorted.size) currentPopupItems = eventTypesSorted.subList(showCounter, eventTypesSorted.size)
} }
override fun onStart() { override fun onStart() {
@@ -275,8 +272,11 @@ class MainActivity : AppCompatActivity() {
numberPicker.wrapSelectorWheel = false numberPicker.wrapSelectorWheel = false
numberPicker.value = event.quantity / 10 numberPicker.value = event.quantity / 10
val numberPickerUnit = dialogView.findViewById<TextView>(R.id.dialog_number_picker_unit)
numberPickerUnit.text = NumericUtils(this).measurement_unit_liquid_base
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker) val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = datePickerHelper(event.time, dateTV) val pickedTime = dateTimePicker(event.time, dateTV)
if (!showTime) { if (!showTime) {
dateTV.visibility = View.GONE dateTV.visibility = View.GONE
@@ -313,8 +313,11 @@ class MainActivity : AppCompatActivity() {
val weightET = dialogView.findViewById<EditText>(R.id.dialog_number_edittext) val weightET = dialogView.findViewById<EditText>(R.id.dialog_number_edittext)
weightET.setText(event.quantity.toString()) weightET.setText(event.quantity.toString())
val unitTV = dialogView.findViewById<TextView>(R.id.dialog_number_unit)
unitTV.text = NumericUtils(this).measurement_unit_weight_base
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker) val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = datePickerHelper(event.time, dateTV) val pickedTime = dateTimePicker(event.time, dateTV)
if (!showTime) { if (!showTime) {
dateTV.visibility = View.GONE dateTV.visibility = View.GONE
@@ -341,6 +344,53 @@ class MainActivity : AppCompatActivity() {
alertDialog.show() alertDialog.show()
} }
fun addHeightEvent(event: LunaEvent) {
setToPreviousQuantity(event)
askHeightValue(event, true) { saveEvent(event) }
}
fun askHeightValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) {
// Show number picker dialog
val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.dialog_edit_height, null)
d.setTitle(event.getDialogTitle(this))
d.setMessage(event.getDialogMessage(this))
d.setView(dialogView)
val heightET = dialogView.findViewById<EditText>(R.id.dialog_number_edittext)
heightET.setText(event.quantity.toString())
val unitTV = dialogView.findViewById<TextView>(R.id.dialog_number_unit)
unitTV.text = NumericUtils(this).measurement_unit_height_base
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = dateTimePicker(event.time, dateTV)
if (!showTime) {
dateTV.visibility = View.GONE
}
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
val height = heightET.text.toString().toIntOrNull()
if (height != null) {
event.time = pickedTime.time.time / 1000
event.quantity = height
onPositive()
} else {
Toast.makeText(this, R.string.toast_integer_error, Toast.LENGTH_SHORT).show()
}
dialogInterface.dismiss()
}
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i ->
dialogInterface.dismiss()
}
val alertDialog = d.create()
alertDialog.show()
}
fun addTemperatureEvent(event: LunaEvent) { fun addTemperatureEvent(event: LunaEvent) {
setToPreviousQuantity(event) setToPreviousQuantity(event)
askTemperatureValue(event, true) { saveEvent(event) } askTemperatureValue(event, true) { saveEvent(event) }
@@ -365,7 +415,7 @@ class MainActivity : AppCompatActivity() {
} }
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker) val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = datePickerHelper(event.time, dateTV) val pickedTime = dateTimePicker(event.time, dateTV)
if (!showTime) { if (!showTime) {
dateTV.visibility = View.GONE dateTV.visibility = View.GONE
} }
@@ -389,7 +439,8 @@ class MainActivity : AppCompatActivity() {
alertDialog.show() alertDialog.show()
} }
fun datePickerHelper(time: Long, dateTextView: TextView, onChange: (Long) -> Unit = {}): Calendar { // Pick a date/time.
fun dateTimePicker(time: Long, dateTextView: TextView, onChange: (Long) -> Unit = {}): Calendar {
dateTextView.text = DateUtils.formatDateTime(time) dateTextView.text = DateUtils.formatDateTime(time)
val dateTime = Calendar.getInstance() val dateTime = Calendar.getInstance()
@@ -420,97 +471,110 @@ class MainActivity : AppCompatActivity() {
return dateTime return dateTime
} }
fun saveEvent(event: LunaEvent) { fun addDurationEvent(event: LunaEvent) {
if (!allEvents.contains(event)) { askDurationEvent(event, true) { saveEvent(event) }
// new event
logEvent(event)
}
logbook?.sort()
recyclerView.adapter?.notifyDataSetChanged()
saveLogbook()
} }
fun addSleepEvent(event: LunaEvent) { fun askDurationEvent(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) {
askSleepValue(event, true) { saveEvent(event) }
}
fun askSleepValue(event: LunaEvent, hideDurationButtons: Boolean, onPositive: () -> Unit) {
val d = AlertDialog.Builder(this) val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.dialog_edit_duration, null) val dialogView = layoutInflater.inflate(R.layout.dialog_edit_duration, null)
d.setTitle(event.getDialogTitle(this)) d.setTitle(event.getDialogTitle(this))
d.setMessage(event.getDialogMessage(this))
d.setView(dialogView) d.setView(dialogView)
val durationTextView = dialogView.findViewById<TextView>(R.id.dialog_date_duration) val durationTV = dialogView.findViewById<TextView>(R.id.dialog_date_duration)
val datePicker = dialogView.findViewById<TextView>(R.id.dialog_date_picker) val datePickerBeginTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker_begin)
val datePickerEndTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker_end)
val dateDelimiterTV = dialogView.findViewById<TextView>(R.id.dialog_date_range_delimiter)
val durationButtons = dialogView.findViewById<LinearLayout>(R.id.duration_buttons) val durationButtons = dialogView.findViewById<LinearLayout>(R.id.duration_buttons)
val durationNowButton = dialogView.findViewById<Button>(R.id.dialog_date_duration_now) val durationNowButton = dialogView.findViewById<Button>(R.id.dialog_date_duration_now)
val durationMinus5Button = dialogView.findViewById<Button>(R.id.dialog_date_duration_minus5) val durationClearButton = dialogView.findViewById<Button>(R.id.dialog_date_duration_clear)
val durationPlus5Button = dialogView.findViewById<Button>(R.id.dialog_date_duration_plus5)
val currentDurationTextColor = durationTextView.currentTextColor val currentDurationTextColor = durationTV.currentTextColor
val invalidDurationTextColor = ContextCompat.getColor(this, R.color.danger) val invalidDurationTextColor = ContextCompat.getColor(this, R.color.danger)
var duration = event.quantity // in seconds
var durationStart = event.getStartTime()
var durationEnd = event.getEndTime()
fun isValidTime(timeSeconds: Long, durationSeconds: Int): Boolean { fun isValidTime(timeUnix: Long): Boolean {
val now = System.currentTimeMillis() / 1000 val now = System.currentTimeMillis() / 1000
return (timeSeconds + durationSeconds) <= now && durationSeconds < (24 * 60 * 60) return timeUnix in 1..now
} }
val onDateChange = { time: Long -> fun isValidTimeSpan(timeBeginUnix: Long, timeEndUnix: Long): Boolean {
durationTextView.setTextColor(currentDurationTextColor) return (timeBeginUnix <= timeEndUnix) && (timeEndUnix - timeBeginUnix) < (24 * 60 * 60)
}
if (duration == 0) { fun updateFields() {
// baby is sleeping datePickerBeginTV.text = DateUtils.formatDateTime(durationStart)
durationTextView.text = "💤" datePickerEndTV.text = DateUtils.formatDateTime(durationEnd)
} else {
durationTextView.text = DateUtils.formatTimeDuration(applicationContext, duration.toLong()) dateTimePicker(durationStart, datePickerBeginTV) { pickedTime: Long ->
if (!isValidTime(time, duration)) { durationStart = pickedTime
durationTextView.setTextColor(invalidDurationTextColor) if (datePickerEndTV.visibility == View.GONE) {
durationEnd = pickedTime
} }
updateFields()
} }
dateTimePicker(durationEnd, datePickerEndTV) { pickedTime: Long ->
durationEnd = pickedTime
updateFields()
}
durationTV.setTextColor(currentDurationTextColor)
val duration = durationEnd - durationStart
if (duration == 0L) {
// event is ongoing
durationTV.text = "💤"
dateDelimiterTV.visibility = View.GONE
datePickerEndTV.visibility = View.GONE
} else {
durationTV.text = DateUtils.formatTimeDuration(applicationContext, duration)
if (!isValidTimeSpan(durationStart, durationEnd)) {
durationTV.setTextColor(invalidDurationTextColor)
}
dateDelimiterTV.visibility = View.VISIBLE
datePickerEndTV.visibility = View.VISIBLE
}
val colorBegin = if (isValidTime(durationStart)) { currentDurationTextColor } else { invalidDurationTextColor }
datePickerBeginTV.setTextColor(colorBegin)
val colorEnd = if (isValidTime(durationEnd)) { currentDurationTextColor } else { invalidDurationTextColor }
datePickerEndTV.setTextColor(colorEnd)
} }
val pickedDateTime = datePickerHelper(event.time, datePicker, onDateChange) if (showTime) {
dateDelimiterTV.visibility = View.GONE
onDateChange(pickedDateTime.time.time / 1000) datePickerEndTV.visibility = View.GONE
durationTV.visibility = View.GONE
if (hideDurationButtons) {
durationButtons.visibility = View.GONE durationButtons.visibility = View.GONE
d.setMessage(getString(R.string.log_sleep_dialog_description_start)) //d.setMessage("")
} else { } else {
dateDelimiterTV.visibility = View.VISIBLE
datePickerEndTV.visibility = View.VISIBLE
durationTV.visibility = View.VISIBLE
durationButtons.visibility = View.VISIBLE durationButtons.visibility = View.VISIBLE
d.setMessage(event.getDialogMessage(this)) d.setMessage(event.getDialogMessage(this))
}
fun adjust(minutes: Int) { updateFields()
duration += minutes * 60
if (duration < 0) {
duration = 0
}
onDateChange(pickedDateTime.time.time / 1000)
}
durationMinus5Button.setOnClickListener { adjust(-5) } durationClearButton.setOnClickListener {
durationPlus5Button.setOnClickListener { adjust(5) } durationEnd = durationStart
updateFields()
}
durationNowButton.setOnClickListener { durationNowButton.setOnClickListener {
val now = System.currentTimeMillis() / 1000 durationEnd = System.currentTimeMillis() / 1000
val start = pickedDateTime.time.time / 1000 updateFields()
if (now > start) {
duration = (now - start).toInt()
duration -= duration % 60 // prevent printing of seconds
onDateChange(pickedDateTime.time.time / 1000)
}
}
} }
d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
val time = pickedDateTime.time.time / 1000 if (isValidTime(durationStart) && isValidTime(durationEnd) && isValidTimeSpan(durationStart, durationEnd)) {
if (isValidTime(time, duration)) { event.time = durationStart
event.time = time event.quantity = (durationEnd - durationStart).toInt()
event.quantity = duration
onPositive() onPositive()
} else { } else {
Toast.makeText(this, R.string.toast_date_error, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.toast_date_error, Toast.LENGTH_SHORT).show()
@@ -548,7 +612,7 @@ class MainActivity : AppCompatActivity() {
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)
val pickedTime = datePickerHelper(event.time, dateTV) val pickedTime = dateTimePicker(event.time, dateTV)
if (!showTime) { if (!showTime) {
dateTV.visibility = View.GONE dateTV.visibility = View.GONE
} }
@@ -581,7 +645,7 @@ class MainActivity : AppCompatActivity() {
d.setView(dialogView) d.setView(dialogView)
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker) val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedDateTime = datePickerHelper(event.time, dateTV) val pickedDateTime = dateTimePicker(event.time, dateTV)
if (!showTime) { if (!showTime) {
dateTV.visibility = View.GONE dateTV.visibility = View.GONE
} }
@@ -616,7 +680,7 @@ class MainActivity : AppCompatActivity() {
val qtyET = dialogView.findViewById<EditText>(R.id.notes_qty_edittext) val qtyET = dialogView.findViewById<EditText>(R.id.notes_qty_edittext)
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker) val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = datePickerHelper(event.time, dateTV) val pickedTime = dateTimePicker(event.time, dateTV)
if (!showTime) { if (!showTime) {
dateTV.visibility = View.GONE dateTV.visibility = View.GONE
@@ -631,9 +695,6 @@ class MainActivity : AppCompatActivity() {
var nextEvent = getNextSameEvent(current, templates) var nextEvent = getNextSameEvent(current, templates)
notesET.setText(current.notes) notesET.setText(current.notes)
if (useQuantity) {
qtyET.setText(current.quantity.toString())
}
if (nextEvent == null && current != event) { if (nextEvent == null && current != event) {
nextEvent = event nextEvent = event
@@ -642,9 +703,6 @@ class MainActivity : AppCompatActivity() {
if (nextEvent != null) { if (nextEvent != null) {
nextTextView.setOnClickListener { nextTextView.setOnClickListener {
notesET.setText(nextEvent.notes) notesET.setText(nextEvent.notes)
if (useQuantity) {
qtyET.setText(nextEvent.quantity.toString())
}
updateContent(nextEvent) updateContent(nextEvent)
} }
nextTextView.alpha = 1.0f nextTextView.alpha = 1.0f
@@ -656,9 +714,6 @@ class MainActivity : AppCompatActivity() {
if (prevEvent != null) { if (prevEvent != null) {
prevTextView.setOnClickListener { prevTextView.setOnClickListener {
notesET.setText(prevEvent.notes) notesET.setText(prevEvent.notes)
if (useQuantity) {
qtyET.setText(prevEvent.quantity.toString())
}
updateContent(prevEvent) updateContent(prevEvent)
} }
prevTextView.alpha = 1.0f prevTextView.alpha = 1.0f
@@ -828,7 +883,7 @@ class MainActivity : AppCompatActivity() {
quantityTextView.text = NumericUtils(this).formatEventQuantity(event) quantityTextView.text = NumericUtils(this).formatEventQuantity(event)
notesTextView.text = event.notes notesTextView.text = event.notes
if (event.type == LunaEvent.Type.SLEEP && event.quantity > 0) { if (event.type == LunaEvent.Type.SLEEP && event.quantity > 0) {
dateEndTextView.text = DateUtils.formatDateTime(event.time + event.quantity) dateEndTextView.text = DateUtils.formatDateTime(event.getEndTime())
dateEndTextView.visibility = View.VISIBLE dateEndTextView.visibility = View.VISIBLE
} else { } else {
dateEndTextView.visibility = View.GONE dateEndTextView.visibility = View.GONE
@@ -842,7 +897,7 @@ class MainActivity : AppCompatActivity() {
} }
updateValues() updateValues()
datePickerHelper(event.time, dateTextView, { newTime: Long -> dateTimePicker(event.time, dateTextView, { newTime: Long ->
event.time = newTime event.time = newTime
updateValues() updateValues()
}) })
@@ -851,12 +906,15 @@ class MainActivity : AppCompatActivity() {
when (event.type) { when (event.type) {
LunaEvent.Type.BABY_BOTTLE -> askBabyBottleContent(event, false, updateValues) LunaEvent.Type.BABY_BOTTLE -> askBabyBottleContent(event, false, updateValues)
LunaEvent.Type.WEIGHT -> askWeightValue(event, false, updateValues) LunaEvent.Type.WEIGHT -> askWeightValue(event, false, updateValues)
LunaEvent.Type.HEIGHT -> askHeightValue(event, false, updateValues)
LunaEvent.Type.DIAPERCHANGE_POO, LunaEvent.Type.DIAPERCHANGE_POO,
LunaEvent.Type.DIAPERCHANGE_PEE, LunaEvent.Type.DIAPERCHANGE_PEE,
LunaEvent.Type.PUKE -> askAmountValue(event, false, updateValues) LunaEvent.Type.PUKE -> askAmountValue(event, false, updateValues)
LunaEvent.Type.TEMPERATURE -> askTemperatureValue(event, false, updateValues) LunaEvent.Type.TEMPERATURE -> askTemperatureValue(event, false, updateValues)
LunaEvent.Type.FOOD,
LunaEvent.Type.MEDICINE,
LunaEvent.Type.NOTE -> askNotes(event, false, updateValues) LunaEvent.Type.NOTE -> askNotes(event, false, updateValues)
LunaEvent.Type.SLEEP -> askSleepValue(event, false, updateValues) LunaEvent.Type.SLEEP -> askDurationEvent(event, false, updateValues)
else -> { else -> {
Log.w(TAG, "Unexpected type: ${event.type}") Log.w(TAG, "Unexpected type: ${event.type}")
} }
@@ -886,7 +944,7 @@ class MainActivity : AppCompatActivity() {
val previousEvent = getPreviousSameEvent(event, allEvents) val previousEvent = getPreviousSameEvent(event, allEvents)
if (previousEvent != null) { if (previousEvent != null) {
val emoji = previousEvent.getHeaderEmoji(applicationContext) val emoji = previousEvent.getHeaderEmoji(applicationContext)
val time = DateUtils.formatTimeDuration(applicationContext, event.time - previousEvent.time) val time = DateUtils.formatTimeDuration(applicationContext, event.getStartTime() - previousEvent.getEndTime())
previousTextView.text = String.format("⬅️ %s %s", emoji, time) previousTextView.text = String.format("⬅️ %s %s", emoji, time)
previousTextView.setOnClickListener { previousTextView.setOnClickListener {
alertDialog.cancel() alertDialog.cancel()
@@ -901,7 +959,7 @@ class MainActivity : AppCompatActivity() {
val nextEvent = getNextSameEvent(event, allEvents) val nextEvent = getNextSameEvent(event, allEvents)
if (nextEvent != null) { if (nextEvent != null) {
val emoji = nextEvent.getHeaderEmoji(applicationContext) val emoji = nextEvent.getHeaderEmoji(applicationContext)
val time = DateUtils.formatTimeDuration(applicationContext, nextEvent.time - event.time) val time = DateUtils.formatTimeDuration(applicationContext, nextEvent.getStartTime() - event.getEndTime())
nextTextView.text = String.format("%s %s ➡️", time, emoji) nextTextView.text = String.format("%s %s ➡️", time, emoji)
nextTextView.setOnClickListener { nextTextView.setOnClickListener {
alertDialog.cancel() alertDialog.cancel()
@@ -1126,23 +1184,6 @@ class MainActivity : AppCompatActivity() {
}) })
} }
fun logEvent(event: LunaEvent) {
savingEvent(true)
event.signature = signature
setLoading(true)
logbook?.logs?.add(0, event)
recyclerView.adapter?.notifyItemInserted(0)
recyclerView.smoothScrollToPosition(0)
saveLogbook(event)
// Check logbook size to avoid OOM errors
if (logbook?.isTooBig() == true) {
askToTrimLogbook()
}
}
fun deleteEvent(event: LunaEvent) { fun deleteEvent(event: LunaEvent) {
// Update view // Update view
savingEvent(true) savingEvent(true)
@@ -1154,6 +1195,32 @@ class MainActivity : AppCompatActivity() {
saveLogbook() saveLogbook()
} }
fun saveEvent(event: LunaEvent) {
if (allEvents.contains(event)) {
// event was modified
logbook?.sort()
recyclerView.adapter?.notifyDataSetChanged()
saveLogbook()
} else {
// add new event
savingEvent(true)
setLoading(true)
if (signature.isNotEmpty()) {
event.signature = signature
}
logbook?.logs?.add(0, event)
logbook?.sort()
recyclerView.adapter?.notifyDataSetChanged()
recyclerView.smoothScrollToPosition(0)
saveLogbook(event)
// Check logbook size to avoid OOM errors
if (logbook?.isTooBig() == true) {
askToTrimLogbook()
}
}
}
/** /**
* Saves the logbook. If saving while adding an event, please specify the event so in case * Saves the logbook. If saving while adding an event, please specify the event so in case
* of error can be removed from the list. * of error can be removed from the list.
@@ -1261,6 +1328,7 @@ class MainActivity : AppCompatActivity() {
when (type) { when (type) {
LunaEvent.Type.BABY_BOTTLE -> addBabyBottleEvent(event) LunaEvent.Type.BABY_BOTTLE -> addBabyBottleEvent(event)
LunaEvent.Type.WEIGHT -> addWeightEvent(event) LunaEvent.Type.WEIGHT -> addWeightEvent(event)
LunaEvent.Type.HEIGHT -> addHeightEvent(event)
LunaEvent.Type.BREASTFEEDING_LEFT_NIPPLE -> addPlainEvent(event) LunaEvent.Type.BREASTFEEDING_LEFT_NIPPLE -> addPlainEvent(event)
LunaEvent.Type.BREASTFEEDING_BOTH_NIPPLE -> addPlainEvent(event) LunaEvent.Type.BREASTFEEDING_BOTH_NIPPLE -> addPlainEvent(event)
LunaEvent.Type.BREASTFEEDING_RIGHT_NIPPLE -> addPlainEvent(event) LunaEvent.Type.BREASTFEEDING_RIGHT_NIPPLE -> addPlainEvent(event)
@@ -1274,7 +1342,7 @@ class MainActivity : AppCompatActivity() {
LunaEvent.Type.FOOD -> addNoteEvent(event) LunaEvent.Type.FOOD -> addNoteEvent(event)
LunaEvent.Type.PUKE -> addAmountEvent(event) LunaEvent.Type.PUKE -> addAmountEvent(event)
LunaEvent.Type.BATH -> addPlainEvent(event) LunaEvent.Type.BATH -> addPlainEvent(event)
LunaEvent.Type.SLEEP -> addSleepEvent(event) LunaEvent.Type.SLEEP -> addDurationEvent(event)
LunaEvent.Type.UNKNOWN -> {} // ignore LunaEvent.Type.UNKNOWN -> {} // ignore
} }
} }

View File

@@ -58,17 +58,12 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
LunaEvent.Type.NOTE -> item.notes LunaEvent.Type.NOTE -> item.notes
else -> item.getRowItemTitle(context) else -> item.getRowItemTitle(context)
} }
val endTime = if (item.type == LunaEvent.Type.SLEEP) { holder.time.text = DateUtils.formatTimeAgo(context, item.getEndTime())
item.quantity + item.time
} else {
item.time
}
holder.time.text = DateUtils.formatTimeAgo(context, endTime)
var quantityText = numericUtils.formatEventQuantity(item) var quantityText = numericUtils.formatEventQuantity(item)
// if the event is weight, show difference with the last one // if the event is weight, show difference with the last one
if (item.type == LunaEvent.Type.WEIGHT) { if (item.type == LunaEvent.Type.WEIGHT) {
val lastWeight = getPreviousWeightEvent(position) val lastWeight = getPreviousEvent(position, LunaEvent.Type.WEIGHT)
if (lastWeight != null) { if (lastWeight != null) {
val differenceInWeight = item.quantity - lastWeight.quantity val differenceInWeight = item.quantity - lastWeight.quantity
val sign = if (differenceInWeight > 0) "+" else "" val sign = if (differenceInWeight > 0) "+" else ""
@@ -79,6 +74,19 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
} }
} }
// if the event is height, show difference with the last one
if (item.type == LunaEvent.Type.HEIGHT) {
val lastHeight = getPreviousEvent(position, LunaEvent.Type.HEIGHT)
if (lastHeight != null) {
val differenceInHeight = item.quantity - lastHeight.quantity
val sign = if (differenceInHeight > 0) "+" else ""
quantityText += "\n($sign$differenceInHeight)"
if (differenceInHeight < 0) {
holder.quantity.setTextColor(ContextCompat.getColor(context, R.color.danger))
}
}
}
holder.quantity.text = quantityText holder.quantity.text = quantityText
// Listeners // Listeners
@@ -93,12 +101,12 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
return items.size return items.size
} }
private fun getPreviousWeightEvent(startFromPosition: Int): LunaEvent? { private fun getPreviousEvent(startFromPosition: Int, type: LunaEvent.Type): LunaEvent? {
if (startFromPosition == items.size - 1) if (startFromPosition == items.size - 1)
return null return null
for (pos in startFromPosition + 1 until items.size) { for (pos in startFromPosition + 1 until items.size) {
val item = items.get(pos) val item = items.get(pos)
if (item.type != LunaEvent.Type.WEIGHT) if (item.type != type)
continue continue
return item return item
} }

View File

@@ -22,6 +22,7 @@ class LunaEvent: Comparable<LunaEvent> {
DIAPERCHANGE_PEE, DIAPERCHANGE_PEE,
SLEEP, SLEEP,
WEIGHT, WEIGHT,
HEIGHT,
MEDICINE, MEDICINE,
ENEMA, ENEMA,
NOTE, NOTE,
@@ -115,6 +116,18 @@ class LunaEvent: Comparable<LunaEvent> {
return getDialogMessage(context, type) return getDialogMessage(context, type)
} }
fun getStartTime(): Long {
return time
}
fun getEndTime(): Long {
return if (type == Type.SLEEP) {
time + quantity
} else {
time
}
}
fun toJson(): JSONObject { fun toJson(): JSONObject {
return jo return jo
} }
@@ -133,6 +146,7 @@ class LunaEvent: Comparable<LunaEvent> {
when (type) { when (type) {
Type.BABY_BOTTLE -> R.string.event_bottle_type Type.BABY_BOTTLE -> R.string.event_bottle_type
Type.WEIGHT -> R.string.event_weight_type Type.WEIGHT -> R.string.event_weight_type
Type.HEIGHT -> R.string.event_height_type
Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type
Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type
Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type
@@ -162,7 +176,8 @@ class LunaEvent: Comparable<LunaEvent> {
Type.DIAPERCHANGE_PEE, Type.DIAPERCHANGE_PEE,
Type.PUKE -> R.string.log_amount_dialog_description Type.PUKE -> R.string.log_amount_dialog_description
Type.WEIGHT -> R.string.log_weight_dialog_description Type.WEIGHT -> R.string.log_weight_dialog_description
Type.SLEEP -> R.string.log_sleep_dialog_description Type.HEIGHT -> R.string.log_height_dialog_description
Type.SLEEP -> R.string.log_duration_dialog_description
else -> R.string.log_unknown_dialog_description else -> R.string.log_unknown_dialog_description
} }
) )
@@ -173,6 +188,7 @@ class LunaEvent: Comparable<LunaEvent> {
when (type) { when (type) {
Type.BABY_BOTTLE -> R.string.event_bottle_desc Type.BABY_BOTTLE -> R.string.event_bottle_desc
Type.WEIGHT -> R.string.event_weight_desc Type.WEIGHT -> R.string.event_weight_desc
Type.HEIGHT -> R.string.event_height_desc
Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc
Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc
Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc
@@ -198,6 +214,7 @@ class LunaEvent: Comparable<LunaEvent> {
when (type) { when (type) {
Type.BABY_BOTTLE -> R.string.event_type_item_bottle Type.BABY_BOTTLE -> R.string.event_type_item_bottle
Type.WEIGHT -> R.string.event_type_item_weight Type.WEIGHT -> R.string.event_type_item_weight
Type.HEIGHT -> R.string.event_type_item_height
Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_type_item_breastfeeding_left Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_type_item_breastfeeding_left
Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_type_item_breastfeeding_both Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_type_item_breastfeeding_both
Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_type_item_breastfeeding_right Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_type_item_breastfeeding_right

View File

@@ -9,10 +9,15 @@ import java.util.Date
class DateUtils { class DateUtils {
companion object { companion object {
/** /**
* Format time duration in seconds as e.g. "2 hours, 1 min". * Format time duration in seconds as e.g. "2 hours, 1 min", rounded to minutes.
* Used for the duration to the next/previous event in the event details dialog. * Used for the duration to the next/previous event in the event details dialog.
*/ */
fun formatTimeDuration(context: Context, secondsDiff: Long): String { fun formatTimeDuration(context: Context, secondsDiff: Long): String {
val adjusted = (secondsDiff + 30) - (secondsDiff + 30) % 60
return formatTimeDurationExact(context, adjusted)
}
fun formatTimeDurationExact(context: Context, secondsDiff: Long): String {
var seconds = secondsDiff var seconds = secondsDiff
val years = (seconds / (365 * 24 * 60 * 60F)).toLong() val years = (seconds / (365 * 24 * 60 * 60F)).toLong()
@@ -56,11 +61,11 @@ class DateUtils {
return builder.toString() return builder.toString()
} }
if (years > 0) { if (years != 0L) {
return format(years, days, R.string.year_ago, R.string.years_ago, R.string.day_ago, R.string.days_ago) return format(years, days, R.string.year_ago, R.string.years_ago, R.string.day_ago, R.string.days_ago)
} else if (days > 0) { } else if (days != 0L) {
return format(days, hours, R.string.day_ago, R.string.days_ago, R.string.hour_ago, R.string.hours_ago) return format(days, hours, R.string.day_ago, R.string.days_ago, R.string.hour_ago, R.string.hours_ago)
} else if (hours > 0) { } else if (hours != 0L) {
return format(hours, minutes, R.string.hour_ago, R.string.hours_ago, R.string.minute_ago, R.string.minutes_ago) return format(hours, minutes, R.string.hour_ago, R.string.hours_ago, R.string.minute_ago, R.string.minutes_ago)
} else { } else {
return format(minutes, seconds, R.string.minute_ago, R.string.minute_ago, R.string.second_ago, R.string.seconds_ago) return format(minutes, seconds, R.string.minute_ago, R.string.minute_ago, R.string.second_ago, R.string.seconds_ago)

View File

@@ -11,10 +11,10 @@ import utils.DateUtils.Companion.formatTimeDuration
import java.text.NumberFormat import java.text.NumberFormat
class NumericUtils (val context: Context) { class NumericUtils (val context: Context) {
val numberFormat: NumberFormat
val measurement_unit_liquid_base: String val measurement_unit_liquid_base: String
val measurement_unit_weight_base: String val measurement_unit_weight_base: String
val measurement_unit_weight_tiny: String val measurement_unit_weight_tiny: String
val measurement_unit_height_base: String
val measurement_unit_temperature_base: String val measurement_unit_temperature_base: String
private fun isMetricSystem(): Boolean { private fun isMetricSystem(): Boolean {
@@ -35,7 +35,6 @@ class NumericUtils (val context: Context) {
} }
init { init {
this.numberFormat = NumberFormat.getInstance()
this.measurement_unit_liquid_base = context.getString( this.measurement_unit_liquid_base = context.getString(
if (isMetricSystem()) if (isMetricSystem())
R.string.measurement_unit_liquid_base_metric R.string.measurement_unit_liquid_base_metric
@@ -54,6 +53,12 @@ class NumericUtils (val context: Context) {
else else
R.string.measurement_unit_weight_tiny_imperial R.string.measurement_unit_weight_tiny_imperial
) )
this.measurement_unit_height_base = context.getString(
if (isMetricSystem())
R.string.measurement_unit_height_base_metric
else
R.string.measurement_unit_height_base_imperial
)
this.measurement_unit_temperature_base = context.getString( this.measurement_unit_temperature_base = context.getString(
if (isMetricSystem()) if (isMetricSystem())
R.string.measurement_unit_temperature_base_metric R.string.measurement_unit_temperature_base_metric
@@ -90,6 +95,7 @@ class NumericUtils (val context: Context) {
when (type) { when (type) {
LunaEvent.Type.BABY_BOTTLE -> measurement_unit_liquid_base LunaEvent.Type.BABY_BOTTLE -> measurement_unit_liquid_base
LunaEvent.Type.WEIGHT -> measurement_unit_weight_base LunaEvent.Type.WEIGHT -> measurement_unit_weight_base
LunaEvent.Type.HEIGHT -> measurement_unit_height_base
LunaEvent.Type.MEDICINE -> measurement_unit_weight_tiny LunaEvent.Type.MEDICINE -> measurement_unit_weight_tiny
LunaEvent.Type.TEMPERATURE -> measurement_unit_temperature_base LunaEvent.Type.TEMPERATURE -> measurement_unit_temperature_base
else -> "" else -> ""

View File

@@ -1,5 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>

View File

@@ -177,15 +177,16 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:textSize="30sp"/> android:textSize="30sp"/>
<ImageView <TextView
android:id="@+id/button_more" android:id="@+id/button_more"
android:layout_width="60dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_margin="5dp" android:layout_margin="5dp"
android:layout_weight="0" android:layout_weight="1"
android:background="@drawable/button_background" android:background="@drawable/button_background"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:src="@drawable/ic_more" android:textSize="30sp"
android:text="☰"
app:tint="@android:color/darker_gray"/> app:tint="@android:color/darker_gray"/>
</LinearLayout> </LinearLayout>
@@ -258,6 +259,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginHorizontal="10dp"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:text="@string/no_connection_explain"/> android:text="@string/no_connection_explain"/>

View File

@@ -21,7 +21,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone" android:visibility="gone"
android:gravity="center" android:gravity="center"
android:text="No Data"/> android:text="@string/statistics_no_data"/>
</FrameLayout> </FrameLayout>
@@ -32,25 +32,18 @@
android:orientation="horizontal"> android:orientation="horizontal">
<Spinner <Spinner
android:id="@+id/type_selection" android:id="@+id/graph_type_selection"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:layout_weight="1"
android:layout_weight="1" /> android:gravity="center"/>
<Spinner <Spinner
android:id="@+id/data_selection" android:id="@+id/time_range_selection"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:layout_weight="1"
android:layout_weight="1"/> android:gravity="center"/>
<Spinner
android:id="@+id/time_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"/>
</LinearLayout> </LinearLayout>

View File

@@ -18,6 +18,7 @@
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<TextView <TextView
android:id="@+id/dialog_number_picker_unit"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"

View File

@@ -11,46 +11,65 @@
android:id="@+id/dialog_date_duration" android:id="@+id/dialog_date_duration"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textSize="20sp" android:textSize="20sp"
android:text="💤"/> android:text="💤"/>
<LinearLayout <LinearLayout
android:id="@+id/duration_buttons" android:id="@+id/duration_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="match_parent"
android:gravity="center" android:gravity="center"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"
android:layout_marginHorizontal="10dp" android:layout_marginHorizontal="10dp"
android:orientation="horizontal"> android:orientation="horizontal">
<Button
android:id="@+id/dialog_date_duration_minus5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="-5"/>
<Button <Button
android:id="@+id/dialog_date_duration_now" android:id="@+id/dialog_date_duration_now"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/now"/> android:text="@string/dialog_duration_button_now"/>
<Button <Button
android:id="@+id/dialog_date_duration_plus5" android:id="@+id/dialog_date_duration_clear"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="+5"/> android:text="@string/dialog_duration_button_clear"/>
</LinearLayout> </LinearLayout>
<TextView <LinearLayout
android:id="@+id/dialog_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_width="match_parent"
android:layout_marginTop="20dp"/> android:gravity="center"
android:layout_marginTop="20dp"
android:layout_marginHorizontal="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/dialog_date_picker_begin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"/>
<TextView
android:id="@+id/dialog_date_range_delimiter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
android:text=""/>
<TextView
android:id="@+id/dialog_date_picker_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal">
<EditText
android:id="@+id/dialog_number_edittext"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="0"
android:background="@drawable/textview_background"/>
<TextView
android:id="@+id/dialog_number_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="cm"/>
</LinearLayout>
<TextView
android:id="@+id/dialog_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"/>
</LinearLayout>

View File

@@ -21,6 +21,7 @@
android:background="@drawable/textview_background"/> android:background="@drawable/textview_background"/>
<TextView <TextView
android:id="@+id/dialog_number_unit"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"

View File

@@ -8,23 +8,19 @@
</string-array> </string-array>
<string-array name="StatisticsTypeLabels"> <string-array name="StatisticsTypeLabels">
<item>Bottle</item> <item>@string/statistics_bottle_sum</item>
<item>Sleep</item> <item>@string/statistics_bottle_events</item>
<item>@string/statistics_sleep_sum</item>
<item>@string/statistics_sleep_events</item>
<item>@string/statistics_sleep_pattern</item>
</string-array> </string-array>
<string-array name="StatisticsTypeValues"> <string-array name="StatisticsTypeValues">
<item>BOTTLE</item> <item>BOTTLE_SUM</item>
<item>SLEEP</item> <item>BOTTLE_EVENTS</item>
</string-array> <item>SLEEP_SUM</item>
<item>SLEEP_EVENTS</item>
<string-array name="StatisticsDataLabels"> <item>SLEEP_PATTERN</item>
<item>Event</item>
<item>Amount</item>
</string-array>
<string-array name="StatisticsDataValues">
<item>EVENT</item>
<item>AMOUNT</item>
</string-array> </string-array>
<string-array name="StatisticsTimeLabels"> <string-array name="StatisticsTimeLabels">
@@ -38,5 +34,4 @@
<item>WEEK</item> <item>WEEK</item>
<item>MONTH</item> <item>MONTH</item>
</string-array> </string-array>
</resources> </resources>

View File

@@ -7,6 +7,7 @@
<string name="event_bottle_type" translatable="false">🍼</string> <string name="event_bottle_type" translatable="false">🍼</string>
<string name="event_food_type" translatable="false">🥣</string> <string name="event_food_type" translatable="false">🥣</string>
<string name="event_weight_type" translatable="false">⚖️</string> <string name="event_weight_type" translatable="false">⚖️</string>
<string name="event_height_type" translatable="false">📏</string>
<string name="event_breastfeeding_left_type" translatable="false">🤱⬅️</string> <string name="event_breastfeeding_left_type" translatable="false">🤱⬅️</string>
<string name="event_breastfeeding_both_type" translatable="false">🤱↔️</string> <string name="event_breastfeeding_both_type" translatable="false">🤱↔️</string>
<string name="event_breastfeeding_right_type" translatable="false">🤱➡️️</string> <string name="event_breastfeeding_right_type" translatable="false">🤱➡️️</string>
@@ -26,6 +27,7 @@
<string name="event_type_item_bottle">🍼 Bottle</string> <string name="event_type_item_bottle">🍼 Bottle</string>
<string name="event_type_item_food">🥣 Food</string> <string name="event_type_item_food">🥣 Food</string>
<string name="event_type_item_weight">⚖️ Weight</string> <string name="event_type_item_weight">⚖️ Weight</string>
<string name="event_type_item_height">📏 Height</string>
<string name="event_type_item_breastfeeding_left">🤱⬅️ Nursing</string> <string name="event_type_item_breastfeeding_left">🤱⬅️ Nursing</string>
<string name="event_type_item_breastfeeding_both">🤱↔️ Nursing</string> <string name="event_type_item_breastfeeding_both">🤱↔️ Nursing</string>
<string name="event_type_item_breastfeeding_right">🤱➡️️ Nursing</string> <string name="event_type_item_breastfeeding_right">🤱➡️️ Nursing</string>
@@ -45,6 +47,7 @@
<string name="event_bottle_desc">Milk Bottle</string> <string name="event_bottle_desc">Milk Bottle</string>
<string name="event_food_desc">Food</string> <string name="event_food_desc">Food</string>
<string name="event_weight_desc">Weight</string> <string name="event_weight_desc">Weight</string>
<string name="event_height_desc">Height</string>
<string name="event_breastfeeding_left_desc">Nursing (left)</string> <string name="event_breastfeeding_left_desc">Nursing (left)</string>
<string name="event_breastfeeding_both_desc">Nursing (both)</string> <string name="event_breastfeeding_both_desc">Nursing (both)</string>
<string name="event_breastfeeding_right_desc">Nursing (right)</string> <string name="event_breastfeeding_right_desc">Nursing (right)</string>
@@ -89,6 +92,7 @@
<string name="no_connection_retry">Retry</string> <string name="no_connection_retry">Retry</string>
<string name="statistics_title">Statistics</string> <string name="statistics_title">Statistics</string>
<string name="statistics_no_data">No Data</string>
<string name="settings_dynamic_menu">Dynamic Menu</string> <string name="settings_dynamic_menu">Dynamic Menu</string>
<string name="settings_dynamic_menu_desc">Populate the header menu with the most used events.</string> <string name="settings_dynamic_menu_desc">Populate the header menu with the most used events.</string>
@@ -129,18 +133,27 @@
<string name="log_temperature_dialog_description">Select the temperature:</string> <string name="log_temperature_dialog_description">Select the temperature:</string>
<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_height_dialog_description">Insert the height:</string>
<string name="log_sleep_dialog_description_start">Start sleep cycle:</string> <string name="log_duration_dialog_description">Set duration:</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>
<string name="measurement_unit_weight_tiny_metric" translatable="false">mg</string> <string name="measurement_unit_weight_tiny_metric" translatable="false">mg</string>
<string name="measurement_unit_height_base_metric" translatable="false">cm</string>
<string name="measurement_unit_liquid_base_imperial" translatable="false">fl oz.</string> <string name="measurement_unit_liquid_base_imperial" translatable="false">fl oz.</string>
<string name="measurement_unit_weight_base_imperial" translatable="false">oz</string> <string name="measurement_unit_weight_base_imperial" translatable="false">oz</string>
<string name="measurement_unit_weight_tiny_imperial" translatable="false">gr</string> <string name="measurement_unit_weight_tiny_imperial" translatable="false">gr</string>
<string name="measurement_unit_height_base_imperial" translatable="false">in</string>
<string name="measurement_unit_temperature_base_imperial" translatable="false">°F</string> <string name="measurement_unit_temperature_base_imperial" translatable="false">°F</string>
<string name="measurement_unit_temperature_base_metric" translatable="false">°C</string> <string name="measurement_unit_temperature_base_metric" translatable="false">°C</string>
<string name="statistics_bottle_events">Bottle Events</string>
<string name="statistics_bottle_sum">Bottle Per Day</string>
<string name="statistics_medicine_events">Medicine Events</string>
<string name="statistics_sleep_sum">Sleep Per Day</string>
<string name="statistics_sleep_events">Sleep Events</string>
<string name="statistics_sleep_pattern">Sleep Pattern</string>
<string name="row_luna_event_description">Description</string> <string name="row_luna_event_description">Description</string>
<string name="row_luna_event_quantity">Qty</string> <string name="row_luna_event_quantity">Qty</string>
<string name="row_luna_event_time">Time</string> <string name="row_luna_event_time">Time</string>
@@ -153,6 +166,11 @@
<string name="dialog_event_detail_notes">Notes</string> <string name="dialog_event_detail_notes">Notes</string>
<string name="dialog_event_detail_signature">by %s</string> <string name="dialog_event_detail_signature">by %s</string>
<string name="dialog_duration_button_clear">Clear</string>
<string name="dialog_duration_button_minus_5">-5 min</string>
<string name="dialog_duration_button_now">Now</string>
<string name="dialog_duration_button_plus_5">+5 min</string>
<string name="dialog_add_logbook_title">Add logbook</string> <string name="dialog_add_logbook_title">Add logbook</string>
<string name="dialog_add_logbook_logbookname">👶 Logbook name</string> <string name="dialog_add_logbook_logbookname">👶 Logbook name</string>
<string name="dialog_add_logbook_message">Write a name to identify this logbook. This name will appear on top of the screen and, if you use WebDAV, will be in the save file name as well.</string> <string name="dialog_add_logbook_message">Write a name to identify this logbook. This name will appear on top of the screen and, if you use WebDAV, will be in the save file name as well.</string>