Compare commits

..
Author SHA1 Message Date
mwarning a8305b8fc9 statistics: complete removal 2026-02-12 23:13:58 +01:00
mwarning 43f0519487 LunaEvent: rework sleep event
Remove adjustment buttons and
simplify design.
2026-02-12 23:01:01 +01:00
mwarning 1bcfc18a71 MainActivity: rename datepicker 2026-02-12 23:01:01 +01:00
mwarning e345e98668 layout: replace menu icon with utf8 character
The three dot menu icosn looks odd when stretched
due to the dynamic menu feature. Thus replace it
with the hamburger menu character that looks better
when scaled.
2026-02-12 23:01:01 +01:00
mwarning 09f5a4e27b MainActivity: sort events before saving
Also replace notifyItemInserted since it does not
call the adapter to redraw the row striping when
a new event is added.
2026-02-12 23:01:01 +01:00
mwarning b3b432525e StatisticsActivity: rework all statistics
Improve the overall code.
2026-02-12 23:00:58 +01:00
mwarning d832aa4330 NumericUtils: remove possible trailing whitespace 2026-02-09 08:23:38 +01:00
mwarning 672ae37049 MainActivity: do not switch logbook on reload 2026-02-09 08:23:38 +01:00
mwarning 672e58c028 LunaEvent: reorganize event text getters
Use method names that better reflect
the use of the returned text.
2026-02-09 08:23:38 +01:00
mwarning d2729af30f MainAcitivty: add dynamic header setting
The setting allows to build the menu and
popup list to be populated by the frequency
of events that has been created.
This also makes the 'no breastfeeding'
setting irrelevant.
2026-02-09 08:23:21 +01:00
mwarning 85567cce77 LunaEvent: use enum class for event types
This helps to have compile errors when some
case it not handled while adding a new type.
The enum class can also be interated over
to create a complete drop down list.
2026-01-23 06:46:11 +01:00
mwarning 80a51ea8ef MainActivity: increase bottle volume to 340ml
This is the maximum amount found in sold bottles.
2026-01-23 06:46:11 +01:00
mwarning 28679a4a66 gradle: use uniform implementation directive for sardine-android 2026-01-23 06:46:11 +01:00
mwarning f73d3562a9 gradle: avoid inclusion of apk signing blobs
See https://android.izzysoft.de/articles/named/iod-scan-apkchecks?lang=en#blobs
2026-01-23 06:46:11 +01:00
mwarning 8a2932b1e7 gradle: set compileSDK/targetSdk to 36 2026-01-23 06:46:11 +01:00
mwarning 2af8989777 StatisticsActivity: add statistics for bottle and sleep events 2026-01-23 06:46:11 +01:00
mwarning b417fe48a6 MainActivity: show save button if any values has changed 2026-01-23 06:46:11 +01:00
mwarning a887d9f29f MainActivity: use unique templates for notes 2026-01-23 06:46:11 +01:00
mwarning 44748506ff LunaEvent: add sleep event 2026-01-23 06:46:07 +01:00
mwarning 9f8f277d75 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.
2025-11-19 20:37:10 +01:00
mwarning 742c5515b1 strings: rename scale to weight in identifiers 2025-11-19 20:37:10 +01:00
mwarning d49701a488 LunaEvent: remove quantity when the value is invalid 2025-11-19 20:37:10 +01:00
mwarning fb6edf981b notes: add icons to use previous/next event as template 2025-11-19 20:37:10 +01:00
mwarning 52b23151d6 MainActivity: preset quantity of bottle, weight and temperature
Use the quantity of previous events
to initialize new events.
2025-11-19 20:37:10 +01:00
mwarning 23372408d5 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.
2025-11-19 20:37:01 +01:00
25 changed files with 341 additions and 399 deletions
-2
View File
@@ -1,8 +1,6 @@
*.iml *.iml
.gradle .gradle
/local.properties /local.properties
/.idea/deviceManager.xml
/.idea/codeStyles
/.idea/caches /.idea/caches
/.idea/libraries /.idea/libraries
/.idea/modules.xml /.idea/modules.xml
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="25" /> <bytecodeTargetLevel target="21" />
</component> </component>
</project> </project>
+1 -18
View File
@@ -13,24 +13,7 @@ Dedicated to my daughter Luna.
![Screenshot](fastlane/metadata/android/en-US/images/phoneScreenshots/1.png) ![Screenshot](fastlane/metadata/android/en-US/images/phoneScreenshots/1.png)
## Contributions ## Thanks for the valuable contributions to:
### Why isn't this hosted on GitHub?
I'm using a private git server just because I'm worried for the vast majority of open source code being hosted in a server property of Microsoft and being used to train theirs AI.
I didn't find a better option, BTW the Gitea project is working on implementing federation, so soon it will be possible to contribute using any other gitea server, selfhosted or not.
### How to contribute
The project is open to contribution, but with some limits:
- I'm sorry I can't accept AI-generated contributions. Reviewing a contribution requires time and effort from my side, while generating code with AI requires very little time and produces non reliable code that must be reviewed in detail. This is effectively shifting the work on my side, and in a forced way. If you feel you need a feature but you're not able to implement it by yourself, I prefer you to create an issue in the repository so I can implement it when I can, in a more mantainable way.
- I prefer to make project-wide changes (i.e. updating Android target, app name and icon, release number...) by myself.
To contribute, you'll have to create an account on this git instance. Unfortunately, I had to disable registration to avoid huge waves of fake accounts created by bots.
You can request an account writing to daniele.verducci@ichibi.eu
### Thanks for the valuable contributions to:
- Chepycou (French translation) - Chepycou (French translation)
- Daniel Neubauer (German translation) - Daniel Neubauer (German translation)
@@ -57,7 +57,6 @@ class MainActivity : AppCompatActivity() {
var currentPopupItems = listOf<LunaEvent.Type>() var currentPopupItems = listOf<LunaEvent.Type>()
} }
lateinit var settingsRepository : LocalSettingsRepository
var logbook: Logbook? = null var logbook: Logbook? = null
var pauseLogbookUpdate = false var pauseLogbookUpdate = false
lateinit var progressIndicator: LinearProgressIndicator lateinit var progressIndicator: LinearProgressIndicator
@@ -78,7 +77,6 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
handler = Handler(mainLooper) handler = Handler(mainLooper)
settingsRepository = LocalSettingsRepository(this@MainActivity)
// Show view // Show view
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
@@ -88,6 +86,8 @@ class MainActivity : AppCompatActivity() {
recyclerView = findViewById(R.id.list_events) recyclerView = findViewById(R.id.list_events)
recyclerView.setLayoutManager(LinearLayoutManager(applicationContext)) recyclerView.setLayoutManager(LinearLayoutManager(applicationContext))
populateHeaderMenu()
// Set listeners // Set listeners
findViewById<View>(R.id.logbooks_add_button).setOnClickListener { findViewById<View>(R.id.logbooks_add_button).setOnClickListener {
showAddLogbookDialog(true) showAddLogbookDialog(true)
@@ -138,30 +138,29 @@ class MainActivity : AppCompatActivity() {
allEvents = logbook?.logs ?: arrayListOf() allEvents = logbook?.logs ?: arrayListOf()
setListAdapter(allEvents) setListAdapter(allEvents)
populateHeaderMenu()
} }
private fun populateHeaderMenu() { private fun populateHeaderMenu() {
val settingsRepository = LocalSettingsRepository(this)
val dynamicMenu = settingsRepository.loadDynamicMenu() val dynamicMenu = settingsRepository.loadDynamicMenu()
val eventTypeStats = mutableMapOf<LunaEvent.Type, Int>() val eventTypeStats = mutableMapOf<LunaEvent.Type, Int>()
if (dynamicMenu) { if (dynamicMenu) {
// populate frequency map from all events of the last two weeks val sampleSize = 100
val lastWeekTime = (System.currentTimeMillis() / 1000) - (14 * 24 * 60 * 60) // populate frequency map from first 100 events
allEvents.forEach { allEvents.take(sampleSize.coerceAtMost(allEvents.size)).forEach {
if (it.time > lastWeekTime) {
eventTypeStats[it.type] = 1 + (eventTypeStats[it.type] ?: 0) eventTypeStats[it.type] = 1 + (eventTypeStats[it.type] ?: 0)
} }
} }
}
// sort all event types by frequency and ordinal // sort all event types by frequency and ordinal
val eventTypesSorted = LunaEvent.Type.entries.toList().sortedWith( val eventTypesSorted = LunaEvent.Type.entries.toList().sortedWith(
compareBy({ -1 * (eventTypeStats[it] ?: 0) }, { it.ordinal }) compareBy({ -1 * (eventTypeStats[it] ?: 0) }, { it.ordinal })
).filter { it != LunaEvent.Type.UNKNOWN } ).filter { it != LunaEvent.Type.UNKNOWN }
val usedEventCount = eventTypeStats.count { it.value > 0 } fun setupMenu(maxButtonCount: Int, sortedEventTypes: List<LunaEvent.Type>): Int {
val maxButtonCount = if (dynamicMenu) { usedEventCount } else { 7 }
val row1 = findViewById<View>(R.id.linear_layout_row1) val row1 = findViewById<View>(R.id.linear_layout_row1)
val row1Button1 = findViewById<TextView>(R.id.button1_row1) val row1Button1 = findViewById<TextView>(R.id.button1_row1)
val row1Button2 = findViewById<TextView>(R.id.button2_row1) val row1Button2 = findViewById<TextView>(R.id.button2_row1)
@@ -187,7 +186,7 @@ class MainActivity : AppCompatActivity() {
fun show(vararg tvs: TextView) { fun show(vararg tvs: TextView) {
for (tv in tvs) { for (tv in tvs) {
val type = eventTypesSorted[showCounter] val type = sortedEventTypes[showCounter]
tv.text = LunaEvent.getHeaderEmoji(applicationContext, type) tv.text = LunaEvent.getHeaderEmoji(applicationContext, type)
tv.setOnClickListener { showCreateDialog(type) } tv.setOnClickListener { showCreateDialog(type) }
tv.visibility = View.VISIBLE tv.visibility = View.VISIBLE
@@ -206,13 +205,21 @@ class MainActivity : AppCompatActivity() {
else -> show(row1Button1, row1Button2, row2Button1, row2Button2, row2Button3, row3Button1, row3Button2) else -> show(row1Button1, row1Button2, row2Button1, row2Button2, row2Button3, row3Button1, row3Button2)
} }
return showCounter
}
val usedEventCount = eventTypeStats.count { it.value > 0 }
val maxButtonCount = if (dynamicMenu) { usedEventCount } else { 7 }
val eventsShown = setupMenu(maxButtonCount, eventTypesSorted)
// store left over events for popup menu // store left over events for popup menu
currentPopupItems = eventTypesSorted.subList(showCounter, eventTypesSorted.size) currentPopupItems = eventTypesSorted.subList(eventsShown, eventTypesSorted.size)
} }
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val settingsRepository = LocalSettingsRepository(this)
if (settingsRepository.loadDataRepository() == LocalSettingsRepository.DATA_REPO.WEBDAV) { if (settingsRepository.loadDataRepository() == LocalSettingsRepository.DATA_REPO.WEBDAV) {
val webDavCredentials = settingsRepository.loadWebdavCredentials() val webDavCredentials = settingsRepository.loadWebdavCredentials()
if (webDavCredentials == null) { if (webDavCredentials == null) {
@@ -268,9 +275,6 @@ class MainActivity : AppCompatActivity() {
numberPicker.wrapSelectorWheel = false numberPicker.wrapSelectorWheel = false
numberPicker.value = event.quantity / 10 numberPicker.value = event.quantity / 10
val numberPickerUnit = dialogView.findViewById<TextView>(R.id.dialog_number_picker_unit)
numberPickerUnit.text = NumericUtils(this).measurement_unit_liquid_base
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker) val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = dateTimePicker(event.time, dateTV) val pickedTime = dateTimePicker(event.time, dateTV)
@@ -309,9 +313,6 @@ class MainActivity : AppCompatActivity() {
val weightET = dialogView.findViewById<EditText>(R.id.dialog_number_edittext) val weightET = dialogView.findViewById<EditText>(R.id.dialog_number_edittext)
weightET.setText(event.quantity.toString()) weightET.setText(event.quantity.toString())
val unitTV = dialogView.findViewById<TextView>(R.id.dialog_number_unit)
unitTV.text = NumericUtils(this).measurement_unit_weight_base
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker) val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = dateTimePicker(event.time, dateTV) val pickedTime = dateTimePicker(event.time, dateTV)
@@ -340,53 +341,6 @@ class MainActivity : AppCompatActivity() {
alertDialog.show() alertDialog.show()
} }
fun addHeightEvent(event: LunaEvent) {
setToPreviousQuantity(event)
askHeightValue(event, true) { saveEvent(event) }
}
fun askHeightValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) {
// Show number picker dialog
val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.dialog_edit_height, null)
d.setTitle(event.getDialogTitle(this))
d.setMessage(event.getDialogMessage(this))
d.setView(dialogView)
val heightET = dialogView.findViewById<EditText>(R.id.dialog_number_edittext)
heightET.setText(event.quantity.toString())
val unitTV = dialogView.findViewById<TextView>(R.id.dialog_number_unit)
unitTV.text = NumericUtils(this).measurement_unit_height_base
val dateTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker)
val pickedTime = dateTimePicker(event.time, dateTV)
if (!showTime) {
dateTV.visibility = View.GONE
}
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
val height = heightET.text.toString().toIntOrNull()
if (height != null) {
event.time = pickedTime.time.time / 1000
event.quantity = height
onPositive()
} else {
Toast.makeText(this, R.string.toast_integer_error, Toast.LENGTH_SHORT).show()
}
dialogInterface.dismiss()
}
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i ->
dialogInterface.dismiss()
}
val alertDialog = d.create()
alertDialog.show()
}
fun addTemperatureEvent(event: LunaEvent) { fun addTemperatureEvent(event: LunaEvent) {
setToPreviousQuantity(event) setToPreviousQuantity(event)
askTemperatureValue(event, true) { saveEvent(event) } askTemperatureValue(event, true) { saveEvent(event) }
@@ -467,110 +421,83 @@ class MainActivity : AppCompatActivity() {
return dateTime return dateTime
} }
fun addDurationEvent(event: LunaEvent) { fun addSleepEvent(event: LunaEvent) {
askDurationEvent(event, true) { saveEvent(event) } askSleepValue(event, true) { saveEvent(event) }
} }
fun askDurationEvent(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) { fun askSleepValue(event: LunaEvent, showTime: Boolean, onPositive: () -> Unit) {
val d = AlertDialog.Builder(this) val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.dialog_edit_duration, null) val dialogView = layoutInflater.inflate(R.layout.dialog_edit_duration, null)
d.setTitle(event.getDialogTitle(this)) d.setTitle(event.getDialogTitle(this))
d.setView(dialogView) d.setView(dialogView)
val durationTV = dialogView.findViewById<TextView>(R.id.dialog_date_duration) val durationTextView = dialogView.findViewById<TextView>(R.id.dialog_date_duration)
val datePickerBeginTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker_begin) val datePickerBegin = dialogView.findViewById<TextView>(R.id.dialog_date_picker_begin)
val datePickerEndTV = dialogView.findViewById<TextView>(R.id.dialog_date_picker_end) val datePickerEnd = dialogView.findViewById<TextView>(R.id.dialog_date_picker_end)
val dateDelimiterTV = dialogView.findViewById<TextView>(R.id.dialog_date_range_delimiter)
val durationButtons = dialogView.findViewById<LinearLayout>(R.id.duration_buttons)
val durationNowButton = dialogView.findViewById<Button>(R.id.dialog_date_duration_now)
val durationClearButton = dialogView.findViewById<Button>(R.id.dialog_date_duration_clear)
val currentDurationTextColor = durationTV.currentTextColor val currentDurationTextColor = durationTextView.currentTextColor
val invalidDurationTextColor = ContextCompat.getColor(this, R.color.danger) val invalidDurationTextColor = ContextCompat.getColor(this, R.color.danger)
// in seconds // in seconds
var durationStart = event.getStartTime() var sleepStart = event.time
var durationEnd = event.getEndTime() var sleepEnd = event.time + event.quantity
fun isValidTime(timeUnix: Long): Boolean {
val now = System.currentTimeMillis() / 1000
return timeUnix in 1..now
}
fun isValidTimeSpan(timeBeginUnix: Long, timeEndUnix: Long): Boolean { fun isValidTimeSpan(timeBeginUnix: Long, timeEndUnix: Long): Boolean {
return (timeBeginUnix <= timeEndUnix) && (timeEndUnix - timeBeginUnix) < (24 * 60 * 60) val now = System.currentTimeMillis() / 1000
return (timeBeginUnix > 0)
&& (timeEndUnix > 0)
&& (timeBeginUnix <= timeEndUnix)
&& (timeBeginUnix <= now)
&& (timeEndUnix <= now)
&& (timeEndUnix - timeBeginUnix) < (24 * 60 * 60)
} }
fun updateFields() { // prevent printing of seconds
datePickerBeginTV.text = DateUtils.formatDateTime(durationStart) fun adjustToMinute(unixTime: Long): Long {
datePickerEndTV.text = DateUtils.formatDateTime(durationEnd) return unixTime - (unixTime % 60)
dateTimePicker(durationStart, datePickerBeginTV) { pickedTime: Long ->
durationStart = pickedTime
if (datePickerEndTV.visibility == View.GONE) {
durationEnd = pickedTime
}
updateFields()
} }
dateTimePicker(durationEnd, datePickerEndTV) { pickedTime: Long -> fun updateDuration() {
durationEnd = pickedTime durationTextView.setTextColor(currentDurationTextColor)
updateFields() val duration = sleepEnd - sleepStart
}
durationTV.setTextColor(currentDurationTextColor)
val duration = durationEnd - durationStart
if (duration == 0L) { if (duration == 0L) {
// event is ongoing // baby is sleeping
durationTV.text = "💤" durationTextView.text = "💤"
dateDelimiterTV.visibility = View.GONE
datePickerEndTV.visibility = View.GONE
} else { } else {
durationTV.text = DateUtils.formatTimeDuration(applicationContext, duration) durationTextView.text = DateUtils.formatTimeDuration(applicationContext, duration)
if (!isValidTimeSpan(durationStart, durationEnd)) { if (!isValidTimeSpan(sleepStart, sleepEnd)) {
durationTV.setTextColor(invalidDurationTextColor) durationTextView.setTextColor(invalidDurationTextColor)
}
} }
dateDelimiterTV.visibility = View.VISIBLE
datePickerEndTV.visibility = View.VISIBLE
} }
val colorBegin = if (isValidTime(durationStart)) { currentDurationTextColor } else { invalidDurationTextColor } val pickedDateTimeBegin = dateTimePicker(event.time, datePickerBegin) { time: Long ->
datePickerBeginTV.setTextColor(colorBegin) sleepStart = adjustToMinute(time)
updateDuration()
val colorEnd = if (isValidTime(durationEnd)) { currentDurationTextColor } else { invalidDurationTextColor }
datePickerEndTV.setTextColor(colorEnd)
} }
val pickedDateTimeEnd = dateTimePicker(event.time + event.quantity, datePickerEnd) { time: Long ->
sleepEnd = adjustToMinute(time)
updateDuration()
}
sleepStart = adjustToMinute(pickedDateTimeBegin.time.time / 1000)
sleepEnd = adjustToMinute(pickedDateTimeEnd.time.time / 1000)
updateDuration()
if (showTime) { if (showTime) {
dateDelimiterTV.visibility = View.GONE datePickerEnd.visibility = View.GONE
datePickerEndTV.visibility = View.GONE durationTextView.visibility = View.GONE
durationTV.visibility = View.GONE
durationButtons.visibility = View.GONE
//d.setMessage("") //d.setMessage("")
} else { } else {
dateDelimiterTV.visibility = View.VISIBLE durationTextView.visibility = View.VISIBLE
datePickerEndTV.visibility = View.VISIBLE
durationTV.visibility = View.VISIBLE
durationButtons.visibility = View.VISIBLE
d.setMessage(event.getDialogMessage(this)) d.setMessage(event.getDialogMessage(this))
} }
updateFields()
durationClearButton.setOnClickListener {
durationEnd = durationStart
updateFields()
}
durationNowButton.setOnClickListener {
durationEnd = System.currentTimeMillis() / 1000
updateFields()
}
d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
if (isValidTime(durationStart) && isValidTime(durationEnd) && isValidTimeSpan(durationStart, durationEnd)) { if (isValidTimeSpan(sleepStart, sleepEnd)) {
event.time = durationStart event.time = sleepStart
event.quantity = (durationEnd - durationStart).toInt() event.quantity = (sleepEnd - sleepStart).toInt()
onPositive() onPositive()
} else { } else {
Toast.makeText(this, R.string.toast_date_error, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.toast_date_error, Toast.LENGTH_SHORT).show()
@@ -691,6 +618,9 @@ class MainActivity : AppCompatActivity() {
var nextEvent = getNextSameEvent(current, templates) var nextEvent = getNextSameEvent(current, templates)
notesET.setText(current.notes) notesET.setText(current.notes)
if (useQuantity) {
qtyET.setText(current.quantity.toString())
}
if (nextEvent == null && current != event) { if (nextEvent == null && current != event) {
nextEvent = event nextEvent = event
@@ -699,6 +629,9 @@ class MainActivity : AppCompatActivity() {
if (nextEvent != null) { if (nextEvent != null) {
nextTextView.setOnClickListener { nextTextView.setOnClickListener {
notesET.setText(nextEvent.notes) notesET.setText(nextEvent.notes)
if (useQuantity) {
qtyET.setText(nextEvent.quantity.toString())
}
updateContent(nextEvent) updateContent(nextEvent)
} }
nextTextView.alpha = 1.0f nextTextView.alpha = 1.0f
@@ -710,6 +643,9 @@ class MainActivity : AppCompatActivity() {
if (prevEvent != null) { if (prevEvent != null) {
prevTextView.setOnClickListener { prevTextView.setOnClickListener {
notesET.setText(prevEvent.notes) notesET.setText(prevEvent.notes)
if (useQuantity) {
qtyET.setText(prevEvent.quantity.toString())
}
updateContent(prevEvent) updateContent(prevEvent)
} }
prevTextView.alpha = 1.0f prevTextView.alpha = 1.0f
@@ -764,7 +700,7 @@ class MainActivity : AppCompatActivity() {
val d = AlertDialog.Builder(this) val d = AlertDialog.Builder(this)
d.setTitle(R.string.trim_logbook_dialog_title) d.setTitle(R.string.trim_logbook_dialog_title)
d.setMessage( d.setMessage(
when (settingsRepository.loadDataRepository()) { when (LocalSettingsRepository(this).loadDataRepository()) {
LocalSettingsRepository.DATA_REPO.WEBDAV -> R.string.trim_logbook_dialog_message_dav LocalSettingsRepository.DATA_REPO.WEBDAV -> R.string.trim_logbook_dialog_message_dav
else -> R.string.trim_logbook_dialog_message_local else -> R.string.trim_logbook_dialog_message_local
} }
@@ -902,15 +838,12 @@ class MainActivity : AppCompatActivity() {
when (event.type) { when (event.type) {
LunaEvent.Type.BABY_BOTTLE -> askBabyBottleContent(event, false, updateValues) LunaEvent.Type.BABY_BOTTLE -> askBabyBottleContent(event, false, updateValues)
LunaEvent.Type.WEIGHT -> askWeightValue(event, false, updateValues) LunaEvent.Type.WEIGHT -> askWeightValue(event, false, updateValues)
LunaEvent.Type.HEIGHT -> askHeightValue(event, false, updateValues)
LunaEvent.Type.DIAPERCHANGE_POO, LunaEvent.Type.DIAPERCHANGE_POO,
LunaEvent.Type.DIAPERCHANGE_PEE, LunaEvent.Type.DIAPERCHANGE_PEE,
LunaEvent.Type.PUKE -> askAmountValue(event, false, updateValues) LunaEvent.Type.PUKE -> askAmountValue(event, false, updateValues)
LunaEvent.Type.TEMPERATURE -> askTemperatureValue(event, false, updateValues) LunaEvent.Type.TEMPERATURE -> askTemperatureValue(event, false, updateValues)
LunaEvent.Type.FOOD,
LunaEvent.Type.MEDICINE,
LunaEvent.Type.NOTE -> askNotes(event, false, updateValues) LunaEvent.Type.NOTE -> askNotes(event, false, updateValues)
LunaEvent.Type.SLEEP -> askDurationEvent(event, false, updateValues) LunaEvent.Type.SLEEP -> askSleepValue(event, false, updateValues)
else -> { else -> {
Log.w(TAG, "Unexpected type: ${event.type}") Log.w(TAG, "Unexpected type: ${event.type}")
} }
@@ -1000,9 +933,7 @@ class MainActivity : AppCompatActivity() {
val spinner = findViewById<Spinner>(R.id.logbooks_spinner) val spinner = findViewById<Spinner>(R.id.logbooks_spinner)
val sAdapter = ArrayAdapter<String>(this@MainActivity, android.R.layout.simple_spinner_item) val sAdapter = ArrayAdapter<String>(this@MainActivity, android.R.layout.simple_spinner_item)
sAdapter.setDropDownViewResource(R.layout.row_logbook_spinner) sAdapter.setDropDownViewResource(R.layout.row_logbook_spinner)
for (ln in logbooksNames) {
val selectedLogbook = settingsRepository.loadSelectedLogbook()
logbooksNames.forEach { ln ->
sAdapter.add( sAdapter.add(
ln.ifEmpty { getString(R.string.default_logbook_name) } ln.ifEmpty { getString(R.string.default_logbook_name) }
) )
@@ -1015,20 +946,14 @@ class MainActivity : AppCompatActivity() {
position: Int, position: Int,
id: Long id: Long
) { ) {
val logbookName = logbooksNames[position]
// Changed logbook: empty list // Changed logbook: empty list
setListAdapter(arrayListOf()) setListAdapter(arrayListOf())
// Load logbook // Load logbook
loadLogbook(logbookName) loadLogbook(logbooksNames.get(position))
// Save logbook selection
settingsRepository.saveSelectedLogbook(logbookName)
} }
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
val selected = logbooksNames.indexOf(selectedLogbook)
if (selected > 0) spinner.setSelection(selected)
}) })
} }
@@ -1131,8 +1056,6 @@ class MainActivity : AppCompatActivity() {
logbook = lb logbook = lb
showLogbook() showLogbook()
populateHeaderMenu()
if (DEBUG_CHECK_LOGBOOK_CONSISTENCY) { if (DEBUG_CHECK_LOGBOOK_CONSISTENCY) {
for (e in logbook?.logs ?: listOf()) { for (e in logbook?.logs ?: listOf()) {
val em = e.getHeaderEmoji(this@MainActivity) val em = e.getHeaderEmoji(this@MainActivity)
@@ -1251,8 +1174,6 @@ class MainActivity : AppCompatActivity() {
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
savingEvent(false) savingEvent(false)
populateHeaderMenu()
}) })
} }
@@ -1336,7 +1257,6 @@ class MainActivity : AppCompatActivity() {
when (type) { when (type) {
LunaEvent.Type.BABY_BOTTLE -> addBabyBottleEvent(event) LunaEvent.Type.BABY_BOTTLE -> addBabyBottleEvent(event)
LunaEvent.Type.WEIGHT -> addWeightEvent(event) LunaEvent.Type.WEIGHT -> addWeightEvent(event)
LunaEvent.Type.HEIGHT -> addHeightEvent(event)
LunaEvent.Type.BREASTFEEDING_LEFT_NIPPLE -> addPlainEvent(event) LunaEvent.Type.BREASTFEEDING_LEFT_NIPPLE -> addPlainEvent(event)
LunaEvent.Type.BREASTFEEDING_BOTH_NIPPLE -> addPlainEvent(event) LunaEvent.Type.BREASTFEEDING_BOTH_NIPPLE -> addPlainEvent(event)
LunaEvent.Type.BREASTFEEDING_RIGHT_NIPPLE -> addPlainEvent(event) LunaEvent.Type.BREASTFEEDING_RIGHT_NIPPLE -> addPlainEvent(event)
@@ -1350,7 +1270,7 @@ class MainActivity : AppCompatActivity() {
LunaEvent.Type.FOOD -> addNoteEvent(event) LunaEvent.Type.FOOD -> addNoteEvent(event)
LunaEvent.Type.PUKE -> addAmountEvent(event) LunaEvent.Type.PUKE -> addAmountEvent(event)
LunaEvent.Type.BATH -> addPlainEvent(event) LunaEvent.Type.BATH -> addPlainEvent(event)
LunaEvent.Type.SLEEP -> addDurationEvent(event) LunaEvent.Type.SLEEP -> addSleepEvent(event)
LunaEvent.Type.UNKNOWN -> {} // ignore LunaEvent.Type.UNKNOWN -> {} // ignore
} }
} }
@@ -63,7 +63,7 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
// if the event is weight, show difference with the last one // if the event is weight, show difference with the last one
if (item.type == LunaEvent.Type.WEIGHT) { if (item.type == LunaEvent.Type.WEIGHT) {
val lastWeight = getPreviousEvent(position, LunaEvent.Type.WEIGHT) val lastWeight = getPreviousWeightEvent(position)
if (lastWeight != null) { if (lastWeight != null) {
val differenceInWeight = item.quantity - lastWeight.quantity val differenceInWeight = item.quantity - lastWeight.quantity
val sign = if (differenceInWeight > 0) "+" else "" val sign = if (differenceInWeight > 0) "+" else ""
@@ -74,19 +74,6 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
} }
} }
// if the event is height, show difference with the last one
if (item.type == LunaEvent.Type.HEIGHT) {
val lastHeight = getPreviousEvent(position, LunaEvent.Type.HEIGHT)
if (lastHeight != null) {
val differenceInHeight = item.quantity - lastHeight.quantity
val sign = if (differenceInHeight > 0) "+" else ""
quantityText += "\n($sign$differenceInHeight)"
if (differenceInHeight < 0) {
holder.quantity.setTextColor(ContextCompat.getColor(context, R.color.danger))
}
}
}
holder.quantity.text = quantityText holder.quantity.text = quantityText
// Listeners // Listeners
@@ -101,12 +88,12 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
return items.size return items.size
} }
private fun getPreviousEvent(startFromPosition: Int, type: LunaEvent.Type): LunaEvent? { private fun getPreviousWeightEvent(startFromPosition: Int): LunaEvent? {
if (startFromPosition == items.size - 1) if (startFromPosition == items.size - 1)
return null return null
for (pos in startFromPosition + 1 until items.size) { for (pos in startFromPosition + 1 until items.size) {
val item = items.get(pos) val item = items.get(pos)
if (item.type != type) if (item.type != LunaEvent.Type.WEIGHT)
continue continue
return item return item
} }
@@ -22,7 +22,6 @@ class LunaEvent: Comparable<LunaEvent> {
DIAPERCHANGE_PEE, DIAPERCHANGE_PEE,
SLEEP, SLEEP,
WEIGHT, WEIGHT,
HEIGHT,
MEDICINE, MEDICINE,
ENEMA, ENEMA,
NOTE, NOTE,
@@ -146,7 +145,6 @@ class LunaEvent: Comparable<LunaEvent> {
when (type) { when (type) {
Type.BABY_BOTTLE -> R.string.event_bottle_type Type.BABY_BOTTLE -> R.string.event_bottle_type
Type.WEIGHT -> R.string.event_weight_type Type.WEIGHT -> R.string.event_weight_type
Type.HEIGHT -> R.string.event_height_type
Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type
Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type
Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type
@@ -176,8 +174,7 @@ class LunaEvent: Comparable<LunaEvent> {
Type.DIAPERCHANGE_PEE, Type.DIAPERCHANGE_PEE,
Type.PUKE -> R.string.log_amount_dialog_description Type.PUKE -> R.string.log_amount_dialog_description
Type.WEIGHT -> R.string.log_weight_dialog_description Type.WEIGHT -> R.string.log_weight_dialog_description
Type.HEIGHT -> R.string.log_height_dialog_description Type.SLEEP -> R.string.log_sleep_dialog_description
Type.SLEEP -> R.string.log_duration_dialog_description
else -> R.string.log_unknown_dialog_description else -> R.string.log_unknown_dialog_description
} }
) )
@@ -188,7 +185,6 @@ class LunaEvent: Comparable<LunaEvent> {
when (type) { when (type) {
Type.BABY_BOTTLE -> R.string.event_bottle_desc Type.BABY_BOTTLE -> R.string.event_bottle_desc
Type.WEIGHT -> R.string.event_weight_desc Type.WEIGHT -> R.string.event_weight_desc
Type.HEIGHT -> R.string.event_height_desc
Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc
Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc
Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc
@@ -214,7 +210,6 @@ class LunaEvent: Comparable<LunaEvent> {
when (type) { when (type) {
Type.BABY_BOTTLE -> R.string.event_type_item_bottle Type.BABY_BOTTLE -> R.string.event_type_item_bottle
Type.WEIGHT -> R.string.event_type_item_weight Type.WEIGHT -> R.string.event_type_item_weight
Type.HEIGHT -> R.string.event_type_item_height
Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_type_item_breastfeeding_left Type.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_type_item_breastfeeding_left
Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_type_item_breastfeeding_both Type.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_type_item_breastfeeding_both
Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_type_item_breastfeeding_right Type.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_type_item_breastfeeding_right
@@ -15,7 +15,6 @@ class LocalSettingsRepository(val context: Context) {
const val SHARED_PREFS_DAV_PASS = "webdav_password" const val SHARED_PREFS_DAV_PASS = "webdav_password"
const val SHARED_PREFS_DYNAMIC_MENU = "dynamic_menu" const val SHARED_PREFS_DYNAMIC_MENU = "dynamic_menu"
const val SHARED_PREFS_SIGNATURE = "signature" const val SHARED_PREFS_SIGNATURE = "signature"
const val SHARED_PREFS_SELECTED_LOGBOOK = "selected_logbook"
} }
enum class DATA_REPO {LOCAL_FILE, WEBDAV} enum class DATA_REPO {LOCAL_FILE, WEBDAV}
val sharedPreferences: SharedPreferences val sharedPreferences: SharedPreferences
@@ -77,14 +76,4 @@ class LocalSettingsRepository(val context: Context) {
return null return null
return arrayOf(url, user, pass) return arrayOf(url, user, pass)
} }
fun saveSelectedLogbook(selectedLogbookName: String) {
sharedPreferences.edit (commit = true) {
putString(SHARED_PREFS_SELECTED_LOGBOOK, selectedLogbookName)
}
}
fun loadSelectedLogbook() : String? {
return sharedPreferences.getString(SHARED_PREFS_SELECTED_LOGBOOK, null)
}
} }
+1 -6
View File
@@ -9,15 +9,10 @@ import java.util.Date
class DateUtils { class DateUtils {
companion object { companion object {
/** /**
* Format time duration in seconds as e.g. "2 hours, 1 min", rounded to minutes. * 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. * Used for the duration to the next/previous event in the event details dialog.
*/ */
fun formatTimeDuration(context: Context, secondsDiff: Long): String { fun formatTimeDuration(context: Context, secondsDiff: Long): String {
val adjusted = (secondsDiff + 30) - (secondsDiff + 30) % 60
return formatTimeDurationExact(context, adjusted)
}
fun formatTimeDurationExact(context: Context, secondsDiff: Long): String {
var seconds = secondsDiff var seconds = secondsDiff
val years = (seconds / (365 * 24 * 60 * 60F)).toLong() val years = (seconds / (365 * 24 * 60 * 60F)).toLong()
+2 -8
View File
@@ -11,10 +11,10 @@ import utils.DateUtils.Companion.formatTimeDuration
import java.text.NumberFormat import java.text.NumberFormat
class NumericUtils (val context: Context) { class NumericUtils (val context: Context) {
val numberFormat: NumberFormat
val measurement_unit_liquid_base: String val measurement_unit_liquid_base: String
val measurement_unit_weight_base: String val measurement_unit_weight_base: String
val measurement_unit_weight_tiny: String val measurement_unit_weight_tiny: String
val measurement_unit_height_base: String
val measurement_unit_temperature_base: String val measurement_unit_temperature_base: String
private fun isMetricSystem(): Boolean { private fun isMetricSystem(): Boolean {
@@ -35,6 +35,7 @@ class NumericUtils (val context: Context) {
} }
init { init {
this.numberFormat = NumberFormat.getInstance()
this.measurement_unit_liquid_base = context.getString( this.measurement_unit_liquid_base = context.getString(
if (isMetricSystem()) if (isMetricSystem())
R.string.measurement_unit_liquid_base_metric R.string.measurement_unit_liquid_base_metric
@@ -53,12 +54,6 @@ class NumericUtils (val context: Context) {
else else
R.string.measurement_unit_weight_tiny_imperial R.string.measurement_unit_weight_tiny_imperial
) )
this.measurement_unit_height_base = context.getString(
if (isMetricSystem())
R.string.measurement_unit_height_base_metric
else
R.string.measurement_unit_height_base_imperial
)
this.measurement_unit_temperature_base = context.getString( this.measurement_unit_temperature_base = context.getString(
if (isMetricSystem()) if (isMetricSystem())
R.string.measurement_unit_temperature_base_metric R.string.measurement_unit_temperature_base_metric
@@ -95,7 +90,6 @@ class NumericUtils (val context: Context) {
when (type) { when (type) {
LunaEvent.Type.BABY_BOTTLE -> measurement_unit_liquid_base LunaEvent.Type.BABY_BOTTLE -> measurement_unit_liquid_base
LunaEvent.Type.WEIGHT -> measurement_unit_weight_base LunaEvent.Type.WEIGHT -> measurement_unit_weight_base
LunaEvent.Type.HEIGHT -> measurement_unit_height_base
LunaEvent.Type.MEDICINE -> measurement_unit_weight_tiny LunaEvent.Type.MEDICINE -> measurement_unit_weight_tiny
LunaEvent.Type.TEMPERATURE -> measurement_unit_temperature_base LunaEvent.Type.TEMPERATURE -> measurement_unit_temperature_base
else -> "" else -> ""
+5
View File
@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
@@ -259,7 +259,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginHorizontal="10dp"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:text="@string/no_connection_explain"/> android:text="@string/no_connection_explain"/>
@@ -18,7 +18,6 @@
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<TextView <TextView
android:id="@+id/dialog_number_picker_unit"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
@@ -7,69 +7,26 @@
android:gravity="center" android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<TextView
android:id="@+id/dialog_date_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textSize="20sp"
android:text="💤"/>
<LinearLayout
android:id="@+id/duration_buttons"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:gravity="center"
android:layout_marginTop="20dp"
android:layout_marginHorizontal="10dp"
android:orientation="horizontal">
<Button
android:id="@+id/dialog_date_duration_now"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/dialog_duration_button_now"/>
<Button
android:id="@+id/dialog_date_duration_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/dialog_duration_button_clear"/>
</LinearLayout>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:gravity="center"
android:layout_marginTop="20dp"
android:layout_marginHorizontal="10dp"
android:orientation="horizontal">
<TextView <TextView
android:id="@+id/dialog_date_picker_begin" android:id="@+id/dialog_date_picker_begin"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:layout_gravity="center"
android:layout_weight="1"/> android:layout_marginTop="10dp"/>
<TextView <TextView
android:id="@+id/dialog_date_range_delimiter" android:id="@+id/dialog_date_duration"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:layout_marginTop="20dp"
android:layout_weight="1" android:textSize="20sp"
android:text="-"/> android:text="💤"/>
<TextView <TextView
android:id="@+id/dialog_date_picker_end" android:id="@+id/dialog_date_picker_end"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:layout_gravity="center"
android:layout_weight="1"/> android:layout_marginTop="20dp"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
@@ -1,39 +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:id="@+id/dialog_number_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="cm"/>
</LinearLayout>
<TextView
android:id="@+id/dialog_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"/>
</LinearLayout>
@@ -21,7 +21,6 @@
android:background="@drawable/textview_background"/> android:background="@drawable/textview_background"/>
<TextView <TextView
android:id="@+id/dialog_number_unit"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:gravity="center"
android:padding="5dip" />
+2 -7
View File
@@ -16,8 +16,6 @@
<string name="event_note_desc">Notiz</string> <string name="event_note_desc">Notiz</string>
<string name="event_temperature_desc">Temperatur</string> <string name="event_temperature_desc">Temperatur</string>
<string name="event_colic_desc">Blähungskolik</string> <string name="event_colic_desc">Blähungskolik</string>
<string name="event_puke_desc">Erbrechen</string>
<string name="event_bath_desc">Bad</string>
<string name="event_unknown_desc"></string> <string name="event_unknown_desc"></string>
<string name="toast_event_added">Ereignis gespeichert</string> <string name="toast_event_added">Ereignis gespeichert</string>
@@ -37,8 +35,6 @@
<string name="no_connection_retry">Erneut versuchen</string> <string name="no_connection_retry">Erneut versuchen</string>
<string name="settings_title">Einstellungen</string> <string name="settings_title">Einstellungen</string>
<string name="settings_signature">Signatur</string>
<string name="settings_signature_desc">Fügen Sie jedem von Ihnen erstellten Ereignis eine Signatur hinzu, die für andere sichtbar ist. Dies ist nützlich, wenn mehrere Personen Ereignisse hinzufügen.</string>
<string name="settings_storage">Speicherort für Daten auswählen</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">Auf dem Gerät</string>
<string name="settings_storage_local_desc">Datenschutzfreundlichste Lösung: Deine Daten verlassen dein Gerät nicht</string> <string name="settings_storage_local_desc">Datenschutzfreundlichste Lösung: Deine Daten verlassen dein Gerät nicht</string>
@@ -52,6 +48,7 @@
<string name="settings_webdav_error_denied">Falscher WebDAV-Benutzer oder falsches Passwort</string> <string name="settings_webdav_error_denied">Falscher WebDAV-Benutzer oder falsches Passwort</string>
<string name="settings_webdav_error_server_offline">WebDAV-Server ist derzeit nicht verfügbar</string> <string name="settings_webdav_error_server_offline">WebDAV-Server ist derzeit nicht verfügbar</string>
<string name="settings_webdav_error_generic">Fehler beim Zugriff auf WebDAV:</string> <string name="settings_webdav_error_generic">Fehler beim Zugriff auf WebDAV:</string>
<string name="settings_webdav_creation_error_generic">Eine Datei konnte auf dem WebDAV-Server nicht gespeichert werden:</string>
<string name="settings_webdav_creation_ok">Erfolgreich mit dem WebDAV-Server verbunden</string> <string name="settings_webdav_creation_ok">Erfolgreich mit dem WebDAV-Server verbunden</string>
<string name="settings_json_error">Es befindet sich eine Speicherdatei auf dem Server, aber sie ist beschädigt oder unlesbar. Bitte lösche die Datei.</string> <string name="settings_json_error">Es befindet sich eine Speicherdatei auf dem Server, aber sie ist beschädigt oder unlesbar. Bitte lösche die Datei.</string>
<string name="settings_generic_error">Fehler: </string> <string name="settings_generic_error">Fehler: </string>
@@ -65,6 +62,7 @@
<string name="log_bottle_dialog_description">Trinkmenge eingeben</string> <string name="log_bottle_dialog_description">Trinkmenge eingeben</string>
<string name="log_medicine_dialog_description">Medikamentenname, Menge, Art, Notizen, …:</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_notes_dialog_note_hint">Notiz eingeben</string>
<string name="log_notes_dialog_qty_hint">Menge (optional)</string> <string name="log_notes_dialog_qty_hint">Menge (optional)</string>
<string name="log_temperature_dialog_description">Temperatur eingeben</string> <string name="log_temperature_dialog_description">Temperatur eingeben</string>
@@ -73,9 +71,6 @@
<string name="dialog_event_detail_title">Ereignisdetails</string> <string name="dialog_event_detail_title">Ereignisdetails</string>
<string name="dialog_event_detail_close_button">OK</string> <string name="dialog_event_detail_close_button">OK</string>
<string name="dialog_event_detail_delete_button">Löschen</string> <string name="dialog_event_detail_delete_button">Löschen</string>
<string name="dialog_event_detail_quantity">Menge</string>
<string name="dialog_event_detail_notes">Notiz</string>
<string name="dialog_event_detail_signature">von %s</string>
<string name="dialog_add_logbook_title">Logbuch hinzufügen</string> <string name="dialog_add_logbook_title">Logbuch hinzufügen</string>
<string name="dialog_add_logbook_logbookname">👶 Logbuchname</string> <string name="dialog_add_logbook_logbookname">👶 Logbuchname</string>
+2
View File
@@ -48,6 +48,7 @@
<string name="settings_webdav_error_denied">Nom d\'utilisateur ou mot de passe incorrect pour le service WebDAV</string> <string name="settings_webdav_error_denied">Nom d\'utilisateur ou mot de passe incorrect pour le service WebDAV</string>
<string name="settings_webdav_error_server_offline">Impossible d\'accéder au serveur WebDAV</string> <string name="settings_webdav_error_server_offline">Impossible d\'accéder au serveur WebDAV</string>
<string name="settings_webdav_error_generic">Une erreur est survenue en essayant d\'accéder au serveur WebDAV:</string> <string name="settings_webdav_error_generic">Une erreur est survenue en essayant d\'accéder au serveur WebDAV:</string>
<string name="settings_webdav_creation_error_generic">Impossible de sauvegarder un fichier sur le serveur WebDAV:</string>
<string name="settings_webdav_creation_ok">Connexion réussie avec le serveur WebDAV</string> <string name="settings_webdav_creation_ok">Connexion réussie avec le serveur WebDAV</string>
<string name="settings_json_error">Il y a un fichier sur le serveur WebDAV, toutefois il est corronpu ou illisible. Merci de le supprimer et réessayer</string> <string name="settings_json_error">Il y a un fichier sur le serveur WebDAV, toutefois il est corronpu ou illisible. Merci de le supprimer et réessayer</string>
<string name="settings_generic_error">Erreur: </string> <string name="settings_generic_error">Erreur: </string>
@@ -61,6 +62,7 @@
<string name="log_bottle_dialog_description">Renseignez la quantité contenue dans le biberon</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_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_notes_dialog_note_hint">Notes ...</string>
<string name="log_notes_dialog_qty_hint">Quantité (ou vide)</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_temperature_dialog_description">Renseignez la Température</string>
+2
View File
@@ -48,6 +48,7 @@
<string name="settings_webdav_error_denied">Nome utente o password WebDAV sbagliati</string> <string name="settings_webdav_error_denied">Nome utente o password WebDAV sbagliati</string>
<string name="settings_webdav_error_server_offline">Il server WebDAV non è al momento disponibile</string> <string name="settings_webdav_error_server_offline">Il server WebDAV non è al momento disponibile</string>
<string name="settings_webdav_error_generic">Si è verificato un errore tentando di accedere al server WebDAV:</string> <string name="settings_webdav_error_generic">Si è verificato un errore tentando di accedere al server WebDAV:</string>
<string name="settings_webdav_creation_error_generic">Impossibile creare un file di salvataggio sul server WebDAV:</string>
<string name="settings_webdav_creation_ok">Connessione al server WebDAV avvenuta con successo</string> <string name="settings_webdav_creation_ok">Connessione al server WebDAV avvenuta con successo</string>
<string name="settings_json_error">Sul server esiste un salvataggio, ma è corrotto o illeggibile. Cancellare il file </string> <string name="settings_json_error">Sul server esiste un salvataggio, ma è corrotto o illeggibile. Cancellare il file </string>
<string name="settings_generic_error">Si è verificato un errore: </string> <string name="settings_generic_error">Si è verificato un errore: </string>
@@ -61,6 +62,7 @@
<string name="log_bottle_dialog_description">Inserisci la quantità contenuta nel biberon</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_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_notes_dialog_note_hint">Inserisci le note</string>
<string name="log_notes_dialog_qty_hint">Quantità, o vuoto</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_temperature_dialog_description">Inserisci la temperatura</string>
+1
View File
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="accent">#FFE68F</color> <color name="accent">#FFE68F</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color> <color name="white">#FFFFFFFF</color>
<color name="translucent">#c000</color> <color name="translucent">#c000</color>
<color name="transparent">#0000</color> <color name="transparent">#0000</color>
+3 -10
View File
@@ -7,7 +7,6 @@
<string name="event_bottle_type" translatable="false">🍼</string> <string name="event_bottle_type" translatable="false">🍼</string>
<string name="event_food_type" translatable="false">🥣</string> <string name="event_food_type" translatable="false">🥣</string>
<string name="event_weight_type" translatable="false">⚖️</string> <string name="event_weight_type" translatable="false">⚖️</string>
<string name="event_height_type" translatable="false">📏</string>
<string name="event_breastfeeding_left_type" translatable="false">🤱⬅️</string> <string name="event_breastfeeding_left_type" translatable="false">🤱⬅️</string>
<string name="event_breastfeeding_both_type" translatable="false">🤱↔️</string> <string name="event_breastfeeding_both_type" translatable="false">🤱↔️</string>
<string name="event_breastfeeding_right_type" translatable="false">🤱➡️️</string> <string name="event_breastfeeding_right_type" translatable="false">🤱➡️️</string>
@@ -27,7 +26,6 @@
<string name="event_type_item_bottle">🍼 Bottle</string> <string name="event_type_item_bottle">🍼 Bottle</string>
<string name="event_type_item_food">🥣 Food</string> <string name="event_type_item_food">🥣 Food</string>
<string name="event_type_item_weight">⚖️ Weight</string> <string name="event_type_item_weight">⚖️ Weight</string>
<string name="event_type_item_height">📏 Height</string>
<string name="event_type_item_breastfeeding_left">🤱⬅️ Nursing</string> <string name="event_type_item_breastfeeding_left">🤱⬅️ Nursing</string>
<string name="event_type_item_breastfeeding_both">🤱↔️ Nursing</string> <string name="event_type_item_breastfeeding_both">🤱↔️ Nursing</string>
<string name="event_type_item_breastfeeding_right">🤱➡️️ Nursing</string> <string name="event_type_item_breastfeeding_right">🤱➡️️ Nursing</string>
@@ -47,7 +45,6 @@
<string name="event_bottle_desc">Milk Bottle</string> <string name="event_bottle_desc">Milk Bottle</string>
<string name="event_food_desc">Food</string> <string name="event_food_desc">Food</string>
<string name="event_weight_desc">Weight</string> <string name="event_weight_desc">Weight</string>
<string name="event_height_desc">Height</string>
<string name="event_breastfeeding_left_desc">Nursing (left)</string> <string name="event_breastfeeding_left_desc">Nursing (left)</string>
<string name="event_breastfeeding_both_desc">Nursing (both)</string> <string name="event_breastfeeding_both_desc">Nursing (both)</string>
<string name="event_breastfeeding_right_desc">Nursing (right)</string> <string name="event_breastfeeding_right_desc">Nursing (right)</string>
@@ -109,6 +106,7 @@
<string name="settings_webdav_error_denied">Wrong WebDAV user or password</string> <string name="settings_webdav_error_denied">Wrong WebDAV user or password</string>
<string name="settings_webdav_error_server_offline">WebDAV server is currently unavailable</string> <string name="settings_webdav_error_server_offline">WebDAV server is currently unavailable</string>
<string name="settings_webdav_error_generic">Error while trying to access WebDAV:</string> <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_webdav_creation_ok">Successfully connected with the WebDAV server</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_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_generic_error">Error: </string>
@@ -123,22 +121,20 @@
<string name="log_amount_dialog_description">Select the amount:</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_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_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_notes_dialog_note_hint">Write some notes</string>
<string name="log_notes_dialog_qty_hint">Quantity (or empty)</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_temperature_dialog_description">Select the temperature:</string>
<string name="log_unknown_dialog_description"></string> <string name="log_unknown_dialog_description"></string>
<string name="log_weight_dialog_description">Insert the weight:</string> <string name="log_weight_dialog_description">Insert the weight:</string>
<string name="log_height_dialog_description">Insert the height:</string> <string name="log_sleep_dialog_description">Set sleep duration:</string>
<string name="log_duration_dialog_description">Set duration:</string>
<string name="measurement_unit_liquid_base_metric" translatable="false">ml</string> <string name="measurement_unit_liquid_base_metric" translatable="false">ml</string>
<string name="measurement_unit_weight_base_metric" translatable="false">g</string> <string name="measurement_unit_weight_base_metric" translatable="false">g</string>
<string name="measurement_unit_weight_tiny_metric" translatable="false">mg</string> <string name="measurement_unit_weight_tiny_metric" translatable="false">mg</string>
<string name="measurement_unit_height_base_metric" translatable="false">cm</string>
<string name="measurement_unit_liquid_base_imperial" translatable="false">fl oz.</string> <string name="measurement_unit_liquid_base_imperial" translatable="false">fl oz.</string>
<string name="measurement_unit_weight_base_imperial" translatable="false">oz</string> <string name="measurement_unit_weight_base_imperial" translatable="false">oz</string>
<string name="measurement_unit_weight_tiny_imperial" translatable="false">gr</string> <string name="measurement_unit_weight_tiny_imperial" translatable="false">gr</string>
<string name="measurement_unit_height_base_imperial" translatable="false">in</string>
<string name="measurement_unit_temperature_base_imperial" translatable="false">°F</string> <string name="measurement_unit_temperature_base_imperial" translatable="false">°F</string>
<string name="measurement_unit_temperature_base_metric" translatable="false">°C</string> <string name="measurement_unit_temperature_base_metric" translatable="false">°C</string>
@@ -154,9 +150,6 @@
<string name="dialog_event_detail_notes">Notes</string> <string name="dialog_event_detail_notes">Notes</string>
<string name="dialog_event_detail_signature">by %s</string> <string name="dialog_event_detail_signature">by %s</string>
<string name="dialog_duration_button_clear">Clear</string>
<string name="dialog_duration_button_now">Now</string>
<string name="dialog_add_logbook_title">Add logbook</string> <string name="dialog_add_logbook_title">Add logbook</string>
<string name="dialog_add_logbook_logbookname">👶 Logbook name</string> <string name="dialog_add_logbook_logbookname">👶 Logbook name</string>
<string name="dialog_add_logbook_message">Write a name to identify this logbook. This name will appear on top of the screen and, if you use WebDAV, will be in the save file name as well.</string> <string name="dialog_add_logbook_message">Write a name to identify this logbook. This name will appear on top of the screen and, if you use WebDAV, will be in the save file name as well.</string>
-10
View File
@@ -21,13 +21,3 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.defaults.buildfeatures.resvalues=true
android.sdk.defaultTargetSdkToCompileSdkIfUnset=false
android.enableAppCompileTimeRClass=false
android.usesSdkInManifest.disallowed=false
android.uniquePackageNames=false
android.dependency.useConstraints=true
android.r8.strictFullModeForKeepRules=false
android.r8.optimizedResourceShrinking=false
android.builtInKotlin=false
android.newDsl=false
+2 -2
View File
@@ -1,6 +1,6 @@
[versions] [versions]
agp = "9.4.1" agp = "8.13.0"
kotlin = "2.2.10" kotlin = "2.0.0"
coreKtx = "1.10.1" coreKtx = "1.10.1"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.1.5" junitVersion = "1.1.5"
+1 -1
View File
@@ -1,6 +1,6 @@
#Sat Nov 02 10:58:51 CET 2024 #Sat Nov 02 10:58:51 CET 2024
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists