From 5d0b2af665c3c3e8580fda3ac58c768a9e1e0a40 Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Tue, 4 Nov 2025 15:10:45 +0100 Subject: [PATCH 01/37] events: allow editing of all used values 1. Allow to change the date/time and other relevant values of an event on creation and after it was created. 2. Harmonize layout file names and variable names. --- .../lunatracker/MainActivity.kt | 518 +++++++++++++----- .../lunatracker/entities/LunaEvent.kt | 9 + .../repository/LocalSettingsRepository.kt | 8 - app/src/main/java/utils/NumericUtils.kt | 14 +- .../main/res/layout/dialog_edit_bottle.xml | 35 ++ ...dialog_notes.xml => dialog_edit_notes.xml} | 7 + ...icker_dialog.xml => dialog_edit_plain.xml} | 14 +- .../{puke_dialog.xml => dialog_edit_puke.xml} | 9 +- ...dialog.xml => dialog_edit_temperature.xml} | 7 + .../main/res/layout/dialog_edit_weight.xml | 38 ++ ...nt_detail.xml => dialog_event_details.xml} | 4 +- .../main/res/layout/number_edit_dialog.xml | 22 - app/src/main/res/values/strings.xml | 1 - 13 files changed, 483 insertions(+), 203 deletions(-) create mode 100644 app/src/main/res/layout/dialog_edit_bottle.xml rename app/src/main/res/layout/{dialog_notes.xml => dialog_edit_notes.xml} (80%) rename app/src/main/res/layout/{number_picker_dialog.xml => dialog_edit_plain.xml} (59%) rename app/src/main/res/layout/{puke_dialog.xml => dialog_edit_puke.xml} (66%) rename app/src/main/res/layout/{temperature_dialog.xml => dialog_edit_temperature.xml} (83%) create mode 100644 app/src/main/res/layout/dialog_edit_weight.xml rename app/src/main/res/layout/{dialog_event_detail.xml => dialog_event_details.xml} (95%) delete mode 100644 app/src/main/res/layout/number_edit_dialog.xml diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index d2900e9..3201035 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -62,7 +62,7 @@ class MainActivity : AppCompatActivity() { val updateListRunnable: Runnable = Runnable { if (logbook != null && !pauseLogbookUpdate) loadLogbook(logbook!!.name) - handler.postDelayed(updateListRunnable, 1000*60) + handler.postDelayed(updateListRunnable, 1000 * 60) } var logbookRepo: LogbookRepository? = null var showingOverflowPopupWindow = false @@ -81,34 +81,30 @@ class MainActivity : AppCompatActivity() { recyclerView.setLayoutManager(LinearLayoutManager(applicationContext)) // Set listeners - findViewById(R.id.logbooks_add_button).setOnClickListener { showAddLogbookDialog(true) } - findViewById(R.id.button_bottle).setOnClickListener { askBabyBottleContent() } - findViewById(R.id.button_food).setOnClickListener { askNotes(LunaEvent(LunaEvent.TYPE_FOOD)) } - findViewById(R.id.button_nipple_left).setOnClickListener { logEvent( - LunaEvent( - LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE - ) - ) } - findViewById(R.id.button_nipple_both).setOnClickListener { logEvent( - LunaEvent( - LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE - ) - ) } - findViewById(R.id.button_nipple_right).setOnClickListener { logEvent( - LunaEvent( - LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE - ) - ) } - findViewById(R.id.button_change_poo).setOnClickListener { logEvent( - LunaEvent( - LunaEvent.TYPE_DIAPERCHANGE_POO - ) - ) } - findViewById(R.id.button_change_pee).setOnClickListener { logEvent( - LunaEvent( - LunaEvent.TYPE_DIAPERCHANGE_PEE - ) - ) } + findViewById(R.id.logbooks_add_button).setOnClickListener { + showAddLogbookDialog(true) + } + findViewById(R.id.button_bottle).setOnClickListener { + addBabyBottleEvent() + } + findViewById(R.id.button_food).setOnClickListener { + addNoteEvent(LunaEvent(LunaEvent.TYPE_FOOD)) + } + findViewById(R.id.button_nipple_left).setOnClickListener { + addPlainEvent(LunaEvent(LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE)) + } + findViewById(R.id.button_nipple_both).setOnClickListener { + addPlainEvent(LunaEvent(LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE)) + } + findViewById(R.id.button_nipple_right).setOnClickListener { + addPlainEvent(LunaEvent(LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE)) + } + findViewById(R.id.button_change_poo).setOnClickListener { + addPlainEvent(LunaEvent(LunaEvent.TYPE_DIAPERCHANGE_POO)) + } + findViewById(R.id.button_change_pee).setOnClickListener { + addPlainEvent(LunaEvent(LunaEvent.TYPE_DIAPERCHANGE_PEE)) + } val moreButton = findViewById(R.id.button_more) moreButton.setOnClickListener { showOverflowPopupWindow(moreButton) @@ -130,9 +126,9 @@ class MainActivity : AppCompatActivity() { private fun setListAdapter(items: ArrayList) { val adapter = LunaEventRecyclerAdapter(this, items) - adapter.onItemClickListener = object: LunaEventRecyclerAdapter.OnItemClickListener { + adapter.onItemClickListener = object : LunaEventRecyclerAdapter.OnItemClickListener { override fun onItemClick(event: LunaEvent) { - showEventDetailDialog(event, items) + showEventDetailDialog(event) } } recyclerView.adapter = adapter @@ -195,118 +191,313 @@ class MainActivity : AppCompatActivity() { super.onStop() } - fun askBabyBottleContent() { - // Show number picker dialog - val localSettings = LocalSettingsRepository(this) + fun getAllEvents(): ArrayList { + return logbook?.logs ?: arrayListOf() + } + + fun addBabyBottleEvent() { + val event = LunaEvent(LunaEvent.TYPE_BABY_BOTTLE) + askBabyBottleContent(event, true) { + saveEvent(event) + } + } + + fun askBabyBottleContent(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { val d = AlertDialog.Builder(this) - val dialogView = layoutInflater.inflate(R.layout.number_picker_dialog, null) + val dialogView = layoutInflater.inflate(R.layout.dialog_edit_bottle, null) d.setTitle(R.string.log_bottle_dialog_title) d.setMessage(R.string.log_bottle_dialog_description) d.setView(dialogView) + val numberPicker = dialogView.findViewById(R.id.dialog_number_picker) numberPicker.minValue = 1 // "10" numberPicker.maxValue = 25 // "250 numberPicker.displayedValues = ((10..250 step 10).map { it.toString() }.toTypedArray()) numberPicker.wrapSelectorWheel = false - numberPicker.value = localSettings.loadBabyBottleContent() - d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> - logEvent(LunaEvent(LunaEvent.TYPE_BABY_BOTTLE, numberPicker.value * 10)) - localSettings.saveBabyBottleContent(numberPicker.value) + numberPicker.value = event.quantity / 10 + + val dateTV = dialogView.findViewById(R.id.dialog_date_picker) + val pickedTime = datePickerHelper(event.time, dateTV) + + if (!showTime) { + dateTV.visibility = View.GONE } - d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() } + + d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> + event.time = pickedTime.time.time / 1000 + event.quantity = numberPicker.value * 10 + onPositive() + dialogInterface.dismiss() + } + + d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> + dialogInterface.dismiss() + } + val alertDialog = d.create() alertDialog.show() } - fun askWeightValue() { + fun addWeightEvent(event: LunaEvent) { + askWeightValue(event, true) { saveEvent(event) } + } + + fun askWeightValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { // Show number picker dialog val d = AlertDialog.Builder(this) - val dialogView = layoutInflater.inflate(R.layout.number_edit_dialog, null) + val dialogView = layoutInflater.inflate(R.layout.dialog_edit_weight, null) d.setTitle(R.string.log_weight_dialog_title) d.setMessage(R.string.log_weight_dialog_description) d.setView(dialogView) + val weightET = dialogView.findViewById(R.id.dialog_number_edittext) + weightET.setText(event.quantity.toString()) + + val dateTV = dialogView.findViewById(R.id.dialog_date_picker) + val pickedTime = datePickerHelper(event.time, dateTV) + + if (!showTime) { + dateTV.visibility = View.GONE + } + d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> val weight = weightET.text.toString().toIntOrNull() - if (weight != null) - logEvent(LunaEvent(LunaEvent.TYPE_WEIGHT, weight)) - else + if (weight != null) { + event.time = pickedTime.time.time / 1000 + event.quantity = weight + 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() } + + d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> + dialogInterface.dismiss() + } + val alertDialog = d.create() alertDialog.show() } - fun askTemperatureValue() { + fun addTemperatureEvent(event: LunaEvent) { + askTemperatureValue(event, true) { saveEvent(event) } + } + + fun askTemperatureValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { // Show number picker dialog val d = AlertDialog.Builder(this) - val dialogView = layoutInflater.inflate(R.layout.temperature_dialog, null) + val dialogView = layoutInflater.inflate(R.layout.dialog_edit_temperature, null) d.setTitle(R.string.log_temperature_dialog_title) d.setMessage(R.string.log_temperature_dialog_description) d.setView(dialogView) + val tempSlider = dialogView.findViewById(R.id.dialog_temperature_value) val range = NumericUtils(this).getValidEventQuantityRange(LunaEvent.TYPE_TEMPERATURE)!! tempSlider.valueFrom = range.first.toFloat() tempSlider.valueTo = range.second.toFloat() - tempSlider.value = range.third.toFloat() - val tempTextView = dialogView.findViewById(R.id.dialog_temperature_display) - tempTextView.text = range.third.toString() - tempSlider.addOnChangeListener({s, v, b -> tempTextView.text = v.toString()}) - d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> - val temperature = (tempSlider.value * 10).toInt() // In tenth of a grade - logEvent(LunaEvent(LunaEvent.TYPE_TEMPERATURE, temperature)) + tempSlider.value = if (event.quantity == 0) { + range.third.toFloat() // default + } else { + event.quantity.toFloat() / 10 } - d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() } + + val dateTV = dialogView.findViewById(R.id.dialog_date_picker) + val pickedTime = datePickerHelper(event.time, dateTV) + if (!showTime) { + dateTV.visibility = View.GONE + } + + val tempTextView = dialogView.findViewById(R.id.dialog_temperature_display) + tempTextView.text = tempSlider.value.toString() + tempSlider.addOnChangeListener({ s, v, b -> tempTextView.text = v.toString() }) + + d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> + event.time = pickedTime.time.time / 1000 + event.quantity = (tempSlider.value * 10).toInt() // temperature in tenth of a grade + onPositive() + dialogInterface.dismiss() + } + + d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> + dialogInterface.dismiss() + } + val alertDialog = d.create() alertDialog.show() } - fun askPukeValue() { + fun datePickerHelper(time: Long, dateTextView: TextView): Calendar { + dateTextView.text = DateUtils.formatDateTime(time) + + val dateTime = Calendar.getInstance() + dateTime.time = Date(time * 1000) + dateTextView.setOnClickListener { + // Show datetime picker + val startYear = dateTime.get(Calendar.YEAR) + val startMonth = dateTime.get(Calendar.MONTH) + val startDay = dateTime.get(Calendar.DAY_OF_MONTH) + val startHour = dateTime.get(Calendar.HOUR_OF_DAY) + val startMinute = dateTime.get(Calendar.MINUTE) + + DatePickerDialog(this, { _, year, month, day -> + TimePickerDialog( + this, + { _, hour, minute -> + dateTime.set(year, month, day, hour, minute) + dateTextView.text = DateUtils.formatDateTime(dateTime.time.time / 1000) + }, + startHour, + startMinute, + android.text.format.DateFormat.is24HourFormat(this@MainActivity) + ).show() + }, startYear, startMonth, startDay).show() + } + + return dateTime + } + + fun saveEvent(event: LunaEvent) { + if (!getAllEvents().contains(event)) { + // new event + logEvent(event) + } + + logbook?.sort() + recyclerView.adapter?.notifyDataSetChanged() + saveLogbook() + } + + fun addPukeEvent(event: LunaEvent) { + askPukeValue(event, true) { saveEvent(event) } + } + + fun askPukeValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { val d = AlertDialog.Builder(this) - val dialogView = layoutInflater.inflate(R.layout.puke_dialog, null) + val dialogView = layoutInflater.inflate(R.layout.dialog_edit_puke, null) d.setTitle(R.string.log_puke_dialog_title) d.setMessage(R.string.log_puke_dialog_description) d.setView(dialogView) val spinner = dialogView.findViewById(R.id.dialog_puke_value) - spinner.adapter = ArrayAdapter.createFromResource(this, R.array.AmountLabels, android.R.layout.simple_spinner_dropdown_item) - spinner.setSelection(1) + spinner.adapter = ArrayAdapter.createFromResource( + this, + R.array.AmountLabels, + android.R.layout.simple_spinner_dropdown_item + ) + spinner.setSelection(event.quantity - 1) + + val dateTV = dialogView.findViewById(R.id.dialog_date_picker) + val pickedTime = datePickerHelper(event.time, dateTV) + if (!showTime) { + dateTV.visibility = View.GONE + } d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> - val pos = spinner.selectedItemPosition - logEvent(LunaEvent(LunaEvent.TYPE_PUKE, pos + 1)) + event.time = pickedTime.time.time / 1000 + event.quantity = spinner.selectedItemPosition + 1 + onPositive() + dialogInterface.dismiss() } - d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() } + + d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> + dialogInterface.dismiss() + } + val alertDialog = d.create() alertDialog.show() } - fun askNotes(lunaEvent: LunaEvent) { + fun addPlainEvent(event: LunaEvent) { + askDateValue(event, true) { saveEvent(event) } + } + + // Ask to edit events to be edited (only affects date) + fun askDateValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { val d = AlertDialog.Builder(this) - val dialogView = layoutInflater.inflate(R.layout.dialog_notes, null) - d.setTitle(lunaEvent.getTypeDescription(this)) - d.setMessage(lunaEvent.getDialogMessage(this)) + val dialogView = layoutInflater.inflate(R.layout.dialog_edit_plain, null) + d.setTitle(event.getTypeDescription(this)) + d.setMessage(event.getDialogMessage(this)) + d.setView(dialogView) + + val dateTV = dialogView.findViewById(R.id.dialog_date_picker) + val pickedDateTime = datePickerHelper(event.time, dateTV) + if (!showTime) { + dateTV.visibility = View.GONE + } + + d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> + event.time = pickedDateTime.time.time / 1000 + onPositive() + dialogInterface.dismiss() + } + + d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> + dialogInterface.dismiss() + } + + val alertDialog = d.create() + alertDialog.show() + } + + fun addNoteEvent(event: LunaEvent) { + askNotes(event, true) { saveEvent(event) } + } + + fun askNotes(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { + val useQuantity = (event.type != LunaEvent.TYPE_NOTE && event.type != LunaEvent.TYPE_CUSTOM) + + val d = AlertDialog.Builder(this) + val dialogView = layoutInflater.inflate(R.layout.dialog_edit_notes, null) + d.setTitle(event.getTypeDescription(this)) + d.setMessage(event.getDialogMessage(this)) d.setView(dialogView) val notesET = dialogView.findViewById(R.id.notes_edittext) val qtyET = dialogView.findViewById(R.id.notes_qty_edittext) - if (lunaEvent.type == LunaEvent.TYPE_NOTE || lunaEvent.type == LunaEvent.TYPE_CUSTOM) - qtyET.visibility = View.GONE - d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> - val qtyStr = qtyET.text.toString() - if (qtyStr.isNotEmpty()) { - val qty = qtyStr.toIntOrNull() - if (qty == null) { - Toast.makeText(this, R.string.toast_integer_error, Toast.LENGTH_SHORT).show() - return@setPositiveButton - } - lunaEvent.quantity = qty - } - val notes = notesET.text.toString() - lunaEvent.notes = notes - logEvent(lunaEvent) + + val dateTV = dialogView.findViewById(R.id.dialog_date_picker) + val pickedTime = datePickerHelper(event.time, dateTV) + + if (!showTime) { + dateTV.visibility = View.GONE } - d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() } + + notesET.setText(event.notes) + + if (useQuantity) { + qtyET.setText(event.quantity.toString()) + } else { + qtyET.visibility = View.GONE + } + + d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> + val notes = notesET.text.toString() + + if (useQuantity) { + val quantity = qtyET.text.toString().toIntOrNull() + if (quantity != null) { + event.time = pickedTime.time.time / 1000 + event.notes = notes + event.quantity = quantity + onPositive() + } else { + Toast.makeText(this, R.string.toast_integer_error, Toast.LENGTH_SHORT).show() + } + + } else { + event.time = pickedTime.time.time / 1000 + event.notes = notes + onPositive() + } + + dialogInterface.dismiss() + } + + d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> + dialogInterface.dismiss() + } + val alertDialog = d.create() alertDialog.show() } @@ -359,55 +550,83 @@ class MainActivity : AppCompatActivity() { return nextEvent } - fun showEventDetailDialog(event: LunaEvent, items: ArrayList) { + fun showEventDetailDialog(originalEvent: LunaEvent) { + val event = LunaEvent(originalEvent) + // Do not update list while the detail is shown, to avoid changing the object below while it is changed by the user pauseLogbookUpdate = true + val d = AlertDialog.Builder(this) d.setTitle(R.string.dialog_event_detail_title) - val dialogView = layoutInflater.inflate(R.layout.dialog_event_detail, null) - dialogView.findViewById(R.id.dialog_event_detail_type_emoji).text = event.getTypeEmoji(this) - dialogView.findViewById(R.id.dialog_event_detail_type_description).text = event.getTypeDescription(this) - dialogView.findViewById(R.id.dialog_event_detail_type_quantity).text = - NumericUtils(this).formatEventQuantity(event) - dialogView.findViewById(R.id.dialog_event_detail_type_notes).text = event.notes - - val currentDateTime = Calendar.getInstance() - currentDateTime.time = Date(event.time * 1000) + val dialogView = layoutInflater.inflate(R.layout.dialog_event_details, null) + val emojiTextView = dialogView.findViewById(R.id.dialog_event_detail_type_emoji) + val descriptionTextView = dialogView.findViewById(R.id.dialog_event_detail_type_description) val dateTextView = dialogView.findViewById(R.id.dialog_event_detail_type_date) - dateTextView.text = String.format(getString(R.string.dialog_event_detail_datetime_icon), DateUtils.formatDateTime(event.time)) - dateTextView.setOnClickListener { - // Show datetime picker - val startYear = currentDateTime.get(Calendar.YEAR) - val startMonth = currentDateTime.get(Calendar.MONTH) - val startDay = currentDateTime.get(Calendar.DAY_OF_MONTH) - val startHour = currentDateTime.get(Calendar.HOUR_OF_DAY) - val startMinute = currentDateTime.get(Calendar.MINUTE) + val quantityTextView = dialogView.findViewById(R.id.dialog_event_detail_type_quantity) + val notesTextView = dialogView.findViewById(R.id.dialog_event_detail_type_notes) - DatePickerDialog(this, { _, year, month, day -> - TimePickerDialog(this, { _, hour, minute -> - val pickedDateTime = Calendar.getInstance() - pickedDateTime.set(year, month, day, hour, minute) - // Save event and move it to the right position in the logbook - event.time = pickedDateTime.time.time / 1000 // Seconds since epoch - dateTextView.text = String.format(getString(R.string.dialog_event_detail_datetime_icon), DateUtils.formatDateTime(event.time)) - logbook?.sort() - recyclerView.adapter?.notifyDataSetChanged() - saveLogbook() - }, startHour, startMinute, android.text.format.DateFormat.is24HourFormat(this@MainActivity)).show() - }, startYear, startMonth, startDay).show() + emojiTextView.text = event.getTypeEmoji(this) + descriptionTextView.text = event.getTypeDescription(this) + + val pickedTime = datePickerHelper(event.time, dateTextView) + val updateValues = { + quantityTextView.text = NumericUtils(this).formatEventQuantity(event) + notesTextView.text = event.notes + } + updateValues() + + quantityTextView.setOnClickListener { + when (event.type) { + LunaEvent.TYPE_BABY_BOTTLE -> askBabyBottleContent(event, false, updateValues) + LunaEvent.TYPE_WEIGHT -> askWeightValue(event, false, updateValues) + LunaEvent.TYPE_PUKE -> askPukeValue(event, false, updateValues) + LunaEvent.TYPE_TEMPERATURE -> askTemperatureValue(event, false, updateValues) + LunaEvent.TYPE_NOTE -> askNotes(event, false, updateValues) + } + } + + notesTextView.setOnClickListener { + when (event.type) { + LunaEvent.TYPE_FOOD, + LunaEvent.TYPE_MEDICINE, + LunaEvent.TYPE_NOTE -> askNotes(event, false, updateValues) + } } d.setView(dialogView) - d.setPositiveButton(R.string.dialog_event_detail_close_button) { dialogInterface, i -> dialogInterface.dismiss() } - d.setNeutralButton(R.string.dialog_event_detail_delete_button) { dialogInterface, i -> deleteEvent(event) } + + d.setNeutralButton(R.string.dialog_event_detail_delete_button) { dialogInterface, i -> + deleteEvent(originalEvent) + dialogInterface.dismiss() + } + + d.setPositiveButton(R.string.dialog_event_detail_close_button) { dialogInterface, i -> + event.time = pickedTime.time.time / 1000 + + if (event.time != originalEvent.time + || event.quantity != originalEvent.quantity + || event.notes != originalEvent.notes) { + originalEvent.time = event.time + originalEvent.quantity = event.quantity + originalEvent.notes = event.notes + saveEvent(originalEvent) + } + + dialogInterface.dismiss() + } + val alertDialog = d.create() alertDialog.show() - alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setTextColor(ContextCompat.getColor(this, R.color.danger)) - alertDialog.setOnDismissListener({ + + alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setTextColor( + ContextCompat.getColor(this, R.color.danger) + ) + + alertDialog.setOnDismissListener { // Resume logbook update pauseLogbookUpdate = false - }) + } // show optional signature if (event.signature.isNotEmpty()) { @@ -416,32 +635,33 @@ class MainActivity : AppCompatActivity() { signatureTextEdit.visibility = View.VISIBLE } - // create next/previous links to events of the same type + val allEvents = getAllEvents() + // create link to prevent event of the same type val previousTextView = dialogView.findViewById(R.id.dialog_event_previous) - val nextTextView = dialogView.findViewById(R.id.dialog_event_next) - val nextEvent = getNextSameEvent(event, items) - val previousEvent = getPreviousSameEvent(event, items) - + val previousEvent = getPreviousSameEvent(event, allEvents) if (previousEvent != null) { val emoji = previousEvent.getTypeEmoji(applicationContext) val time = DateUtils.formatTimeDuration(applicationContext, event.time - previousEvent.time) previousTextView.text = String.format("⬅️ %s %s", emoji, time) previousTextView.setOnClickListener { alertDialog.cancel() - showEventDetailDialog(previousEvent, items) + showEventDetailDialog(previousEvent) } } else { previousTextView.visibility = View.GONE } + // create link to next event of the same type + val nextTextView = dialogView.findViewById(R.id.dialog_event_next) + val nextEvent = getNextSameEvent(event, allEvents) if (nextEvent != null) { val emoji = nextEvent.getTypeEmoji(applicationContext) val time = DateUtils.formatTimeDuration(applicationContext, nextEvent.time - event.time) nextTextView.text = String.format("%s %s ➡️", time, emoji) nextTextView.setOnClickListener { alertDialog.cancel() - showEventDetailDialog(nextEvent, items) + showEventDetailDialog(nextEvent) } } else { nextTextView.visibility = View.GONE @@ -801,41 +1021,37 @@ class MainActivity : AppCompatActivity() { val inflater = LayoutInflater.from(anchor.context) contentView = inflater.inflate(R.layout.more_events_popup, null) contentView.findViewById(R.id.button_medicine).setOnClickListener { - askNotes(LunaEvent(LunaEvent.TYPE_MEDICINE)) + addNoteEvent(LunaEvent(LunaEvent.TYPE_MEDICINE)) dismiss() } - contentView.findViewById(R.id.button_enema).setOnClickListener({ - logEvent(LunaEvent(LunaEvent.TYPE_ENEMA)) + contentView.findViewById(R.id.button_enema).setOnClickListener { + addPlainEvent(LunaEvent(LunaEvent.TYPE_ENEMA)) dismiss() - }) - contentView.findViewById(R.id.button_note).setOnClickListener({ - askNotes(LunaEvent(LunaEvent.TYPE_NOTE)) + } + contentView.findViewById(R.id.button_note).setOnClickListener { + addNoteEvent(LunaEvent(LunaEvent.TYPE_NOTE)) dismiss() - }) - contentView.findViewById(R.id.button_temperature).setOnClickListener({ - askTemperatureValue() + } + contentView.findViewById(R.id.button_temperature).setOnClickListener { + addTemperatureEvent(LunaEvent(LunaEvent.TYPE_TEMPERATURE)) dismiss() - }) - contentView.findViewById(R.id.button_puke).setOnClickListener({ - askPukeValue() + } + contentView.findViewById(R.id.button_puke).setOnClickListener { + addPukeEvent(LunaEvent(LunaEvent.TYPE_PUKE, 1)) dismiss() - }) - contentView.findViewById(R.id.button_colic).setOnClickListener({ - logEvent( - LunaEvent(LunaEvent.TYPE_COLIC) - ) + } + contentView.findViewById(R.id.button_colic).setOnClickListener { + addPlainEvent(LunaEvent(LunaEvent.TYPE_COLIC)) dismiss() - }) - contentView.findViewById(R.id.button_scale).setOnClickListener({ - askWeightValue() + } + contentView.findViewById(R.id.button_scale).setOnClickListener { + addWeightEvent(LunaEvent(LunaEvent.TYPE_WEIGHT)) dismiss() - }) - contentView.findViewById(R.id.button_bath).setOnClickListener({ - logEvent( - LunaEvent(LunaEvent.TYPE_BATH) - ) + } + contentView.findViewById(R.id.button_bath).setOnClickListener { + addPlainEvent(LunaEvent(LunaEvent.TYPE_BATH)) dismiss() - }) + } }.also { popupWindow -> popupWindow.setOnDismissListener({ Handler(mainLooper).postDelayed({ diff --git a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt index e46ab25..58160e3 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt @@ -69,6 +69,15 @@ class LunaEvent: Comparable { throw IllegalArgumentException("JSONObject is not a LunaEvent") } + constructor(event: LunaEvent) { + this.jo = JSONObject() + this.type = event.type + this.time = event.time + this.quantity = event.quantity + this.notes = event.notes + this.signature = event.signature + } + constructor(type: String) { this.jo = JSONObject() this.time = System.currentTimeMillis() / 1000 diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt index 1462820..9431194 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt @@ -23,14 +23,6 @@ class LocalSettingsRepository(val context: Context) { sharedPreferences = context.getSharedPreferences(SHARED_PREFS_FILE_NAME, MODE_PRIVATE) } - fun saveBabyBottleContent(content: Int) { - sharedPreferences.edit { putInt(SHARED_PREFS_BB_CONTENT, content) } - } - - fun loadBabyBottleContent(): Int { - return sharedPreferences.getInt(SHARED_PREFS_BB_CONTENT, 1) - } - fun saveSignature(content: String) { sharedPreferences.edit { putString(SHARED_PREFS_SIGNATURE, content) } } diff --git a/app/src/main/java/utils/NumericUtils.kt b/app/src/main/java/utils/NumericUtils.kt index 23ac4f1..23cd5c6 100644 --- a/app/src/main/java/utils/NumericUtils.kt +++ b/app/src/main/java/utils/NumericUtils.kt @@ -60,21 +60,21 @@ class NumericUtils (val context: Context) { ) } - fun formatEventQuantity(item: LunaEvent): String { + fun formatEventQuantity(event: LunaEvent): String { val formatted = StringBuilder() - if (item.quantity > 0) { - formatted.append(when (item.type) { + if (event.quantity > 0) { + formatted.append(when (event.type) { LunaEvent.TYPE_TEMPERATURE -> - (item.quantity / 10.0f).toString() + (event.quantity / 10.0f).toString() LunaEvent.TYPE_PUKE -> - context.resources.getStringArray(R.array.AmountLabels)[item.quantity - 1] + context.resources.getStringArray(R.array.AmountLabels)[event.quantity - 1] else -> - item.quantity + event.quantity }) formatted.append(" ") formatted.append( - when (item.type) { + when (event.type) { LunaEvent.TYPE_BABY_BOTTLE -> measurement_unit_liquid_base LunaEvent.TYPE_WEIGHT -> measurement_unit_weight_base LunaEvent.TYPE_MEDICINE -> measurement_unit_weight_tiny diff --git a/app/src/main/res/layout/dialog_edit_bottle.xml b/app/src/main/res/layout/dialog_edit_bottle.xml new file mode 100644 index 0000000..9300467 --- /dev/null +++ b/app/src/main/res/layout/dialog_edit_bottle.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_notes.xml b/app/src/main/res/layout/dialog_edit_notes.xml similarity index 80% rename from app/src/main/res/layout/dialog_notes.xml rename to app/src/main/res/layout/dialog_edit_notes.xml index 6d30a5e..274557b 100644 --- a/app/src/main/res/layout/dialog_notes.xml +++ b/app/src/main/res/layout/dialog_edit_notes.xml @@ -24,4 +24,11 @@ android:hint="@string/log_notes_dialog_note_hint" android:background="@drawable/textview_background"/> + + \ No newline at end of file diff --git a/app/src/main/res/layout/number_picker_dialog.xml b/app/src/main/res/layout/dialog_edit_plain.xml similarity index 59% rename from app/src/main/res/layout/number_picker_dialog.xml rename to app/src/main/res/layout/dialog_edit_plain.xml index ce2aa8b..62cc4eb 100644 --- a/app/src/main/res/layout/number_picker_dialog.xml +++ b/app/src/main/res/layout/dialog_edit_plain.xml @@ -4,16 +4,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" - android:gravity="center"> - - + android:gravity="center" + android:orientation="vertical"> + android:layout_height="wrap_content"/> + diff --git a/app/src/main/res/layout/puke_dialog.xml b/app/src/main/res/layout/dialog_edit_puke.xml similarity index 66% rename from app/src/main/res/layout/puke_dialog.xml rename to app/src/main/res/layout/dialog_edit_puke.xml index 6f924a0..7cea41c 100644 --- a/app/src/main/res/layout/puke_dialog.xml +++ b/app/src/main/res/layout/dialog_edit_puke.xml @@ -11,7 +11,12 @@ android:id="@+id/dialog_puke_value" android:layout_width="250dp" android:layout_height="wrap_content" - android:paddingHorizontal="16dp" - android:paddingVertical="8dp"/> + android:paddingHorizontal="16dp"/> + + diff --git a/app/src/main/res/layout/temperature_dialog.xml b/app/src/main/res/layout/dialog_edit_temperature.xml similarity index 83% rename from app/src/main/res/layout/temperature_dialog.xml rename to app/src/main/res/layout/dialog_edit_temperature.xml index 292ed19..b4b3f65 100644 --- a/app/src/main/res/layout/temperature_dialog.xml +++ b/app/src/main/res/layout/dialog_edit_temperature.xml @@ -23,4 +23,11 @@ android:layout_height="wrap_content" android:textSize="30sp" android:textColor="@color/accent"/> + + + diff --git a/app/src/main/res/layout/dialog_edit_weight.xml b/app/src/main/res/layout/dialog_edit_weight.xml new file mode 100644 index 0000000..31dc8de --- /dev/null +++ b/app/src/main/res/layout/dialog_edit_weight.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_event_detail.xml b/app/src/main/res/layout/dialog_event_details.xml similarity index 95% rename from app/src/main/res/layout/dialog_event_detail.xml rename to app/src/main/res/layout/dialog_event_details.xml index 137baa9..3335b95 100644 --- a/app/src/main/res/layout/dialog_event_detail.xml +++ b/app/src/main/res/layout/dialog_event_details.xml @@ -36,9 +36,7 @@ android:drawablePadding="10dp" android:drawableTint="@color/accent" android:textSize="16sp" - android:textStyle="bold" - android:text="@string/dialog_event_detail_datetime_icon" - app:drawableEndCompat="@drawable/ic_edit" /> + android:textStyle="bold"/> - - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c612ca8..afd4eab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -133,7 +133,6 @@ Time Event detail - 🕒 %s OK Delete Quantity From 24a73969bec1cb1ffe79738f8297e91ca6660936 Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Fri, 14 Nov 2025 09:26:16 +0100 Subject: [PATCH 02/37] MainActivity: preset quantity of bottle, weight and temperature Use the quantity of previous events to initialize new events. --- .../danieleverducci/lunatracker/MainActivity.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 3201035..eb17ecf 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -85,7 +85,7 @@ class MainActivity : AppCompatActivity() { showAddLogbookDialog(true) } findViewById(R.id.button_bottle).setOnClickListener { - addBabyBottleEvent() + addBabyBottleEvent(LunaEvent(LunaEvent.TYPE_BABY_BOTTLE)) } findViewById(R.id.button_food).setOnClickListener { addNoteEvent(LunaEvent(LunaEvent.TYPE_FOOD)) @@ -195,8 +195,8 @@ class MainActivity : AppCompatActivity() { return logbook?.logs ?: arrayListOf() } - fun addBabyBottleEvent() { - val event = LunaEvent(LunaEvent.TYPE_BABY_BOTTLE) + fun addBabyBottleEvent(event: LunaEvent) { + setToPreviousQuantity(event) askBabyBottleContent(event, true) { saveEvent(event) } @@ -239,6 +239,7 @@ class MainActivity : AppCompatActivity() { } fun addWeightEvent(event: LunaEvent) { + setToPreviousQuantity(event) askWeightValue(event, true) { saveEvent(event) } } @@ -282,6 +283,7 @@ class MainActivity : AppCompatActivity() { } fun addTemperatureEvent(event: LunaEvent) { + setToPreviousQuantity(event) askTemperatureValue(event, true) { saveEvent(event) } } @@ -522,6 +524,13 @@ class MainActivity : AppCompatActivity() { alertDialog.show() } + fun setToPreviousQuantity(event: LunaEvent) { + val prev = getPreviousSameEvent(event, getAllEvents()) + if (prev != null) { + event.quantity = prev.quantity + } + } + fun getPreviousSameEvent(event: LunaEvent, items: ArrayList): LunaEvent? { var previousEvent: LunaEvent? = null for (item in items) { From 1368c09c58e0147f92437c334d6f74b21a5d38b7 Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Tue, 4 Nov 2025 15:08:10 +0100 Subject: [PATCH 03/37] notes: add icons to use previous/next event as template --- .../lunatracker/MainActivity.kt | 48 +++++++++++++++++++ app/src/main/res/layout/dialog_edit_notes.xml | 23 +++++++++ 2 files changed, 71 insertions(+) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index eb17ecf..3a4fa6d 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -465,6 +465,52 @@ class MainActivity : AppCompatActivity() { dateTV.visibility = View.GONE } + val nextTextView = dialogView.findViewById(R.id.notes_template_next) + val prevTextView = dialogView.findViewById(R.id.notes_template_prev) + + fun updateContent(current: LunaEvent) { + val allEvents = getAllEvents() + val prevEvent = getPreviousSameEvent(current, allEvents) + var nextEvent = getNextSameEvent(current, allEvents) + + notesET.setText(current.notes) + if (useQuantity) { + qtyET.setText(current.quantity.toString()) + } + + if (nextEvent == null && current != event) { + nextEvent = event + } + + if (nextEvent != null) { + nextTextView.setOnClickListener { + notesET.setText(nextEvent.notes) + if (useQuantity) { + qtyET.setText(nextEvent.quantity.toString()) + } + updateContent(nextEvent) + } + nextTextView.alpha = 1.0f + } else { + nextTextView.setOnClickListener {} + nextTextView.alpha = 0.5f + } + + if (prevEvent != null) { + prevTextView.setOnClickListener { + notesET.setText(prevEvent.notes) + if (useQuantity) { + qtyET.setText(prevEvent.quantity.toString()) + } + updateContent(prevEvent) + } + prevTextView.alpha = 1.0f + } else { + prevTextView.setOnClickListener {} + prevTextView.alpha = 0.5f + } + } + notesET.setText(event.notes) if (useQuantity) { @@ -473,6 +519,8 @@ class MainActivity : AppCompatActivity() { qtyET.visibility = View.GONE } + updateContent(event) + d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> val notes = notesET.text.toString() diff --git a/app/src/main/res/layout/dialog_edit_notes.xml b/app/src/main/res/layout/dialog_edit_notes.xml index 274557b..97c04d3 100644 --- a/app/src/main/res/layout/dialog_edit_notes.xml +++ b/app/src/main/res/layout/dialog_edit_notes.xml @@ -24,6 +24,29 @@ android:hint="@string/log_notes_dialog_note_hint" android:background="@drawable/textview_background"/> + + + + + + + + Date: Fri, 14 Nov 2025 18:07:27 +0100 Subject: [PATCH 04/37] LunaEvent: remove quantity when the value is invalid --- .../java/it/danieleverducci/lunatracker/entities/LunaEvent.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt index 58160e3..b7cd0f0 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt @@ -49,6 +49,8 @@ class LunaEvent: Comparable { set(value) { if (value > 0) jo.put("quantity", value) + else + jo.remove("quantity") } var notes: String get(): String = jo.optString("notes") From 4fc327342dd1667b7e29225e1a6b30f1789609dd Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Wed, 12 Nov 2025 22:43:09 +0100 Subject: [PATCH 05/37] strings: rename scale to weight in identifiers --- .../it/danieleverducci/lunatracker/entities/LunaEvent.kt | 4 ++-- app/src/main/res/layout/more_events_popup.xml | 2 +- app/src/main/res/values-de/strings.xml | 4 ++-- app/src/main/res/values-fr/strings.xml | 4 ++-- app/src/main/res/values-it/strings.xml | 4 ++-- app/src/main/res/values/strings.xml | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt index b7cd0f0..587da29 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt @@ -97,7 +97,7 @@ class LunaEvent: Comparable { return context.getString( when (type) { TYPE_BABY_BOTTLE -> R.string.event_bottle_type - TYPE_WEIGHT -> R.string.event_scale_type + TYPE_WEIGHT -> R.string.event_weight_type TYPE_BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type TYPE_BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type TYPE_BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type @@ -120,7 +120,7 @@ class LunaEvent: Comparable { return context.getString( when (type) { TYPE_BABY_BOTTLE -> R.string.event_bottle_desc - TYPE_WEIGHT -> R.string.event_scale_desc + TYPE_WEIGHT -> R.string.event_weight_desc TYPE_BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc TYPE_BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc TYPE_BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc diff --git a/app/src/main/res/layout/more_events_popup.xml b/app/src/main/res/layout/more_events_popup.xml index 34ee31b..b1dfbea 100644 --- a/app/src/main/res/layout/more_events_popup.xml +++ b/app/src/main/res/layout/more_events_popup.xml @@ -67,7 +67,7 @@ android:padding="10dp" android:background="@drawable/dropdown_list_item_background" style="@style/OverflowMenuText" - android:text="@string/overflow_event_scale"/> + android:text="@string/overflow_event_weight"/> Fläschchen Essen - Gewicht + Gewicht Stillen (links) Stillen Stillen (rechts) @@ -27,7 +27,7 @@ Blähungskolik - ⚖️ Gewicht + ⚖️ Gewicht 💊 Medikament 🪠 Einlauf 📝 Notiz diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 3d783db..0afd508 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -14,7 +14,7 @@ Biberon Nourriture - Poids + Poids Allaitement (sein gauche) Allaitement Allaitement (sein droit) @@ -27,7 +27,7 @@ Colique gazeuse - ⚖️ Poids + ⚖️ Poids 💊 Médicament 🪠 Lavement 📝 Note diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index e6adaa2..69dc200 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -12,7 +12,7 @@ Temperatura Inserisci la temperatura - ⚖️ Peso + ⚖️ Peso 💊 Medicina 🪠 Clistere 📝 Nota @@ -21,7 +21,7 @@ Biberon Cibo - Pesata + Pesata Allatt. al seno (sx) Allatt. al seno Allatt. al seno (dx) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index afd4eab..6242696 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -17,7 +17,7 @@ 🍼 🥣 - ⚖️ + ⚖️ 🤱 ← 🤱 ↔ 🤱 → @@ -34,7 +34,7 @@ Baby bottle Food - Weight + Weight Breastfeeding (left) Breastfeeding Breastfeeding (right) @@ -49,7 +49,7 @@ Bath - ⚖️ Weight + ⚖️ Weight 💊 Medicine 🪠 Enema 📝 Note From 386586ccaedbe94d36539327436496e8b8defdf5 Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Wed, 12 Nov 2025 22:47:09 +0100 Subject: [PATCH 06/37] MainActivity: allow amount for poo and pee events An unspecified amount has also been added to have the same semantics as before. During these actions, the strings for title and description of dialogs have been cleaned up. --- .../lunatracker/MainActivity.kt | 41 ++++++++++--------- .../lunatracker/entities/LunaEvent.kt | 16 ++++++-- app/src/main/java/utils/NumericUtils.kt | 12 +++++- ...g_edit_puke.xml => dialog_edit_amount.xml} | 2 +- app/src/main/res/values-de/strings.xml | 16 +++----- app/src/main/res/values-fr/strings.xml | 16 +++----- app/src/main/res/values-it/strings.xml | 16 +++----- app/src/main/res/values/arrays.xml | 1 + app/src/main/res/values/strings.xml | 22 ++++------ 9 files changed, 69 insertions(+), 73 deletions(-) rename app/src/main/res/layout/{dialog_edit_puke.xml => dialog_edit_amount.xml} (93%) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 3a4fa6d..2f4fea2 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -100,10 +100,10 @@ class MainActivity : AppCompatActivity() { addPlainEvent(LunaEvent(LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE)) } findViewById(R.id.button_change_poo).setOnClickListener { - addPlainEvent(LunaEvent(LunaEvent.TYPE_DIAPERCHANGE_POO)) + addAmountEvent(LunaEvent(LunaEvent.TYPE_DIAPERCHANGE_POO)) } findViewById(R.id.button_change_pee).setOnClickListener { - addPlainEvent(LunaEvent(LunaEvent.TYPE_DIAPERCHANGE_PEE)) + addAmountEvent(LunaEvent(LunaEvent.TYPE_DIAPERCHANGE_PEE)) } val moreButton = findViewById(R.id.button_more) moreButton.setOnClickListener { @@ -205,8 +205,8 @@ class MainActivity : AppCompatActivity() { fun askBabyBottleContent(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { val d = AlertDialog.Builder(this) val dialogView = layoutInflater.inflate(R.layout.dialog_edit_bottle, null) - d.setTitle(R.string.log_bottle_dialog_title) - d.setMessage(R.string.log_bottle_dialog_description) + d.setTitle(event.getTypeDescription(this)) + d.setMessage(event.getDialogMessage(this)) d.setView(dialogView) val numberPicker = dialogView.findViewById(R.id.dialog_number_picker) @@ -247,8 +247,8 @@ class MainActivity : AppCompatActivity() { // Show number picker dialog val d = AlertDialog.Builder(this) val dialogView = layoutInflater.inflate(R.layout.dialog_edit_weight, null) - d.setTitle(R.string.log_weight_dialog_title) - d.setMessage(R.string.log_weight_dialog_description) + d.setTitle(event.getTypeDescription(this)) + d.setMessage(event.getDialogMessage(this)) d.setView(dialogView) val weightET = dialogView.findViewById(R.id.dialog_number_edittext) @@ -291,8 +291,8 @@ class MainActivity : AppCompatActivity() { // Show number picker dialog val d = AlertDialog.Builder(this) val dialogView = layoutInflater.inflate(R.layout.dialog_edit_temperature, null) - d.setTitle(R.string.log_temperature_dialog_title) - d.setMessage(R.string.log_temperature_dialog_description) + d.setTitle(event.getTypeDescription(this)) + d.setMessage(event.getDialogMessage(this)) d.setView(dialogView) val tempSlider = dialogView.findViewById(R.id.dialog_temperature_value) @@ -371,24 +371,25 @@ class MainActivity : AppCompatActivity() { saveLogbook() } - fun addPukeEvent(event: LunaEvent) { - askPukeValue(event, true) { saveEvent(event) } + fun addAmountEvent(event: LunaEvent) { + askAmountValue(event, true) { saveEvent(event) } } - fun askPukeValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { + fun askAmountValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { val d = AlertDialog.Builder(this) - val dialogView = layoutInflater.inflate(R.layout.dialog_edit_puke, null) - d.setTitle(R.string.log_puke_dialog_title) - d.setMessage(R.string.log_puke_dialog_description) + val dialogView = layoutInflater.inflate(R.layout.dialog_edit_amount, null) + d.setTitle(event.getTypeDescription(this)) + d.setMessage(event.getDialogMessage(this)) d.setView(dialogView) - val spinner = dialogView.findViewById(R.id.dialog_puke_value) + val spinner = dialogView.findViewById(R.id.dialog_amount_value) spinner.adapter = ArrayAdapter.createFromResource( this, R.array.AmountLabels, android.R.layout.simple_spinner_dropdown_item ) - spinner.setSelection(event.quantity - 1) + // set pre-selected item and ensure the quantity to index is in bounds + spinner.setSelection(event.quantity.coerceIn(0, spinner.count - 1)) val dateTV = dialogView.findViewById(R.id.dialog_date_picker) val pickedTime = datePickerHelper(event.time, dateTV) @@ -398,7 +399,7 @@ class MainActivity : AppCompatActivity() { d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> event.time = pickedTime.time.time / 1000 - event.quantity = spinner.selectedItemPosition + 1 + event.quantity = spinner.selectedItemPosition onPositive() dialogInterface.dismiss() } @@ -637,7 +638,9 @@ class MainActivity : AppCompatActivity() { when (event.type) { LunaEvent.TYPE_BABY_BOTTLE -> askBabyBottleContent(event, false, updateValues) LunaEvent.TYPE_WEIGHT -> askWeightValue(event, false, updateValues) - LunaEvent.TYPE_PUKE -> askPukeValue(event, false, updateValues) + LunaEvent.TYPE_DIAPERCHANGE_POO, + LunaEvent.TYPE_DIAPERCHANGE_PEE, + LunaEvent.TYPE_PUKE -> askAmountValue(event, false, updateValues) LunaEvent.TYPE_TEMPERATURE -> askTemperatureValue(event, false, updateValues) LunaEvent.TYPE_NOTE -> askNotes(event, false, updateValues) } @@ -1094,7 +1097,7 @@ class MainActivity : AppCompatActivity() { dismiss() } contentView.findViewById(R.id.button_puke).setOnClickListener { - addPukeEvent(LunaEvent(LunaEvent.TYPE_PUKE, 1)) + addAmountEvent(LunaEvent(LunaEvent.TYPE_PUKE)) dismiss() } contentView.findViewById(R.id.button_colic).setOnClickListener { diff --git a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt index 587da29..1eade79 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt @@ -140,10 +140,18 @@ class LunaEvent: Comparable { } fun getDialogMessage(context: Context): String? { - return when(type) { - TYPE_MEDICINE -> context.getString(R.string.log_medicine_dialog_description) - else -> null - } + return context.getString( + when(type) { + TYPE_BABY_BOTTLE -> R.string.log_bottle_dialog_description + TYPE_MEDICINE -> R.string.log_medicine_dialog_description + TYPE_TEMPERATURE -> R.string.log_temperature_dialog_description + TYPE_DIAPERCHANGE_POO, + TYPE_DIAPERCHANGE_PEE, + TYPE_PUKE -> R.string.log_amount_dialog_description + TYPE_WEIGHT -> R.string.log_weight_dialog_description + else -> R.string.log_unknown_dialog_description + } + ) } fun toJson(): JSONObject { diff --git a/app/src/main/java/utils/NumericUtils.kt b/app/src/main/java/utils/NumericUtils.kt index 23cd5c6..8d12c35 100644 --- a/app/src/main/java/utils/NumericUtils.kt +++ b/app/src/main/java/utils/NumericUtils.kt @@ -4,6 +4,7 @@ import android.content.Context import android.icu.util.LocaleData import android.icu.util.ULocale import android.os.Build +import android.util.Log import it.danieleverducci.lunatracker.R import it.danieleverducci.lunatracker.entities.LunaEvent import java.text.NumberFormat @@ -66,8 +67,15 @@ class NumericUtils (val context: Context) { formatted.append(when (event.type) { LunaEvent.TYPE_TEMPERATURE -> (event.quantity / 10.0f).toString() - LunaEvent.TYPE_PUKE -> - context.resources.getStringArray(R.array.AmountLabels)[event.quantity - 1] + LunaEvent.TYPE_DIAPERCHANGE_POO, + LunaEvent.TYPE_DIAPERCHANGE_PEE, + LunaEvent.TYPE_PUKE -> { + val array = context.resources.getStringArray(R.array.AmountLabels) + return array.getOrElse(event.quantity) { + Log.e("NumericUtils", "Invalid index ${event.quantity}") + return "" + } + } else -> event.quantity }) diff --git a/app/src/main/res/layout/dialog_edit_puke.xml b/app/src/main/res/layout/dialog_edit_amount.xml similarity index 93% rename from app/src/main/res/layout/dialog_edit_puke.xml rename to app/src/main/res/layout/dialog_edit_amount.xml index 7cea41c..653713e 100644 --- a/app/src/main/res/layout/dialog_edit_puke.xml +++ b/app/src/main/res/layout/dialog_edit_amount.xml @@ -8,7 +8,7 @@ android:orientation="vertical"> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e76bd4f..b7ddd7d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -3,15 +3,6 @@ 🌜 LunaTracker 🌛 Ereignisprotokoll - Fläschchen - Trinkmenge eingeben - - Gewicht - Gewicht eingeben - - Temperatur - Temperatur eingeben - Fläschchen Essen Gewicht @@ -77,10 +68,13 @@ Jetzt bereinigen Später erinnern - Notizen: + Trinkmenge eingeben Medikamentenname, Menge, Art, Notizen, …: - Menge (optional) + Notizen: Notiz eingeben + Menge (optional) + Temperatur eingeben + Gewicht eingeben Ereignisdetails OK diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0afd508..9364887 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -3,15 +3,6 @@ 🌜 LunaTracker 🌛 Entrées enregistrées - Biberon - Renseignez la quantité contenue dans le biberon - - Poids - Renseignez le poids - - Température - Renseignez la Température - Biberon Nourriture Poids @@ -76,10 +67,13 @@ Supprimer les vieilles entrées maintenant Me rappeller plus tard - Notes: + Renseignez la quantité contenue dans le biberon nom du médicament, quantité, type, notes …: - Quantité (ou vide) + Notes: Notes ... + Quantité (ou vide) + Renseignez la Température + Renseignez le poids Détails de l\'entrée OK diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 69dc200..3e1fdd2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -3,15 +3,6 @@ 🌜 LunaTracker 🌛 Diario di bordo - Biberon - Inserisci la quantità contenuta nel biberon - - Pesata - Inserisci il peso rilevato - - Temperatura - Inserisci la temperatura - ⚖️ Peso 💊 Medicina 🪠 Clistere @@ -76,10 +67,13 @@ Cancella i più vecchi Ricordamelo dopo - Note: + Inserisci la quantità contenuta nel biberon Nome della medicina, quantità, formato, note…: - Quantità, o vuoto + Note: Inserisci le note + Quantità, o vuoto + Inserisci la temperatura + Inserisci il peso rilevato Dettaglio evento OK diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 4f5ae1a..87a93d3 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -1,6 +1,7 @@ + @string/amount_unspecified @string/amount_little @string/amount_normal @string/amount_plenty diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6242696..487a06c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,18 +3,6 @@ 🌜 LunaTracker 🌛 Logged events - Baby bottle - Insert the quantity contained in the baby bottle - - Weight - Insert the weight - - Temperature - Insert the temperature - - Puke - Select the amount - 🍼 🥣 ⚖️ @@ -75,6 +63,7 @@ year years + Little Normal Plenty @@ -114,10 +103,15 @@ Trim it now Remind me later - Notes: + Select the amount: + Insert the quantity contained in the baby bottle: Medicine name, quantity, type, notes…: - Quantity (or empty) + Notes: Write some notes + Quantity (or empty) + Select the temperature: + + Insert the weight: ml g From 85896d0f977fe55d15c5a5fcb3281f96df27110e Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Fri, 14 Nov 2025 21:42:35 +0100 Subject: [PATCH 07/37] LunaEvent: add sleep event --- .../lunatracker/MainActivity.kt | 120 +++++++++++++++++- .../adapters/LunaEventRecyclerAdapter.kt | 2 +- .../lunatracker/entities/LunaEvent.kt | 16 +++ app/src/main/java/utils/DateUtils.kt | 4 +- app/src/main/java/utils/NumericUtils.kt | 7 + .../main/res/layout/dialog_edit_duration.xml | 56 ++++++++ .../main/res/layout/dialog_event_details.xml | 12 ++ app/src/main/res/layout/more_events_popup.xml | 10 ++ app/src/main/res/values/strings.xml | 6 + 9 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 app/src/main/res/layout/dialog_edit_duration.xml diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 2f4fea2..31cdfdf 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -12,6 +12,7 @@ import android.view.View import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter +import android.widget.Button import android.widget.EditText import android.widget.NumberPicker import android.widget.PopupWindow @@ -330,7 +331,7 @@ class MainActivity : AppCompatActivity() { alertDialog.show() } - fun datePickerHelper(time: Long, dateTextView: TextView): Calendar { + fun datePickerHelper(time: Long, dateTextView: TextView, onChange: (Long) -> Unit = {}): Calendar { dateTextView.text = DateUtils.formatDateTime(time) val dateTime = Calendar.getInstance() @@ -349,6 +350,7 @@ class MainActivity : AppCompatActivity() { { _, hour, minute -> dateTime.set(year, month, day, hour, minute) dateTextView.text = DateUtils.formatDateTime(dateTime.time.time / 1000) + onChange.invoke(dateTime.time.time / 1000) }, startHour, startMinute, @@ -371,7 +373,103 @@ class MainActivity : AppCompatActivity() { saveLogbook() } + fun addSleepEvent(event: LunaEvent) { + askSleepValue(event, true) { saveEvent(event) } + } + + fun askSleepValue(event: LunaEvent, hideDurationButtons: Boolean, onPositive: () -> Unit) { + val d = AlertDialog.Builder(this) + val dialogView = layoutInflater.inflate(R.layout.dialog_edit_duration, null) + d.setTitle(event.getTypeDescription(this)) + d.setMessage(event.getDialogMessage(this)) + d.setView(dialogView) + + val durationTextView = dialogView.findViewById(R.id.dialog_date_duration) + val datePicker = dialogView.findViewById(R.id.dialog_date_picker) + val durationButtons = dialogView.findViewById(R.id.duration_buttons) + val durationNowButton = dialogView.findViewById