4 Commits

Author SHA1 Message Date
6af9c09a89 more_events_popup: move enema to bottom and adjust padding
Enemas are usually are rare thing. Let's
move it to the bottom. Also adjust padding
to have more space to display all items.
2025-09-26 16:28:59 +02:00
af88ec5ace add puke event 2025-09-26 16:28:55 +02:00
eeebaeb7f3 NumericUtils: provide fallback for LocaleData.getMeasurementSystem
LocaleData.getMeasurementSystem is available at API level 28
but the app supports API level 21.
2025-09-25 23:51:13 +02:00
242bfe39ae small code cleanup 2025-09-25 23:46:46 +02:00
23 changed files with 339 additions and 710 deletions

View File

@@ -12,8 +12,8 @@ android {
applicationId = "it.danieleverducci.lunatracker"
minSdk = 21
targetSdk = 34
versionCode = 7
versionName = "0.9"
versionCode = 5
versionName = "0.7"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -41,14 +41,15 @@ import okio.IOException
import org.json.JSONException
import utils.DateUtils
import utils.NumericUtils
import java.text.DateFormat
import java.util.Calendar
import java.util.Date
class MainActivity : AppCompatActivity() {
companion object {
const val TAG = "MainActivity"
const val UPDATE_EVERY_SECS: Long = 30
const val DEBUG_CHECK_LOGBOOK_CONSISTENCY = false
val TAG = "MainActivity"
val UPDATE_EVERY_SECS: Long = 30
val DEBUG_CHECK_LOGBOOK_CONSISTENCY = false
}
var logbook: Logbook? = null
@@ -57,7 +58,6 @@ class MainActivity : AppCompatActivity() {
lateinit var buttonsContainer: ViewGroup
lateinit var recyclerView: RecyclerView
lateinit var handler: Handler
var signature = ""
var savingEvent = false
val updateListRunnable: Runnable = Runnable {
if (logbook != null && !pauseLogbookUpdate)
@@ -81,54 +81,58 @@ class MainActivity : AppCompatActivity() {
recyclerView.setLayoutManager(LinearLayoutManager(applicationContext))
// Set listeners
findViewById<View>(R.id.logbooks_add_button).setOnClickListener {
showAddLogbookDialog(true)
}
findViewById<View>(R.id.button_bottle).setOnClickListener {
addBabyBottleEvent()
}
findViewById<View>(R.id.button_food).setOnClickListener {
addNoteEvent(LunaEvent(LunaEvent.TYPE_FOOD))
}
findViewById<View>(R.id.button_nipple_left).setOnClickListener {
addPlainEvent(LunaEvent(LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE))
}
findViewById<View>(R.id.button_nipple_both).setOnClickListener {
addPlainEvent(LunaEvent(LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE))
}
findViewById<View>(R.id.button_nipple_right).setOnClickListener {
addPlainEvent(LunaEvent(LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE))
}
findViewById<View>(R.id.button_change_poo).setOnClickListener {
addAmountEvent(LunaEvent(LunaEvent.TYPE_DIAPERCHANGE_POO))
}
findViewById<View>(R.id.button_change_pee).setOnClickListener {
addAmountEvent(LunaEvent(LunaEvent.TYPE_DIAPERCHANGE_PEE))
}
findViewById<View>(R.id.logbooks_add_button).setOnClickListener { showAddLogbookDialog(true) }
findViewById<View>(R.id.button_bottle).setOnClickListener { askBabyBottleContent() }
findViewById<View>(R.id.button_food).setOnClickListener { askNotes(LunaEvent(LunaEvent.TYPE_FOOD)) }
findViewById<View>(R.id.button_nipple_left).setOnClickListener { logEvent(
LunaEvent(
LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE
)
) }
findViewById<View>(R.id.button_nipple_both).setOnClickListener { logEvent(
LunaEvent(
LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE
)
) }
findViewById<View>(R.id.button_nipple_right).setOnClickListener { logEvent(
LunaEvent(
LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE
)
) }
findViewById<View>(R.id.button_change_poo).setOnClickListener { logEvent(
LunaEvent(
LunaEvent.TYPE_DIAPERCHANGE_POO
)
) }
findViewById<View>(R.id.button_change_pee).setOnClickListener { logEvent(
LunaEvent(
LunaEvent.TYPE_DIAPERCHANGE_PEE
)
) }
val moreButton = findViewById<View>(R.id.button_more)
moreButton.setOnClickListener {
showOverflowPopupWindow(moreButton)
}
findViewById<View>(R.id.button_no_connection_settings).setOnClickListener {
findViewById<View>(R.id.button_no_connection_settings).setOnClickListener({
showSettings()
}
findViewById<View>(R.id.button_settings).setOnClickListener {
})
findViewById<View>(R.id.button_settings).setOnClickListener({
showSettings()
}
findViewById<View>(R.id.button_no_connection_retry).setOnClickListener {
})
findViewById<View>(R.id.button_no_connection_retry).setOnClickListener({
// This may happen at start, when logbook is still null: better ask the logbook list
loadLogbookList()
}
findViewById<View>(R.id.button_sync).setOnClickListener {
})
findViewById<View>(R.id.button_sync).setOnClickListener({
loadLogbookList()
}
})
}
private fun setListAdapter(items: ArrayList<LunaEvent>) {
val adapter = LunaEventRecyclerAdapter(this, items)
adapter.onItemClickListener = object: LunaEventRecyclerAdapter.OnItemClickListener{
override fun onItemClick(event: LunaEvent) {
showEventDetailDialog(event)
showEventDetailDialog(event, items)
}
}
recyclerView.adapter = adapter
@@ -165,8 +169,6 @@ class MainActivity : AppCompatActivity() {
logbookRepo = FileLogbookRepository()
}
signature = settingsRepository.loadSignature()
val noBreastfeeding = settingsRepository.loadNoBreastfeeding()
findViewById<View>(R.id.layout_nipples).visibility = when (noBreastfeeding) {
true -> View.GONE
@@ -191,314 +193,118 @@ class MainActivity : AppCompatActivity() {
super.onStop()
}
fun getAllEvents(): ArrayList<LunaEvent> {
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) {
fun askBabyBottleContent() {
// Show number picker dialog
val localSettings = LocalSettingsRepository(this)
val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.dialog_edit_bottle, null)
d.setTitle(event.getTypeDescription(this))
d.setMessage(event.getDialogMessage(this))
val dialogView = layoutInflater.inflate(R.layout.number_picker_dialog, null)
d.setTitle(R.string.log_bottle_dialog_title)
d.setMessage(R.string.log_bottle_dialog_description)
d.setView(dialogView)
val numberPicker = dialogView.findViewById<NumberPicker>(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 = event.quantity / 10
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = datePickerHelper(event.time, dateTV)
if (!showTime) {
dateTV.visibility = View.GONE
}
numberPicker.value = localSettings.loadBabyBottleContent()
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
event.time = pickedTime.time.time / 1000
event.quantity = numberPicker.value * 10
onPositive()
dialogInterface.dismiss()
logEvent(LunaEvent(LunaEvent.TYPE_BABY_BOTTLE, numberPicker.value * 10))
localSettings.saveBabyBottleContent(numberPicker.value)
}
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 addWeightEvent(event: LunaEvent) {
askWeightValue(event, true) { saveEvent(event) }
}
fun askWeightValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) {
fun askWeightValue() {
// Show number picker dialog
val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.dialog_edit_weight, null)
d.setTitle(event.getTypeDescription(this))
d.setMessage(event.getDialogMessage(this))
val dialogView = layoutInflater.inflate(R.layout.number_edit_dialog, null)
d.setTitle(R.string.log_weight_dialog_title)
d.setMessage(R.string.log_weight_dialog_description)
d.setView(dialogView)
val weightET = dialogView.findViewById<EditText>(R.id.dialog_number_edittext)
weightET.setText(event.quantity.toString())
val dateTV = dialogView.findViewById<TextView>(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) {
event.time = pickedTime.time.time / 1000
event.quantity = weight
onPositive()
} else {
if (weight != null)
logEvent(LunaEvent(LunaEvent.TYPE_WEIGHT, weight))
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 addTemperatureEvent(event: LunaEvent) {
askTemperatureValue(event, true) { saveEvent(event) }
}
fun askTemperatureValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) {
fun askTemperatureValue() {
// Show number picker dialog
val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.dialog_edit_temperature, null)
d.setTitle(event.getTypeDescription(this))
d.setMessage(event.getDialogMessage(this))
val dialogView = layoutInflater.inflate(R.layout.temperature_dialog, null)
d.setTitle(R.string.log_temperature_dialog_title)
d.setMessage(R.string.log_temperature_dialog_description)
d.setView(dialogView)
val tempSlider = dialogView.findViewById<Slider>(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 = if (event.quantity == 0) {
range.third.toFloat() // default
} else {
event.quantity.toFloat() / 10
}
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = datePickerHelper(event.time, dateTV)
if (!showTime) {
dateTV.visibility = View.GONE
}
tempSlider.value = range.third.toFloat()
val tempTextView = dialogView.findViewById<TextView>(R.id.dialog_temperature_display)
tempTextView.text = tempSlider.value.toString()
tempTextView.text = range.third.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()
val temperature = (tempSlider.value * 10).toInt() // In tenth of a grade
logEvent(LunaEvent(LunaEvent.TYPE_TEMPERATURE, temperature))
}
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 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 addAmountEvent(event: LunaEvent) {
askAmountValue(event, true) { saveEvent(event) }
}
fun askAmountValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) {
fun askPukeValue() {
val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.dialog_edit_amount, null)
d.setTitle(event.getTypeDescription(this))
d.setMessage(event.getDialogMessage(this))
val dialogView = layoutInflater.inflate(R.layout.puke_dialog, null)
d.setTitle(R.string.log_puke_dialog_title)
d.setMessage(R.string.log_puke_dialog_description)
d.setView(dialogView)
val spinner = dialogView.findViewById<Spinner>(R.id.dialog_amount_value)
spinner.adapter = ArrayAdapter.createFromResource(
this,
R.array.AmountLabels,
android.R.layout.simple_spinner_dropdown_item
)
// set pre-selected item and ensure the quantity to index is in bounds
spinner.setSelection(event.quantity.coerceIn(0, spinner.count - 1))
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = datePickerHelper(event.time, dateTV)
if (!showTime) {
dateTV.visibility = View.GONE
}
val spinner = dialogView.findViewById<Spinner>(R.id.dialog_puke_value)
spinner.adapter = ArrayAdapter.createFromResource(this, R.array.AmountLabels, android.R.layout.simple_spinner_dropdown_item)
spinner.setSelection(1)
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
event.time = pickedTime.time.time / 1000
event.quantity = spinner.selectedItemPosition
onPositive()
dialogInterface.dismiss()
val pos = spinner.selectedItemPosition
logEvent(LunaEvent(LunaEvent.TYPE_PUKE, pos))
}
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 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) {
fun askNotes(lunaEvent: LunaEvent) {
val d = AlertDialog.Builder(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<TextView>(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))
val dialogView = layoutInflater.inflate(R.layout.dialog_notes, null)
d.setTitle(lunaEvent.getTypeDescription(this))
d.setMessage(lunaEvent.getDialogMessage(this))
d.setView(dialogView)
val notesET = dialogView.findViewById<EditText>(R.id.notes_edittext)
val qtyET = dialogView.findViewById<EditText>(R.id.notes_qty_edittext)
val dateTV = dialogView.findViewById<TextView>(R.id.notes_date_picker)
val pickedTime = datePickerHelper(event.time, dateTV)
if (!showTime) {
dateTV.visibility = View.GONE
}
notesET.setText(event.notes)
if (useQuantity) {
qtyET.setText(event.quantity.toString())
} else {
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()
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(applicationContext, R.string.toast_integer_error, Toast.LENGTH_SHORT).show()
lunaEvent.notes = notes
logEvent(lunaEvent)
}
} else {
event.time = pickedTime.time.time / 1000
event.notes = notes
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()
}
@@ -551,120 +357,84 @@ class MainActivity : AppCompatActivity() {
return nextEvent
}
fun showEventDetailDialog(originalEvent: LunaEvent) {
val event = LunaEvent(originalEvent)
fun showEventDetailDialog(event: LunaEvent, items: ArrayList<LunaEvent>) {
// 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 dateFormat = DateFormat.getDateTimeInstance()
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<TextView>(R.id.dialog_event_detail_type_emoji).text = event.getTypeEmoji(this)
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_description).text = event.getTypeDescription(this)
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_quantity).text =
NumericUtils(this).formatEventQuantity(event)
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_notes).text = event.notes
val dialogView = layoutInflater.inflate(R.layout.dialog_event_details, null)
val emojiTextView = dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_emoji)
val descriptionTextView = dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_description)
val currentDateTime = Calendar.getInstance()
currentDateTime.time = Date(event.time * 1000)
val dateTextView = dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_date)
val quantityTextView = dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_quantity)
val notesTextView = dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_notes)
dateTextView.text = String.format(getString(R.string.dialog_event_detail_datetime_icon), dateFormat.format(currentDateTime.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)
emojiTextView.text = event.getTypeEmoji(this)
descriptionTextView.text = event.getTypeDescription(this)
DatePickerDialog(this, { _, year, month, day ->
TimePickerDialog(this, { _, hour, minute ->
val pickedDateTime = Calendar.getInstance()
pickedDateTime.set(year, month, day, hour, minute)
currentDateTime.time = pickedDateTime.time
dateTextView.text = String.format(getString(R.string.dialog_event_detail_datetime_icon), dateFormat.format(currentDateTime.time))
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_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)
}
}
notesTextView.setOnClickListener {
when (event.type) {
LunaEvent.TYPE_FOOD,
LunaEvent.TYPE_MEDICINE,
LunaEvent.TYPE_NOTE -> askNotes(event, false, updateValues)
}
// Save event and move it to the right position in the logbook
event.time = currentDateTime.time.time / 1000 // Seconds since epoch
logbook?.sort()
recyclerView.adapter?.notifyDataSetChanged()
saveLogbook()
}, startHour, startMinute, android.text.format.DateFormat.is24HourFormat(this@MainActivity)).show()
}, startYear, startMonth, startDay).show()
}
d.setView(dialogView)
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()
}
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) }
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()) {
val signatureTextEdit = dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_signature)
signatureTextEdit.text = String.format(getString(R.string.dialog_event_detail_signature), event.signature)
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<TextView>(R.id.dialog_event_previous)
val previousEvent = getPreviousSameEvent(event, allEvents)
val nextTextView = dialogView.findViewById<TextView>(R.id.dialog_event_next)
val nextEvent = getNextSameEvent(event, items)
val previousEvent = getPreviousSameEvent(event, items)
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)
showEventDetailDialog(previousEvent, items)
}
} else {
previousTextView.visibility = View.GONE
}
// create link to next event of the same type
val nextTextView = dialogView.findViewById<TextView>(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)
showEventDetailDialog(nextEvent, items)
}
} else {
nextTextView.visibility = View.GONE
@@ -772,7 +542,7 @@ class MainActivity : AppCompatActivity() {
runOnUiThread({
setLoading(false)
loadLogbookList()
Toast.makeText(applicationContext, getString(R.string.logbook_created) + logbookName, Toast.LENGTH_SHORT).show()
Toast.makeText(this@MainActivity, getString(R.string.logbook_created) + logbookName, Toast.LENGTH_SHORT).show()
})
}
@@ -888,8 +658,6 @@ class MainActivity : AppCompatActivity() {
fun logEvent(event: LunaEvent) {
savingEvent(true)
event.signature = signature
setLoading(true)
logbook?.logs?.add(0, event)
recyclerView.adapter?.notifyItemInserted(0)
@@ -929,7 +697,7 @@ class MainActivity : AppCompatActivity() {
setLoading(false)
Toast.makeText(
applicationContext,
this@MainActivity,
if (lastEventAdded != null)
R.string.toast_event_added
else
@@ -991,7 +759,7 @@ class MainActivity : AppCompatActivity() {
runOnUiThread({
setLoading(false)
Toast.makeText(applicationContext, R.string.toast_event_add_error, Toast.LENGTH_SHORT).show()
Toast.makeText(this@MainActivity, R.string.toast_event_add_error, Toast.LENGTH_SHORT).show()
recyclerView.adapter?.notifyDataSetChanged()
savingEvent(false)
})
@@ -1024,37 +792,35 @@ class MainActivity : AppCompatActivity() {
val inflater = LayoutInflater.from(anchor.context)
contentView = inflater.inflate(R.layout.more_events_popup, null)
contentView.findViewById<View>(R.id.button_medicine).setOnClickListener {
addNoteEvent(LunaEvent(LunaEvent.TYPE_MEDICINE))
askNotes(LunaEvent(LunaEvent.TYPE_MEDICINE))
dismiss()
}
contentView.findViewById<View>(R.id.button_enema).setOnClickListener {
addPlainEvent(LunaEvent(LunaEvent.TYPE_ENEMA))
contentView.findViewById<View>(R.id.button_enema).setOnClickListener({
logEvent(LunaEvent(LunaEvent.TYPE_ENEMA))
dismiss()
}
contentView.findViewById<View>(R.id.button_note).setOnClickListener {
addNoteEvent(LunaEvent(LunaEvent.TYPE_NOTE))
})
contentView.findViewById<View>(R.id.button_note).setOnClickListener({
askNotes(LunaEvent(LunaEvent.TYPE_NOTE))
dismiss()
}
contentView.findViewById<View>(R.id.button_temperature).setOnClickListener {
addTemperatureEvent(LunaEvent(LunaEvent.TYPE_TEMPERATURE))
})
contentView.findViewById<View>(R.id.button_temperature).setOnClickListener({
askTemperatureValue()
dismiss()
}
contentView.findViewById<View>(R.id.button_puke).setOnClickListener {
addAmountEvent(LunaEvent(LunaEvent.TYPE_PUKE))
})
contentView.findViewById<View>(R.id.button_puke).setOnClickListener({
askPukeValue()
dismiss()
}
contentView.findViewById<View>(R.id.button_colic).setOnClickListener {
addPlainEvent(LunaEvent(LunaEvent.TYPE_COLIC))
})
contentView.findViewById<View>(R.id.button_colic).setOnClickListener({
logEvent(
LunaEvent(LunaEvent.TYPE_COLIC)
)
dismiss()
}
contentView.findViewById<View>(R.id.button_scale).setOnClickListener {
addWeightEvent(LunaEvent(LunaEvent.TYPE_WEIGHT))
})
contentView.findViewById<View>(R.id.button_scale).setOnClickListener({
askWeightValue()
dismiss()
}
contentView.findViewById<View>(R.id.button_bath).setOnClickListener {
addPlainEvent(LunaEvent(LunaEvent.TYPE_BATH))
dismiss()
}
})
}.also { popupWindow ->
popupWindow.setOnDismissListener({
Handler(mainLooper).postDelayed({

View File

@@ -2,7 +2,6 @@ package it.danieleverducci.lunatracker
import android.os.Bundle
import android.view.View
import android.widget.EditText
import android.widget.RadioButton
import android.widget.TextView
import android.widget.Toast
@@ -25,7 +24,6 @@ open class SettingsActivity : AppCompatActivity() {
protected lateinit var textViewWebDAVPass: TextView
protected lateinit var progressIndicator: LinearProgressIndicator
protected lateinit var switchNoBreastfeeding: SwitchMaterial
protected lateinit var textViewSignature: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -38,7 +36,6 @@ open class SettingsActivity : AppCompatActivity() {
textViewWebDAVPass = findViewById(R.id.settings_data_webdav_pass)
progressIndicator = findViewById(R.id.progress_indicator)
switchNoBreastfeeding = findViewById(R.id.switch_no_breastfeeding)
textViewSignature = findViewById(R.id.settings_signature)
findViewById<View>(R.id.settings_save).setOnClickListener({
validateAndSave()
@@ -55,20 +52,18 @@ open class SettingsActivity : AppCompatActivity() {
val dataRepo = settingsRepository.loadDataRepository()
val webDavCredentials = settingsRepository.loadWebdavCredentials()
val noBreastfeeding = settingsRepository.loadNoBreastfeeding()
val signature = settingsRepository.loadSignature()
when (dataRepo) {
LocalSettingsRepository.DATA_REPO.LOCAL_FILE -> radioDataLocal.isChecked = true
LocalSettingsRepository.DATA_REPO.WEBDAV -> radioDataWebDAV.isChecked = true
}
textViewSignature.setText(signature)
switchNoBreastfeeding.isChecked = noBreastfeeding
if (webDavCredentials != null) {
textViewWebDAVUrl.text = webDavCredentials[0]
textViewWebDAVUser.text = webDavCredentials[1]
textViewWebDAVPass.text = webDavCredentials[2]
textViewWebDAVUrl.setText(webDavCredentials[0])
textViewWebDAVUser.setText(webDavCredentials[1])
textViewWebDAVPass.setText(webDavCredentials[2])
}
}
@@ -161,7 +156,6 @@ open class SettingsActivity : AppCompatActivity() {
else LocalSettingsRepository.DATA_REPO.LOCAL_FILE
)
settingsRepository.saveNoBreastfeeding(switchNoBreastfeeding.isChecked)
settingsRepository.saveSignature(textViewSignature.text.toString())
settingsRepository.saveWebdavCredentials(
textViewWebDAVUrl.text.toString(),
textViewWebDAVUser.text.toString(),
@@ -176,7 +170,7 @@ open class SettingsActivity : AppCompatActivity() {
*/
private fun copyLocalLogbooksToWebdav(webDAVLogbookRepository: WebDAVLogbookRepository, listener: OnCopyLocalLogbooksToWebdavFinishedListener) {
Thread(Runnable {
val errors = StringBuilder()
var errors = StringBuilder()
val fileLogbookRepo = FileLogbookRepository()
val logbooks = fileLogbookRepo.getAllLogbooks(this)
for (logbook in logbooks) {

View File

@@ -29,7 +29,6 @@ class LunaEvent: Comparable<LunaEvent> {
const val TYPE_TEMPERATURE = "TEMPERATURE"
const val TYPE_FOOD = "FOOD"
const val TYPE_PUKE = "PUKE"
const val TYPE_BATH = "BATH"
}
private val jo: JSONObject
@@ -45,9 +44,9 @@ class LunaEvent: Comparable<LunaEvent> {
jo.put("type", value)
}
var quantity: Int
get() = jo.optInt("quantity", -1)
get() = jo.optInt("quantity")
set(value) {
if (value >= 0)
if (value > 0)
jo.put("quantity", value)
}
var notes: String
@@ -55,12 +54,6 @@ class LunaEvent: Comparable<LunaEvent> {
set(value) {
jo.put("notes", value)
}
var signature: String
get(): String = jo.optString("signature")
set(value) {
if (value.isNotEmpty())
jo.put("signature", value)
}
constructor(jo: JSONObject) {
this.jo = jo
@@ -69,15 +62,6 @@ class LunaEvent: Comparable<LunaEvent> {
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
@@ -95,7 +79,7 @@ class LunaEvent: Comparable<LunaEvent> {
return context.getString(
when (type) {
TYPE_BABY_BOTTLE -> R.string.event_bottle_type
TYPE_WEIGHT -> R.string.event_weight_type
TYPE_WEIGHT -> R.string.event_scale_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
@@ -108,7 +92,6 @@ class LunaEvent: Comparable<LunaEvent> {
TYPE_COLIC -> R.string.event_colic_type
TYPE_FOOD -> R.string.event_food_type
TYPE_PUKE -> R.string.event_puke_type
TYPE_BATH -> R.string.event_bath_type
else -> R.string.event_unknown_type
}
)
@@ -118,7 +101,7 @@ class LunaEvent: Comparable<LunaEvent> {
return context.getString(
when (type) {
TYPE_BABY_BOTTLE -> R.string.event_bottle_desc
TYPE_WEIGHT -> R.string.event_weight_desc
TYPE_WEIGHT -> R.string.event_scale_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
@@ -131,25 +114,16 @@ class LunaEvent: Comparable<LunaEvent> {
TYPE_COLIC -> R.string.event_colic_desc
TYPE_FOOD -> R.string.event_food_desc
TYPE_PUKE -> R.string.event_puke_desc
TYPE_BATH -> R.string.event_bath_desc
else -> R.string.event_unknown_desc
}
)
}
fun getDialogMessage(context: Context): String? {
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
return when(type) {
TYPE_MEDICINE -> context.getString(R.string.log_medicine_dialog_description)
else -> null
}
)
}
fun toJson(): JSONObject {
@@ -157,7 +131,7 @@ class LunaEvent: Comparable<LunaEvent> {
}
override fun toString(): String {
return "$type qty: $quantity time: ${Date(time * 1000)}"
return "${type} qty: $quantity time: ${Date(time * 1000)}"
}
override fun compareTo(other: LunaEvent): Int {

View File

@@ -7,14 +7,13 @@ import androidx.core.content.edit
class LocalSettingsRepository(val context: Context) {
companion object {
const val SHARED_PREFS_FILE_NAME = "lunasettings"
const val SHARED_PREFS_BB_CONTENT = "bbcontent"
const val SHARED_PREFS_DATA_REPO = "data_repo"
const val SHARED_PREFS_DAV_URL = "webdav_url"
const val SHARED_PREFS_DAV_USER = "webdav_user"
const val SHARED_PREFS_DAV_PASS = "webdav_password"
const val SHARED_PREFS_NO_BREASTFEEDING = "no_breastfeeding"
const val SHARED_PREFS_SIGNATURE = "signature"
val SHARED_PREFS_FILE_NAME = "lunasettings"
val SHARED_PREFS_BB_CONTENT = "bbcontent"
val SHARED_PREFS_DATA_REPO = "data_repo"
val SHARED_PREFS_DAV_URL = "webdav_url"
val SHARED_PREFS_DAV_USER = "webdav_user"
val SHARED_PREFS_DAV_PASS = "webdav_password"
val SHARED_PREFS_NO_BREASTFEEDING = "no_breastfeeding"
}
enum class DATA_REPO {LOCAL_FILE, WEBDAV}
val sharedPreferences: SharedPreferences
@@ -23,16 +22,16 @@ class LocalSettingsRepository(val context: Context) {
sharedPreferences = context.getSharedPreferences(SHARED_PREFS_FILE_NAME, MODE_PRIVATE)
}
fun saveSignature(content: String) {
sharedPreferences.edit { putString(SHARED_PREFS_SIGNATURE, content) }
fun saveBabyBottleContent(content: Int) {
sharedPreferences.edit().putInt(SHARED_PREFS_BB_CONTENT, content).apply()
}
fun loadSignature(): String {
return sharedPreferences.getString(SHARED_PREFS_SIGNATURE, "") ?: ""
fun loadBabyBottleContent(): Int {
return sharedPreferences.getInt(SHARED_PREFS_BB_CONTENT, 1)
}
fun saveNoBreastfeeding(content: Boolean) {
sharedPreferences.edit { putBoolean(SHARED_PREFS_NO_BREASTFEEDING, content) }
sharedPreferences.edit().putBoolean(SHARED_PREFS_NO_BREASTFEEDING, content).apply()
}
fun loadNoBreastfeeding(): Boolean {
@@ -40,15 +39,15 @@ class LocalSettingsRepository(val context: Context) {
}
fun saveDataRepository(repo: DATA_REPO) {
sharedPreferences.edit(commit = true) {
putString(
val spe = sharedPreferences.edit()
spe.putString(
SHARED_PREFS_DATA_REPO,
when (repo) {
DATA_REPO.WEBDAV -> "webdav"
DATA_REPO.LOCAL_FILE -> "localfile"
}
)
}
spe.commit()
}
fun loadDataRepository(): DATA_REPO {
@@ -61,11 +60,11 @@ class LocalSettingsRepository(val context: Context) {
}
fun saveWebdavCredentials(url: String, username: String, password: String) {
sharedPreferences.edit(commit = true) {
putString(SHARED_PREFS_DAV_URL, url)
putString(SHARED_PREFS_DAV_USER, username)
putString(SHARED_PREFS_DAV_PASS, password)
}
val spe = sharedPreferences.edit()
spe.putString(SHARED_PREFS_DAV_URL, url)
spe.putString(SHARED_PREFS_DAV_USER, username)
spe.putString(SHARED_PREFS_DAV_PASS, password)
spe.commit()
}
fun loadWebdavCredentials(): Array<String>? {

View File

@@ -1,17 +1,12 @@
package utils
import android.content.Context
import android.os.Build
import android.text.format.DateFormat
import it.danieleverducci.lunatracker.R
import java.util.Date
class DateUtils {
companion object {
/**
* Format time duration in seconds as e.g. "2 hours, 1 min".
* Used for the duration to the next/previous event in the event details dialog.
*/
fun formatTimeDuration(context: Context, secondsDiff: Long): String {
var seconds = secondsDiff
@@ -70,8 +65,7 @@ class DateUtils {
}
/**
* Formats the provided unix timestamp in a string like "3 hours, 26 minutes ago".
* Used for the event list.
* Formats the provided unix timestamp in a string like "3 hours, 26 minutes ago)
*/
fun formatTimeAgo(context: Context, unixTime: Long): String {
val secondsDiff = (System.currentTimeMillis() / 1000) - unixTime
@@ -106,21 +100,5 @@ class DateUtils {
}
return formattedTime.toString()
}
/**
* Format time as localized string without seconds. E.g. "Sept 18, 2025, 03:36 PM".
* Used in the event detail dialog.
*/
fun formatDateTime(unixTime: Long): String {
val date = Date(unixTime * 1000)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val dateFormat = android.icu.text.DateFormat.getDateTimeInstance(android.icu.text.DateFormat.DEFAULT, android.icu.text.DateFormat.SHORT)
return dateFormat.format(date)
} else {
// fallback
val dateFormat = java.text.DateFormat.getDateTimeInstance()
return dateFormat.format(date)
}
}
}
}

View File

@@ -4,7 +4,6 @@ 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
@@ -61,28 +60,21 @@ class NumericUtils (val context: Context) {
)
}
fun formatEventQuantity(event: LunaEvent): String {
fun formatEventQuantity(item: LunaEvent): String {
val formatted = StringBuilder()
if (event.quantity >= 0) {
formatted.append(when (event.type) {
if (item.quantity > 0) {
formatted.append(when (item.type) {
LunaEvent.TYPE_TEMPERATURE ->
(event.quantity / 10.0f).toString()
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 ""
}
}
(item.quantity / 10.0f).toString()
LunaEvent.TYPE_PUKE ->
context.resources.getStringArray(R.array.AmountLabels)[item.quantity]
else ->
event.quantity
item.quantity
})
formatted.append(" ")
formatted.append(
when (event.type) {
when (item.type) {
LunaEvent.TYPE_BABY_BOTTLE -> measurement_unit_liquid_base
LunaEvent.TYPE_WEIGHT -> measurement_unit_weight_base
LunaEvent.TYPE_MEDICINE -> measurement_unit_weight_tiny

View File

@@ -119,57 +119,28 @@
android:visibility="invisible"/>
</RadioGroup>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/settings_signature" />
<EditText
android:id="@+id/settings_signature"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="5dp"
android:inputType="textEmailAddress"
android:background="@drawable/textview_background"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="5dp"
android:text="@string/settings_signature_desc"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="20dp">
android:layout_marginTop="5dp"
android:layout_marginEnd="30dp">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/settings_no_breastfeeding" />
android:text="@string/no_breastfeeding" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switch_no_breastfeeding"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_weight="1" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="5dp"
android:text="@string/settings_no_breastfeeding_desc"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -1,35 +0,0 @@
<?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">
<NumberPicker
android:id="@+id/dialog_number_picker"
android:layout_width="150dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="ml"/>
</LinearLayout>
<TextView
android:id="@+id/dialog_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="20dp"/>
</LinearLayout>

View File

@@ -1,38 +0,0 @@
<?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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="g"/>
</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

@@ -4,9 +4,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="20dp"
android:paddingBottom="10dp"
android:paddingHorizontal="20dp">
android:padding="20dp">
<TextView
android:id="@+id/dialog_event_detail_type_emoji"
@@ -36,7 +34,9 @@
android:drawablePadding="10dp"
android:drawableTint="@color/accent"
android:textSize="16sp"
android:textStyle="bold"/>
android:textStyle="bold"
android:text="@string/dialog_event_detail_datetime_icon"
app:drawableEndCompat="@drawable/ic_edit" />
<TextView
android:id="@+id/dialog_event_detail_type_quantity"
@@ -61,14 +61,6 @@
</ScrollView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:id="@+id/dialog_event_detail_type_signature"
android:layout_marginBottom="5dp"
android:visibility="gone"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -24,11 +24,4 @@
android:hint="@string/log_notes_dialog_note_hint"
android:background="@drawable/textview_background"/>
<TextView
android:id="@+id/notes_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"/>
</LinearLayout>

View File

@@ -67,17 +67,7 @@
android:padding="10dp"
android:background="@drawable/dropdown_list_item_background"
style="@style/OverflowMenuText"
android:text="@string/overflow_event_weight"/>
<TextView
android:id="@+id/button_bath"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:padding="10dp"
android:background="@drawable/dropdown_list_item_background"
style="@style/OverflowMenuText"
android:text="@string/overflow_event_bath"/>
android:text="@string/overflow_event_scale"/>
<TextView
android:id="@+id/button_enema"

View File

@@ -0,0 +1,22 @@
<?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:layout_gravity="center"
android:gravity="center">
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="g"/>
</LinearLayout>

View File

@@ -0,0 +1,19 @@
<?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:layout_gravity="center"
android:gravity="center">
<NumberPicker
android:id="@+id/dialog_number_picker"
android:layout_width="150dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="ml"/>
</LinearLayout>

View File

@@ -3,14 +3,15 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/dialog_date_picker"
android:layout_width="wrap_content"
<Spinner
android:id="@+id/dialog_puke_value"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"/>
android:paddingHorizontal="16dp"
android:paddingVertical="8dp"/>
</LinearLayout>

View File

@@ -23,12 +23,4 @@
android:layout_height="wrap_content"
android:textSize="30sp"
android:textColor="@color/accent"/>
<TextView
android:id="@+id/dialog_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="20dp"/>
</LinearLayout>

View File

@@ -3,9 +3,18 @@
<string name="title">🌜 LunaTracker 🌛</string>
<string name="logbook">Ereignisprotokoll</string>
<string name="log_bottle_dialog_title">Fläschchen</string>
<string name="log_bottle_dialog_description">Trinkmenge eingeben</string>
<string name="log_weight_dialog_title">Gewicht</string>
<string name="log_weight_dialog_description">Gewicht eingeben</string>
<string name="log_temperature_dialog_title">Temperatur</string>
<string name="log_temperature_dialog_description">Temperatur eingeben</string>
<string name="event_bottle_desc">Fläschchen</string>
<string name="event_food_desc">Essen</string>
<string name="event_weight_desc">Gewicht</string>
<string name="event_scale_desc">Gewicht</string>
<string name="event_breastfeeding_left_desc">Stillen (links)</string>
<string name="event_breastfeeding_both_desc">Stillen</string>
<string name="event_breastfeeding_right_desc">Stillen (rechts)</string>
@@ -18,7 +27,7 @@
<string name="event_colic_desc">Blähungskolik</string>
<string name="event_unknown_desc"></string>
<string name="overflow_event_weight">⚖️ Gewicht</string>
<string name="overflow_event_scale">⚖️ Gewicht</string>
<string name="overflow_event_medicine">💊 Medikament</string>
<string name="overflow_event_enema">🪠 Einlauf</string>
<string name="overflow_event_note">📝 Notiz</string>
@@ -41,8 +50,9 @@
<string name="no_connection_go_to_settings">Einstellungen</string>
<string name="no_connection_retry">Erneut versuchen</string>
<string name="no_breastfeeding">Kein Stillen</string>
<string name="settings_title">Einstellungen</string>
<string name="settings_no_breastfeeding">Kein Stillen</string>
<string name="settings_storage">Speicherort für Daten auswählen</string>
<string name="settings_storage_local">Auf dem Gerät</string>
<string name="settings_storage_local_desc">Datenschutzfreundlichste Lösung: Deine Daten verlassen dein Gerät nicht</string>
@@ -68,13 +78,10 @@
<string name="trim_logbook_dialog_button_ok">Jetzt bereinigen</string>
<string name="trim_logbook_dialog_button_cancel">Später erinnern</string>
<string name="log_bottle_dialog_description">Trinkmenge eingeben</string>
<string name="log_medicine_dialog_description">Medikamentenname, Menge, Art, Notizen, …:</string>
<string name="log_notes_dialog_description">Notizen:</string>
<string name="log_notes_dialog_note_hint">Notiz eingeben</string>
<string name="log_medicine_dialog_description">Medikamentenname, Menge, Art, Notizen, …:</string>
<string name="log_notes_dialog_qty_hint">Menge (optional)</string>
<string name="log_temperature_dialog_description">Temperatur eingeben</string>
<string name="log_weight_dialog_description">Gewicht eingeben</string>
<string name="log_notes_dialog_note_hint">Notiz eingeben</string>
<string name="dialog_event_detail_title">Ereignisdetails</string>
<string name="dialog_event_detail_close_button">OK</string>

View File

@@ -3,9 +3,18 @@
<string name="title">🌜 LunaTracker 🌛</string>
<string name="logbook">Entrées enregistrées</string>
<string name="log_bottle_dialog_title">Biberon</string>
<string name="log_bottle_dialog_description">Renseignez la quantité contenue dans le biberon</string>
<string name="log_weight_dialog_title">Poids</string>
<string name="log_weight_dialog_description">Renseignez le poids</string>
<string name="log_temperature_dialog_title">Température</string>
<string name="log_temperature_dialog_description">Renseignez la Température</string>
<string name="event_bottle_desc">Biberon</string>
<string name="event_food_desc">Nourriture</string>
<string name="event_weight_desc">Poids</string>
<string name="event_scale_desc">Poids</string>
<string name="event_breastfeeding_left_desc">Allaitement (sein gauche)</string>
<string name="event_breastfeeding_both_desc">Allaitement</string>
<string name="event_breastfeeding_right_desc">Allaitement (sein droit)</string>
@@ -18,7 +27,7 @@
<string name="event_colic_desc">Colique gazeuse</string>
<string name="event_unknown_desc"></string>
<string name="overflow_event_weight">⚖️ Poids</string>
<string name="overflow_event_scale">⚖️ Poids</string>
<string name="overflow_event_medicine">💊 Médicament</string>
<string name="overflow_event_enema">🪠 Lavement</string>
<string name="overflow_event_note">📝 Note</string>
@@ -67,13 +76,10 @@
<string name="trim_logbook_dialog_button_ok">Supprimer les vieilles entrées maintenant</string>
<string name="trim_logbook_dialog_button_cancel">Me rappeller plus tard</string>
<string name="log_bottle_dialog_description">Renseignez la quantité contenue dans le biberon</string>
<string name="log_medicine_dialog_description">nom du médicament, quantité, type, notes …:</string>
<string name="log_notes_dialog_description">Notes:</string>
<string name="log_notes_dialog_note_hint">Notes ...</string>
<string name="log_medicine_dialog_description">nom du médicament, quantité, type, notes …:</string>
<string name="log_notes_dialog_qty_hint">Quantité (ou vide)</string>
<string name="log_temperature_dialog_description">Renseignez la Température</string>
<string name="log_weight_dialog_description">Renseignez le poids</string>
<string name="log_notes_dialog_note_hint">Notes ...</string>
<string name="dialog_event_detail_title">Détails de l\'entrée</string>
<string name="dialog_event_detail_close_button">OK</string>

View File

@@ -3,7 +3,16 @@
<string name="title">🌜 LunaTracker 🌛</string>
<string name="logbook">Diario di bordo</string>
<string name="overflow_event_weight">⚖️ Peso</string>
<string name="log_bottle_dialog_title">Biberon</string>
<string name="log_bottle_dialog_description">Inserisci la quantità contenuta nel biberon</string>
<string name="log_weight_dialog_title">Pesata</string>
<string name="log_weight_dialog_description">Inserisci il peso rilevato</string>
<string name="log_temperature_dialog_title">Temperatura</string>
<string name="log_temperature_dialog_description">Inserisci la temperatura</string>
<string name="overflow_event_scale">⚖️ Peso</string>
<string name="overflow_event_medicine">💊 Medicina</string>
<string name="overflow_event_enema">🪠 Clistere</string>
<string name="overflow_event_note">📝 Nota</string>
@@ -12,7 +21,7 @@
<string name="event_bottle_desc">Biberon</string>
<string name="event_food_desc">Cibo</string>
<string name="event_weight_desc">Pesata</string>
<string name="event_scale_desc">Pesata</string>
<string name="event_breastfeeding_left_desc">Allatt. al seno (sx)</string>
<string name="event_breastfeeding_both_desc">Allatt. al seno</string>
<string name="event_breastfeeding_right_desc">Allatt. al seno (dx)</string>
@@ -67,13 +76,10 @@
<string name="trim_logbook_dialog_button_ok">Cancella i più vecchi</string>
<string name="trim_logbook_dialog_button_cancel">Ricordamelo dopo</string>
<string name="log_bottle_dialog_description">Inserisci la quantità contenuta nel biberon</string>
<string name="log_medicine_dialog_description">Nome della medicina, quantità, formato, note…:</string>
<string name="log_notes_dialog_description">Note:</string>
<string name="log_notes_dialog_note_hint">Inserisci le note</string>
<string name="log_medicine_dialog_description">Nome della medicina, quantità, formato, note…:</string>
<string name="log_notes_dialog_qty_hint">Quantità, o vuoto</string>
<string name="log_temperature_dialog_description">Inserisci la temperatura</string>
<string name="log_weight_dialog_description">Inserisci il peso rilevato</string>
<string name="log_notes_dialog_note_hint">Inserisci le note</string>
<string name="dialog_event_detail_title">Dettaglio evento</string>
<string name="dialog_event_detail_close_button">OK</string>

View File

@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="AmountLabels">
<item>@string/amount_unspecified</item>
<item>@string/amount_little</item>
<item>@string/amount_low</item>
<item>@string/amount_normal</item>
<item>@string/amount_plenty</item>
<item>@string/amount_high</item>
</string-array>
</resources>

View File

@@ -3,9 +3,21 @@
<string name="title">🌜 LunaTracker 🌛</string>
<string name="logbook">Logged events</string>
<string name="log_bottle_dialog_title">Baby bottle</string>
<string name="log_bottle_dialog_description">Insert the quantity contained in the baby bottle</string>
<string name="log_weight_dialog_title">Weight</string>
<string name="log_weight_dialog_description">Insert the weight</string>
<string name="log_temperature_dialog_title">Temperature</string>
<string name="log_temperature_dialog_description">Insert the temperature</string>
<string name="log_puke_dialog_title">Puke</string>
<string name="log_puke_dialog_description">Select the amount</string>
<string name="event_bottle_type" translatable="false">🍼</string>
<string name="event_food_type" translatable="false">🥣</string>
<string name="event_weight_type" translatable="false">⚖️</string>
<string name="event_scale_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_right_type" translatable="false">🤱 →</string>
@@ -17,12 +29,11 @@
<string name="event_temperature_type" translatable="false">🌡️</string>
<string name="event_colic_type" translatable="false">💨</string>
<string name="event_puke_type" translatable="false">🤮</string>
<string name="event_bath_type" translatable="false">🛁</string>
<string name="event_unknown_type" translatable="false">\?</string>
<string name="event_bottle_desc">Baby bottle</string>
<string name="event_food_desc">Food</string>
<string name="event_weight_desc">Weight</string>
<string name="event_scale_desc">Weight</string>
<string name="event_breastfeeding_left_desc">Breastfeeding (left)</string>
<string name="event_breastfeeding_both_desc">Breastfeeding</string>
<string name="event_breastfeeding_right_desc">Breastfeeding (right)</string>
@@ -34,17 +45,15 @@
<string name="event_temperature_desc">Temperature</string>
<string name="event_colic_desc">Gaseous colic</string>
<string name="event_puke_desc">Puke</string>
<string name="event_bath_desc">Bath</string>
<string name="event_unknown_desc"></string>
<string name="overflow_event_weight">⚖️ Weight</string>
<string name="overflow_event_scale">⚖️ Weight</string>
<string name="overflow_event_medicine">💊 Medicine</string>
<string name="overflow_event_enema">🪠 Enema</string>
<string name="overflow_event_note">📝 Note</string>
<string name="overflow_event_temperature">🌡️ Temperature</string>
<string name="overflow_event_colic">💨 Gaseous colic</string>
<string name="overflow_event_puke">🤮 Puke</string>
<string name="overflow_event_bath">🛁 Bath</string>
<string name="toast_event_added">Event logged</string>
<string name="toast_logbook_saved">Logbook saved</string>
@@ -63,19 +72,18 @@
<string name="year_ago">year</string>
<string name="years_ago">years</string>
<string name="amount_unspecified"></string>
<string name="amount_little">Little</string>
<string name="amount_low">Low</string>
<string name="amount_normal">Normal</string>
<string name="amount_plenty">Plenty</string>
<string name="amount_high">High</string>
<string name="no_connection">No connection</string>
<string name="no_connection_explain">Unable to reach WebDAV service</string>
<string name="no_connection_go_to_settings">Settings</string>
<string name="no_connection_retry">Retry</string>
<string name="no_breastfeeding">No Breastfeeding</string>
<string name="settings_title">Settings</string>
<string name="settings_signature">Signature</string>
<string name="settings_signature_desc">Attach a signature to each event you create and for others to see. Useful if multiple people add events.</string>
<string name="settings_storage">Choose where to save data</string>
<string name="settings_storage_local">On device</string>
<string name="settings_storage_local_desc">Most privacy-friendly solution: data doesn\'t leave your device</string>
@@ -91,8 +99,6 @@
<string name="settings_webdav_error_generic">Error while trying to access WebDAV:</string>
<string name="settings_webdav_creation_error_generic">Unable to save a file on the WebDAV server:</string>
<string name="settings_webdav_creation_ok">Successfully connected with the WebDAV server</string>
<string name="settings_no_breastfeeding">No Breastfeeding</string>
<string name="settings_no_breastfeeding_desc">Hide the Breastfeeding buttons for when they are not needed.</string>
<string name="settings_json_error">There\'s a save file on the server, but it is corrupted or unreadable. Please delete it </string>
<string name="settings_generic_error">Error: </string>
<string name="settings_webdav_upload_error">Error while uploading local logbook %1$s to webdav: %2$s</string>
@@ -103,15 +109,10 @@
<string name="trim_logbook_dialog_button_ok">Trim it now</string>
<string name="trim_logbook_dialog_button_cancel">Remind me later</string>
<string name="log_amount_dialog_description">Select the amount:</string>
<string name="log_bottle_dialog_description">Insert the quantity contained in the baby bottle:</string>
<string name="log_medicine_dialog_description">Medicine name, quantity, type, notes…:</string>
<string name="log_notes_dialog_description">Notes:</string>
<string name="log_notes_dialog_note_hint">Write some notes</string>
<string name="log_medicine_dialog_description">Medicine name, quantity, type, notes…:</string>
<string name="log_notes_dialog_qty_hint">Quantity (or empty)</string>
<string name="log_temperature_dialog_description">Select the temperature:</string>
<string name="log_unknown_dialog_description"></string>
<string name="log_weight_dialog_description">Insert the weight:</string>
<string name="log_notes_dialog_note_hint">Write some notes</string>
<string name="measurement_unit_liquid_base_metric" translatable="false">ml</string>
<string name="measurement_unit_weight_base_metric" translatable="false">g</string>
@@ -127,11 +128,11 @@
<string name="row_luna_event_time">Time</string>
<string name="dialog_event_detail_title">Event detail</string>
<string name="dialog_event_detail_datetime_icon" translatable="false">🕒 %s</string>
<string name="dialog_event_detail_close_button">OK</string>
<string name="dialog_event_detail_delete_button">Delete</string>
<string name="dialog_event_detail_quantity">Quantity</string>
<string name="dialog_event_detail_notes">Notes</string>
<string name="dialog_event_detail_signature">by %s</string>
<string name="dialog_add_logbook_title">Add logbook</string>
<string name="dialog_add_logbook_logbookname">👶 Logbook name</string>