forked from penguin86/luna-tracker
Compare commits
15 Commits
be77c7fb22
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c906369df | |||
| 5eeb462836 | |||
| 5b777c0b88 | |||
| 6f3061974d | |||
| 3ea396c045 | |||
| 193e21ce25 | |||
| 7f67c758c9 | |||
| dfa64d71a8 | |||
| b7180068f3 | |||
| 36b848b95e | |||
| a1bde917f8 | |||
| 4f4ff5ed21 | |||
| 453d838470 | |||
| 34aa092722 | |||
| 961e7b90e7 |
19
README.md
19
README.md
@@ -13,7 +13,24 @@ Dedicated to my daughter Luna.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 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)
|
- Chepycou (French translation)
|
||||||
- Daniel Neubauer (German translation)
|
- Daniel Neubauer (German translation)
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId = "it.danieleverducci.lunatracker"
|
applicationId = "it.danieleverducci.lunatracker"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 5
|
versionCode = 7
|
||||||
versionName = "0.7"
|
versionName = "0.9"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -60,4 +60,4 @@ dependencies {
|
|||||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||||
debugImplementation(libs.androidx.ui.tooling)
|
debugImplementation(libs.androidx.ui.tooling)
|
||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,15 +41,14 @@ import okio.IOException
|
|||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import utils.DateUtils
|
import utils.DateUtils
|
||||||
import utils.NumericUtils
|
import utils.NumericUtils
|
||||||
import java.text.DateFormat
|
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "MainActivity"
|
const val TAG = "MainActivity"
|
||||||
val UPDATE_EVERY_SECS: Long = 30
|
const val UPDATE_EVERY_SECS: Long = 30
|
||||||
val DEBUG_CHECK_LOGBOOK_CONSISTENCY = false
|
const val DEBUG_CHECK_LOGBOOK_CONSISTENCY = false
|
||||||
}
|
}
|
||||||
|
|
||||||
var logbook: Logbook? = null
|
var logbook: Logbook? = null
|
||||||
@@ -58,6 +57,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
lateinit var buttonsContainer: ViewGroup
|
lateinit var buttonsContainer: ViewGroup
|
||||||
lateinit var recyclerView: RecyclerView
|
lateinit var recyclerView: RecyclerView
|
||||||
lateinit var handler: Handler
|
lateinit var handler: Handler
|
||||||
|
var signature = ""
|
||||||
var savingEvent = false
|
var savingEvent = false
|
||||||
val updateListRunnable: Runnable = Runnable {
|
val updateListRunnable: Runnable = Runnable {
|
||||||
if (logbook != null && !pauseLogbookUpdate)
|
if (logbook != null && !pauseLogbookUpdate)
|
||||||
@@ -113,24 +113,24 @@ class MainActivity : AppCompatActivity() {
|
|||||||
moreButton.setOnClickListener {
|
moreButton.setOnClickListener {
|
||||||
showOverflowPopupWindow(moreButton)
|
showOverflowPopupWindow(moreButton)
|
||||||
}
|
}
|
||||||
findViewById<View>(R.id.button_no_connection_settings).setOnClickListener({
|
findViewById<View>(R.id.button_no_connection_settings).setOnClickListener {
|
||||||
showSettings()
|
showSettings()
|
||||||
})
|
}
|
||||||
findViewById<View>(R.id.button_settings).setOnClickListener({
|
findViewById<View>(R.id.button_settings).setOnClickListener {
|
||||||
showSettings()
|
showSettings()
|
||||||
})
|
}
|
||||||
findViewById<View>(R.id.button_no_connection_retry).setOnClickListener({
|
findViewById<View>(R.id.button_no_connection_retry).setOnClickListener {
|
||||||
// This may happen at start, when logbook is still null: better ask the logbook list
|
// This may happen at start, when logbook is still null: better ask the logbook list
|
||||||
loadLogbookList()
|
loadLogbookList()
|
||||||
})
|
}
|
||||||
findViewById<View>(R.id.button_sync).setOnClickListener({
|
findViewById<View>(R.id.button_sync).setOnClickListener {
|
||||||
loadLogbookList()
|
loadLogbookList()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setListAdapter(items: ArrayList<LunaEvent>) {
|
private fun setListAdapter(items: ArrayList<LunaEvent>) {
|
||||||
val adapter = LunaEventRecyclerAdapter(this, items)
|
val adapter = LunaEventRecyclerAdapter(this, items)
|
||||||
adapter.onItemClickListener = object: LunaEventRecyclerAdapter.OnItemClickListener{
|
adapter.onItemClickListener = object: LunaEventRecyclerAdapter.OnItemClickListener {
|
||||||
override fun onItemClick(event: LunaEvent) {
|
override fun onItemClick(event: LunaEvent) {
|
||||||
showEventDetailDialog(event, items)
|
showEventDetailDialog(event, items)
|
||||||
}
|
}
|
||||||
@@ -169,6 +169,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
logbookRepo = FileLogbookRepository()
|
logbookRepo = FileLogbookRepository()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signature = settingsRepository.loadSignature()
|
||||||
|
|
||||||
val noBreastfeeding = settingsRepository.loadNoBreastfeeding()
|
val noBreastfeeding = settingsRepository.loadNoBreastfeeding()
|
||||||
findViewById<View>(R.id.layout_nipples).visibility = when (noBreastfeeding) {
|
findViewById<View>(R.id.layout_nipples).visibility = when (noBreastfeeding) {
|
||||||
true -> View.GONE
|
true -> View.GONE
|
||||||
@@ -260,6 +262,26 @@ class MainActivity : AppCompatActivity() {
|
|||||||
alertDialog.show()
|
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<Spinner>(R.id.dialog_puke_value)
|
||||||
|
spinner.adapter = ArrayAdapter.createFromResource(this, R.array.AmountLabels, android.R.layout.simple_spinner_dropdown_item)
|
||||||
|
spinner.setSelection(1)
|
||||||
|
|
||||||
|
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
|
||||||
|
val pos = spinner.selectedItemPosition
|
||||||
|
logEvent(LunaEvent(LunaEvent.TYPE_PUKE, pos + 1))
|
||||||
|
}
|
||||||
|
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() }
|
||||||
|
val alertDialog = d.create()
|
||||||
|
alertDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
fun askNotes(lunaEvent: LunaEvent) {
|
fun askNotes(lunaEvent: LunaEvent) {
|
||||||
val d = AlertDialog.Builder(this)
|
val d = AlertDialog.Builder(this)
|
||||||
val dialogView = layoutInflater.inflate(R.layout.dialog_notes, null)
|
val dialogView = layoutInflater.inflate(R.layout.dialog_notes, null)
|
||||||
@@ -340,7 +362,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
fun showEventDetailDialog(event: LunaEvent, items: ArrayList<LunaEvent>) {
|
fun showEventDetailDialog(event: LunaEvent, items: ArrayList<LunaEvent>) {
|
||||||
// Do not update list while the detail is shown, to avoid changing the object below while it is changed by the user
|
// Do not update list while the detail is shown, to avoid changing the object below while it is changed by the user
|
||||||
pauseLogbookUpdate = true
|
pauseLogbookUpdate = true
|
||||||
val dateFormat = DateFormat.getDateTimeInstance()
|
|
||||||
val d = AlertDialog.Builder(this)
|
val d = AlertDialog.Builder(this)
|
||||||
d.setTitle(R.string.dialog_event_detail_title)
|
d.setTitle(R.string.dialog_event_detail_title)
|
||||||
val dialogView = layoutInflater.inflate(R.layout.dialog_event_detail, null)
|
val dialogView = layoutInflater.inflate(R.layout.dialog_event_detail, null)
|
||||||
@@ -352,8 +373,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val currentDateTime = Calendar.getInstance()
|
val currentDateTime = Calendar.getInstance()
|
||||||
currentDateTime.time = Date(event.time * 1000)
|
currentDateTime.time = Date(event.time * 1000)
|
||||||
|
|
||||||
val dateTextView = dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_date)
|
val dateTextView = dialogView.findViewById<TextView>(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 {
|
dateTextView.setOnClickListener {
|
||||||
// Show datetime picker
|
// Show datetime picker
|
||||||
val startYear = currentDateTime.get(Calendar.YEAR)
|
val startYear = currentDateTime.get(Calendar.YEAR)
|
||||||
@@ -366,11 +388,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
TimePickerDialog(this, { _, hour, minute ->
|
TimePickerDialog(this, { _, hour, minute ->
|
||||||
val pickedDateTime = Calendar.getInstance()
|
val pickedDateTime = Calendar.getInstance()
|
||||||
pickedDateTime.set(year, month, day, hour, minute)
|
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
|
// 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()
|
logbook?.sort()
|
||||||
recyclerView.adapter?.notifyDataSetChanged()
|
recyclerView.adapter?.notifyDataSetChanged()
|
||||||
saveLogbook()
|
saveLogbook()
|
||||||
@@ -389,6 +409,13 @@ class MainActivity : AppCompatActivity() {
|
|||||||
pauseLogbookUpdate = false
|
pauseLogbookUpdate = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// show optional signature
|
||||||
|
if (event.signature.isNotEmpty()) {
|
||||||
|
val signatureTextEdit = dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_signature)
|
||||||
|
signatureTextEdit.text = String.format(getString(R.string.dialog_event_detail_signature), event.signature)
|
||||||
|
signatureTextEdit.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
// create next/previous links to events of the same type
|
// create next/previous links to events of the same type
|
||||||
|
|
||||||
val previousTextView = dialogView.findViewById<TextView>(R.id.dialog_event_previous)
|
val previousTextView = dialogView.findViewById<TextView>(R.id.dialog_event_previous)
|
||||||
@@ -638,6 +665,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
fun logEvent(event: LunaEvent) {
|
fun logEvent(event: LunaEvent) {
|
||||||
savingEvent(true)
|
savingEvent(true)
|
||||||
|
|
||||||
|
event.signature = signature
|
||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
logbook?.logs?.add(0, event)
|
logbook?.logs?.add(0, event)
|
||||||
recyclerView.adapter?.notifyItemInserted(0)
|
recyclerView.adapter?.notifyItemInserted(0)
|
||||||
@@ -787,6 +816,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
askTemperatureValue()
|
askTemperatureValue()
|
||||||
dismiss()
|
dismiss()
|
||||||
})
|
})
|
||||||
|
contentView.findViewById<View>(R.id.button_puke).setOnClickListener({
|
||||||
|
askPukeValue()
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
contentView.findViewById<View>(R.id.button_colic).setOnClickListener({
|
contentView.findViewById<View>(R.id.button_colic).setOnClickListener({
|
||||||
logEvent(
|
logEvent(
|
||||||
LunaEvent(LunaEvent.TYPE_COLIC)
|
LunaEvent(LunaEvent.TYPE_COLIC)
|
||||||
@@ -797,6 +830,12 @@ class MainActivity : AppCompatActivity() {
|
|||||||
askWeightValue()
|
askWeightValue()
|
||||||
dismiss()
|
dismiss()
|
||||||
})
|
})
|
||||||
|
contentView.findViewById<View>(R.id.button_bath).setOnClickListener({
|
||||||
|
logEvent(
|
||||||
|
LunaEvent(LunaEvent.TYPE_BATH)
|
||||||
|
)
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
}.also { popupWindow ->
|
}.also { popupWindow ->
|
||||||
popupWindow.setOnDismissListener({
|
popupWindow.setOnDismissListener({
|
||||||
Handler(mainLooper).postDelayed({
|
Handler(mainLooper).postDelayed({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package it.danieleverducci.lunatracker
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.RadioButton
|
import android.widget.RadioButton
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@@ -24,6 +25,7 @@ open class SettingsActivity : AppCompatActivity() {
|
|||||||
protected lateinit var textViewWebDAVPass: TextView
|
protected lateinit var textViewWebDAVPass: TextView
|
||||||
protected lateinit var progressIndicator: LinearProgressIndicator
|
protected lateinit var progressIndicator: LinearProgressIndicator
|
||||||
protected lateinit var switchNoBreastfeeding: SwitchMaterial
|
protected lateinit var switchNoBreastfeeding: SwitchMaterial
|
||||||
|
protected lateinit var textViewSignature: EditText
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -36,6 +38,7 @@ open class SettingsActivity : AppCompatActivity() {
|
|||||||
textViewWebDAVPass = findViewById(R.id.settings_data_webdav_pass)
|
textViewWebDAVPass = findViewById(R.id.settings_data_webdav_pass)
|
||||||
progressIndicator = findViewById(R.id.progress_indicator)
|
progressIndicator = findViewById(R.id.progress_indicator)
|
||||||
switchNoBreastfeeding = findViewById(R.id.switch_no_breastfeeding)
|
switchNoBreastfeeding = findViewById(R.id.switch_no_breastfeeding)
|
||||||
|
textViewSignature = findViewById(R.id.settings_signature)
|
||||||
|
|
||||||
findViewById<View>(R.id.settings_save).setOnClickListener({
|
findViewById<View>(R.id.settings_save).setOnClickListener({
|
||||||
validateAndSave()
|
validateAndSave()
|
||||||
@@ -52,18 +55,20 @@ open class SettingsActivity : AppCompatActivity() {
|
|||||||
val dataRepo = settingsRepository.loadDataRepository()
|
val dataRepo = settingsRepository.loadDataRepository()
|
||||||
val webDavCredentials = settingsRepository.loadWebdavCredentials()
|
val webDavCredentials = settingsRepository.loadWebdavCredentials()
|
||||||
val noBreastfeeding = settingsRepository.loadNoBreastfeeding()
|
val noBreastfeeding = settingsRepository.loadNoBreastfeeding()
|
||||||
|
val signature = settingsRepository.loadSignature()
|
||||||
|
|
||||||
when (dataRepo) {
|
when (dataRepo) {
|
||||||
LocalSettingsRepository.DATA_REPO.LOCAL_FILE -> radioDataLocal.isChecked = true
|
LocalSettingsRepository.DATA_REPO.LOCAL_FILE -> radioDataLocal.isChecked = true
|
||||||
LocalSettingsRepository.DATA_REPO.WEBDAV -> radioDataWebDAV.isChecked = true
|
LocalSettingsRepository.DATA_REPO.WEBDAV -> radioDataWebDAV.isChecked = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textViewSignature.setText(signature)
|
||||||
switchNoBreastfeeding.isChecked = noBreastfeeding
|
switchNoBreastfeeding.isChecked = noBreastfeeding
|
||||||
|
|
||||||
if (webDavCredentials != null) {
|
if (webDavCredentials != null) {
|
||||||
textViewWebDAVUrl.setText(webDavCredentials[0])
|
textViewWebDAVUrl.text = webDavCredentials[0]
|
||||||
textViewWebDAVUser.setText(webDavCredentials[1])
|
textViewWebDAVUser.text = webDavCredentials[1]
|
||||||
textViewWebDAVPass.setText(webDavCredentials[2])
|
textViewWebDAVPass.text = webDavCredentials[2]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +161,7 @@ open class SettingsActivity : AppCompatActivity() {
|
|||||||
else LocalSettingsRepository.DATA_REPO.LOCAL_FILE
|
else LocalSettingsRepository.DATA_REPO.LOCAL_FILE
|
||||||
)
|
)
|
||||||
settingsRepository.saveNoBreastfeeding(switchNoBreastfeeding.isChecked)
|
settingsRepository.saveNoBreastfeeding(switchNoBreastfeeding.isChecked)
|
||||||
|
settingsRepository.saveSignature(textViewSignature.text.toString())
|
||||||
settingsRepository.saveWebdavCredentials(
|
settingsRepository.saveWebdavCredentials(
|
||||||
textViewWebDAVUrl.text.toString(),
|
textViewWebDAVUrl.text.toString(),
|
||||||
textViewWebDAVUser.text.toString(),
|
textViewWebDAVUser.text.toString(),
|
||||||
@@ -170,7 +176,7 @@ open class SettingsActivity : AppCompatActivity() {
|
|||||||
*/
|
*/
|
||||||
private fun copyLocalLogbooksToWebdav(webDAVLogbookRepository: WebDAVLogbookRepository, listener: OnCopyLocalLogbooksToWebdavFinishedListener) {
|
private fun copyLocalLogbooksToWebdav(webDAVLogbookRepository: WebDAVLogbookRepository, listener: OnCopyLocalLogbooksToWebdavFinishedListener) {
|
||||||
Thread(Runnable {
|
Thread(Runnable {
|
||||||
var errors = StringBuilder()
|
val errors = StringBuilder()
|
||||||
val fileLogbookRepo = FileLogbookRepository()
|
val fileLogbookRepo = FileLogbookRepository()
|
||||||
val logbooks = fileLogbookRepo.getAllLogbooks(this)
|
val logbooks = fileLogbookRepo.getAllLogbooks(this)
|
||||||
for (logbook in logbooks) {
|
for (logbook in logbooks) {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
|
|||||||
holder.quantity.setTextColor(ContextCompat.getColor(context, R.color.textColor))
|
holder.quantity.setTextColor(ContextCompat.getColor(context, R.color.textColor))
|
||||||
// Contents
|
// Contents
|
||||||
holder.type.text = item.getTypeEmoji(context)
|
holder.type.text = item.getTypeEmoji(context)
|
||||||
holder.description.text = when(item.type) {
|
holder.description.text = when (item.type) {
|
||||||
LunaEvent.TYPE_MEDICINE -> item.notes
|
LunaEvent.TYPE_MEDICINE -> item.notes
|
||||||
LunaEvent.TYPE_NOTE -> item.notes
|
LunaEvent.TYPE_NOTE -> item.notes
|
||||||
LunaEvent.TYPE_CUSTOM -> item.notes
|
LunaEvent.TYPE_CUSTOM -> item.notes
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ class LunaEvent: Comparable<LunaEvent> {
|
|||||||
const val TYPE_COLIC = "COLIC"
|
const val TYPE_COLIC = "COLIC"
|
||||||
const val TYPE_TEMPERATURE = "TEMPERATURE"
|
const val TYPE_TEMPERATURE = "TEMPERATURE"
|
||||||
const val TYPE_FOOD = "FOOD"
|
const val TYPE_FOOD = "FOOD"
|
||||||
|
const val TYPE_PUKE = "PUKE"
|
||||||
|
const val TYPE_BATH = "BATH"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val jo: JSONObject
|
private val jo: JSONObject
|
||||||
@@ -53,6 +55,12 @@ class LunaEvent: Comparable<LunaEvent> {
|
|||||||
set(value) {
|
set(value) {
|
||||||
jo.put("notes", 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) {
|
constructor(jo: JSONObject) {
|
||||||
this.jo = jo
|
this.jo = jo
|
||||||
@@ -90,6 +98,8 @@ class LunaEvent: Comparable<LunaEvent> {
|
|||||||
TYPE_TEMPERATURE -> R.string.event_temperature_type
|
TYPE_TEMPERATURE -> R.string.event_temperature_type
|
||||||
TYPE_COLIC -> R.string.event_colic_type
|
TYPE_COLIC -> R.string.event_colic_type
|
||||||
TYPE_FOOD -> R.string.event_food_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
|
else -> R.string.event_unknown_type
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -111,6 +121,8 @@ class LunaEvent: Comparable<LunaEvent> {
|
|||||||
TYPE_TEMPERATURE -> R.string.event_temperature_desc
|
TYPE_TEMPERATURE -> R.string.event_temperature_desc
|
||||||
TYPE_COLIC -> R.string.event_colic_desc
|
TYPE_COLIC -> R.string.event_colic_desc
|
||||||
TYPE_FOOD -> R.string.event_food_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
|
else -> R.string.event_unknown_desc
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -128,7 +140,7 @@ class LunaEvent: Comparable<LunaEvent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
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 {
|
override fun compareTo(other: LunaEvent): Int {
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import java.io.FilenameFilter
|
|||||||
|
|
||||||
class FileLogbookRepository: LogbookRepository {
|
class FileLogbookRepository: LogbookRepository {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "FileLogbookRepository"
|
const val TAG = "FileLogbookRepository"
|
||||||
val FILE_NAME_START = "data"
|
const val FILE_NAME_START = "data"
|
||||||
val FILE_NAME_END = ".json"
|
const val FILE_NAME_END = ".json"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLogbook(context: Context, name: String, listener: LogbookLoadedListener) {
|
override fun loadLogbook(context: Context, name: String, listener: LogbookLoadedListener) {
|
||||||
@@ -32,7 +32,7 @@ class FileLogbookRepository: LogbookRepository {
|
|||||||
fun loadLogbook(context: Context, name: String): Logbook {
|
fun loadLogbook(context: Context, name: String): Logbook {
|
||||||
val logbook = Logbook(name)
|
val logbook = Logbook(name)
|
||||||
val fileName = getFileName(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 json = FileInputStream(file).bufferedReader().use { it.readText() }
|
||||||
val ja = JSONArray(json)
|
val ja = JSONArray(json)
|
||||||
for (i in 0 until ja.length()) {
|
for (i in 0 until ja.length()) {
|
||||||
@@ -58,7 +58,7 @@ class FileLogbookRepository: LogbookRepository {
|
|||||||
|
|
||||||
fun saveLogbook(context: Context, logbook: Logbook) {
|
fun saveLogbook(context: Context, logbook: Logbook) {
|
||||||
val fileName = getFileName(logbook.name)
|
val fileName = getFileName(logbook.name)
|
||||||
val file = File(context.getFilesDir(), fileName)
|
val file = File(context.filesDir, fileName)
|
||||||
val ja = JSONArray()
|
val ja = JSONArray()
|
||||||
for (l in logbook.logs) {
|
for (l in logbook.logs) {
|
||||||
ja.put(l.toJson())
|
ja.put(l.toJson())
|
||||||
@@ -82,7 +82,7 @@ class FileLogbookRepository: LogbookRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun listLogbooks(context: Context): ArrayList<String> {
|
private fun listLogbooks(context: Context): ArrayList<String> {
|
||||||
val logbooksFileNames = context.getFilesDir().list(object: FilenameFilter {
|
val logbooksFileNames = context.filesDir.list(object: FilenameFilter {
|
||||||
override fun accept(dir: File?, name: String?): Boolean {
|
override fun accept(dir: File?, name: String?): Boolean {
|
||||||
if (name == null)
|
if (name == null)
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ import androidx.core.content.edit
|
|||||||
|
|
||||||
class LocalSettingsRepository(val context: Context) {
|
class LocalSettingsRepository(val context: Context) {
|
||||||
companion object {
|
companion object {
|
||||||
val SHARED_PREFS_FILE_NAME = "lunasettings"
|
const val SHARED_PREFS_FILE_NAME = "lunasettings"
|
||||||
val SHARED_PREFS_BB_CONTENT = "bbcontent"
|
const val SHARED_PREFS_BB_CONTENT = "bbcontent"
|
||||||
val SHARED_PREFS_DATA_REPO = "data_repo"
|
const val SHARED_PREFS_DATA_REPO = "data_repo"
|
||||||
val SHARED_PREFS_DAV_URL = "webdav_url"
|
const val SHARED_PREFS_DAV_URL = "webdav_url"
|
||||||
val SHARED_PREFS_DAV_USER = "webdav_user"
|
const val SHARED_PREFS_DAV_USER = "webdav_user"
|
||||||
val SHARED_PREFS_DAV_PASS = "webdav_password"
|
const val SHARED_PREFS_DAV_PASS = "webdav_password"
|
||||||
val SHARED_PREFS_NO_BREASTFEEDING = "no_breastfeeding"
|
const val SHARED_PREFS_NO_BREASTFEEDING = "no_breastfeeding"
|
||||||
|
const val SHARED_PREFS_SIGNATURE = "signature"
|
||||||
}
|
}
|
||||||
enum class DATA_REPO {LOCAL_FILE, WEBDAV}
|
enum class DATA_REPO {LOCAL_FILE, WEBDAV}
|
||||||
val sharedPreferences: SharedPreferences
|
val sharedPreferences: SharedPreferences
|
||||||
@@ -23,15 +24,23 @@ class LocalSettingsRepository(val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun saveBabyBottleContent(content: Int) {
|
fun saveBabyBottleContent(content: Int) {
|
||||||
sharedPreferences.edit().putInt(SHARED_PREFS_BB_CONTENT, content).apply()
|
sharedPreferences.edit { putInt(SHARED_PREFS_BB_CONTENT, content) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadBabyBottleContent(): Int {
|
fun loadBabyBottleContent(): Int {
|
||||||
return sharedPreferences.getInt(SHARED_PREFS_BB_CONTENT, 1)
|
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) {
|
fun saveNoBreastfeeding(content: Boolean) {
|
||||||
sharedPreferences.edit().putBoolean(SHARED_PREFS_NO_BREASTFEEDING, content).apply()
|
sharedPreferences.edit { putBoolean(SHARED_PREFS_NO_BREASTFEEDING, content) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadNoBreastfeeding(): Boolean {
|
fun loadNoBreastfeeding(): Boolean {
|
||||||
@@ -39,15 +48,15 @@ class LocalSettingsRepository(val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun saveDataRepository(repo: DATA_REPO) {
|
fun saveDataRepository(repo: DATA_REPO) {
|
||||||
val spe = sharedPreferences.edit()
|
sharedPreferences.edit(commit = true) {
|
||||||
spe.putString(
|
putString(
|
||||||
SHARED_PREFS_DATA_REPO,
|
SHARED_PREFS_DATA_REPO,
|
||||||
when (repo) {
|
when (repo) {
|
||||||
DATA_REPO.WEBDAV -> "webdav"
|
DATA_REPO.WEBDAV -> "webdav"
|
||||||
DATA_REPO.LOCAL_FILE -> "localfile"
|
DATA_REPO.LOCAL_FILE -> "localfile"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
spe.commit()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadDataRepository(): DATA_REPO {
|
fun loadDataRepository(): DATA_REPO {
|
||||||
@@ -60,11 +69,11 @@ class LocalSettingsRepository(val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun saveWebdavCredentials(url: String, username: String, password: String) {
|
fun saveWebdavCredentials(url: String, username: String, password: String) {
|
||||||
val spe = sharedPreferences.edit()
|
sharedPreferences.edit(commit = true) {
|
||||||
spe.putString(SHARED_PREFS_DAV_URL, url)
|
putString(SHARED_PREFS_DAV_URL, url)
|
||||||
spe.putString(SHARED_PREFS_DAV_USER, username)
|
putString(SHARED_PREFS_DAV_USER, username)
|
||||||
spe.putString(SHARED_PREFS_DAV_PASS, password)
|
putString(SHARED_PREFS_DAV_PASS, password)
|
||||||
spe.commit()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadWebdavCredentials(): Array<String>? {
|
fun loadWebdavCredentials(): Array<String>? {
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
import it.danieleverducci.lunatracker.R
|
import it.danieleverducci.lunatracker.R
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
class DateUtils {
|
class DateUtils {
|
||||||
companion object {
|
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 {
|
fun formatTimeDuration(context: Context, secondsDiff: Long): String {
|
||||||
var seconds = secondsDiff
|
var seconds = secondsDiff
|
||||||
|
|
||||||
@@ -65,7 +70,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 {
|
fun formatTimeAgo(context: Context, unixTime: Long): String {
|
||||||
val secondsDiff = (System.currentTimeMillis() / 1000) - unixTime
|
val secondsDiff = (System.currentTimeMillis() / 1000) - unixTime
|
||||||
@@ -100,5 +106,21 @@ class DateUtils {
|
|||||||
}
|
}
|
||||||
return formattedTime.toString()
|
return formattedTime.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format time as localized string without seconds. E.g. "Sept 18, 2025, 03:36 PM".
|
||||||
|
* Used in the event detail dialog.
|
||||||
|
*/
|
||||||
|
fun formatDateTime(unixTime: Long): String {
|
||||||
|
val date = Date(unixTime * 1000)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
val dateFormat = android.icu.text.DateFormat.getDateTimeInstance(android.icu.text.DateFormat.DEFAULT, android.icu.text.DateFormat.SHORT)
|
||||||
|
return dateFormat.format(date)
|
||||||
|
} else {
|
||||||
|
// fallback
|
||||||
|
val dateFormat = java.text.DateFormat.getDateTimeInstance()
|
||||||
|
return dateFormat.format(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package utils
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.icu.util.LocaleData
|
import android.icu.util.LocaleData
|
||||||
import android.icu.util.ULocale
|
import android.icu.util.ULocale
|
||||||
|
import android.os.Build
|
||||||
import it.danieleverducci.lunatracker.R
|
import it.danieleverducci.lunatracker.R
|
||||||
import it.danieleverducci.lunatracker.entities.LunaEvent
|
import it.danieleverducci.lunatracker.entities.LunaEvent
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
@@ -14,29 +15,45 @@ class NumericUtils (val context: Context) {
|
|||||||
val measurement_unit_weight_tiny: String
|
val measurement_unit_weight_tiny: String
|
||||||
val measurement_unit_temperature_base: 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 // METRIC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.numberFormat = NumberFormat.getInstance()
|
this.numberFormat = NumberFormat.getInstance()
|
||||||
val measurementSystem = LocaleData.getMeasurementSystem(ULocale.getDefault())
|
|
||||||
this.measurement_unit_liquid_base = context.getString(
|
this.measurement_unit_liquid_base = context.getString(
|
||||||
if (measurementSystem == LocaleData. MeasurementSystem.SI)
|
if (isMetricSystem())
|
||||||
R.string.measurement_unit_liquid_base_metric
|
R.string.measurement_unit_liquid_base_metric
|
||||||
else
|
else
|
||||||
R.string.measurement_unit_liquid_base_imperial
|
R.string.measurement_unit_liquid_base_imperial
|
||||||
)
|
)
|
||||||
this.measurement_unit_weight_base = context.getString(
|
this.measurement_unit_weight_base = context.getString(
|
||||||
if (measurementSystem == LocaleData. MeasurementSystem.SI)
|
if (isMetricSystem())
|
||||||
R.string.measurement_unit_weight_base_metric
|
R.string.measurement_unit_weight_base_metric
|
||||||
else
|
else
|
||||||
R.string.measurement_unit_weight_base_imperial
|
R.string.measurement_unit_weight_base_imperial
|
||||||
)
|
)
|
||||||
this.measurement_unit_weight_tiny = context.getString(
|
this.measurement_unit_weight_tiny = context.getString(
|
||||||
if (measurementSystem == LocaleData. MeasurementSystem.SI)
|
if (isMetricSystem())
|
||||||
R.string.measurement_unit_weight_tiny_metric
|
R.string.measurement_unit_weight_tiny_metric
|
||||||
else
|
else
|
||||||
R.string.measurement_unit_weight_tiny_imperial
|
R.string.measurement_unit_weight_tiny_imperial
|
||||||
)
|
)
|
||||||
this.measurement_unit_temperature_base = context.getString(
|
this.measurement_unit_temperature_base = context.getString(
|
||||||
if (measurementSystem == LocaleData. MeasurementSystem.SI)
|
if (isMetricSystem())
|
||||||
R.string.measurement_unit_temperature_base_metric
|
R.string.measurement_unit_temperature_base_metric
|
||||||
else
|
else
|
||||||
R.string.measurement_unit_temperature_base_imperial
|
R.string.measurement_unit_temperature_base_imperial
|
||||||
@@ -45,11 +62,15 @@ class NumericUtils (val context: Context) {
|
|||||||
|
|
||||||
fun formatEventQuantity(item: LunaEvent): String {
|
fun formatEventQuantity(item: LunaEvent): String {
|
||||||
val formatted = StringBuilder()
|
val formatted = StringBuilder()
|
||||||
if ((item.quantity ?: 0) > 0) {
|
if (item.quantity > 0) {
|
||||||
if (item.type == LunaEvent.TYPE_TEMPERATURE)
|
formatted.append(when (item.type) {
|
||||||
formatted.append((item.quantity / 10.0f).toString())
|
LunaEvent.TYPE_TEMPERATURE ->
|
||||||
else
|
(item.quantity / 10.0f).toString()
|
||||||
formatted.append(item.quantity)
|
LunaEvent.TYPE_PUKE ->
|
||||||
|
context.resources.getStringArray(R.array.AmountLabels)[item.quantity - 1]
|
||||||
|
else ->
|
||||||
|
item.quantity
|
||||||
|
})
|
||||||
|
|
||||||
formatted.append(" ")
|
formatted.append(" ")
|
||||||
formatted.append(
|
formatted.append(
|
||||||
@@ -70,10 +91,9 @@ class NumericUtils (val context: Context) {
|
|||||||
* @return min, max, normal
|
* @return min, max, normal
|
||||||
*/
|
*/
|
||||||
fun getValidEventQuantityRange(lunaEventType: String): Triple<Int, Int, Int>? {
|
fun getValidEventQuantityRange(lunaEventType: String): Triple<Int, Int, Int>? {
|
||||||
val measurementSystem = LocaleData.getMeasurementSystem(ULocale.getDefault())
|
|
||||||
return when (lunaEventType) {
|
return when (lunaEventType) {
|
||||||
LunaEvent.TYPE_TEMPERATURE -> {
|
LunaEvent.TYPE_TEMPERATURE -> {
|
||||||
if (measurementSystem == LocaleData. MeasurementSystem.SI)
|
if (isMetricSystem())
|
||||||
Triple(
|
Triple(
|
||||||
context.resources.getInteger(R.integer.human_body_temp_min_metric),
|
context.resources.getInteger(R.integer.human_body_temp_min_metric),
|
||||||
context.resources.getInteger(R.integer.human_body_temp_max_metric),
|
context.resources.getInteger(R.integer.human_body_temp_max_metric),
|
||||||
|
|||||||
@@ -119,28 +119,57 @@
|
|||||||
android:visibility="invisible"/>
|
android:visibility="invisible"/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/settings_signature" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/settings_signature"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:background="@drawable/textview_background"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="30dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:text="@string/settings_signature_desc"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="20dp">
|
||||||
android:layout_marginEnd="30dp">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:text="@string/no_breastfeeding" />
|
android:text="@string/settings_no_breastfeeding" />
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
android:id="@+id/switch_no_breastfeeding"
|
android:id="@+id/switch_no_breastfeeding"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
android:layout_weight="1" />
|
android:layout_weight="1" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="30dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:text="@string/settings_no_breastfeeding_desc"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="20dp">
|
android:paddingTop="20dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:paddingHorizontal="20dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/dialog_event_detail_type_emoji"
|
android:id="@+id/dialog_event_detail_type_emoji"
|
||||||
@@ -61,6 +63,14 @@
|
|||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:id="@+id/dialog_event_detail_type_signature"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="20dp"
|
android:padding="10dp"
|
||||||
android:background="@color/transparent">
|
android:background="@color/transparent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -14,27 +14,17 @@
|
|||||||
android:id="@+id/button_medicine"
|
android:id="@+id/button_medicine"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="20dp"
|
android:padding="10dp"
|
||||||
android:background="@drawable/dropdown_list_item_background"
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
style="@style/OverflowMenuText"
|
style="@style/OverflowMenuText"
|
||||||
android:text="@string/overflow_event_medicine"/>
|
android:text="@string/overflow_event_medicine"/>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/button_enema"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:padding="20dp"
|
|
||||||
android:background="@drawable/dropdown_list_item_background"
|
|
||||||
style="@style/OverflowMenuText"
|
|
||||||
android:text="@string/overflow_event_enema"/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/button_note"
|
android:id="@+id/button_note"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:padding="20dp"
|
android:padding="10dp"
|
||||||
android:background="@drawable/dropdown_list_item_background"
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
style="@style/OverflowMenuText"
|
style="@style/OverflowMenuText"
|
||||||
android:text="@string/overflow_event_note"/>
|
android:text="@string/overflow_event_note"/>
|
||||||
@@ -44,17 +34,27 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:padding="20dp"
|
android:padding="10dp"
|
||||||
android:background="@drawable/dropdown_list_item_background"
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
style="@style/OverflowMenuText"
|
style="@style/OverflowMenuText"
|
||||||
android:text="@string/overflow_event_temperature"/>
|
android:text="@string/overflow_event_temperature"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_puke"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
|
style="@style/OverflowMenuText"
|
||||||
|
android:text="@string/overflow_event_puke"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/button_colic"
|
android:id="@+id/button_colic"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:padding="20dp"
|
android:padding="10dp"
|
||||||
android:background="@drawable/dropdown_list_item_background"
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
style="@style/OverflowMenuText"
|
style="@style/OverflowMenuText"
|
||||||
android:text="@string/overflow_event_colic"/>
|
android:text="@string/overflow_event_colic"/>
|
||||||
@@ -64,11 +64,31 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:padding="20dp"
|
android:padding="10dp"
|
||||||
android:background="@drawable/dropdown_list_item_background"
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
style="@style/OverflowMenuText"
|
style="@style/OverflowMenuText"
|
||||||
android:text="@string/overflow_event_scale"/>
|
android:text="@string/overflow_event_scale"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_bath"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
|
style="@style/OverflowMenuText"
|
||||||
|
android:text="@string/overflow_event_bath"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_enema"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
|
style="@style/OverflowMenuText"
|
||||||
|
android:text="@string/overflow_event_enema"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
17
app/src/main/res/layout/puke_dialog.xml
Normal file
17
app/src/main/res/layout/puke_dialog.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/dialog_puke_value"
|
||||||
|
android:layout_width="250dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:paddingVertical="8dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -50,9 +50,8 @@
|
|||||||
<string name="no_connection_go_to_settings">Einstellungen</string>
|
<string name="no_connection_go_to_settings">Einstellungen</string>
|
||||||
<string name="no_connection_retry">Erneut versuchen</string>
|
<string name="no_connection_retry">Erneut versuchen</string>
|
||||||
|
|
||||||
<string name="no_breastfeeding">Kein Stillen</string>
|
|
||||||
|
|
||||||
<string name="settings_title">Einstellungen</string>
|
<string name="settings_title">Einstellungen</string>
|
||||||
|
<string name="settings_no_breastfeeding">Kein Stillen</string>
|
||||||
<string name="settings_storage">Speicherort für Daten auswählen</string>
|
<string name="settings_storage">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>
|
||||||
|
|||||||
8
app/src/main/res/values/arrays.xml
Normal file
8
app/src/main/res/values/arrays.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string-array name="AmountLabels">
|
||||||
|
<item>@string/amount_little</item>
|
||||||
|
<item>@string/amount_normal</item>
|
||||||
|
<item>@string/amount_plenty</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
||||||
@@ -12,6 +12,9 @@
|
|||||||
<string name="log_temperature_dialog_title">Temperature</string>
|
<string name="log_temperature_dialog_title">Temperature</string>
|
||||||
<string name="log_temperature_dialog_description">Insert the temperature</string>
|
<string name="log_temperature_dialog_description">Insert the temperature</string>
|
||||||
|
|
||||||
|
<string name="log_puke_dialog_title">Puke</string>
|
||||||
|
<string name="log_puke_dialog_description">Select the amount</string>
|
||||||
|
|
||||||
<string name="event_bottle_type" translatable="false">🍼</string>
|
<string name="event_bottle_type" translatable="false">🍼</string>
|
||||||
<string name="event_food_type" translatable="false">🥣</string>
|
<string name="event_food_type" translatable="false">🥣</string>
|
||||||
<string name="event_scale_type" translatable="false">⚖️</string>
|
<string name="event_scale_type" translatable="false">⚖️</string>
|
||||||
@@ -25,6 +28,8 @@
|
|||||||
<string name="event_note_type" translatable="false">📝</string>
|
<string name="event_note_type" translatable="false">📝</string>
|
||||||
<string name="event_temperature_type" translatable="false">🌡️</string>
|
<string name="event_temperature_type" translatable="false">🌡️</string>
|
||||||
<string name="event_colic_type" translatable="false">💨</string>
|
<string name="event_colic_type" translatable="false">💨</string>
|
||||||
|
<string name="event_puke_type" translatable="false">🤮</string>
|
||||||
|
<string name="event_bath_type" translatable="false">🛁</string>
|
||||||
<string name="event_unknown_type" translatable="false">\?</string>
|
<string name="event_unknown_type" translatable="false">\?</string>
|
||||||
|
|
||||||
<string name="event_bottle_desc">Baby bottle</string>
|
<string name="event_bottle_desc">Baby bottle</string>
|
||||||
@@ -40,6 +45,8 @@
|
|||||||
<string name="event_note_desc">Note</string>
|
<string name="event_note_desc">Note</string>
|
||||||
<string name="event_temperature_desc">Temperature</string>
|
<string name="event_temperature_desc">Temperature</string>
|
||||||
<string name="event_colic_desc">Gaseous colic</string>
|
<string name="event_colic_desc">Gaseous colic</string>
|
||||||
|
<string name="event_puke_desc">Puke</string>
|
||||||
|
<string name="event_bath_desc">Bath</string>
|
||||||
<string name="event_unknown_desc"></string>
|
<string name="event_unknown_desc"></string>
|
||||||
|
|
||||||
<string name="overflow_event_scale">⚖️ Weight</string>
|
<string name="overflow_event_scale">⚖️ Weight</string>
|
||||||
@@ -48,6 +55,8 @@
|
|||||||
<string name="overflow_event_note">📝 Note</string>
|
<string name="overflow_event_note">📝 Note</string>
|
||||||
<string name="overflow_event_temperature">🌡️ Temperature</string>
|
<string name="overflow_event_temperature">🌡️ Temperature</string>
|
||||||
<string name="overflow_event_colic">💨 Gaseous colic</string>
|
<string name="overflow_event_colic">💨 Gaseous colic</string>
|
||||||
|
<string name="overflow_event_puke">🤮 Puke</string>
|
||||||
|
<string name="overflow_event_bath">🛁 Bath</string>
|
||||||
|
|
||||||
<string name="toast_event_added">Event logged</string>
|
<string name="toast_event_added">Event logged</string>
|
||||||
<string name="toast_logbook_saved">Logbook saved</string>
|
<string name="toast_logbook_saved">Logbook saved</string>
|
||||||
@@ -66,14 +75,18 @@
|
|||||||
<string name="year_ago">year</string>
|
<string name="year_ago">year</string>
|
||||||
<string name="years_ago">years</string>
|
<string name="years_ago">years</string>
|
||||||
|
|
||||||
|
<string name="amount_little">Little</string>
|
||||||
|
<string name="amount_normal">Normal</string>
|
||||||
|
<string name="amount_plenty">Plenty</string>
|
||||||
|
|
||||||
<string name="no_connection">No connection</string>
|
<string name="no_connection">No connection</string>
|
||||||
<string name="no_connection_explain">Unable to reach WebDAV service</string>
|
<string name="no_connection_explain">Unable to reach WebDAV service</string>
|
||||||
<string name="no_connection_go_to_settings">Settings</string>
|
<string name="no_connection_go_to_settings">Settings</string>
|
||||||
<string name="no_connection_retry">Retry</string>
|
<string name="no_connection_retry">Retry</string>
|
||||||
|
|
||||||
<string name="no_breastfeeding">No Breastfeeding</string>
|
|
||||||
|
|
||||||
<string name="settings_title">Settings</string>
|
<string name="settings_title">Settings</string>
|
||||||
|
<string name="settings_signature">Signature</string>
|
||||||
|
<string name="settings_signature_desc">Attach a signature to each event you create and for others to see. Useful if multiple people add events.</string>
|
||||||
<string name="settings_storage">Choose where to save data</string>
|
<string name="settings_storage">Choose where to save data</string>
|
||||||
<string name="settings_storage_local">On device</string>
|
<string name="settings_storage_local">On device</string>
|
||||||
<string name="settings_storage_local_desc">Most privacy-friendly solution: data doesn\'t leave your device</string>
|
<string name="settings_storage_local_desc">Most privacy-friendly solution: data doesn\'t leave your device</string>
|
||||||
@@ -89,6 +102,8 @@
|
|||||||
<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_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_no_breastfeeding">No Breastfeeding</string>
|
||||||
|
<string name="settings_no_breastfeeding_desc">Hide the Breastfeeding buttons for when they are not needed.</string>
|
||||||
<string name="settings_json_error">There\'s a save file on the server, but it is corrupted or unreadable. Please delete it </string>
|
<string name="settings_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>
|
||||||
<string name="settings_webdav_upload_error">Error while uploading local logbook %1$s to webdav: %2$s</string>
|
<string name="settings_webdav_upload_error">Error while uploading local logbook %1$s to webdav: %2$s</string>
|
||||||
@@ -123,6 +138,7 @@
|
|||||||
<string name="dialog_event_detail_delete_button">Delete</string>
|
<string name="dialog_event_detail_delete_button">Delete</string>
|
||||||
<string name="dialog_event_detail_quantity">Quantity</string>
|
<string name="dialog_event_detail_quantity">Quantity</string>
|
||||||
<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_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>
|
||||||
|
|||||||
Reference in New Issue
Block a user