From abee1be72c8078f2b303c21bb042b67a434c56d2 Mon Sep 17 00:00:00 2001 From: Daniele Verducci Date: Fri, 22 Nov 2024 08:01:19 +0100 Subject: [PATCH] Reimplemented LunaEvent as a bare JSONObject to allow for expandability --- .../lunatracker/MainActivity.kt | 16 ++- .../adapters/LunaEventRecyclerAdapter.kt | 30 +++-- .../lunatracker/entities/LunaEvent.kt | 106 +++++++++--------- .../repository/FileLogbookRepository.kt | 11 +- .../repository/WebDAVLogbookRepository.kt | 10 +- 5 files changed, 90 insertions(+), 83 deletions(-) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 3b3a93c..88de4db 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -15,11 +15,9 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.progressindicator.LinearProgressIndicator import com.thegrizzlylabs.sardineandroid.impl.SardineException -import it.danieleverducci.lunatracker.SettingsActivity import it.danieleverducci.lunatracker.adapters.LunaEventRecyclerAdapter import it.danieleverducci.lunatracker.entities.Logbook import it.danieleverducci.lunatracker.entities.LunaEvent -import it.danieleverducci.lunatracker.entities.LunaEventType import it.danieleverducci.lunatracker.repository.FileLogbookRepository import it.danieleverducci.lunatracker.repository.LocalSettingsRepository import it.danieleverducci.lunatracker.repository.LogbookLoadedListener @@ -68,27 +66,27 @@ class MainActivity : AppCompatActivity() { findViewById(R.id.button_scale).setOnClickListener { askWeightValue() } findViewById(R.id.button_nipple_left).setOnClickListener { logEvent( LunaEvent( - LunaEventType.BREASTFEEDING_LEFT_NIPPLE + LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE ) ) } findViewById(R.id.button_nipple_both).setOnClickListener { logEvent( LunaEvent( - LunaEventType.BREASTFEEDING_BOTH_NIPPLE + LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE ) ) } findViewById(R.id.button_nipple_right).setOnClickListener { logEvent( LunaEvent( - LunaEventType.BREASTFEEDING_RIGHT_NIPPLE + LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE ) ) } findViewById(R.id.button_change_poo).setOnClickListener { logEvent( LunaEvent( - LunaEventType.DIAPERCHANGE_POO + LunaEvent.TYPE_DIAPERCHANGE_POO ) ) } findViewById(R.id.button_change_pee).setOnClickListener { logEvent( LunaEvent( - LunaEventType.DIAPERCHANGE_PEE + LunaEvent.TYPE_DIAPERCHANGE_PEE ) ) } findViewById(R.id.button_no_connection_settings).setOnClickListener({ @@ -164,7 +162,7 @@ class MainActivity : AppCompatActivity() { numberPicker.wrapSelectorWheel = false numberPicker.value = localSettings.loadBabyBottleContent() d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> - logEvent(LunaEvent(LunaEventType.BABY_BOTTLE, numberPicker.value * 10)) + logEvent(LunaEvent(LunaEvent.TYPE_BABY_BOTTLE, numberPicker.value * 10)) localSettings.saveBabyBottleContent(numberPicker.value) } d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() } @@ -183,7 +181,7 @@ class MainActivity : AppCompatActivity() { d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> val weight = weightET.text.toString().toIntOrNull() if (weight != null) - logEvent(LunaEvent(LunaEventType.WEIGHT, weight)) + logEvent(LunaEvent(LunaEvent.TYPE_WEIGHT, weight)) else Toast.makeText(this, R.string.toast_integer_error, Toast.LENGTH_SHORT).show() } 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 f5fe5bc..6cd378e 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/adapters/LunaEventRecyclerAdapter.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/adapters/LunaEventRecyclerAdapter.kt @@ -2,14 +2,12 @@ package it.danieleverducci.lunatracker.adapters import android.content.Context import android.text.format.DateFormat -import android.text.format.DateUtils import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import it.danieleverducci.lunatracker.entities.LunaEvent -import it.danieleverducci.lunatracker.entities.LunaEventType import it.danieleverducci.lunatracker.R import java.util.Date @@ -37,25 +35,25 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter R.string.event_bottle_type - LunaEventType.WEIGHT -> R.string.event_scale_type - LunaEventType.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type - LunaEventType.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type - LunaEventType.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type - LunaEventType.DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_type - LunaEventType.DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_type + LunaEvent.TYPE_BABY_BOTTLE -> R.string.event_bottle_type + LunaEvent.TYPE_WEIGHT -> R.string.event_scale_type + LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type + LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type + LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type + LunaEvent.TYPE_DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_type + LunaEvent.TYPE_DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_type else -> R.string.event_unknown_type } ) holder.description.text = context.getString( when (item.type) { - LunaEventType.BABY_BOTTLE -> R.string.event_bottle_desc - LunaEventType.WEIGHT -> R.string.event_scale_desc - LunaEventType.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc - LunaEventType.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc - LunaEventType.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc - LunaEventType.DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_desc - LunaEventType.DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_desc + LunaEvent.TYPE_BABY_BOTTLE -> R.string.event_bottle_desc + LunaEvent.TYPE_WEIGHT -> R.string.event_scale_desc + LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc + LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc + LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc + LunaEvent.TYPE_DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_desc + LunaEvent.TYPE_DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_desc else -> R.string.event_unknown_desc } ) 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 b8bf146..73280ca 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/LunaEvent.kt @@ -3,66 +3,68 @@ package it.danieleverducci.lunatracker.entities import org.json.JSONObject import java.util.Date -class LunaEvent( - val type: LunaEventType, - val quantity: Int? = null, -){ - var time: Long // In unix time (seconds since 1970) +/** + * Represents a logged event. + * It encloses, but doesn't parse entirely, a jsonObject. This was done to + * allow expandability and backwards compatibility (if a field is added in a + * release, it is simply ignored by previous ones). + */ +class LunaEvent { - init { - time = System.currentTimeMillis() / 1000 + companion object { + val TYPE_BABY_BOTTLE = "BABY_BOTTLE" + val TYPE_WEIGHT = "WEIGHT" + val TYPE_BREASTFEEDING_LEFT_NIPPLE = "BREASTFEEDING_LEFT_NIPPLE" + val TYPE_BREASTFEEDING_BOTH_NIPPLE = "BREASTFEEDING_BOTH_NIPPLE" + val TYPE_BREASTFEEDING_RIGHT_NIPPLE = "BREASTFEEDING_RIGHT_NIPPLE" + val TYPE_DIAPERCHANGE_POO = "DIAPERCHANGE_POO" + val TYPE_DIAPERCHANGE_PEE = "DIAPERCHANGE_PEE" } - override fun toString(): String { - return "${type.toString()} qty: $quantity time: ${Date(time * 1000)}" + private val jo: JSONObject + + var time: Long // In unix time (seconds since 1970) + get() = jo.getLong("time") + set(value) { + jo.put("time", value) + } + var type: String + get(): String = jo.getString("type") + set(value) { + jo.put("type", value) + } + var quantity: Int + get() = jo.optInt("quantity") + set(value) { + if (value > 0) + jo.put("quantity", value) + } + + constructor(jo: JSONObject) { + this.jo = jo + // A minimal LunaEvent should have at least time and type + if (!jo.has("time") || !jo.has("type")) + throw IllegalArgumentException("JSONObject is not a LunaEvent") + } + + constructor(type: String) { + this.jo = JSONObject() + this.time = System.currentTimeMillis() / 1000 + this.type = type + } + + constructor(type: String, quantity: Int) { + this.jo = JSONObject() + this.time = System.currentTimeMillis() / 1000 + this.type = type + this.quantity = quantity } fun toJson(): JSONObject { - val jo = JSONObject() - val type = when (type) { - LunaEventType.BABY_BOTTLE -> "BABY_BOTTLE" - LunaEventType.WEIGHT -> "SCALE" - LunaEventType.BREASTFEEDING_LEFT_NIPPLE -> "BREASTFEEDING_LEFT_NIPPLE" - LunaEventType.BREASTFEEDING_BOTH_NIPPLE -> "BREASTFEEDING_BOTH_NIPPLE" - LunaEventType.BREASTFEEDING_RIGHT_NIPPLE -> "BREASTFEEDING_RIGHT_NIPPLE" - LunaEventType.DIAPERCHANGE_POO -> "DIAPERCHANGE_POO" - LunaEventType.DIAPERCHANGE_PEE -> "DIAPERCHANGE_PEE" - else -> "UNKNOWN" - } - jo.put("type", type) - jo.put("quantity", quantity) - jo.put("time", time) return jo } - companion object { - fun fromJson(j: JSONObject): LunaEvent { - val type = when (j.getString("type")) { - "BABY_BOTTLE" -> LunaEventType.BABY_BOTTLE - "SCALE" -> LunaEventType.WEIGHT - "BREASTFEEDING_LEFT_NIPPLE" -> LunaEventType.BREASTFEEDING_LEFT_NIPPLE - "BREASTFEEDING_BOTH_NIPPLE" -> LunaEventType.BREASTFEEDING_BOTH_NIPPLE - "BREASTFEEDING_RIGHT_NIPPLE" -> LunaEventType.BREASTFEEDING_RIGHT_NIPPLE - "DIAPERCHANGE_POO" -> LunaEventType.DIAPERCHANGE_POO - "DIAPERCHANGE_PEE" -> LunaEventType.DIAPERCHANGE_PEE - else -> LunaEventType.UNKNOWN - } - val quantity = j.optInt("quantity") - val time = j.getLong("time") - val evt = LunaEvent(type, quantity) - evt.time = time - return evt - } + override fun toString(): String { + return "${type} qty: $quantity time: ${Date(time * 1000)}" } - } - -enum class LunaEventType { - BABY_BOTTLE, - WEIGHT, - BREASTFEEDING_LEFT_NIPPLE, - BREASTFEEDING_BOTH_NIPPLE, - BREASTFEEDING_RIGHT_NIPPLE, - DIAPERCHANGE_POO, - DIAPERCHANGE_PEE, - UNKNOWN } \ No newline at end of file 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 fc4741d..0a5ff97 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt @@ -5,6 +5,7 @@ import it.danieleverducci.lunatracker.entities.Logbook import android.util.Log import it.danieleverducci.lunatracker.entities.LunaEvent import org.json.JSONArray +import org.json.JSONException import java.io.File import java.io.FileInputStream import java.io.FileNotFoundException @@ -31,9 +32,13 @@ class FileLogbookRepository: LogbookRepository { val json = FileInputStream(file).bufferedReader().use { it.readText() } val ja = JSONArray(json) for (i in 0 until ja.length()) { - val jo = ja.getJSONObject(i) - val evt = LunaEvent.fromJson(jo) - logbook.logs.add(evt) + try { + val evt: LunaEvent = LunaEvent(ja.getJSONObject(i)) + logbook.logs.add(evt) + } catch (e: IllegalArgumentException) { + // Mangled JSON? + throw JSONException(e.toString()) + } } return logbook } diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt index 3475ce0..7cb17ef 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt @@ -59,9 +59,13 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p val ja = JSONArray(json) val logbook = Logbook() for (i in 0 until ja.length()) { - val jo = ja.getJSONObject(i) - val evt = LunaEvent.fromJson(jo) - logbook.logs.add(evt) + try { + val evt: LunaEvent = LunaEvent(ja.getJSONObject(i)) + logbook.logs.add(evt) + } catch (e: IllegalArgumentException) { + // Mangled JSON? + throw JSONException(e.toString()) + } } Log.d(TAG, "Loaded ${logbook.logs.size} events into logbook") return logbook