5 Commits

Author SHA1 Message Date
8c906369df Updated readme with contribution instructions 2026-01-09 07:20:00 +01:00
5eeb462836 Bumped version 2025-11-06 22:21:05 +01:00
5b777c0b88 Merge pull request 'fix puke event quantity' (#18) from mwarning/luna-tracker:fixpuke into master
I already tagged the commit when I accepted the previous PR, so it may have already been picked up by the F-Droid build pipeline. I'll send this one too and see if it makes in time.
2025-11-06 22:17:40 +01:00
6f3061974d fix puke event quantity
The LunaEvent class treats quantities of 0
as a value to set. To workaround this, the
quantity index needs to start at >0.
2025-11-06 21:49:08 +01:00
3ea396c045 Bumped version 2025-11-05 07:58:28 +01:00
12 changed files with 38 additions and 414 deletions

View File

@@ -13,7 +13,24 @@ Dedicated to my daughter Luna.
![Screenshot](fastlane/metadata/android/en-US/images/phoneScreenshots/1.png)
## Thanks for the valuable contributions to:
## Contributions
### 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)
- Daniel Neubauer (German translation)

View File

@@ -12,8 +12,8 @@ android {
applicationId = "it.danieleverducci.lunatracker"
minSdk = 21
targetSdk = 34
versionCode = 5
versionName = "0.7"
versionCode = 7
versionName = "0.9"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -60,4 +60,4 @@ dependencies {
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
}
}

View File

@@ -4,7 +4,6 @@ import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.DialogInterface
import android.content.Intent
import android.content.res.ColorStateList
import android.os.Bundle
import android.os.Handler
import android.util.Log
@@ -27,7 +26,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.slider.Slider
import com.thegrizzlylabs.sardineandroid.impl.SardineException
import it.danieleverducci.lunatracker.adapters.DaySeparatorDecoration
import it.danieleverducci.lunatracker.adapters.LunaEventRecyclerAdapter
import it.danieleverducci.lunatracker.entities.Logbook
import it.danieleverducci.lunatracker.entities.LunaEvent
@@ -69,13 +67,6 @@ class MainActivity : AppCompatActivity() {
var logbookRepo: LogbookRepository? = null
var showingOverflowPopupWindow = false
// Breastfeeding timer state
var bfTimerStartTime: Long = 0
var bfTimerType: String? = null
var bfTimerDialog: AlertDialog? = null
var bfTimerHandler: Handler? = null
var bfTimerRunnable: Runnable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -93,27 +84,21 @@ class MainActivity : AppCompatActivity() {
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 {
startBreastfeedingTimer(LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE)
}
findViewById<View>(R.id.button_nipple_left).setOnLongClickListener {
askBreastfeedingDuration(LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE)
true
}
findViewById<View>(R.id.button_nipple_both).setOnClickListener {
startBreastfeedingTimer(LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE)
}
findViewById<View>(R.id.button_nipple_both).setOnLongClickListener {
askBreastfeedingDuration(LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE)
true
}
findViewById<View>(R.id.button_nipple_right).setOnClickListener {
startBreastfeedingTimer(LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE)
}
findViewById<View>(R.id.button_nipple_right).setOnLongClickListener {
askBreastfeedingDuration(LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE)
true
}
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
@@ -151,12 +136,6 @@ class MainActivity : AppCompatActivity() {
}
}
recyclerView.adapter = adapter
// Tages-Trenner hinzufügen
while (recyclerView.itemDecorationCount > 0) {
recyclerView.removeItemDecorationAt(0)
}
recyclerView.addItemDecoration(DaySeparatorDecoration(this, items))
}
fun showSettings() {
@@ -201,9 +180,6 @@ class MainActivity : AppCompatActivity() {
// Update list dates
recyclerView.adapter?.notifyDataSetChanged()
// Check for ongoing breastfeeding timer
restoreBreastfeedingTimerIfNeeded()
if (logbook != null) {
// Already running: reload data for currently selected logbook
loadLogbook(logbook!!.name)
@@ -216,10 +192,6 @@ class MainActivity : AppCompatActivity() {
override fun onStop() {
handler.removeCallbacks(updateListRunnable)
// Clean up breastfeeding timer UI (state is preserved in SharedPreferences)
bfTimerRunnable?.let { bfTimerHandler?.removeCallbacks(it) }
bfTimerDialog?.dismiss()
super.onStop()
}
@@ -303,7 +275,7 @@ class MainActivity : AppCompatActivity() {
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
val pos = spinner.selectedItemPosition
logEvent(LunaEvent(LunaEvent.TYPE_PUKE, pos))
logEvent(LunaEvent(LunaEvent.TYPE_PUKE, pos + 1))
}
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() }
val alertDialog = d.create()
@@ -339,119 +311,6 @@ class MainActivity : AppCompatActivity() {
alertDialog.show()
}
fun startBreastfeedingTimer(eventType: String) {
// Check if timer already running
if (bfTimerType != null) {
Toast.makeText(this, R.string.breastfeeding_timer_already_running, Toast.LENGTH_SHORT).show()
return
}
// Save timer state
bfTimerStartTime = System.currentTimeMillis()
bfTimerType = eventType
saveBreastfeedingTimerState()
// Show timer dialog
showBreastfeedingTimerDialog(eventType)
}
fun showBreastfeedingTimerDialog(eventType: String) {
val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.breastfeeding_timer_dialog, null)
d.setTitle(R.string.breastfeeding_timer_title)
d.setView(dialogView)
d.setCancelable(false)
val timerDisplay = dialogView.findViewById<TextView>(R.id.breastfeeding_timer_display)
val sideEmoji = dialogView.findViewById<TextView>(R.id.breastfeeding_side_emoji)
sideEmoji.text = LunaEvent(eventType).getTypeEmoji(this)
// Set up timer updates
bfTimerHandler = Handler(mainLooper)
bfTimerRunnable = object : Runnable {
override fun run() {
val elapsed = (System.currentTimeMillis() - bfTimerStartTime) / 1000
val minutes = elapsed / 60
val seconds = elapsed % 60
timerDisplay.text = String.format("%02d:%02d", minutes, seconds)
bfTimerHandler?.postDelayed(this, 1000)
}
}
bfTimerHandler?.post(bfTimerRunnable!!)
d.setPositiveButton(R.string.breastfeeding_timer_stop) { _, _ ->
stopBreastfeedingTimer()
}
d.setNegativeButton(android.R.string.cancel) { dialogInterface, _ ->
cancelBreastfeedingTimer()
dialogInterface.dismiss()
}
bfTimerDialog = d.create()
bfTimerDialog?.show()
}
fun stopBreastfeedingTimer() {
bfTimerHandler?.removeCallbacks(bfTimerRunnable!!)
val durationMillis = System.currentTimeMillis() - bfTimerStartTime
val durationMinutes = Math.max(1, (durationMillis / 60000).toInt()) // Minimum 1 minute
val eventType = bfTimerType
clearBreastfeedingTimerState()
if (eventType != null) {
logEvent(LunaEvent(eventType, durationMinutes))
}
}
fun cancelBreastfeedingTimer() {
bfTimerHandler?.removeCallbacks(bfTimerRunnable!!)
clearBreastfeedingTimerState()
}
fun askBreastfeedingDuration(eventType: String) {
val d = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.breastfeeding_duration_dialog, null)
d.setTitle(R.string.breastfeeding_duration_title)
d.setMessage(R.string.breastfeeding_duration_description)
d.setView(dialogView)
val numberPicker = dialogView.findViewById<NumberPicker>(R.id.breastfeeding_duration_picker)
numberPicker.minValue = 1
numberPicker.maxValue = 60
numberPicker.value = 15 // Default 15 minutes
numberPicker.wrapSelectorWheel = false
d.setPositiveButton(android.R.string.ok) { _, _ ->
logEvent(LunaEvent(eventType, numberPicker.value))
}
d.setNegativeButton(android.R.string.cancel) { dialogInterface, _ ->
dialogInterface.dismiss()
}
d.create().show()
}
fun saveBreastfeedingTimerState() {
LocalSettingsRepository(this).saveBreastfeedingTimer(bfTimerStartTime, bfTimerType ?: "")
}
fun clearBreastfeedingTimerState() {
bfTimerStartTime = 0
bfTimerType = null
bfTimerDialog = null
LocalSettingsRepository(this).clearBreastfeedingTimer()
}
fun restoreBreastfeedingTimerIfNeeded() {
val timerState = LocalSettingsRepository(this).loadBreastfeedingTimer()
if (timerState != null && timerState.first > 0 && timerState.second.isNotEmpty()) {
bfTimerStartTime = timerState.first
bfTimerType = timerState.second
showBreastfeedingTimerDialog(timerState.second)
}
}
fun askToTrimLogbook() {
val d = AlertDialog.Builder(this)
d.setTitle(R.string.trim_logbook_dialog_title)
@@ -539,36 +398,6 @@ class MainActivity : AppCompatActivity() {
}, startYear, startMonth, startDay).show()
}
// Make quantity editable for breastfeeding events
val quantityTextView = dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_quantity)
if (event.type in listOf(
LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE,
LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE,
LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE
) && event.quantity > 0) {
quantityTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_edit, 0)
quantityTextView.compoundDrawableTintList = ColorStateList.valueOf(getColor(R.color.accent))
quantityTextView.setOnClickListener {
val pickerDialog = AlertDialog.Builder(this@MainActivity)
val pickerView = layoutInflater.inflate(R.layout.breastfeeding_duration_dialog, null)
val picker = pickerView.findViewById<NumberPicker>(R.id.breastfeeding_duration_picker)
picker.minValue = 1
picker.maxValue = 60
picker.value = if (event.quantity > 0) event.quantity else 15
pickerDialog.setTitle(R.string.breastfeeding_duration_title)
pickerDialog.setView(pickerView)
pickerDialog.setPositiveButton(android.R.string.ok) { _, _ ->
event.quantity = picker.value
quantityTextView.text = NumericUtils(this@MainActivity).formatEventQuantity(event)
recyclerView.adapter?.notifyDataSetChanged()
saveLogbook()
}
pickerDialog.setNegativeButton(android.R.string.cancel, null)
pickerDialog.show()
}
}
d.setView(dialogView)
d.setPositiveButton(R.string.dialog_event_detail_close_button) { dialogInterface, i -> dialogInterface.dismiss() }
d.setNeutralButton(R.string.dialog_event_detail_delete_button) { dialogInterface, i -> deleteEvent(event) }

View File

@@ -1,80 +0,0 @@
package it.danieleverducci.lunatracker.adapters
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.text.format.DateFormat
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import it.danieleverducci.lunatracker.R
import it.danieleverducci.lunatracker.entities.LunaEvent
import java.util.Calendar
import java.util.Date
class DaySeparatorDecoration(
private val context: Context,
private val items: List<LunaEvent>
) : RecyclerView.ItemDecoration() {
private val textPaint = Paint().apply {
color = context.getColor(R.color.grey)
textSize = 32f
textAlign = Paint.Align.CENTER
isAntiAlias = true
}
private val linePaint = Paint().apply {
color = context.getColor(R.color.grey)
strokeWidth = 1f
isAntiAlias = true
}
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val position = parent.getChildAdapterPosition(view)
if (shouldShowHeader(position)) {
outRect.top = 48
}
}
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
for (i in 0 until parent.childCount) {
val child = parent.getChildAt(i)
val position = parent.getChildAdapterPosition(child)
if (shouldShowHeader(position)) {
val dateText = formatDate(items[position].time)
val y = child.top - 16f
// Linie links
canvas.drawLine(20f, y, parent.width / 2f - 80f, y, linePaint)
// Datum in der Mitte
canvas.drawText(dateText, parent.width / 2f, y + 10f, textPaint)
// Linie rechts
canvas.drawLine(parent.width / 2f + 80f, y, parent.width - 20f, y, linePaint)
}
}
}
private fun shouldShowHeader(position: Int): Boolean {
if (position <= 0 || position >= items.size) return false
val currentDay = getDay(items[position].time)
val previousDay = getDay(items[position - 1].time)
return currentDay != previousDay
}
private fun getDay(timestamp: Long): Long {
val cal = Calendar.getInstance()
cal.timeInMillis = timestamp * 1000
cal.set(Calendar.HOUR_OF_DAY, 0)
cal.set(Calendar.MINUTE, 0)
cal.set(Calendar.SECOND, 0)
cal.set(Calendar.MILLISECOND, 0)
return cal.timeInMillis
}
private fun formatDate(timestamp: Long): String {
return DateFormat.getDateFormat(context).format(Date(timestamp * 1000))
}
}

View File

@@ -15,8 +15,6 @@ class LocalSettingsRepository(val context: Context) {
const val SHARED_PREFS_DAV_PASS = "webdav_password"
const val SHARED_PREFS_NO_BREASTFEEDING = "no_breastfeeding"
const val SHARED_PREFS_SIGNATURE = "signature"
const val SHARED_PREFS_BF_TIMER_START = "bf_timer_start"
const val SHARED_PREFS_BF_TIMER_TYPE = "bf_timer_type"
}
enum class DATA_REPO {LOCAL_FILE, WEBDAV}
val sharedPreferences: SharedPreferences
@@ -86,25 +84,4 @@ class LocalSettingsRepository(val context: Context) {
return null
return arrayOf(url, user, pass)
}
fun saveBreastfeedingTimer(startTime: Long, eventType: String) {
sharedPreferences.edit {
putLong(SHARED_PREFS_BF_TIMER_START, startTime)
putString(SHARED_PREFS_BF_TIMER_TYPE, eventType)
}
}
fun loadBreastfeedingTimer(): Pair<Long, String>? {
val startTime = sharedPreferences.getLong(SHARED_PREFS_BF_TIMER_START, 0)
val eventType = sharedPreferences.getString(SHARED_PREFS_BF_TIMER_TYPE, null)
if (startTime == 0L || eventType == null) return null
return Pair(startTime, eventType)
}
fun clearBreastfeedingTimer() {
sharedPreferences.edit {
remove(SHARED_PREFS_BF_TIMER_START)
remove(SHARED_PREFS_BF_TIMER_TYPE)
}
}
}

