37 Commits

Author SHA1 Message Date
f3fb584ec2 activity_setting: fine tune layout style 2025-09-29 19:12:46 +02:00
f38b889248 add signature setting
For multiple users it helps to
keep track about who did what.
2025-09-29 19:12:42 +02:00
301e8d0476 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.
2025-09-29 17:50:06 +02:00
0633b4d084 add bath event type 2025-09-29 17:50:06 +02:00
0a9821aef7 add no-breastfeeding help text 2025-09-29 17:50:02 +02:00
01f24694b5 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.
2025-09-29 09:33:10 +02:00
693405cadb simplify puke event 2025-09-29 09:33:07 +02:00
730ef95220 add complex puke event 2025-09-29 04:56:14 +02:00
34aa092722 NumericUtils: provide fallback for LocaleData.getMeasurementSystem
LocaleData.getMeasurementSystem is available at API level 28
but the app supports API level 21.
2025-09-29 03:34:08 +02:00
961e7b90e7 small code cleanup
No code behavior has been changed.
2025-09-29 03:33:30 +02:00
be77c7fb22 Added "get on F-Droid" button to readme 2025-09-21 09:28:02 +02:00
a7c44df553 Bumped version 2025-09-21 09:21:04 +02:00
928112adb8 Updated gradle wrapper 2025-09-21 09:20:47 +02:00
b90dc92874 Link to contributors in fastlane 2025-09-21 09:20:21 +02:00
36481a1194 Added thanks to Moritz Warning contribution 2025-09-21 09:12:11 +02:00
d4adb9d981 Merge pull request 'improvements' (#10) from mwarning/luna-tracker:improvements into master
Reviewed-on: #10
2025-09-21 09:01:03 +02:00
5df3b31e64 activity_main: lower margin from 10 to 5dp
The buttons are big enough for fingers.
Let's make the view more compact.
2025-09-19 12:41:55 +02:00
0a96fb91d7 DateUtils: simplify code 2025-09-19 12:41:55 +02:00
f4c526ff8d add previous/next event link to details dialog
The links will point to the previous/next
event of the same type
2025-09-19 12:41:51 +02:00
ea88fd6bb6 make more strings translateable 2025-09-16 21:11:52 +02:00
3e2bdca083 make some val constant 2025-09-16 21:11:52 +02:00
e5e486c315 remove unused imports 2025-09-16 21:11:52 +02:00
3f648a6490 remove stray 1 character from time 2025-09-16 21:11:52 +02:00
cc6ebc398c activity_main: move progress bar next to event list title
Save some vertical space and prevent vertical size change/flicker.
2025-09-16 21:11:52 +02:00
e1f48e6524 MainActivity: support 24h format 2025-09-16 21:11:52 +02:00
5e1c67d52d MainActivity: small code cleanup 2025-09-16 21:11:52 +02:00
3a457b2ae6 bigger font for event detail dialog 2025-09-16 21:11:52 +02:00
a2fd04d233 remove log event title
Keep the main page simpler and have a bit more
space for log messages.
2025-09-16 21:11:52 +02:00
f1f73b65f7 add setting to disable breastfeeding buttons
Some women do not breastfeed. Hide the buttons
in order to have more space for log messages.
2025-09-16 21:11:49 +02:00
5c247f5948 replace dp with sp
As suggested by android-studio.
2025-09-16 12:23:44 +02:00
83b77b40d6 values: escape apostrophe characters 2025-09-16 12:23:44 +02:00
25f7b1fc00 add password toggle view icon 2025-09-16 12:23:44 +02:00
a635e46180 layout_marginLeft is deprecated 2025-09-16 12:23:40 +02:00
1080fea3f2 Merge pull request 'German translation by Daniel Neubauer + updated README.md' (#9) from develop into master
Reviewed-on: #9
2025-09-02 09:22:47 +02:00
6232aedd04 German translation by Daniel Neubauer 2025-09-02 08:58:42 +02:00
30dc958a0c Merge pull request 'added french language translation and corrected a few english typos' (#6) from chepycou/luna-tracker:master into master
Reviewed-on: #6
2025-07-02 13:22:15 +02:00
Chepycou
1de99e89e5 added french language translation and corrected a few english typos 2025-07-01 16:58:51 +02:00
35 changed files with 836 additions and 238 deletions

1
.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
LunaTracker

6
.idea/AndroidProjectSystem.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

6
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

View File

@@ -1,5 +1,7 @@
# 🌜 LunaTracker 🌛
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/it/packages/it.danieleverducci.lunatracker/)
LunaTracker is a newborn baby tracking app.
Parenting can be tough. You get home from the hospital, exhausted, with this little fragile unknown thingy that has no user manual and a single way to let you know something's not ok: crying.
@@ -10,3 +12,9 @@ This app is meant to log all the relevant events (diaper change, breastfeeding,
Dedicated to my daughter Luna.
![Screenshot](fastlane/metadata/android/en-US/images/phoneScreenshots/1.png)
## Thanks for the valuable contributions to:
- Chepycou (French translation)
- Daniel Neubauer (German translation)
- Moritz Warning (Various bugfixes and new features)

View File

@@ -12,8 +12,8 @@ android {
applicationId = "it.danieleverducci.lunatracker"
minSdk = 21
targetSdk = 34
versionCode = 4
versionName = "0.6"
versionCode = 5
versionName = "0.7"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -39,16 +39,16 @@ import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
import kotlinx.coroutines.Runnable
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
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
@@ -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)
@@ -74,9 +75,9 @@ class MainActivity : AppCompatActivity() {
// Show view
setContentView(R.layout.activity_main)
progressIndicator = findViewById<LinearProgressIndicator>(R.id.progress_indicator)
buttonsContainer = findViewById<ViewGroup>(R.id.buttons_container)
recyclerView = findViewById<RecyclerView>(R.id.list_events)
progressIndicator = findViewById(R.id.progress_indicator)
buttonsContainer = findViewById(R.id.buttons_container)
recyclerView = findViewById(R.id.list_events)
recyclerView.setLayoutManager(LinearLayoutManager(applicationContext))
// Set listeners
@@ -112,26 +113,26 @@ class MainActivity : AppCompatActivity() {
moreButton.setOnClickListener {
showOverflowPopupWindow(moreButton)
}
findViewById<View>(R.id.button_no_connection_settings).setOnClickListener({
findViewById<View>(R.id.button_no_connection_settings).setOnClickListener {
showSettings()
})
findViewById<View>(R.id.button_settings).setOnClickListener({
}
findViewById<View>(R.id.button_settings).setOnClickListener {
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
loadLogbookList()
})
findViewById<View>(R.id.button_sync).setOnClickListener({
}
findViewById<View>(R.id.button_sync).setOnClickListener {
loadLogbookList()
})
}
}
private fun setListAdapter(items: ArrayList<LunaEvent>) {
val adapter = LunaEventRecyclerAdapter(this, items)
adapter.onItemClickListener = object: LunaEventRecyclerAdapter.OnItemClickListener{
adapter.onItemClickListener = object: LunaEventRecyclerAdapter.OnItemClickListener {
override fun onItemClick(event: LunaEvent) {
showEventDetailDialog(event)
showEventDetailDialog(event, items)
}
}
recyclerView.adapter = adapter
@@ -168,6 +169,14 @@ class MainActivity : AppCompatActivity() {
logbookRepo = FileLogbookRepository()
}
signature = settingsRepository.loadSignature()
val noBreastfeeding = settingsRepository.loadNoBreastfeeding()
findViewById<View>(R.id.layout_nipples).visibility = when (noBreastfeeding) {
true -> View.GONE
false -> View.VISIBLE
}
// Update list dates
recyclerView.adapter?.notifyDataSetChanged()
@@ -302,25 +311,52 @@ class MainActivity : AppCompatActivity() {
alertDialog.show()
}
fun showEventDetailDialog(event: LunaEvent) {
fun getPreviousSameEvent(event: LunaEvent, items: ArrayList<LunaEvent>): LunaEvent? {
var previousEvent: LunaEvent? = null
for (item in items) {
if (item.type == event.type && item.time < event.time) {
if (previousEvent == null) {
previousEvent = item
} else if (previousEvent.time < item.time) {
previousEvent = item
}
}
}
return previousEvent
}
fun getNextSameEvent(event: LunaEvent, items: ArrayList<LunaEvent>): LunaEvent? {
var nextEvent: LunaEvent? = null
for (item in items) {
if (item.type == event.type && item.time > event.time) {
if (nextEvent == null) {
nextEvent = item
} else if (nextEvent.time > item.time) {
nextEvent = item
}
}
}
return nextEvent
}
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
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)
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_emoji).setText(event.getTypeEmoji(this))
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_description).setText(event.getTypeDescription(this))
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_quantity).setText(
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_emoji).text = event.getTypeEmoji(this)
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_description).text = event.getTypeDescription(this)
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_quantity).text =
NumericUtils(this).formatEventQuantity(event)
)
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_notes).setText(event.notes)
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_notes).text = event.notes
val currentDateTime = Calendar.getInstance()
currentDateTime.time = Date(event.time * 1000)
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.setOnClickListener({
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)
val startMonth = currentDateTime.get(Calendar.MONTH)
@@ -328,21 +364,19 @@ class MainActivity : AppCompatActivity() {
val startHour = currentDateTime.get(Calendar.HOUR_OF_DAY)
val startMinute = currentDateTime.get(Calendar.MINUTE)
DatePickerDialog(this, DatePickerDialog.OnDateSetListener { _, year, month, day ->
TimePickerDialog(this, TimePickerDialog.OnTimeSetListener { _, hour, minute ->
DatePickerDialog(this, { _, year, month, day ->
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()
}, startHour, startMinute, false).show()
}, startHour, startMinute, android.text.format.DateFormat.is24HourFormat(this@MainActivity)).show()
}, startYear, startMonth, startDay).show()
})
}
d.setView(dialogView)
d.setPositiveButton(R.string.dialog_event_detail_close_button) { dialogInterface, i -> dialogInterface.dismiss() }
@@ -354,6 +388,44 @@ class MainActivity : AppCompatActivity() {
// Resume logbook update
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
val previousTextView = dialogView.findViewById<TextView>(R.id.dialog_event_previous)
val nextTextView = dialogView.findViewById<TextView>(R.id.dialog_event_next)
val nextEvent = getNextSameEvent(event, items)
val previousEvent = getPreviousSameEvent(event, items)
if (previousEvent != null) {
val emoji = previousEvent.getTypeEmoji(applicationContext)
val time = DateUtils.formatTimeDuration(applicationContext, event.time - previousEvent.time)
previousTextView.text = String.format("⬅️ %s %s", emoji, time)
previousTextView.setOnClickListener {
alertDialog.cancel()
showEventDetailDialog(previousEvent, items)
}
} else {
previousTextView.visibility = View.GONE
}
if (nextEvent != null) {
val emoji = nextEvent.getTypeEmoji(applicationContext)
val time = DateUtils.formatTimeDuration(applicationContext, nextEvent.time - event.time)
nextTextView.text = String.format("%s %s ➡️", time, emoji)
nextTextView.setOnClickListener {
alertDialog.cancel()
showEventDetailDialog(nextEvent, items)
}
} else {
nextTextView.visibility = View.GONE
}
}
fun showAddLogbookDialog(requestedByUser: Boolean) {
@@ -392,7 +464,7 @@ class MainActivity : AppCompatActivity() {
sAdapter.setDropDownViewResource(R.layout.row_logbook_spinner)
for (ln in logbooksNames) {
sAdapter.add(
if (ln.isEmpty()) getString(R.string.default_logbook_name) else ln
ln.ifEmpty { getString(R.string.default_logbook_name) }
)
}
spinner.adapter = sAdapter
@@ -410,7 +482,6 @@ class MainActivity : AppCompatActivity() {
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
})
}
@@ -560,7 +631,6 @@ class MainActivity : AppCompatActivity() {
onRepoError(getString(R.string.settings_generic_error) + error.toString())
})
}
})
}
@@ -575,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)
@@ -708,10 +780,10 @@ class MainActivity : AppCompatActivity() {
isOutsideTouchable = true
val inflater = LayoutInflater.from(anchor.context)
contentView = inflater.inflate(R.layout.more_events_popup, null)
contentView.findViewById<View>(R.id.button_medicine).setOnClickListener({
contentView.findViewById<View>(R.id.button_medicine).setOnClickListener {
askNotes(LunaEvent(LunaEvent.TYPE_MEDICINE))
dismiss()
})
}
contentView.findViewById<View>(R.id.button_enema).setOnClickListener({
logEvent(LunaEvent(LunaEvent.TYPE_ENEMA))
dismiss()
@@ -724,6 +796,10 @@ class MainActivity : AppCompatActivity() {
askTemperatureValue()
dismiss()
})
contentView.findViewById<View>(R.id.button_puke).setOnClickListener({
logEvent(LunaEvent(LunaEvent.TYPE_PUKE))
dismiss()
})
contentView.findViewById<View>(R.id.button_colic).setOnClickListener({
logEvent(
LunaEvent(LunaEvent.TYPE_COLIC)
@@ -734,6 +810,12 @@ class MainActivity : AppCompatActivity() {
askWeightValue()
dismiss()
})
contentView.findViewById<View>(R.id.button_bath).setOnClickListener({
logEvent(
LunaEvent(LunaEvent.TYPE_BATH)
)
dismiss()
})
}.also { popupWindow ->
popupWindow.setOnDismissListener({
Handler(mainLooper).postDelayed({

View File

@@ -2,19 +2,19 @@ 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
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.switchmaterial.SwitchMaterial
import com.thegrizzlylabs.sardineandroid.impl.SardineException
import it.danieleverducci.lunatracker.repository.FileLogbookRepository
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
import it.danieleverducci.lunatracker.repository.LogbookListObtainedListener
import it.danieleverducci.lunatracker.repository.LogbookRepository
import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
import okio.IOException
import org.json.JSONException
open class SettingsActivity : AppCompatActivity() {
protected lateinit var settingsRepository: LocalSettingsRepository
@@ -24,6 +24,8 @@ open class SettingsActivity : AppCompatActivity() {
protected lateinit var textViewWebDAVUser: TextView
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)
@@ -35,6 +37,9 @@ open class SettingsActivity : AppCompatActivity() {
textViewWebDAVUser = findViewById(R.id.settings_data_webdav_user)
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<View>(R.id.settings_save).setOnClickListener({
validateAndSave()
})
@@ -49,15 +54,21 @@ open class SettingsActivity : AppCompatActivity() {
fun loadSettings() {
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) {
textViewWebDAVUrl.setText(webDavCredentials[0])
textViewWebDAVUser.setText(webDavCredentials[1])
textViewWebDAVPass.setText(webDavCredentials[2])
textViewWebDAVUrl.text = webDavCredentials[0]
textViewWebDAVUser.text = webDavCredentials[1]
textViewWebDAVPass.text = webDavCredentials[2]
}
}
@@ -149,6 +160,8 @@ open class SettingsActivity : AppCompatActivity() {
if (radioDataWebDAV.isChecked) LocalSettingsRepository.DATA_REPO.WEBDAV
else LocalSettingsRepository.DATA_REPO.LOCAL_FILE
)
settingsRepository.saveNoBreastfeeding(switchNoBreastfeeding.isChecked)
settingsRepository.saveSignature(textViewSignature.text.toString())
settingsRepository.saveWebdavCredentials(
textViewWebDAVUrl.text.toString(),
textViewWebDAVUser.text.toString(),
@@ -163,7 +176,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) {

View File

@@ -53,7 +53,7 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
holder.quantity.setTextColor(ContextCompat.getColor(context, R.color.textColor))
// Contents
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_NOTE -> item.notes
LunaEvent.TYPE_CUSTOM -> item.notes

View File

@@ -2,7 +2,7 @@ package it.danieleverducci.lunatracker.entities
class Logbook(val name: String) {
companion object {
val MAX_SAFE_LOGBOOK_SIZE = 30000
const val MAX_SAFE_LOGBOOK_SIZE = 30000
}
val logs = ArrayList<LunaEvent>()

View File

@@ -14,20 +14,22 @@ import java.util.Date
class LunaEvent: Comparable<LunaEvent> {
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"
val TYPE_MEDICINE = "MEDICINE"
val TYPE_ENEMA = "ENEMA"
val TYPE_NOTE = "NOTE"
val TYPE_CUSTOM = "CUSTOM"
val TYPE_COLIC = "COLIC"
val TYPE_TEMPERATURE = "TEMPERATURE"
val TYPE_FOOD = "FOOD"
const val TYPE_BABY_BOTTLE = "BABY_BOTTLE"
const val TYPE_WEIGHT = "WEIGHT"
const val TYPE_BREASTFEEDING_LEFT_NIPPLE = "BREASTFEEDING_LEFT_NIPPLE"
const val TYPE_BREASTFEEDING_BOTH_NIPPLE = "BREASTFEEDING_BOTH_NIPPLE"
const val TYPE_BREASTFEEDING_RIGHT_NIPPLE = "BREASTFEEDING_RIGHT_NIPPLE"
const val TYPE_DIAPERCHANGE_POO = "DIAPERCHANGE_POO"
const val TYPE_DIAPERCHANGE_PEE = "DIAPERCHANGE_PEE"
const val TYPE_MEDICINE = "MEDICINE"
const val TYPE_ENEMA = "ENEMA"
const val TYPE_NOTE = "NOTE"
const val TYPE_CUSTOM = "CUSTOM"
const val TYPE_COLIC = "COLIC"
const val TYPE_TEMPERATURE = "TEMPERATURE"
const val TYPE_FOOD = "FOOD"
const val TYPE_PUKE = "PUKE"
const val TYPE_BATH = "BATH"
}
private val jo: JSONObject
@@ -53,6 +55,12 @@ class LunaEvent: Comparable<LunaEvent> {
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
@@ -90,6 +98,8 @@ class LunaEvent: Comparable<LunaEvent> {
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
TYPE_BATH -> R.string.event_bath_type
else -> R.string.event_unknown_type
}
)
@@ -111,6 +121,8 @@ class LunaEvent: Comparable<LunaEvent> {
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
TYPE_BATH -> R.string.event_bath_desc
else -> R.string.event_unknown_desc
}
)
@@ -128,7 +140,7 @@ class LunaEvent: Comparable<LunaEvent> {
}
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 {

View File

@@ -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<String> {
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

View File

@@ -3,15 +3,18 @@ package it.danieleverducci.lunatracker.repository
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
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"
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"
const val SHARED_PREFS_SIGNATURE = "signature"
}
enum class DATA_REPO {LOCAL_FILE, WEBDAV}
val sharedPreferences: SharedPreferences
@@ -21,23 +24,39 @@ 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 {
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) }
}
fun loadNoBreastfeeding(): Boolean {
return sharedPreferences.getBoolean(SHARED_PREFS_NO_BREASTFEEDING, false)
}
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 {
@@ -50,11 +69,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<String>? {

View File

@@ -7,9 +7,70 @@ 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
val years = (seconds / (365 * 24 * 60 * 60F)).toLong()
seconds -= years * (365 * 24 * 60 * 60)
val days = (seconds / (24 * 60 * 60F)).toLong()
seconds -= days * (24 * 60 * 60)
val hours = (seconds / (60 * 60F)).toLong()
seconds -= hours * (60 * 60)
val minutes = (seconds / 60F).toLong()
seconds -= minutes * 60
fun format(value1: Long, value2: Long, resIdSingular1: Int, resIdPlural1: Int, resIdSingular2: Int, resIdPlural2: Int): String {
val builder = StringBuilder()
if (value1 == 0L) {
// omit
} else if (value1 == 1L) {
builder.append(value1)
builder.append(" ")
builder.append(context.getString(resIdSingular1))
} else {
builder.append(value1)
builder.append(" ")
builder.append(context.getString(resIdPlural1))
}
if (value1 > 0L && value2 > 0L) {
builder.append(", ")
}
if (value2 == 0L) {
// omit
} else if (value2 == 1L) {
builder.append(value2)
builder.append(" ")
builder.append(context.getString(resIdSingular2))
} else {
builder.append(value2)
builder.append(" ")
builder.append(context.getString(resIdPlural2))
}
return builder.toString()
}
if (years > 0) {
return format(years, days, R.string.year_ago, R.string.years_ago, R.string.day_ago, R.string.days_ago)
} else if (days > 0) {
return format(days, hours, R.string.day_ago, R.string.days_ago, R.string.hour_ago, R.string.hours_ago)
} else if (hours > 0) {
return format(hours, minutes, R.string.hour_ago, R.string.hours_ago, R.string.minute_ago, R.string.minutes_ago)
} else if (minutes > 0) {
return format(minutes, seconds, R.string.minute_ago, R.string.minute_ago, R.string.second_ago, R.string.seconds_ago)
} else {
return context.getString(R.string.now)
}
}
/**
* 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
@@ -25,10 +86,10 @@ class DateUtils {
return DateFormat.getDateFormat(context).format(Date(unixTime*1000)) + "\n" +
DateFormat.getTimeFormat(context).format(Date(unixTime*1000))
var formattedTime = StringBuilder()
val formattedTime = StringBuilder()
if (hoursAgo > 0) {
formattedTime.append(hoursAgo).append(" ")
if (hoursAgo.toInt() == 1)
if (hoursAgo == 1)
formattedTime.append(context.getString(R.string.hour_ago))
else
formattedTime.append(context.getString(R.string.hours_ago))
@@ -37,12 +98,24 @@ class DateUtils {
if (formattedTime.isNotEmpty())
formattedTime.append(", ")
formattedTime.append(minutesAgo).append(" ")
if (minutesAgo.toInt() == 1)
if (minutesAgo == 1)
formattedTime.append(context.getString(R.string.minute_ago))
else
formattedTime.append(context.getString(R.string.minutes_ago))
}
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)
}
}
}

View File

@@ -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 // 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
@@ -45,11 +62,13 @@ 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()
else ->
item.quantity
})
formatted.append(" ")
formatted.append(
@@ -70,10 +89,9 @@ class NumericUtils (val context: Context) {
* @return min, max, normal
*/
fun getValidEventQuantityRange(lunaEventType: String): Triple<Int, Int, Int>? {
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),

View File

@@ -30,7 +30,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/title"
android:textSize="26dp"
android:textSize="26sp"
android:gravity="center"/>
<ImageView
@@ -47,7 +47,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_margin="10dp"
android:layout_margin="5dp"
android:orientation="horizontal"
android:gravity="center_vertical">
@@ -68,23 +68,17 @@
android:id="@+id/logbooks_add_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textStyle="bold"
android:textColor="@color/accent"
android:textSize="20dp"
android:textSize="20sp"
android:text="+"
android:background="@drawable/button_background"/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/log_an_event"
android:gravity="center_horizontal"/>
<LinearLayout
android:id="@+id/buttons_container"
android:layout_width="match_parent"
@@ -101,10 +95,10 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:layout_margin="10dp"
android:layout_margin="5dp"
android:background="@drawable/button_background"
android:gravity="center_horizontal"
android:textSize="50dp"
android:textSize="50sp"
android:text="@string/event_bottle_type"/>
<TextView
@@ -112,49 +106,50 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="10dp"
android:layout_margin="5dp"
android:background="@drawable/button_background"
android:gravity="center_horizontal"
android:textSize="50dp"
android:textSize="50sp"
android:text="@string/event_food_type"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:id="@+id/layout_nipples">
<TextView
android:id="@+id/button_nipple_left"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="@drawable/button_background"
android:gravity="center_horizontal"
android:textSize="30dp"
android:textSize="30sp"
android:text="🤱⬅️"/>
<TextView
android:id="@+id/button_nipple_both"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="@drawable/button_background"
android:gravity="center_horizontal"
android:textSize="30dp"
android:textSize="30sp"
android:text="🤱↔️"/>
<TextView
android:id="@+id/button_nipple_right"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="@drawable/button_background"
android:gravity="center_horizontal"
android:textSize="30dp"
android:textSize="30sp"
android:text="🤱➡️️"/>
</LinearLayout>
@@ -167,29 +162,29 @@
android:id="@+id/button_change_poo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_margin="5dp"
android:layout_weight="2"
android:background="@drawable/button_background"
android:gravity="center_horizontal"
android:textSize="30dp"
android:textSize="30sp"
android:text="🚼 💩"/>
<TextView
android:id="@+id/button_change_pee"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_margin="5dp"
android:layout_weight="2"
android:background="@drawable/button_background"
android:gravity="center_horizontal"
android:textSize="30dp"
android:textSize="30sp"
android:text="🚼 💧"/>
<ImageView
android:id="@+id/button_more"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="@drawable/button_background"
android:gravity="center_horizontal"
@@ -200,21 +195,31 @@
</LinearLayout>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_indicator"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:indeterminate="true"
app:indicatorColor="@color/accent"
android:visibility="invisible"/>
android:layout_marginTop="5dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/logbook"
android:textColor="@color/accent"
android:textStyle="bold"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/logbook"
android:textColor="@color/accent"
android:textStyle="bold"/>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_indicator"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center"
android:layout_marginHorizontal="10dp"
android:indeterminate="true"
app:indicatorColor="@color/accent"
android:visibility="invisible"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_events"
@@ -247,7 +252,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textSize="30dp"
android:textSize="30sp"
android:textColor="@color/accent"
android:text="@string/no_connection"/>
@@ -264,7 +269,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:drawableLeft="@drawable/ic_sync"
android:drawableStart="@drawable/ic_sync"
android:drawableTint="@color/white"
android:drawablePadding="10dp"
android:text="@string/no_connection_retry"
@@ -275,7 +280,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:drawableLeft="@drawable/ic_settings"
android:drawableStart="@drawable/ic_settings"
android:drawableTint="@color/white"
android:drawablePadding="10dp"
android:text="@string/no_connection_go_to_settings"

View File

@@ -39,26 +39,26 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:text="@string/settings_storage_local_desc"/>
<RadioButton android:id="@+id/settings_data_webdav"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginTop="20dp"
android:text="@string/settings_storage_dav"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:text="@string/settings_storage_dav_desc"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:textStyle="bold"
android:text="@string/settings_storage_dav_url"/>
@@ -66,7 +66,7 @@
android:id="@+id/settings_data_webdav_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:hint="@string/settings_storage_dav_url_hint"
android:inputType="textUri"
android:background="@drawable/textview_background"/>
@@ -75,7 +75,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:textStyle="bold"
android:text="@string/settings_storage_dav_user"/>
@@ -83,7 +83,7 @@
android:id="@+id/settings_data_webdav_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:inputType="textEmailAddress"
android:background="@drawable/textview_background"/>
@@ -91,17 +91,23 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:textStyle="bold"
android:text="@string/settings_storage_dav_pass"/>
<EditText
android:id="@+id/settings_data_webdav_pass"
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:inputType="textPassword"
android:background="@drawable/textview_background"/>
android:layout_marginStart="30dp"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/settings_data_webdav_pass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/textview_background"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_indicator"
@@ -109,36 +115,88 @@
android:layout_height="wrap_content"
android:indeterminate="true"
app:indicatorColor="@color/accent"
android:layout_margin="20dp"
android:layout_marginTop="20dp"
android:visibility="invisible"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/settings_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="20dp"
android:background="@drawable/button_background"
android:textColor="@color/accent"
android:text="@android:string/cancel"/>
<Button
android:id="@+id/settings_save"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/accent"
android:background="@drawable/button_background"
android:text="@android:string/ok"/>
</LinearLayout>
</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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="20dp">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/settings_no_breastfeeding" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switch_no_breastfeeding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_weight="1" />
</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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal">
<Button
android:id="@+id/settings_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="20dp"
android:background="@drawable/button_background"
android:textColor="@color/accent"
android:text="@android:string/cancel"/>
<Button
android:id="@+id/settings_save"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/accent"
android:background="@drawable/button_background"
android:text="@android:string/ok"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -10,18 +11,18 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textSize="60dp"
android:text="@string/event_diaperchange_pee_type"/>
android:textSize="60sp"
android:text="@string/event_diaperchange_pee_type" />
<TextView
android:id="@+id/dialog_event_detail_type_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginTop="12dp"
android:gravity="center_horizontal"
android:textColor="@color/accent"
android:textSize="24dp"
android:text="@string/event_diaperchange_pee_desc"/>
android:textSize="32sp"
android:text="@string/event_diaperchange_pee_desc" />
<TextView
android:id="@+id/dialog_event_detail_type_date"
@@ -30,18 +31,20 @@
android:layout_marginTop="20dp"
android:layout_gravity="center_horizontal"
android:gravity="center_vertical"
android:drawableEnd="@drawable/ic_edit"
android:drawablePadding="10dp"
android:drawableTint="@color/accent"
android:textSize="16sp"
android:textStyle="bold"
android:text="@string/dialog_event_detail_datetime_icon"/>
android:text="@string/dialog_event_detail_datetime_icon"
app:drawableEndCompat="@drawable/ic_edit" />
<TextView
android:id="@+id/dialog_event_detail_type_quantity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Quantity"/>
android:textSize="28sp"
android:text="@string/dialog_event_detail_quantity"/>
<ScrollView
android:layout_width="match_parent"
@@ -53,7 +56,45 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="italic"
android:text="Notes"/>
android:textSize="20sp"
android:text="@string/dialog_event_detail_notes"/>
</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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/dialog_event_previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textSize="12sp"
android:text="" />
<Space
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent" />
<TextView
android:id="@+id/dialog_event_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:textSize="12sp"
android:text="" />
</LinearLayout>
</LinearLayout>

View File

@@ -2,7 +2,7 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:padding="10dp"
android:background="@color/transparent">
<LinearLayout
@@ -14,27 +14,17 @@
android:id="@+id/button_medicine"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:padding="10dp"
android:background="@drawable/dropdown_list_item_background"
style="@style/OverflowMenuText"
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
android:id="@+id/button_note"
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_note"/>
@@ -44,17 +34,27 @@
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"/>
<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
android:id="@+id/button_colic"
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"/>
@@ -64,11 +64,31 @@
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"/>
<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>
</ScrollView>

View File

@@ -17,6 +17,6 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:text="g"/>
</LinearLayout>

View File

@@ -14,6 +14,6 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:text="ml"/>
</LinearLayout>

View File

@@ -11,7 +11,7 @@
android:id="@+id/type"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingStart="10dp"
android:textSize="28sp"
android:lines="1"
android:maxLines="1"
@@ -25,7 +25,7 @@
android:ellipsize="end"
android:maxLines="2"
android:textColor="@color/accent"
android:text="Description"/>
android:text="@string/row_luna_event_description"/>
<TextView
android:id="@+id/quantity"
@@ -35,7 +35,7 @@
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:gravity="center_horizontal"
android:text="Qty"/>
android:text="@string/row_luna_event_quantity"/>
<TextView
android:id="@+id/time"
@@ -46,6 +46,6 @@
android:textStyle="bold"
android:ellipsize="end"
android:maxLines="2"
android:text="Time"/>
android:text="@string/row_luna_event_time"/>
</LinearLayout>

View File

@@ -31,7 +31,7 @@
android:maxLines="2"
android:gravity="center_horizontal"
android:textColor="@color/accent"
android:text="Description"/>
android:text="@string/row_luna_event_description"/>
<TextView
android:id="@+id/quantity"
@@ -41,7 +41,7 @@
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:gravity="center_horizontal"
android:text="Qty"/>
android:text="@string/dialog_event_detail_quantity"/>
<TextView
android:id="@+id/time"
@@ -52,7 +52,7 @@
android:textStyle="bold"
android:ellipsize="end"
android:maxLines="2"
android:text="Time"/>
android:text="@string/row_luna_event_time"/>
</LinearLayout>

View File

@@ -0,0 +1,97 @@
<resources>
<string name="app_name">LunaTracker</string>
<string name="title">🌜 LunaTracker 🌛</string>
<string name="logbook">Ereignisprotokoll</string>
<string name="log_bottle_dialog_title">Fläschchen</string>
<string name="log_bottle_dialog_description">Trinkmenge eingeben</string>
<string name="log_weight_dialog_title">Gewicht</string>
<string name="log_weight_dialog_description">Gewicht eingeben</string>
<string name="log_temperature_dialog_title">Temperatur</string>
<string name="log_temperature_dialog_description">Temperatur eingeben</string>
<string name="event_bottle_desc">Fläschchen</string>
<string name="event_food_desc">Essen</string>
<string name="event_scale_desc">Gewicht</string>
<string name="event_breastfeeding_left_desc">Stillen (links)</string>
<string name="event_breastfeeding_both_desc">Stillen</string>
<string name="event_breastfeeding_right_desc">Stillen (rechts)</string>
<string name="event_diaperchange_poo_desc">Windelwechsel (Stuhl)</string>
<string name="event_diaperchange_pee_desc">Windelwechsel (Urin)</string>
<string name="event_medicine_desc">Medikament</string>
<string name="event_enema_desc">Einlauf</string>
<string name="event_note_desc">Notiz</string>
<string name="event_temperature_desc">Temperatur</string>
<string name="event_colic_desc">Blähungskolik</string>
<string name="event_unknown_desc"></string>
<string name="overflow_event_scale">⚖️ Gewicht</string>
<string name="overflow_event_medicine">💊 Medikament</string>
<string name="overflow_event_enema">🪠 Einlauf</string>
<string name="overflow_event_note">📝 Notiz</string>
<string name="overflow_event_temperature">🌡️ Temperatur</string>
<string name="overflow_event_colic">💨 Blähungskolik</string>
<string name="toast_event_added">Ereignis gespeichert</string>
<string name="toast_logbook_saved">Logbuch gespeichert</string>
<string name="toast_event_add_error">Ereignis konnte nicht protokolliert werden</string>
<string name="toast_integer_error">Ungültiger Wert. Bitte eine Ganzzahl eingeben.</string>
<string name="now">jetzt</string>
<string name="hour_ago">Std.</string>
<string name="hours_ago">Std.</string>
<string name="minute_ago">Min.</string>
<string name="minutes_ago">Min.</string>
<string name="no_connection">Keine Verbindung</string>
<string name="no_connection_explain">WebDAV-Dienst nicht erreichbar</string>
<string name="no_connection_go_to_settings">Einstellungen</string>
<string name="no_connection_retry">Erneut versuchen</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_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_dav">Auf einem WebDAV-Server</string>
<string name="settings_storage_dav_desc">Du kannst jeden WebDAV-Dienst (z.B. Nextcloud, ownCloud, Dropbox, …) verwenden, um die Daten zu speichern. Auf diese Weise kannst du sie zwischen mehreren Geräten synchronisieren (z.B. dem vom Vater, der Mutter, der Großmutter, …). Dazu benötigst du die WebDAV-URL. Du findest diese in der Dokumentation deines Anbieters (z.B. bei Nextcloud im Bereich Einstellungen der Dateien-App).</string>
<string name="settings_storage_dav_url">WebDAV URL</string>
<string name="settings_storage_dav_url_hint">https://</string>
<string name="settings_storage_dav_user">Benutzername</string>
<string name="settings_storage_dav_pass">Passwort</string>
<string name="settings_network_error">Server nicht erreichbar: </string>
<string name="settings_webdav_error_denied">Falscher WebDAV-Benutzer oder falsches Passwort</string>
<string name="settings_webdav_error_server_offline">WebDAV-Server ist derzeit nicht verfügbar</string>
<string name="settings_webdav_error_generic">Fehler beim Zugriff auf WebDAV:</string>
<string name="settings_webdav_creation_error_generic">Eine Datei konnte auf dem WebDAV-Server nicht gespeichert werden:</string>
<string name="settings_webdav_creation_ok">Erfolgreich mit dem WebDAV-Server verbunden</string>
<string name="settings_json_error">Es befindet sich eine Speicherdatei auf dem Server, aber sie ist beschädigt oder unlesbar. Bitte lösche die Datei.</string>
<string name="settings_generic_error">Fehler: </string>
<string name="settings_webdav_upload_error">Fehler beim Hochladen des lokalen Logbuchs %1$s zu WebDAV: %2$s</string>
<string name="trim_logbook_dialog_title">Dein Logbuch ist ziemlich groß!</string>
<string name="trim_logbook_dialog_message_local">Deine Logbuchdatei wird sehr groß. Wir empfehlen, die ältesten Einträge zu entfernen, um Abstürze zu vermeiden.</string>
<string name="trim_logbook_dialog_message_dav">Deine Logbuchdatei wird sehr groß. Wir empfehlen, die ältesten Einträge (durch Entfernen) zu bereinigen, um Abstürze zu vermeiden. Wenn du alle Einträge behalten möchtest, sichere bitte die Datei "lunatracker_logbook.json" auf dem WebDAVServer oder benenne sie um, um ein neues Logbuch zu beginnen und das alte zu behalten.</string>
<string name="trim_logbook_dialog_button_ok">Jetzt bereinigen</string>
<string name="trim_logbook_dialog_button_cancel">Später erinnern</string>
<string name="log_notes_dialog_description">Notizen:</string>
<string name="log_medicine_dialog_description">Medikamentenname, Menge, Art, Notizen, …:</string>
<string name="log_notes_dialog_qty_hint">Menge (optional)</string>
<string name="log_notes_dialog_note_hint">Notiz eingeben</string>
<string name="dialog_event_detail_title">Ereignisdetails</string>
<string name="dialog_event_detail_close_button">OK</string>
<string name="dialog_event_detail_delete_button">Löschen</string>
<string name="dialog_add_logbook_title">Logbuch hinzufügen</string>
<string name="dialog_add_logbook_logbookname">👶 Logbuchname</string>
<string name="dialog_add_logbook_message">Gib einen Namen ein, um dieses Logbuch zu bezeichnen. Dieser Name erscheint oben auf dem Bildschirm und wird, falls du WebDAV verwendest, auch im Dateinamen der gespeicherten Datei enthalten sein.</string>
<string name="dialog_add_logbook_message_intro">Willkommen! Um diese App zu benutzen, musst du mindestens ein Logbuch erstellen. Am besten benennst du es nach dem Namen deines Kindes.</string>
<string name="default_logbook_name">👶 Mein erstes Logbuch</string>
<string name="logbook_created">Neues Logbuch erstellt: </string>
</resources>

View File

@@ -0,0 +1,96 @@
<resources>
<string name="app_name">LunaTracker</string>
<string name="title">🌜 LunaTracker 🌛</string>
<string name="logbook">Entrées enregistrées</string>
<string name="log_bottle_dialog_title">Biberon</string>
<string name="log_bottle_dialog_description">Renseignez la quantité contenue dans le biberon</string>
<string name="log_weight_dialog_title">Poids</string>
<string name="log_weight_dialog_description">Renseignez le poids</string>
<string name="log_temperature_dialog_title">Température</string>
<string name="log_temperature_dialog_description">Renseignez la Température</string>
<string name="event_bottle_desc">Biberon</string>
<string name="event_food_desc">Nourriture</string>
<string name="event_scale_desc">Poids</string>
<string name="event_breastfeeding_left_desc">Allaitement (sein gauche)</string>
<string name="event_breastfeeding_both_desc">Allaitement</string>
<string name="event_breastfeeding_right_desc">Allaitement (sein droit)</string>
<string name="event_diaperchange_poo_desc">Change (selle)</string>
<string name="event_diaperchange_pee_desc">Change (urine)</string>
<string name="event_medicine_desc">Médicament</string>
<string name="event_enema_desc">Lavement</string>
<string name="event_note_desc">Note</string>
<string name="event_temperature_desc">Température</string>
<string name="event_colic_desc">Colique gazeuse</string>
<string name="event_unknown_desc"></string>
<string name="overflow_event_scale">⚖️ Poids</string>
<string name="overflow_event_medicine">💊 Médicament</string>
<string name="overflow_event_enema">🪠 Lavement</string>
<string name="overflow_event_note">📝 Note</string>
<string name="overflow_event_temperature">🌡️ Température</string>
<string name="overflow_event_colic">💨 Colique gazeuse</string>
<string name="toast_event_added">Entrée ajoutée</string>
<string name="toast_logbook_saved">Journal ajouté</string>
<string name="toast_event_add_error">Impossible d\'enregistrer cette entrée</string>
<string name="toast_integer_error">Valeur invalide. Renseignez un nombre.</string>
<string name="now">maintenant </string>
<string name="hour_ago">heure</string>
<string name="hours_ago">heures</string>
<string name="minute_ago">min</string>
<string name="minutes_ago">mins</string>
<string name="no_connection">Pas de connexion internet</string>
<string name="no_connection_explain">Impossible de se connecter au service WebDAV</string>
<string name="no_connection_go_to_settings">Paramètres</string>
<string name="no_connection_retry">Réessayer</string>
<string name="settings_title">Paramètres</string>
<string name="settings_storage">Choisir le lieu de stockage des données</string>
<string name="settings_storage_local">Sur l\'appareil\'</string>
<string name="settings_storage_local_desc">La solution la plus respectueuse de la vie privée : les données ne quittent pas l\'appareil</string>
<string name="settings_storage_dav">Sur un serveur WebDAV</string>
<string name="settings_storage_dav_desc">Vous pouvez utiliser n\'importe quel service WebDAV (par exemple Nextcloud, Owncloud, Dropbox, Box...) pour sauvegarder les données. De cette façon, vous pouvez les synchroniser entre plusieurs appareils (Ex : celui de papa, celui de maman, celui de grand-mère...). Vous aurez besoin de l\'adresse WebDAV, que vous trouverez dans la documentation de votre fournisseur (par exemple, pour Nextcloud web, vous la trouverez dans les paramètres de l\'application Fichiers).</string>
<string name="settings_storage_dav_url">URL WebDAV</string>
<string name="settings_storage_dav_url_hint">https://</string>
<string name="settings_storage_dav_user">Nom d\'utilisateur</string>
<string name="settings_storage_dav_pass">Mot de passe</string>
<string name="settings_network_error">Impossible d\'accéder au serveur: </string>
<string name="settings_webdav_error_denied">Nom d\'utilisateur ou mot de passe incorrect pour le service WebDAV</string>
<string name="settings_webdav_error_server_offline">Impossible d\'accéder au serveur WebDAV</string>
<string name="settings_webdav_error_generic">Une erreur est survenue en essayant d\'accéder au serveur WebDAV:</string>
<string name="settings_webdav_creation_error_generic">Impossible de sauvegarder un fichier sur le serveur WebDAV:</string>
<string name="settings_webdav_creation_ok">Connexion réussie avec le serveur WebDAV</string>
<string name="settings_json_error">Il y a un fichier sur le serveur WebDAV, toutefois il est corronpu ou illisible. Merci de le supprimer et réessayer</string>
<string name="settings_generic_error">Erreur: </string>
<string name="settings_webdav_upload_error">Une erreur est survenue en téléversant le journal local %1$s sur %2$s</string>
<string name="trim_logbook_dialog_title">Votre journal grossit !</string>
<string name="trim_logbook_dialog_message_local">Le fichier de votre journal a beaucoup grossi. Nous recommandons de supprimer les entrées les plus vieilles pour éviter des crashs de l\'application.</string>
<string name="trim_logbook_dialog_message_dav">Le fichier de votre journal a beaucoup grossi. Nous recommandons de supprimer les entrées les plus vieilles pour éviter des crashs de l\'application. Si vous voulez garder un historique, vous pouvez sauvegarder le fichier "lunatracker_logbook.json" ou le renommer pour créer un nouveau journal.</string>
<string name="trim_logbook_dialog_button_ok">Supprimer les vieilles entrées maintenant</string>
<string name="trim_logbook_dialog_button_cancel">Me rappeller plus tard</string>
<string name="log_notes_dialog_description">Notes:</string>
<string name="log_medicine_dialog_description">nom du médicament, quantité, type, notes …:</string>
<string name="log_notes_dialog_qty_hint">Quantité (ou vide)</string>
<string name="log_notes_dialog_note_hint">Notes ...</string>
<string name="dialog_event_detail_title">Détails de l\'entrée</string>
<string name="dialog_event_detail_close_button">OK</string>
<string name="dialog_event_detail_delete_button">Supprimer</string>
<string name="dialog_add_logbook_title">Ajouter un journal</string>
<string name="dialog_add_logbook_logbookname">👶 Nom du journal</string>
<string name="dialog_add_logbook_message">Renseignez un nom pour identifier ce journal. Ce nom apparaîtra en haut de l\'écran et, si vous utilisez WebDAV, il figurera également dans le nom du fichier enreigstré.</string>
<string name="dialog_add_logbook_message_intro">Bienvenue ! Pour utiliser cette application, vous devez créer au moins un carnet de bord. Vous voudrez probablement l\'appeler avec le nom de votre enfant.</string>
<string name="default_logbook_name">👶 Mon premier carnet de bord</string>
<string name="logbook_created">Journal ajouté: </string>
</resources>

View File

@@ -1,7 +1,6 @@
<resources>
<string name="app_name">LunaTracker</string>
<string name="title">🌜 LunaTracker 🌛</string>
<string name="log_an_event">Logga un evento:</string>
<string name="logbook">Diario di bordo</string>
<string name="log_bottle_dialog_title">Biberon</string>

View File

@@ -1,7 +1,6 @@
<resources>
<string name="app_name">LunaTracker</string>
<string name="title">🌜 LunaTracker 🌛</string>
<string name="log_an_event">Log an event:</string>
<string name="logbook">Logged events</string>
<string name="log_bottle_dialog_title">Baby bottle</string>
@@ -26,6 +25,8 @@
<string name="event_note_type" translatable="false">📝</string>
<string name="event_temperature_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_bottle_desc">Baby bottle</string>
@@ -41,6 +42,8 @@
<string name="event_note_desc">Note</string>
<string name="event_temperature_desc">Temperature</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="overflow_event_scale">⚖️ Weight</string>
@@ -49,6 +52,8 @@
<string name="overflow_event_note">📝 Note</string>
<string name="overflow_event_temperature">🌡️ Temperature</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_logbook_saved">Logbook saved</string>
@@ -56,10 +61,16 @@
<string name="toast_integer_error">Invalid value. Insert an integer.</string>
<string name="now">now</string>
<string name="hour_ago">hour</string>
<string name="hours_ago">hours</string>
<string name="second_ago">sec</string>
<string name="seconds_ago">secs</string>
<string name="minute_ago">min</string>
<string name="minutes_ago">mins</string>
<string name="hour_ago">hour</string>
<string name="hours_ago">hours</string>
<string name="day_ago">day</string>
<string name="days_ago">days</string>
<string name="year_ago">year</string>
<string name="years_ago">years</string>
<string name="no_connection">No connection</string>
<string name="no_connection_explain">Unable to reach WebDAV service</string>
@@ -67,12 +78,14 @@
<string name="no_connection_retry">Retry</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_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_dav">On a WebDAV server</string>
<string name="settings_storage_dav_desc">You can use any WebDAV service (i.e. Nextcloud, Owncloud, Dropbox, Box…) to save the data. In this way you can syncronize it between more devices, i.e. the dad\'s, the mom\'s, the grandma\'s… You will need the WebDAV url, you can find it in your provider\'s documentation (i.e. in Nextcloud web is in the Files app settings)</string>
<string name="settings_storage_dav_url">WebDAV url</string>
<string name="settings_storage_dav_desc">You can use any WebDAV service (i.e. Nextcloud, Owncloud, Dropbox, Box…) to save the data. In this way, you can syncronize it between more devices, i.e., the dad\'s, the mom\'s, the grandma\'s… You will need the WebDAV URL, you can find it in your provider\'s documentation (i.e. in Nextcloud web is in the Files app settings)</string>
<string name="settings_storage_dav_url">WebDAV URL</string>
<string name="settings_storage_dav_url_hint">https://</string>
<string name="settings_storage_dav_user">Username</string>
<string name="settings_storage_dav_pass">Password</string>
@@ -81,14 +94,16 @@
<string name="settings_webdav_error_server_offline">WebDAV server is currently unavailable</string>
<string name="settings_webdav_error_generic">Error while trying to access WebDAV:</string>
<string name="settings_webdav_creation_error_generic">Unable to save a file on the WebDAV server:</string>
<string name="settings_webdav_creation_ok">Successfully connected with WebDAV server</string>
<string name="settings_json_error">There\'s a save file on the server, but is corrupted or unreadable. Please delete it </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_generic_error">Error: </string>
<string name="settings_webdav_upload_error">Error while uploading local logbook %1$s to webdav: %2$s</string>
<string name="trim_logbook_dialog_title">Your logbook is pretty big!</string>
<string name="trim_logbook_dialog_message_local">Your logbook file is growing a lot. We suggest trimming the oldest events to avoid crashes.</string>
<string name="trim_logbook_dialog_message_dav">Your logbook file is growing a lot. We suggest trimming the oldest events to avoid crashes. If you want to preserve all the events, please backup the "lunatracker_logbook.json" file on the WebDAV server or rename it to start a new logbook keeping the old one.</string>
<string name="trim_logbook_dialog_message_dav">Your logbook file is growing a lot. We suggest trimming the oldest events to avoid crashes. If you want to preserve all the events, please back up the "lunatracker_logbook.json" file on the WebDAV server or rename it to start a new logbook, keeping the old one.</string>
<string name="trim_logbook_dialog_button_ok">Trim it now</string>
<string name="trim_logbook_dialog_button_cancel">Remind me later</string>
@@ -106,15 +121,22 @@
<string name="measurement_unit_temperature_base_imperial" translatable="false">°F</string>
<string name="measurement_unit_temperature_base_metric" translatable="false">°C</string>
<string name="row_luna_event_description">Description</string>
<string name="row_luna_event_quantity">Qty</string>
<string name="row_luna_event_time">Time</string>
<string name="dialog_event_detail_title">Event detail</string>
<string name="dialog_event_detail_datetime_icon" translatable="false">🕒 %s1</string>
<string name="dialog_event_detail_datetime_icon" translatable="false">🕒 %s</string>
<string name="dialog_event_detail_close_button">OK</string>
<string name="dialog_event_detail_delete_button">Delete</string>
<string name="dialog_event_detail_quantity">Quantity</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_logbookname">👶 Logbook name</string>
<string name="dialog_add_logbook_message">Write a name to identify this logbook. This name will appear on top of the screen and, if you use WebDAV, will be in the save file name as well.</string>
<string name="dialog_add_logbook_message_intro">Welcome! To use this app you need to create at least one logbook. You would probably want to call it with your child\'s name.</string>
<string name="dialog_add_logbook_message_intro">Welcome! To use this app, you need to create at least one logbook. You would probably want to call it with your child\'s name.</string>
<string name="default_logbook_name">👶 My first logbook</string>
<string name="logbook_created">New logbook created: </string>

View File

@@ -1,11 +1,12 @@
Parenting can be tough. You get home from the hospital, exhausted, with this little fragile unknown thingy that has no user manual and a single way to let you know something's not ok: crying.
You have to react fast, even if it's 4 AM and you have no idea why is crying.
You have to react fast, even if it's 4 AM and you have no idea of the reason behind the cries.
This app is meant to log all the relevant events (diaper change, breastfeeding, baby bottle feeding...), so you can always remember when has been done last time. It supports syncing the data between different devices (using your WebDAV server for the best privacy) so that when the baby needs attentions, the dad can step in leaving the mom sleeping peacefully.
This app is meant to log all the relevant events (diaper change, breastfeeding, baby bottle feeding, etc.), so you can always remember what was done last time and when. It supports syncing the data between different devices (using your WebDAV server for the best privacy) so that when the baby needs attention, the dad can step in, leaving the mom sleeping peacefully.
Dedicated to my daughter Luna.
A HUGE thanks to all our contributors. See https://git.ichibi.eu/penguin86/luna-tracker/src/branch/master/README.md
NOTE: the content on this app is for informational or educational purposes only and does not substitute professional medical advice or consultations with healthcare professionals.
NOTE: The content on this app is for informational or educational purposes only and does not substitute professional medical advice or consultations with healthcare professionals.
Feature graphic ("Baby and baby milk bottle. Baby feeding."): © Vyacheslav Argenberg / http://www.vascoplanet.com/, CC BY 4.0 <https://creativecommons.org/licenses/by/4.0>, via Wikimedia Commons

View File

@@ -0,0 +1 @@
Première version alpha : carnet de bord, synchronisation.

View File

@@ -0,0 +1,2 @@
Prise en charge de plusieurs enfants
Correction de l'interface sur les appareils avec une grande taille de police.

View File

@@ -0,0 +1,15 @@
Être parent n'est pas toujours facile. Vous rentrez de la maternité, épuisé, avec cette petite créature inconnue et fragile qui n'a pas de mode d'emploi et une seule façon de vous faire savoir que quelque chose ne va pas : les pleurs.
Vous devez réagir rapidement, même s'il est 4 heures du matin et que vous n'avez aucune idée de la raison de ses pleurs.
Cette application est destinée à enregistrer tous les événements importants (changement de couche, allaitement, biberon...), afin que vous puissiez toujours vous souvenir de ce qui a été fait la dernière fois (et quand).
Elle permet de synchroniser les données entre différents appareils (en utilisant votre serveur WebDAV pour une meilleure confidentialité). Ainsi, lorsque le bébé a besoin d'attention, le papa peut intervenir et laisser la maman dormir tranquillement.
Dédié à ma fille Luna.
Un grand merci à tous nos contributeurs. Voir https://git.ichibi.eu/penguin86/luna-tracker/src/branch/master/README.md
REMARQUE : le contenu de cette application est fourni à titre informatif ou éducatif uniquement et ne remplace pas les conseils médicaux professionnels ou les consultations avec des professionnels de la santé.
Crédit image ("Baby and baby milk bottle. Baby feeding."): © Vyacheslav Argenberg / http://www.vascoplanet.com/, CC BY 4.0 <https://creativecommons.org/licenses/by/4.0>, via Wikimedia Commons

View File

@@ -0,0 +1 @@
LunaTracker est une application de suivi des nouveau-nés.

View File

@@ -0,0 +1 @@
🌜 LunaTracker 🌛

View File

@@ -6,6 +6,7 @@ Quest'app è pensata per memorizzare tutti gli eventi del bambino (cambio di pan
Dedicato a mia figlia Luna.
Un grandissimo ringraziamento a tutti i contributors! Vedi https://git.ichibi.eu/penguin86/luna-tracker/src/branch/master/README.md
NOTA: il contenuto di quest'app ha solo scopo informativo o didattico e non sostituisce il consulto medico professionale o le consulenze con operatori sanitari.
Feature graphic ("Baby and baby milk bottle. Baby feeding."): © Vyacheslav Argenberg / http://www.vascoplanet.com/, CC BY 4.0 <https://creativecommons.org/licenses/by/4.0>, via Wikimedia Commons

View File

@@ -1,5 +1,5 @@
[versions]
agp = "8.7.2"
agp = "8.13.0"
kotlin = "2.0.0"
coreKtx = "1.10.1"
junit = "4.13.2"

View File

@@ -1,6 +1,6 @@
#Sat Nov 02 10:58:51 CET 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists