From 961e7b90e7aa54f3056e12db440bcc2f14062042 Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Thu, 25 Sep 2025 23:46:46 +0200 Subject: [PATCH 01/10] small code cleanup No code behavior has been changed. --- .../lunatracker/MainActivity.kt | 24 +++++----- .../lunatracker/SettingsActivity.kt | 8 ++-- .../adapters/LunaEventRecyclerAdapter.kt | 2 +- .../lunatracker/entities/LunaEvent.kt | 2 +- .../repository/FileLogbookRepository.kt | 12 ++--- .../repository/LocalSettingsRepository.kt | 46 +++++++++---------- 6 files changed, 47 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 0d51fd0..1678bf6 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -47,9 +47,9 @@ import java.util.Date class MainActivity : AppCompatActivity() { companion object { - val TAG = "MainActivity" - val UPDATE_EVERY_SECS: Long = 30 - val DEBUG_CHECK_LOGBOOK_CONSISTENCY = false + const val TAG = "MainActivity" + const val UPDATE_EVERY_SECS: Long = 30 + const val DEBUG_CHECK_LOGBOOK_CONSISTENCY = false } var logbook: Logbook? = null @@ -113,24 +113,24 @@ class MainActivity : AppCompatActivity() { moreButton.setOnClickListener { showOverflowPopupWindow(moreButton) } - findViewById(R.id.button_no_connection_settings).setOnClickListener({ + findViewById(R.id.button_no_connection_settings).setOnClickListener { showSettings() - }) - findViewById(R.id.button_settings).setOnClickListener({ + } + findViewById(R.id.button_settings).setOnClickListener { showSettings() - }) - findViewById(R.id.button_no_connection_retry).setOnClickListener({ + } + findViewById(R.id.button_no_connection_retry).setOnClickListener { // This may happen at start, when logbook is still null: better ask the logbook list loadLogbookList() - }) - findViewById(R.id.button_sync).setOnClickListener({ + } + findViewById(R.id.button_sync).setOnClickListener { loadLogbookList() - }) + } } private fun setListAdapter(items: ArrayList) { val adapter = LunaEventRecyclerAdapter(this, items) - adapter.onItemClickListener = object: LunaEventRecyclerAdapter.OnItemClickListener{ + adapter.onItemClickListener = object: LunaEventRecyclerAdapter.OnItemClickListener { override fun onItemClick(event: LunaEvent) { showEventDetailDialog(event, items) } diff --git a/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt index 0cbd0cf..7d03b4e 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt @@ -61,9 +61,9 @@ open class SettingsActivity : AppCompatActivity() { switchNoBreastfeeding.isChecked = noBreastfeeding if (webDavCredentials != null) { - textViewWebDAVUrl.setText(webDavCredentials[0]) - textViewWebDAVUser.setText(webDavCredentials[1]) - textViewWebDAVPass.setText(webDavCredentials[2]) + textViewWebDAVUrl.text = webDavCredentials[0] + textViewWebDAVUser.text = webDavCredentials[1] + textViewWebDAVPass.text = webDavCredentials[2] } } @@ -170,7 +170,7 @@ open class SettingsActivity : AppCompatActivity() { */ private fun copyLocalLogbooksToWebdav(webDAVLogbookRepository: WebDAVLogbookRepository, listener: OnCopyLocalLogbooksToWebdavFinishedListener) { Thread(Runnable { - var errors = StringBuilder() + val errors = StringBuilder() val fileLogbookRepo = FileLogbookRepository() val logbooks = fileLogbookRepo.getAllLogbooks(this) for (logbook in logbooks) { diff --git a/app/src/main/java/it/danieleverducci/lunatracker/adapters/LunaEventRecyclerAdapter.kt b/app/src/main/java/it/danieleverducci/lunatracker/adapters/LunaEventRecyclerAdapter.kt index ea07924..409471f 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/adapters/LunaEventRecyclerAdapter.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/adapters/LunaEventRecyclerAdapter.kt @@ -53,7 +53,7 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter item.notes LunaEvent.TYPE_NOTE -> item.notes LunaEvent.TYPE_CUSTOM -> item.notes diff --git a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt index 30444c9..a56c8a2 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt @@ -128,7 +128,7 @@ class LunaEvent: Comparable { } 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 { diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt index 3e976f7..4b0904b 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt @@ -13,9 +13,9 @@ import java.io.FilenameFilter class FileLogbookRepository: LogbookRepository { companion object { - val TAG = "FileLogbookRepository" - val FILE_NAME_START = "data" - val FILE_NAME_END = ".json" + const val TAG = "FileLogbookRepository" + const val FILE_NAME_START = "data" + const val FILE_NAME_END = ".json" } override fun loadLogbook(context: Context, name: String, listener: LogbookLoadedListener) { @@ -32,7 +32,7 @@ class FileLogbookRepository: LogbookRepository { fun loadLogbook(context: Context, name: String): Logbook { val logbook = Logbook(name) val fileName = getFileName(name) - val file = File(context.getFilesDir(), fileName) + val file = File(context.filesDir, fileName) val json = FileInputStream(file).bufferedReader().use { it.readText() } val ja = JSONArray(json) for (i in 0 until ja.length()) { @@ -58,7 +58,7 @@ class FileLogbookRepository: LogbookRepository { fun saveLogbook(context: Context, logbook: Logbook) { val fileName = getFileName(logbook.name) - val file = File(context.getFilesDir(), fileName) + val file = File(context.filesDir, fileName) val ja = JSONArray() for (l in logbook.logs) { ja.put(l.toJson()) @@ -82,7 +82,7 @@ class FileLogbookRepository: LogbookRepository { } private fun listLogbooks(context: Context): ArrayList { - val logbooksFileNames = context.getFilesDir().list(object: FilenameFilter { + val logbooksFileNames = context.filesDir.list(object: FilenameFilter { override fun accept(dir: File?, name: String?): Boolean { if (name == null) return false diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt index ede0b93..577d8f5 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt @@ -7,13 +7,13 @@ import androidx.core.content.edit class LocalSettingsRepository(val context: Context) { companion object { - 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" + 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" } enum class DATA_REPO {LOCAL_FILE, WEBDAV} val sharedPreferences: SharedPreferences @@ -23,7 +23,7 @@ class LocalSettingsRepository(val context: Context) { } fun saveBabyBottleContent(content: Int) { - sharedPreferences.edit().putInt(SHARED_PREFS_BB_CONTENT, content).apply() + sharedPreferences.edit { putInt(SHARED_PREFS_BB_CONTENT, content) } } fun loadBabyBottleContent(): Int { @@ -31,7 +31,7 @@ class LocalSettingsRepository(val context: Context) { } fun saveNoBreastfeeding(content: Boolean) { - sharedPreferences.edit().putBoolean(SHARED_PREFS_NO_BREASTFEEDING, content).apply() + sharedPreferences.edit { putBoolean(SHARED_PREFS_NO_BREASTFEEDING, content) } } fun loadNoBreastfeeding(): Boolean { @@ -39,15 +39,15 @@ class LocalSettingsRepository(val context: Context) { } fun saveDataRepository(repo: DATA_REPO) { - val spe = sharedPreferences.edit() - spe.putString( - SHARED_PREFS_DATA_REPO, - when (repo) { - DATA_REPO.WEBDAV -> "webdav" - DATA_REPO.LOCAL_FILE -> "localfile" - } - ) - spe.commit() + sharedPreferences.edit(commit = true) { + putString( + SHARED_PREFS_DATA_REPO, + when (repo) { + DATA_REPO.WEBDAV -> "webdav" + DATA_REPO.LOCAL_FILE -> "localfile" + } + ) + } } fun loadDataRepository(): DATA_REPO { @@ -60,11 +60,11 @@ class LocalSettingsRepository(val context: Context) { } fun saveWebdavCredentials(url: String, username: String, password: String) { - 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() + sharedPreferences.edit(commit = true) { + putString(SHARED_PREFS_DAV_URL, url) + putString(SHARED_PREFS_DAV_USER, username) + putString(SHARED_PREFS_DAV_PASS, password) + } } fun loadWebdavCredentials(): Array? { -- 2.39.5 From 34aa092722e3619927192d01e28c4797943efa6c Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Thu, 25 Sep 2025 23:49:38 +0200 Subject: [PATCH 02/10] NumericUtils: provide fallback for LocaleData.getMeasurementSystem LocaleData.getMeasurementSystem is available at API level 28 but the app supports API level 21. --- app/src/main/java/utils/NumericUtils.kt | 30 +++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/utils/NumericUtils.kt b/app/src/main/java/utils/NumericUtils.kt index 49b8552..f847ef9 100644 --- a/app/src/main/java/utils/NumericUtils.kt +++ b/app/src/main/java/utils/NumericUtils.kt @@ -3,6 +3,7 @@ package utils import android.content.Context import android.icu.util.LocaleData import android.icu.util.ULocale +import android.os.Build import it.danieleverducci.lunatracker.R import it.danieleverducci.lunatracker.entities.LunaEvent import java.text.NumberFormat @@ -14,29 +15,45 @@ class NumericUtils (val context: Context) { val measurement_unit_weight_tiny: String val measurement_unit_temperature_base: String + private fun isMetricSystem(): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val measurementSystem = LocaleData.getMeasurementSystem(ULocale.getDefault()) + return (measurementSystem == LocaleData. MeasurementSystem.SI) + } else { + val locale = context.resources.configuration.locale + return when (locale.country) { + // https://en.wikipedia.org/wiki/United_States_customary_units + // https://en.wikipedia.org/wiki/Imperial_units + "US" -> false // US IMPERIAL + // UK, Myanmar, Liberia, + "GB", "MM", "LR" -> false // IMPERIAL + else -> true // UnitSystem.METRIC + } + } + } + init { this.numberFormat = NumberFormat.getInstance() - val measurementSystem = LocaleData.getMeasurementSystem(ULocale.getDefault()) this.measurement_unit_liquid_base = context.getString( - if (measurementSystem == LocaleData. MeasurementSystem.SI) + if (isMetricSystem()) R.string.measurement_unit_liquid_base_metric else R.string.measurement_unit_liquid_base_imperial ) this.measurement_unit_weight_base = context.getString( - if (measurementSystem == LocaleData. MeasurementSystem.SI) + if (isMetricSystem()) R.string.measurement_unit_weight_base_metric else R.string.measurement_unit_weight_base_imperial ) this.measurement_unit_weight_tiny = context.getString( - if (measurementSystem == LocaleData. MeasurementSystem.SI) + if (isMetricSystem()) R.string.measurement_unit_weight_tiny_metric else R.string.measurement_unit_weight_tiny_imperial ) this.measurement_unit_temperature_base = context.getString( - if (measurementSystem == LocaleData. MeasurementSystem.SI) + if (isMetricSystem()) R.string.measurement_unit_temperature_base_metric else R.string.measurement_unit_temperature_base_imperial @@ -70,10 +87,9 @@ class NumericUtils (val context: Context) { * @return min, max, normal */ fun getValidEventQuantityRange(lunaEventType: String): Triple? { - val measurementSystem = LocaleData.getMeasurementSystem(ULocale.getDefault()) return when (lunaEventType) { LunaEvent.TYPE_TEMPERATURE -> { - if (measurementSystem == LocaleData. MeasurementSystem.SI) + if (isMetricSystem()) Triple( context.resources.getInteger(R.integer.human_body_temp_min_metric), context.resources.getInteger(R.integer.human_body_temp_max_metric), -- 2.39.5 From 730ef9522039d1e02b93ebdb0dadf75280c5c52e Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Thu, 25 Sep 2025 23:52:10 +0200 Subject: [PATCH 03/10] add complex puke event --- .../lunatracker/MainActivity.kt | 24 +++++++++++++++++++ .../lunatracker/entities/LunaEvent.kt | 3 +++ app/src/main/java/utils/NumericUtils.kt | 22 ++++++++++------- app/src/main/res/layout/more_events_popup.xml | 10 ++++++++ app/src/main/res/layout/puke_dialog.xml | 17 +++++++++++++ app/src/main/res/values/arrays.xml | 8 +++++++ app/src/main/res/values/strings.xml | 10 ++++++++ 7 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 app/src/main/res/layout/puke_dialog.xml create mode 100644 app/src/main/res/values/arrays.xml diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 1678bf6..30699d1 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -260,6 +260,26 @@ class MainActivity : AppCompatActivity() { alertDialog.show() } + fun askPukeValue() { + val d = AlertDialog.Builder(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(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 -> + val pos = spinner.selectedItemPosition + logEvent(LunaEvent(LunaEvent.TYPE_PUKE, pos)) + } + d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() } + val alertDialog = d.create() + alertDialog.show() + } + fun askNotes(lunaEvent: LunaEvent) { val d = AlertDialog.Builder(this) val dialogView = layoutInflater.inflate(R.layout.dialog_notes, null) @@ -787,6 +807,10 @@ class MainActivity : AppCompatActivity() { askTemperatureValue() dismiss() }) + contentView.findViewById(R.id.button_puke).setOnClickListener({ + askPukeValue() + dismiss() + }) contentView.findViewById(R.id.button_colic).setOnClickListener({ logEvent( LunaEvent(LunaEvent.TYPE_COLIC) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt index a56c8a2..10db157 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt @@ -28,6 +28,7 @@ class LunaEvent: Comparable { const val TYPE_COLIC = "COLIC" const val TYPE_TEMPERATURE = "TEMPERATURE" const val TYPE_FOOD = "FOOD" + const val TYPE_PUKE = "PUKE" } private val jo: JSONObject @@ -90,6 +91,7 @@ class LunaEvent: Comparable { TYPE_TEMPERATURE -> R.string.event_temperature_type TYPE_COLIC -> R.string.event_colic_type TYPE_FOOD -> R.string.event_food_type + TYPE_PUKE -> R.string.event_puke_type else -> R.string.event_unknown_type } ) @@ -111,6 +113,7 @@ class LunaEvent: Comparable { TYPE_TEMPERATURE -> R.string.event_temperature_desc TYPE_COLIC -> R.string.event_colic_desc TYPE_FOOD -> R.string.event_food_desc + TYPE_PUKE -> R.string.event_puke_desc else -> R.string.event_unknown_desc } ) diff --git a/app/src/main/java/utils/NumericUtils.kt b/app/src/main/java/utils/NumericUtils.kt index f847ef9..73b95bc 100644 --- a/app/src/main/java/utils/NumericUtils.kt +++ b/app/src/main/java/utils/NumericUtils.kt @@ -18,8 +18,8 @@ class NumericUtils (val context: Context) { private fun isMetricSystem(): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { val measurementSystem = LocaleData.getMeasurementSystem(ULocale.getDefault()) - return (measurementSystem == LocaleData. MeasurementSystem.SI) - } else { + return (measurementSystem == LocaleData.MeasurementSystem.SI) + } else { val locale = context.resources.configuration.locale return when (locale.country) { // https://en.wikipedia.org/wiki/United_States_customary_units @@ -27,9 +27,9 @@ class NumericUtils (val context: Context) { "US" -> false // US IMPERIAL // UK, Myanmar, Liberia, "GB", "MM", "LR" -> false // IMPERIAL - else -> true // UnitSystem.METRIC + else -> true // METRIC } - } + } } init { @@ -62,11 +62,15 @@ class NumericUtils (val context: Context) { fun formatEventQuantity(item: LunaEvent): String { val formatted = StringBuilder() - if ((item.quantity ?: 0) > 0) { - if (item.type == LunaEvent.TYPE_TEMPERATURE) - formatted.append((item.quantity / 10.0f).toString()) - else - formatted.append(item.quantity) + if (item.quantity > 0) { + formatted.append(when (item.type) { + LunaEvent.TYPE_TEMPERATURE -> + (item.quantity / 10.0f).toString() + LunaEvent.TYPE_PUKE -> + context.resources.getStringArray(R.array.AmountLabels)[item.quantity] + else -> + item.quantity + }) formatted.append(" ") formatted.append( diff --git a/app/src/main/res/layout/more_events_popup.xml b/app/src/main/res/layout/more_events_popup.xml index 596bc0c..05758d0 100644 --- a/app/src/main/res/layout/more_events_popup.xml +++ b/app/src/main/res/layout/more_events_popup.xml @@ -49,6 +49,16 @@ style="@style/OverflowMenuText" android:text="@string/overflow_event_temperature"/> + + + + + + + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000..1abb5c6 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,8 @@ + + + + @string/amount_low + @string/amount_normal + @string/amount_high + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index be25ce8..f04959e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,9 @@ Temperature Insert the temperature + Puke + Select the amount + 🍼 🥣 ⚖️ @@ -25,6 +28,7 @@ 📝 🌡️ 💨 + 🤮 \? Baby bottle @@ -40,6 +44,7 @@ Note Temperature Gaseous colic + Puke ⚖️ Weight @@ -48,6 +53,7 @@ 📝 Note 🌡️ Temperature 💨 Gaseous colic + 🤮 Puke Event logged Logbook saved @@ -66,6 +72,10 @@ year years + Little + - + A Lot + No connection Unable to reach WebDAV service Settings -- 2.39.5 From 693405cadb4f21c735827c68c73de8edf5e323a0 Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Mon, 29 Sep 2025 04:55:28 +0200 Subject: [PATCH 04/10] simplify puke event --- .../lunatracker/MainActivity.kt | 22 +------------------ app/src/main/java/utils/NumericUtils.kt | 2 -- app/src/main/res/layout/puke_dialog.xml | 17 -------------- app/src/main/res/values/arrays.xml | 8 ------- app/src/main/res/values/strings.xml | 7 ------ 5 files changed, 1 insertion(+), 55 deletions(-) delete mode 100644 app/src/main/res/layout/puke_dialog.xml delete mode 100644 app/src/main/res/values/arrays.xml diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 30699d1..d8b8c5f 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -260,26 +260,6 @@ class MainActivity : AppCompatActivity() { alertDialog.show() } - fun askPukeValue() { - val d = AlertDialog.Builder(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(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 -> - val pos = spinner.selectedItemPosition - logEvent(LunaEvent(LunaEvent.TYPE_PUKE, pos)) - } - d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() } - val alertDialog = d.create() - alertDialog.show() - } - fun askNotes(lunaEvent: LunaEvent) { val d = AlertDialog.Builder(this) val dialogView = layoutInflater.inflate(R.layout.dialog_notes, null) @@ -808,7 +788,7 @@ class MainActivity : AppCompatActivity() { dismiss() }) contentView.findViewById(R.id.button_puke).setOnClickListener({ - askPukeValue() + logEvent(LunaEvent(LunaEvent.TYPE_PUKE)) dismiss() }) contentView.findViewById(R.id.button_colic).setOnClickListener({ diff --git a/app/src/main/java/utils/NumericUtils.kt b/app/src/main/java/utils/NumericUtils.kt index 73b95bc..f933c24 100644 --- a/app/src/main/java/utils/NumericUtils.kt +++ b/app/src/main/java/utils/NumericUtils.kt @@ -66,8 +66,6 @@ class NumericUtils (val context: Context) { formatted.append(when (item.type) { LunaEvent.TYPE_TEMPERATURE -> (item.quantity / 10.0f).toString() - LunaEvent.TYPE_PUKE -> - context.resources.getStringArray(R.array.AmountLabels)[item.quantity] else -> item.quantity }) diff --git a/app/src/main/res/layout/puke_dialog.xml b/app/src/main/res/layout/puke_dialog.xml deleted file mode 100644 index 6f924a0..0000000 --- a/app/src/main/res/layout/puke_dialog.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml deleted file mode 100644 index 1abb5c6..0000000 --- a/app/src/main/res/values/arrays.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - @string/amount_low - @string/amount_normal - @string/amount_high - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f04959e..7f78262 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,9 +12,6 @@ Temperature Insert the temperature - Puke - Select the amount - 🍼 🥣 ⚖️ @@ -72,10 +69,6 @@ year years - Little - - - A Lot - No connection Unable to reach WebDAV service Settings -- 2.39.5 From 01f24694b5ef41ea4dc28d167001a86f001d2581 Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Fri, 26 Sep 2025 16:21:14 +0200 Subject: [PATCH 05/10] 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. --- app/src/main/res/layout/more_events_popup.xml | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/src/main/res/layout/more_events_popup.xml b/app/src/main/res/layout/more_events_popup.xml index 05758d0..b7ac137 100644 --- a/app/src/main/res/layout/more_events_popup.xml +++ b/app/src/main/res/layout/more_events_popup.xml @@ -2,7 +2,7 @@ - - @@ -44,7 +34,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="10dp" - android:padding="20dp" + android:padding="10dp" android:background="@drawable/dropdown_list_item_background" style="@style/OverflowMenuText" android:text="@string/overflow_event_temperature"/> @@ -54,7 +44,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="10dp" - android:padding="20dp" + android:padding="10dp" android:background="@drawable/dropdown_list_item_background" style="@style/OverflowMenuText" android:text="@string/overflow_event_puke"/> @@ -64,7 +54,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="10dp" - android:padding="20dp" + android:padding="10dp" android:background="@drawable/dropdown_list_item_background" style="@style/OverflowMenuText" android:text="@string/overflow_event_colic"/> @@ -74,11 +64,21 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="10dp" - android:padding="20dp" + android:padding="10dp" android:background="@drawable/dropdown_list_item_background" style="@style/OverflowMenuText" android:text="@string/overflow_event_scale"/> + + \ No newline at end of file -- 2.39.5 From 0a9821aef7076db5e63918ef29356b27edc52e8d Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Sat, 27 Sep 2025 01:33:40 +0200 Subject: [PATCH 06/10] add no-breastfeeding help text --- app/src/main/res/layout/activity_settings.xml | 8 +++++++- app/src/main/res/values-de/strings.xml | 3 +-- app/src/main/res/values/strings.xml | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 76f5904..a4cfb2d 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -131,7 +131,7 @@ android:layout_weight="1" android:layout_height="wrap_content" android:textStyle="bold" - android:text="@string/no_breastfeeding" /> + android:text="@string/settings_no_breastfeeding" /> + + Einstellungen Erneut versuchen - Kein Stillen - Einstellungen + Kein Stillen Speicherort für Daten auswählen Auf dem Gerät Datenschutzfreundlichste Lösung: Deine Daten verlassen dein Gerät nicht diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7f78262..a2aa50f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -74,8 +74,6 @@ Settings Retry - No Breastfeeding - Settings Choose where to save data On device @@ -92,6 +90,8 @@ Error while trying to access WebDAV: Unable to save a file on the WebDAV server: Successfully connected with the WebDAV server + No Breastfeeding + Hide the Breastfeeding buttons for when they are not needed. There\'s a save file on the server, but it is corrupted or unreadable. Please delete it Error: Error while uploading local logbook %1$s to webdav: %2$s -- 2.39.5 From 0633b4d08487008fd253e55dead9d4ed8d8fd89c Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Mon, 29 Sep 2025 03:20:18 +0200 Subject: [PATCH 07/10] add bath event type --- .../it/danieleverducci/lunatracker/MainActivity.kt | 6 ++++++ .../danieleverducci/lunatracker/entities/LunaEvent.kt | 3 +++ app/src/main/res/layout/more_events_popup.xml | 10 ++++++++++ app/src/main/res/values/strings.xml | 3 +++ 4 files changed, 22 insertions(+) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index d8b8c5f..584dd9f 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -801,6 +801,12 @@ class MainActivity : AppCompatActivity() { askWeightValue() dismiss() }) + contentView.findViewById(R.id.button_bath).setOnClickListener({ + logEvent( + LunaEvent(LunaEvent.TYPE_BATH) + ) + dismiss() + }) }.also { popupWindow -> popupWindow.setOnDismissListener({ Handler(mainLooper).postDelayed({ diff --git a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt index 10db157..f00f64a 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt @@ -29,6 +29,7 @@ class LunaEvent: Comparable { const val TYPE_TEMPERATURE = "TEMPERATURE" const val TYPE_FOOD = "FOOD" const val TYPE_PUKE = "PUKE" + const val TYPE_BATH = "BATH" } private val jo: JSONObject @@ -92,6 +93,7 @@ class LunaEvent: Comparable { 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 } ) @@ -114,6 +116,7 @@ class LunaEvent: Comparable { 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 } ) diff --git a/app/src/main/res/layout/more_events_popup.xml b/app/src/main/res/layout/more_events_popup.xml index b7ac137..34ee31b 100644 --- a/app/src/main/res/layout/more_events_popup.xml +++ b/app/src/main/res/layout/more_events_popup.xml @@ -69,6 +69,16 @@ style="@style/OverflowMenuText" android:text="@string/overflow_event_scale"/> + + 🌡️ 💨 🤮 + 🛁 \? Baby bottle @@ -42,6 +43,7 @@ Temperature Gaseous colic Puke + Bath ⚖️ Weight @@ -51,6 +53,7 @@ 🌡️ Temperature 💨 Gaseous colic 🤮 Puke + 🛁 Bath Event logged Logbook saved -- 2.39.5 From 301e8d04769544263b2163f073a7d3013f139820 Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Mon, 29 Sep 2025 03:29:50 +0200 Subject: [PATCH 08/10] DateUtils: move event details formatting to DateUtils Also display second as 0 since it is easier to read and does not have meaning for the user. --- .../lunatracker/MainActivity.kt | 11 ++++------- app/src/main/java/utils/DateUtils.kt | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 584dd9f..d8f4efc 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -41,7 +41,6 @@ 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 @@ -340,7 +339,6 @@ class MainActivity : AppCompatActivity() { fun showEventDetailDialog(event: LunaEvent, items: ArrayList) { // 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) @@ -352,8 +350,9 @@ class MainActivity : AppCompatActivity() { val currentDateTime = Calendar.getInstance() currentDateTime.time = Date(event.time * 1000) + val dateTextView = dialogView.findViewById(R.id.dialog_event_detail_type_date) - dateTextView.text = String.format(getString(R.string.dialog_event_detail_datetime_icon), dateFormat.format(currentDateTime.time)) + dateTextView.text = String.format(getString(R.string.dialog_event_detail_datetime_icon), DateUtils.formatDateTime(event.time)) dateTextView.setOnClickListener { // Show datetime picker val startYear = currentDateTime.get(Calendar.YEAR) @@ -366,11 +365,9 @@ class MainActivity : AppCompatActivity() { 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)) - // Save event and move it to the right position in the logbook - event.time = currentDateTime.time.time / 1000 // Seconds since epoch + event.time = pickedDateTime.time.time / 1000 // Seconds since epoch + dateTextView.text = String.format(getString(R.string.dialog_event_detail_datetime_icon), DateUtils.formatDateTime(event.time)) logbook?.sort() recyclerView.adapter?.notifyDataSetChanged() saveLogbook() diff --git a/app/src/main/java/utils/DateUtils.kt b/app/src/main/java/utils/DateUtils.kt index 25803da..59a310f 100644 --- a/app/src/main/java/utils/DateUtils.kt +++ b/app/src/main/java/utils/DateUtils.kt @@ -7,6 +7,10 @@ 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 @@ -65,7 +69,8 @@ class DateUtils { } /** - * Formats the provided unix timestamp in a string like "3 hours, 26 minutes ago) + * Formats the provided unix timestamp in a string like "3 hours, 26 minutes ago". + * Used for the event list. */ fun formatTimeAgo(context: Context, unixTime: Long): String { val secondsDiff = (System.currentTimeMillis() / 1000) - unixTime @@ -100,5 +105,17 @@ class DateUtils { } return formattedTime.toString() } + + /** + * Format time as localized string. E.g. "28 Sept 03:36:00". + * The seconds are set to 0 since they are distracting and not relevant. + * Used in the event detail dialog. + */ + fun formatDateTime(unixTime: Long): String { + val roundedUnixTime = unixTime - (unixTime % 60) + val date = Date(roundedUnixTime * 1000) + val dateFormat = java.text.DateFormat.getDateTimeInstance() + return dateFormat.format(date) + } } } \ No newline at end of file -- 2.39.5 From f38b88924874c0623f0960546275bed4d30bcdee Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Mon, 29 Sep 2025 03:31:16 +0200 Subject: [PATCH 09/10] add signature setting For multiple users it helps to keep track about who did what. --- .../lunatracker/MainActivity.kt | 12 ++++++++++ .../lunatracker/SettingsActivity.kt | 6 +++++ .../lunatracker/entities/LunaEvent.kt | 6 +++++ .../repository/LocalSettingsRepository.kt | 9 ++++++++ app/src/main/res/layout/activity_settings.xml | 22 +++++++++++++++++++ .../main/res/layout/dialog_event_detail.xml | 8 +++++++ app/src/main/res/values/strings.xml | 3 +++ 7 files changed, 66 insertions(+) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index d8f4efc..c79c728 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -57,6 +57,7 @@ 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) @@ -168,6 +169,8 @@ class MainActivity : AppCompatActivity() { logbookRepo = FileLogbookRepository() } + signature = settingsRepository.loadSignature() + val noBreastfeeding = settingsRepository.loadNoBreastfeeding() findViewById(R.id.layout_nipples).visibility = when (noBreastfeeding) { true -> View.GONE @@ -386,6 +389,13 @@ class MainActivity : AppCompatActivity() { pauseLogbookUpdate = false }) + // show optional signature + if (event.signature.isNotEmpty()) { + val signatureTextEdit = dialogView.findViewById(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 previousTextView = dialogView.findViewById(R.id.dialog_event_previous) @@ -635,6 +645,8 @@ class MainActivity : AppCompatActivity() { fun logEvent(event: LunaEvent) { savingEvent(true) + event.signature = signature + setLoading(true) logbook?.logs?.add(0, event) recyclerView.adapter?.notifyItemInserted(0) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt index 7d03b4e..8006986 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt @@ -2,6 +2,7 @@ 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 @@ -24,6 +25,7 @@ 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) @@ -36,6 +38,7 @@ 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(R.id.settings_save).setOnClickListener({ validateAndSave() @@ -52,12 +55,14 @@ 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) { @@ -156,6 +161,7 @@ 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(), diff --git a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt index f00f64a..e46ab25 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt @@ -55,6 +55,12 @@ class LunaEvent: Comparable { 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 diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt index 577d8f5..1462820 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt @@ -14,6 +14,7 @@ class LocalSettingsRepository(val context: Context) { 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" } enum class DATA_REPO {LOCAL_FILE, WEBDAV} val sharedPreferences: SharedPreferences @@ -30,6 +31,14 @@ class LocalSettingsRepository(val context: Context) { return sharedPreferences.getInt(SHARED_PREFS_BB_CONTENT, 1) } + fun saveSignature(content: String) { + sharedPreferences.edit { putString(SHARED_PREFS_SIGNATURE, content) } + } + + fun loadSignature(): String { + return sharedPreferences.getString(SHARED_PREFS_SIGNATURE, "") ?: "" + } + fun saveNoBreastfeeding(content: Boolean) { sharedPreferences.edit { putBoolean(SHARED_PREFS_NO_BREASTFEEDING, content) } } diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index a4cfb2d..20ada2e 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -119,6 +119,28 @@ android:visibility="invisible"/> + + + + + + + + Retry Settings + Signature + Attach a signature to each event you create and for others to see. Useful if multiple people add events. Choose where to save data On device Most privacy-friendly solution: data doesn\'t leave your device @@ -129,6 +131,7 @@ Delete Quantity Notes + by %s Add logbook 👶 Logbook name -- 2.39.5 From f3fb584ec2dec6059bc3034ae087c06e79755614 Mon Sep 17 00:00:00 2001 From: Moritz Warning Date: Mon, 29 Sep 2025 03:31:49 +0200 Subject: [PATCH 10/10] activity_setting: fine tune layout style --- app/src/main/res/layout/activity_settings.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 20ada2e..cd689aa 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -145,8 +145,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:layout_marginTop="5dp" - android:layout_marginEnd="30dp"> + android:layout_marginTop="20dp"> @@ -167,6 +167,7 @@ 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"/>