View File

@@ -67,7 +67,7 @@ class NumericUtils (val context: Context) {
LunaEvent.TYPE_TEMPERATURE ->
(item.quantity / 10.0f).toString()
LunaEvent.TYPE_PUKE ->
context.resources.getStringArray(R.array.AmountLabels)[item.quantity]
context.resources.getStringArray(R.array.AmountLabels)[item.quantity - 1]
else ->
item.quantity
})
@@ -79,10 +79,6 @@ class NumericUtils (val context: Context) {
LunaEvent.TYPE_WEIGHT -> measurement_unit_weight_base
LunaEvent.TYPE_MEDICINE -> measurement_unit_weight_tiny
LunaEvent.TYPE_TEMPERATURE -> measurement_unit_temperature_base
LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE,
LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE,
LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE ->
context.getString(R.string.measurement_unit_time_minutes)
else -> ""
}
)

View File

@@ -1,20 +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:layout_gravity="center"
android:gravity="center">
<NumberPicker
android:id="@+id/breastfeeding_duration_picker"
android:layout_width="100dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@string/measurement_unit_time_minutes"/>
</LinearLayout>

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:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:id="@+id/breastfeeding_side_emoji"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="60sp"/>
<TextView
android:id="@+id/breastfeeding_timer_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textSize="48sp"
android:textColor="@color/accent"
android:fontFamily="monospace"
android:text="00:00"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textSize="14sp"
android:textColor="@color/grey"
android:text="@string/breastfeeding_timer_hint"/>
</LinearLayout>

View File

@@ -94,42 +94,4 @@
<string name="default_logbook_name">👶 Mein erstes Logbuch</string>
<string name="logbook_created">Neues Logbuch erstellt: </string>
<string name="breastfeeding_timer_title">Stillen läuft</string>
<string name="breastfeeding_timer_stop">Stopp</string>
<string name="breastfeeding_timer_hint">Tippe Stopp wenn fertig</string>
<string name="breastfeeding_timer_already_running">Es läuft bereits eine Stillsitzung</string>
<string name="breastfeeding_duration_title">Stilldauer</string>
<string name="breastfeeding_duration_description">Dauer in Minuten eingeben</string>
<!-- Puke/Bath Events -->
<string name="log_puke_dialog_title">Spucken</string>
<string name="log_puke_dialog_description">Menge auswählen</string>
<string name="event_puke_desc">Spucken</string>
<string name="event_bath_desc">Baden</string>
<string name="overflow_event_puke">🤮 Spucken</string>
<string name="overflow_event_bath">🛁 Baden</string>
<!-- Zeitangaben -->
<string name="second_ago">Sek.</string>
<string name="seconds_ago">Sek.</string>
<string name="day_ago">Tag</string>
<string name="days_ago">Tage</string>
<string name="year_ago">Jahr</string>
<string name="years_ago">Jahre</string>
<!-- Mengenangaben -->
<string name="amount_little">Wenig</string>
<string name="amount_normal">Normal</string>
<string name="amount_plenty">Viel</string>
<!-- Signatur-Einstellungen -->
<string name="settings_signature">Signatur</string>
<string name="settings_signature_desc">Füge jedem Event eine Signatur hinzu, die andere sehen können. Nützlich wenn mehrere Personen Events hinzufügen.</string>
<string name="settings_no_breastfeeding_desc">Verstecke die Stillbuttons wenn sie nicht benötigt werden.</string>
<!-- Event-Detail-Dialog -->
<string name="dialog_event_detail_quantity">Menge</string>
<string name="dialog_event_detail_notes">Notizen</string>
<string name="dialog_event_detail_signature">von %s</string>
</resources>

View File

@@ -93,11 +93,4 @@
<string name="default_logbook_name">👶 Mon premier carnet de bord</string>
<string name="logbook_created">Journal ajouté: </string>
<string name="breastfeeding_timer_title">Allaitement en cours</string>
<string name="breastfeeding_timer_stop">Arrêter</string>
<string name="breastfeeding_timer_hint">Appuyez sur Arrêter quand terminé</string>
<string name="breastfeeding_timer_already_running">Une session d\'allaitement est déjà en cours</string>
<string name="breastfeeding_duration_title">Durée d\'allaitement</string>
<string name="breastfeeding_duration_description">Entrez la durée en minutes</string>
</resources>

View File

@@ -93,11 +93,4 @@
<string name="default_logbook_name">👶 Il mio primo diario</string>
<string name="logbook_created">Creato nuovo diario: </string>
<string name="breastfeeding_timer_title">Allattamento in corso</string>
<string name="breastfeeding_timer_stop">Stop</string>
<string name="breastfeeding_timer_hint">Premi Stop quando hai finito</string>
<string name="breastfeeding_timer_already_running">Una sessione di allattamento è già in corso</string>
<string name="breastfeeding_duration_title">Durata allattamento</string>
<string name="breastfeeding_duration_description">Inserisci la durata in minuti</string>
</resources>

View File

@@ -148,12 +148,4 @@
<string name="default_logbook_name">👶 My first logbook</string>
<string name="logbook_created">New logbook created: </string>
<string name="breastfeeding_timer_title">Breastfeeding in progress</string>
<string name="breastfeeding_timer_stop">Stop</string>
<string name="breastfeeding_timer_hint">Tap Stop when finished</string>
<string name="breastfeeding_timer_already_running">A breastfeeding session is already in progress</string>
<string name="breastfeeding_duration_title">Breastfeeding duration</string>
<string name="breastfeeding_duration_description">Enter the duration in minutes</string>
<string name="measurement_unit_time_minutes" translatable="false">min</string>
</resources>