Compare commits
14 Commits
v0.1
...
3cbc4139ae
Author | SHA1 | Date | |
---|---|---|---|
3cbc4139ae | |||
f31c68193a | |||
de5b1d4ead | |||
de75ed584b | |||
a4b43ffb10 | |||
37b68450da | |||
841da9e28e | |||
876c68ce81 | |||
35a3b42a32 | |||
d5cb30cdd7 | |||
57f997e6f1 | |||
7b44d0c610 | |||
abee1be72c | |||
653c57e6e0 |
@ -8,3 +8,5 @@ You have to react fast, event if it's 4 AM and you have no idea why is crying.
|
|||||||
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...), 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.
|
||||||
|
|
||||||
Dedicated to my daughter Luna.
|
Dedicated to my daughter Luna.
|
||||||
|
|
||||||
|

|
||||||
|
@ -10,10 +10,10 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "it.danieleverducci.lunatracker"
|
applicationId = "it.danieleverducci.lunatracker"
|
||||||
minSdk = 29
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "0.2"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,12 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.NumberPicker
|
import android.widget.NumberPicker
|
||||||
|
import android.widget.PopupWindow
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
@ -15,11 +18,9 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
import com.thegrizzlylabs.sardineandroid.impl.SardineException
|
import com.thegrizzlylabs.sardineandroid.impl.SardineException
|
||||||
import it.danieleverducci.lunatracker.SettingsActivity
|
|
||||||
import it.danieleverducci.lunatracker.adapters.LunaEventRecyclerAdapter
|
import it.danieleverducci.lunatracker.adapters.LunaEventRecyclerAdapter
|
||||||
import it.danieleverducci.lunatracker.entities.Logbook
|
import it.danieleverducci.lunatracker.entities.Logbook
|
||||||
import it.danieleverducci.lunatracker.entities.LunaEvent
|
import it.danieleverducci.lunatracker.entities.LunaEvent
|
||||||
import it.danieleverducci.lunatracker.entities.LunaEventType
|
|
||||||
import it.danieleverducci.lunatracker.repository.FileLogbookRepository
|
import it.danieleverducci.lunatracker.repository.FileLogbookRepository
|
||||||
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
|
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
|
||||||
import it.danieleverducci.lunatracker.repository.LogbookLoadedListener
|
import it.danieleverducci.lunatracker.repository.LogbookLoadedListener
|
||||||
@ -29,10 +30,15 @@ import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
|
|||||||
import kotlinx.coroutines.Runnable
|
import kotlinx.coroutines.Runnable
|
||||||
import okio.IOException
|
import okio.IOException
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
|
import utils.DateUtils
|
||||||
|
import utils.NumericUtils
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "MainActivity"
|
val TAG = "MainActivity"
|
||||||
|
val UPDATE_EVERY_SECS: Long = 30
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var logbook: Logbook
|
lateinit var logbook: Logbook
|
||||||
@ -53,6 +59,11 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
handler = Handler(mainLooper)
|
handler = Handler(mainLooper)
|
||||||
adapter = LunaEventRecyclerAdapter(this)
|
adapter = LunaEventRecyclerAdapter(this)
|
||||||
|
adapter.onItemClickListener = object: LunaEventRecyclerAdapter.OnItemClickListener{
|
||||||
|
override fun onItemClick(event: LunaEvent) {
|
||||||
|
showEventDetailDialog(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show view
|
// Show view
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
@ -68,29 +79,33 @@ class MainActivity : AppCompatActivity() {
|
|||||||
findViewById<View>(R.id.button_scale).setOnClickListener { askWeightValue() }
|
findViewById<View>(R.id.button_scale).setOnClickListener { askWeightValue() }
|
||||||
findViewById<View>(R.id.button_nipple_left).setOnClickListener { logEvent(
|
findViewById<View>(R.id.button_nipple_left).setOnClickListener { logEvent(
|
||||||
LunaEvent(
|
LunaEvent(
|
||||||
LunaEventType.BREASTFEEDING_LEFT_NIPPLE
|
LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE
|
||||||
)
|
)
|
||||||
) }
|
) }
|
||||||
findViewById<View>(R.id.button_nipple_both).setOnClickListener { logEvent(
|
findViewById<View>(R.id.button_nipple_both).setOnClickListener { logEvent(
|
||||||
LunaEvent(
|
LunaEvent(
|
||||||
LunaEventType.BREASTFEEDING_BOTH_NIPPLE
|
LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE
|
||||||
)
|
)
|
||||||
) }
|
) }
|
||||||
findViewById<View>(R.id.button_nipple_right).setOnClickListener { logEvent(
|
findViewById<View>(R.id.button_nipple_right).setOnClickListener { logEvent(
|
||||||
LunaEvent(
|
LunaEvent(
|
||||||
LunaEventType.BREASTFEEDING_RIGHT_NIPPLE
|
LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE
|
||||||
)
|
)
|
||||||
) }
|
) }
|
||||||
findViewById<View>(R.id.button_change_poo).setOnClickListener { logEvent(
|
findViewById<View>(R.id.button_change_poo).setOnClickListener { logEvent(
|
||||||
LunaEvent(
|
LunaEvent(
|
||||||
LunaEventType.DIAPERCHANGE_POO
|
LunaEvent.TYPE_DIAPERCHANGE_POO
|
||||||
)
|
)
|
||||||
) }
|
) }
|
||||||
findViewById<View>(R.id.button_change_pee).setOnClickListener { logEvent(
|
findViewById<View>(R.id.button_change_pee).setOnClickListener { logEvent(
|
||||||
LunaEvent(
|
LunaEvent(
|
||||||
LunaEventType.DIAPERCHANGE_PEE
|
LunaEvent.TYPE_DIAPERCHANGE_PEE
|
||||||
)
|
)
|
||||||
) }
|
) }
|
||||||
|
val moreButton = findViewById<View>(R.id.button_more)
|
||||||
|
moreButton.setOnClickListener {
|
||||||
|
showOverflowPopupWindow(moreButton)
|
||||||
|
}
|
||||||
findViewById<View>(R.id.button_no_connection_settings).setOnClickListener({
|
findViewById<View>(R.id.button_no_connection_settings).setOnClickListener({
|
||||||
showSettings()
|
showSettings()
|
||||||
})
|
})
|
||||||
@ -140,7 +155,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Reload data
|
// Reload data
|
||||||
loadLogbook()
|
loadLogbook()
|
||||||
handler.postDelayed(updateListRunnable, 1000*30)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
@ -164,7 +178,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
numberPicker.wrapSelectorWheel = false
|
numberPicker.wrapSelectorWheel = false
|
||||||
numberPicker.value = localSettings.loadBabyBottleContent()
|
numberPicker.value = localSettings.loadBabyBottleContent()
|
||||||
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
|
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
|
||||||
logEvent(LunaEvent(LunaEventType.BABY_BOTTLE, numberPicker.value * 10))
|
logEvent(LunaEvent(LunaEvent.TYPE_BABY_BOTTLE, numberPicker.value * 10))
|
||||||
localSettings.saveBabyBottleContent(numberPicker.value)
|
localSettings.saveBabyBottleContent(numberPicker.value)
|
||||||
}
|
}
|
||||||
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() }
|
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() }
|
||||||
@ -179,11 +193,11 @@ class MainActivity : AppCompatActivity() {
|
|||||||
d.setTitle(R.string.log_weight_dialog_title)
|
d.setTitle(R.string.log_weight_dialog_title)
|
||||||
d.setMessage(R.string.log_weight_dialog_description)
|
d.setMessage(R.string.log_weight_dialog_description)
|
||||||
d.setView(dialogView)
|
d.setView(dialogView)
|
||||||
val weightET = dialogView.findViewById<TextView>(R.id.dialog_number_edittext)
|
val weightET = dialogView.findViewById<EditText>(R.id.dialog_number_edittext)
|
||||||
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
|
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
|
||||||
val weight = weightET.text.toString().toIntOrNull()
|
val weight = weightET.text.toString().toIntOrNull()
|
||||||
if (weight != null)
|
if (weight != null)
|
||||||
logEvent(LunaEvent(LunaEventType.WEIGHT, weight))
|
logEvent(LunaEvent(LunaEvent.TYPE_WEIGHT, weight))
|
||||||
else
|
else
|
||||||
Toast.makeText(this, R.string.toast_integer_error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.toast_integer_error, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
@ -192,10 +206,86 @@ class MainActivity : AppCompatActivity() {
|
|||||||
alertDialog.show()
|
alertDialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun askNotes(lunaEvent: LunaEvent) {
|
||||||
|
val d = AlertDialog.Builder(this)
|
||||||
|
val dialogView = layoutInflater.inflate(R.layout.dialog_notes, null)
|
||||||
|
d.setTitle(lunaEvent.getTypeDescription(this))
|
||||||
|
d.setMessage(
|
||||||
|
when (lunaEvent.type){
|
||||||
|
LunaEvent.TYPE_MEDICINE -> R.string.log_medicine_dialog_description
|
||||||
|
else -> R.string.log_notes_dialog_description
|
||||||
|
}
|
||||||
|
)
|
||||||
|
d.setView(dialogView)
|
||||||
|
val notesET = dialogView.findViewById<EditText>(R.id.notes_edittext)
|
||||||
|
val qtyET = dialogView.findViewById<EditText>(R.id.notes_qty_edittext)
|
||||||
|
if (lunaEvent.type == LunaEvent.TYPE_NOTE || lunaEvent.type == LunaEvent.TYPE_CUSTOM)
|
||||||
|
qtyET.visibility = View.GONE
|
||||||
|
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
|
||||||
|
val qtyStr = qtyET.text.toString()
|
||||||
|
if (qtyStr.isNotEmpty()) {
|
||||||
|
val qty = qtyStr.toIntOrNull()
|
||||||
|
if (qty == null) {
|
||||||
|
Toast.makeText(this, R.string.toast_integer_error, Toast.LENGTH_SHORT).show()
|
||||||
|
return@setPositiveButton
|
||||||
|
}
|
||||||
|
lunaEvent.quantity = qty
|
||||||
|
}
|
||||||
|
val notes = notesET.text.toString()
|
||||||
|
lunaEvent.notes = notes
|
||||||
|
logEvent(lunaEvent)
|
||||||
|
}
|
||||||
|
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() }
|
||||||
|
val alertDialog = d.create()
|
||||||
|
alertDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun askToTrimLogbook() {
|
||||||
|
val d = AlertDialog.Builder(this)
|
||||||
|
d.setTitle(R.string.trim_logbook_dialog_title)
|
||||||
|
d.setMessage(
|
||||||
|
when (LocalSettingsRepository(this).loadDataRepository()) {
|
||||||
|
LocalSettingsRepository.DATA_REPO.WEBDAV -> R.string.trim_logbook_dialog_message_dav
|
||||||
|
else -> R.string.trim_logbook_dialog_message_local
|
||||||
|
}
|
||||||
|
)
|
||||||
|
d.setPositiveButton(R.string.trim_logbook_dialog_button_ok) { dialogInterface, i ->
|
||||||
|
logbook.trim()
|
||||||
|
saveLogbook()
|
||||||
|
}
|
||||||
|
d.setNegativeButton(R.string.trim_logbook_dialog_button_cancel) { dialogInterface, i ->
|
||||||
|
dialogInterface.dismiss()
|
||||||
|
}
|
||||||
|
val alertDialog = d.create()
|
||||||
|
alertDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showEventDetailDialog(event: LunaEvent) {
|
||||||
|
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_date).setText(dateFormat.format(Date(event.time * 1000)))
|
||||||
|
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_quantity).setText(
|
||||||
|
NumericUtils(this).formatEventQuantity(event)
|
||||||
|
)
|
||||||
|
dialogView.findViewById<TextView>(R.id.dialog_event_detail_type_notes).setText(event.notes)
|
||||||
|
d.setView(dialogView)
|
||||||
|
d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> dialogInterface.dismiss() }
|
||||||
|
val alertDialog = d.create()
|
||||||
|
alertDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
fun loadLogbook() {
|
fun loadLogbook() {
|
||||||
if (savingEvent)
|
if (savingEvent)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
// Reset time counter
|
||||||
|
handler.removeCallbacks(updateListRunnable)
|
||||||
|
handler.postDelayed(updateListRunnable, UPDATE_EVERY_SECS*1000)
|
||||||
|
|
||||||
// Load data
|
// Load data
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
logbookRepo?.loadLogbook(this, object: LogbookLoadedListener{
|
logbookRepo?.loadLogbook(this, object: LogbookLoadedListener{
|
||||||
@ -261,13 +351,33 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
logbook.logs.add(0, event)
|
logbook.logs.add(0, event)
|
||||||
|
saveLogbook(event)
|
||||||
|
|
||||||
|
// Check logbook size to avoid OOM errors
|
||||||
|
if (logbook.isTooBig()) {
|
||||||
|
askToTrimLogbook()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the logbook. If saving while adding an event, please specify the event so in case
|
||||||
|
* of error can be removed from the list.
|
||||||
|
*/
|
||||||
|
fun saveLogbook(lastEventAdded: LunaEvent? = null) {
|
||||||
logbookRepo?.saveLogbook(this, logbook, object: LogbookSavedListener{
|
logbookRepo?.saveLogbook(this, logbook, object: LogbookSavedListener{
|
||||||
override fun onLogbookSaved() {
|
override fun onLogbookSaved() {
|
||||||
Log.d(TAG, "Logbook saved")
|
Log.d(TAG, "Logbook saved")
|
||||||
runOnUiThread({
|
runOnUiThread({
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
Toast.makeText(this@MainActivity, R.string.toast_event_added, Toast.LENGTH_SHORT).show()
|
Toast.makeText(
|
||||||
|
this@MainActivity,
|
||||||
|
if (lastEventAdded != null)
|
||||||
|
R.string.toast_event_added
|
||||||
|
else
|
||||||
|
R.string.toast_logbook_saved,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
savingEvent(false)
|
savingEvent(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -276,7 +386,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
runOnUiThread({
|
runOnUiThread({
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
onRepoError(getString(R.string.settings_network_error) + error.toString())
|
onRepoError(getString(R.string.settings_network_error) + error.toString())
|
||||||
onAddError(event, error.toString())
|
if (lastEventAdded != null)
|
||||||
|
onAddError(lastEventAdded, error.toString())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +401,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
getString(R.string.settings_webdav_error_generic) + error.toString()
|
getString(R.string.settings_webdav_error_generic) + error.toString()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
onAddError(event, error.toString())
|
if (lastEventAdded != null)
|
||||||
|
onAddError(lastEventAdded, error.toString())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +410,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
runOnUiThread({
|
runOnUiThread({
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
onRepoError(getString(R.string.settings_json_error) + error.toString())
|
onRepoError(getString(R.string.settings_json_error) + error.toString())
|
||||||
onAddError(event, error.toString())
|
if (lastEventAdded != null)
|
||||||
|
onAddError(lastEventAdded, error.toString())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +419,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
runOnUiThread({
|
runOnUiThread({
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
onRepoError(getString(R.string.settings_generic_error) + error.toString())
|
onRepoError(getString(R.string.settings_generic_error) + error.toString())
|
||||||
onAddError(event, error.toString())
|
if (lastEventAdded != null)
|
||||||
|
onAddError(lastEventAdded, error.toString())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -342,4 +456,29 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showOverflowPopupWindow(anchor: View) {
|
||||||
|
PopupWindow(anchor.context).apply {
|
||||||
|
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({
|
||||||
|
askNotes(LunaEvent(LunaEvent.TYPE_MEDICINE))
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
|
contentView.findViewById<View>(R.id.button_enema).setOnClickListener({
|
||||||
|
logEvent(LunaEvent(LunaEvent.TYPE_ENEMA))
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
|
contentView.findViewById<View>(R.id.button_note).setOnClickListener({
|
||||||
|
askNotes(LunaEvent(LunaEvent.TYPE_NOTE))
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
|
contentView.findViewById<View>(R.id.button_custom).setOnClickListener({
|
||||||
|
Toast.makeText(anchor.context, "TODO: Implement custom events", Toast.LENGTH_SHORT).show()
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
|
}.also { popupWindow ->
|
||||||
|
popupWindow.showAsDropDown(anchor)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
package it.danieleverducci.lunatracker.adapters
|
package it.danieleverducci.lunatracker.adapters
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.format.DateFormat
|
|
||||||
import android.text.format.DateUtils
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import it.danieleverducci.lunatracker.entities.LunaEvent
|
import it.danieleverducci.lunatracker.entities.LunaEvent
|
||||||
import it.danieleverducci.lunatracker.entities.LunaEventType
|
|
||||||
import it.danieleverducci.lunatracker.R
|
import it.danieleverducci.lunatracker.R
|
||||||
import java.util.Date
|
import utils.DateUtils
|
||||||
|
import utils.NumericUtils
|
||||||
|
|
||||||
class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.LunaEventVH> {
|
class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.LunaEventVH> {
|
||||||
private val context: Context
|
private val context: Context
|
||||||
val items = ArrayList<LunaEvent>()
|
val items = ArrayList<LunaEvent>()
|
||||||
|
val numericUtils: NumericUtils
|
||||||
|
var onItemClickListener: OnItemClickListener? = null
|
||||||
|
|
||||||
constructor(context: Context) {
|
constructor(context: Context) {
|
||||||
this.context = context
|
this.context = context
|
||||||
|
this.numericUtils = NumericUtils(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
override fun onCreateViewHolder(
|
||||||
@ -35,86 +36,49 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
|
|||||||
position: Int
|
position: Int
|
||||||
) {
|
) {
|
||||||
val item = items.get(position)
|
val item = items.get(position)
|
||||||
holder.type.text = context.getString(
|
// Colors
|
||||||
when (item.type) {
|
holder.root.setBackgroundResource(
|
||||||
LunaEventType.BABY_BOTTLE -> R.string.event_bottle_type
|
if (position % 2 == 0) R.color.list_background_even else R.color.list_background_odd
|
||||||
LunaEventType.WEIGHT -> R.string.event_scale_type
|
|
||||||
LunaEventType.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type
|
|
||||||
LunaEventType.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type
|
|
||||||
LunaEventType.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type
|
|
||||||
LunaEventType.DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_type
|
|
||||||
LunaEventType.DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_type
|
|
||||||
else -> R.string.event_unknown_type
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
holder.description.text = context.getString(
|
// Contents
|
||||||
when (item.type) {
|
holder.type.text = item.getTypeEmoji(context)
|
||||||
LunaEventType.BABY_BOTTLE -> R.string.event_bottle_desc
|
holder.description.text = when(item.type) {
|
||||||
LunaEventType.WEIGHT -> R.string.event_scale_desc
|
LunaEvent.TYPE_MEDICINE -> item.notes
|
||||||
LunaEventType.BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc
|
LunaEvent.TYPE_NOTE -> item.notes
|
||||||
LunaEventType.BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc
|
LunaEvent.TYPE_CUSTOM -> item.notes
|
||||||
LunaEventType.BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc
|
else -> item.getTypeDescription(context)
|
||||||
LunaEventType.DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_desc
|
}
|
||||||
LunaEventType.DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_desc
|
holder.time.text = DateUtils.formatTimeAgo(context, item.time)
|
||||||
else -> R.string.event_unknown_desc
|
holder.quantity.text = numericUtils.formatEventQuantity(item)
|
||||||
|
// Listeners
|
||||||
|
if (onItemClickListener != null) {
|
||||||
|
holder.root.setOnClickListener({
|
||||||
|
onItemClickListener?.onItemClick(item)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
|
||||||
holder.quantity.text = if ((item.quantity ?: 0) > 0) item.quantity.toString() else ""
|
|
||||||
holder.time.text = formatTimeAgo(context, item.time)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return items.size
|
return items.size
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the provided unix timestamp in a string like "3 hours, 26 minutes ago)
|
|
||||||
*/
|
|
||||||
fun formatTimeAgo(context: Context, unixTime: Long): String {
|
|
||||||
val secondsDiff = (System.currentTimeMillis() / 1000) - unixTime
|
|
||||||
val minutesDiff = secondsDiff / 60
|
|
||||||
|
|
||||||
if (minutesDiff < 1)
|
|
||||||
return context.getString(R.string.now)
|
|
||||||
|
|
||||||
val hoursAgo = (secondsDiff / (60 * 60)).toInt()
|
|
||||||
val minutesAgo = (minutesDiff % 60).toInt()
|
|
||||||
|
|
||||||
if (hoursAgo > 24)
|
|
||||||
return DateFormat.getDateFormat(context).format(Date(unixTime*1000)) + "\n" +
|
|
||||||
DateFormat.getTimeFormat(context).format(Date(unixTime*1000))
|
|
||||||
|
|
||||||
var formattedTime = StringBuilder()
|
|
||||||
if (hoursAgo > 0) {
|
|
||||||
formattedTime.append(hoursAgo).append(" ")
|
|
||||||
if (hoursAgo.toInt() == 1)
|
|
||||||
formattedTime.append(context.getString(R.string.hour_ago))
|
|
||||||
else
|
|
||||||
formattedTime.append(context.getString(R.string.hours_ago))
|
|
||||||
}
|
|
||||||
if (formattedTime.isNotEmpty())
|
|
||||||
formattedTime.append(", ")
|
|
||||||
if (minutesAgo > 0) {
|
|
||||||
formattedTime.append(minutesAgo).append(" ")
|
|
||||||
if (minutesAgo.toInt() == 1)
|
|
||||||
formattedTime.append(context.getString(R.string.minute_ago))
|
|
||||||
else
|
|
||||||
formattedTime.append(context.getString(R.string.minutes_ago))
|
|
||||||
}
|
|
||||||
return formattedTime.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
class LunaEventVH: RecyclerView.ViewHolder {
|
class LunaEventVH: RecyclerView.ViewHolder {
|
||||||
|
val root: View
|
||||||
val type: TextView
|
val type: TextView
|
||||||
val description: TextView
|
val description: TextView
|
||||||
val quantity: TextView
|
val quantity: TextView
|
||||||
val time: TextView
|
val time: TextView
|
||||||
|
|
||||||
constructor(v: View) : super(v) {
|
constructor(v: View) : super(v) {
|
||||||
|
root = v
|
||||||
type = v.findViewById<TextView>(R.id.type)
|
type = v.findViewById<TextView>(R.id.type)
|
||||||
description = v.findViewById<TextView>(R.id.description)
|
description = v.findViewById<TextView>(R.id.description)
|
||||||
quantity = v.findViewById<TextView>(R.id.quantity)
|
quantity = v.findViewById<TextView>(R.id.quantity)
|
||||||
time = v.findViewById<TextView>(R.id.time)
|
time = v.findViewById<TextView>(R.id.time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OnItemClickListener {
|
||||||
|
fun onItemClick(event: LunaEvent)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,5 +1,19 @@
|
|||||||
package it.danieleverducci.lunatracker.entities
|
package it.danieleverducci.lunatracker.entities
|
||||||
|
|
||||||
class Logbook {
|
class Logbook {
|
||||||
|
companion object {
|
||||||
|
val MAX_SAFE_LOGBOOK_SIZE = 30000
|
||||||
|
}
|
||||||
val logs = ArrayList<LunaEvent>()
|
val logs = ArrayList<LunaEvent>()
|
||||||
|
|
||||||
|
fun isTooBig(): Boolean {
|
||||||
|
return logs.size > MAX_SAFE_LOGBOOK_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Halves the logbook to avoid the file being too big
|
||||||
|
*/
|
||||||
|
fun trim() {
|
||||||
|
logs.subList(MAX_SAFE_LOGBOOK_SIZE/2, logs.size).clear()
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,68 +1,117 @@
|
|||||||
package it.danieleverducci.lunatracker.entities
|
package it.danieleverducci.lunatracker.entities
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import it.danieleverducci.lunatracker.R
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
class LunaEvent(
|
/**
|
||||||
val type: LunaEventType,
|
* Represents a logged event.
|
||||||
val quantity: Int? = null,
|
* It encloses, but doesn't parse entirely, a jsonObject. This was done to
|
||||||
){
|
* allow expandability and backwards compatibility (if a field is added in a
|
||||||
var time: Long // In unix time (seconds since 1970)
|
* release, it is simply ignored by previous ones).
|
||||||
|
*/
|
||||||
|
class LunaEvent {
|
||||||
|
|
||||||
init {
|
companion object {
|
||||||
time = System.currentTimeMillis() / 1000
|
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"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
private val jo: JSONObject
|
||||||
return "${type.toString()} qty: $quantity time: ${Date(time * 1000)}"
|
|
||||||
|
var time: Long // In unix time (seconds since 1970)
|
||||||
|
get() = jo.getLong("time")
|
||||||
|
set(value) {
|
||||||
|
jo.put("time", value)
|
||||||
|
}
|
||||||
|
var type: String
|
||||||
|
get(): String = jo.getString("type")
|
||||||
|
set(value) {
|
||||||
|
jo.put("type", value)
|
||||||
|
}
|
||||||
|
var quantity: Int
|
||||||
|
get() = jo.optInt("quantity")
|
||||||
|
set(value) {
|
||||||
|
if (value > 0)
|
||||||
|
jo.put("quantity", value)
|
||||||
|
}
|
||||||
|
var notes: String
|
||||||
|
get(): String = jo.optString("notes")
|
||||||
|
set(value) {
|
||||||
|
jo.put("notes", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(jo: JSONObject) {
|
||||||
|
this.jo = jo
|
||||||
|
// A minimal LunaEvent should have at least time and type
|
||||||
|
if (!jo.has("time") || !jo.has("type"))
|
||||||
|
throw IllegalArgumentException("JSONObject is not a LunaEvent")
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(type: String) {
|
||||||
|
this.jo = JSONObject()
|
||||||
|
this.time = System.currentTimeMillis() / 1000
|
||||||
|
this.type = type
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(type: String, quantity: Int) {
|
||||||
|
this.jo = JSONObject()
|
||||||
|
this.time = System.currentTimeMillis() / 1000
|
||||||
|
this.type = type
|
||||||
|
this.quantity = quantity
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTypeEmoji(context: Context): String {
|
||||||
|
return context.getString(
|
||||||
|
when (type) {
|
||||||
|
TYPE_BABY_BOTTLE -> R.string.event_bottle_type
|
||||||
|
TYPE_WEIGHT -> R.string.event_scale_type
|
||||||
|
TYPE_BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type
|
||||||
|
TYPE_BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type
|
||||||
|
TYPE_BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type
|
||||||
|
TYPE_DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_type
|
||||||
|
TYPE_DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_type
|
||||||
|
TYPE_MEDICINE -> R.string.event_medicine_type
|
||||||
|
TYPE_ENEMA -> R.string.event_enema_type
|
||||||
|
TYPE_NOTE -> R.string.event_note_type
|
||||||
|
else -> R.string.event_unknown_type
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTypeDescription(context: Context): String {
|
||||||
|
return context.getString(
|
||||||
|
when (type) {
|
||||||
|
TYPE_BABY_BOTTLE -> R.string.event_bottle_desc
|
||||||
|
TYPE_WEIGHT -> R.string.event_scale_desc
|
||||||
|
TYPE_BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc
|
||||||
|
TYPE_BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc
|
||||||
|
TYPE_BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc
|
||||||
|
TYPE_DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_desc
|
||||||
|
TYPE_DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_desc
|
||||||
|
TYPE_MEDICINE -> R.string.event_medicine_desc
|
||||||
|
TYPE_ENEMA -> R.string.event_enema_desc
|
||||||
|
TYPE_NOTE -> R.string.event_note_desc
|
||||||
|
else -> R.string.event_unknown_desc
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toJson(): JSONObject {
|
fun toJson(): JSONObject {
|
||||||
val jo = JSONObject()
|
|
||||||
val type = when (type) {
|
|
||||||
LunaEventType.BABY_BOTTLE -> "BABY_BOTTLE"
|
|
||||||
LunaEventType.WEIGHT -> "SCALE"
|
|
||||||
LunaEventType.BREASTFEEDING_LEFT_NIPPLE -> "BREASTFEEDING_LEFT_NIPPLE"
|
|
||||||
LunaEventType.BREASTFEEDING_BOTH_NIPPLE -> "BREASTFEEDING_BOTH_NIPPLE"
|
|
||||||
LunaEventType.BREASTFEEDING_RIGHT_NIPPLE -> "BREASTFEEDING_RIGHT_NIPPLE"
|
|
||||||
LunaEventType.DIAPERCHANGE_POO -> "DIAPERCHANGE_POO"
|
|
||||||
LunaEventType.DIAPERCHANGE_PEE -> "DIAPERCHANGE_PEE"
|
|
||||||
else -> "UNKNOWN"
|
|
||||||
}
|
|
||||||
jo.put("type", type)
|
|
||||||
jo.put("quantity", quantity)
|
|
||||||
jo.put("time", time)
|
|
||||||
return jo
|
return jo
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
override fun toString(): String {
|
||||||
fun fromJson(j: JSONObject): LunaEvent {
|
return "${type} qty: $quantity time: ${Date(time * 1000)}"
|
||||||
val type = when (j.getString("type")) {
|
|
||||||
"BABY_BOTTLE" -> LunaEventType.BABY_BOTTLE
|
|
||||||
"SCALE" -> LunaEventType.WEIGHT
|
|
||||||
"BREASTFEEDING_LEFT_NIPPLE" -> LunaEventType.BREASTFEEDING_LEFT_NIPPLE
|
|
||||||
"BREASTFEEDING_BOTH_NIPPLE" -> LunaEventType.BREASTFEEDING_BOTH_NIPPLE
|
|
||||||
"BREASTFEEDING_RIGHT_NIPPLE" -> LunaEventType.BREASTFEEDING_RIGHT_NIPPLE
|
|
||||||
"DIAPERCHANGE_POO" -> LunaEventType.DIAPERCHANGE_POO
|
|
||||||
"DIAPERCHANGE_PEE" -> LunaEventType.DIAPERCHANGE_PEE
|
|
||||||
else -> LunaEventType.UNKNOWN
|
|
||||||
}
|
}
|
||||||
val quantity = j.optInt("quantity")
|
|
||||||
val time = j.getLong("time")
|
|
||||||
val evt = LunaEvent(type, quantity)
|
|
||||||
evt.time = time
|
|
||||||
return evt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class LunaEventType {
|
|
||||||
BABY_BOTTLE,
|
|
||||||
WEIGHT,
|
|
||||||
BREASTFEEDING_LEFT_NIPPLE,
|
|
||||||
BREASTFEEDING_BOTH_NIPPLE,
|
|
||||||
BREASTFEEDING_RIGHT_NIPPLE,
|
|
||||||
DIAPERCHANGE_POO,
|
|
||||||
DIAPERCHANGE_PEE,
|
|
||||||
UNKNOWN
|
|
||||||
}
|
}
|
@ -5,6 +5,7 @@ import it.danieleverducci.lunatracker.entities.Logbook
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import it.danieleverducci.lunatracker.entities.LunaEvent
|
import it.danieleverducci.lunatracker.entities.LunaEvent
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
@ -31,9 +32,13 @@ class FileLogbookRepository: LogbookRepository {
|
|||||||
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()) {
|
||||||
val jo = ja.getJSONObject(i)
|
try {
|
||||||
val evt = LunaEvent.fromJson(jo)
|
val evt: LunaEvent = LunaEvent(ja.getJSONObject(i))
|
||||||
logbook.logs.add(evt)
|
logbook.logs.add(evt)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
// Mangled JSON?
|
||||||
|
throw JSONException(e.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return logbook
|
return logbook
|
||||||
}
|
}
|
||||||
|
@ -59,10 +59,15 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
|||||||
val ja = JSONArray(json)
|
val ja = JSONArray(json)
|
||||||
val logbook = Logbook()
|
val logbook = Logbook()
|
||||||
for (i in 0 until ja.length()) {
|
for (i in 0 until ja.length()) {
|
||||||
val jo = ja.getJSONObject(i)
|
try {
|
||||||
val evt = LunaEvent.fromJson(jo)
|
val evt: LunaEvent = LunaEvent(ja.getJSONObject(i))
|
||||||
logbook.logs.add(evt)
|
logbook.logs.add(evt)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
// Mangled JSON?
|
||||||
|
throw JSONException(e.toString())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Loaded ${logbook.logs.size} events into logbook")
|
||||||
return logbook
|
return logbook
|
||||||
}
|
}
|
||||||
|
|
||||||
|
48
app/src/main/java/utils/DateUtils.kt
Normal file
48
app/src/main/java/utils/DateUtils.kt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.format.DateFormat
|
||||||
|
import it.danieleverducci.lunatracker.R
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
class DateUtils {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the provided unix timestamp in a string like "3 hours, 26 minutes ago)
|
||||||
|
*/
|
||||||
|
fun formatTimeAgo(context: Context, unixTime: Long): String {
|
||||||
|
val secondsDiff = (System.currentTimeMillis() / 1000) - unixTime
|
||||||
|
val minutesDiff = secondsDiff / 60
|
||||||
|
|
||||||
|
if (minutesDiff < 1)
|
||||||
|
return context.getString(R.string.now)
|
||||||
|
|
||||||
|
val hoursAgo = (secondsDiff / (60 * 60)).toInt()
|
||||||
|
val minutesAgo = (minutesDiff % 60).toInt()
|
||||||
|
|
||||||
|
if (hoursAgo > 24)
|
||||||
|
return DateFormat.getDateFormat(context).format(Date(unixTime*1000)) + "\n" +
|
||||||
|
DateFormat.getTimeFormat(context).format(Date(unixTime*1000))
|
||||||
|
|
||||||
|
var formattedTime = StringBuilder()
|
||||||
|
if (hoursAgo > 0) {
|
||||||
|
formattedTime.append(hoursAgo).append(" ")
|
||||||
|
if (hoursAgo.toInt() == 1)
|
||||||
|
formattedTime.append(context.getString(R.string.hour_ago))
|
||||||
|
else
|
||||||
|
formattedTime.append(context.getString(R.string.hours_ago))
|
||||||
|
}
|
||||||
|
if (minutesAgo > 0) {
|
||||||
|
if (formattedTime.isNotEmpty())
|
||||||
|
formattedTime.append(", ")
|
||||||
|
formattedTime.append(minutesAgo).append(" ")
|
||||||
|
if (minutesAgo.toInt() == 1)
|
||||||
|
formattedTime.append(context.getString(R.string.minute_ago))
|
||||||
|
else
|
||||||
|
formattedTime.append(context.getString(R.string.minutes_ago))
|
||||||
|
}
|
||||||
|
return formattedTime.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
app/src/main/java/utils/NumericUtils.kt
Normal file
51
app/src/main/java/utils/NumericUtils.kt
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.icu.util.LocaleData
|
||||||
|
import android.icu.util.ULocale
|
||||||
|
import it.danieleverducci.lunatracker.R
|
||||||
|
import it.danieleverducci.lunatracker.entities.LunaEvent
|
||||||
|
import java.text.NumberFormat
|
||||||
|
|
||||||
|
class NumericUtils (val context: Context) {
|
||||||
|
val numberFormat: NumberFormat
|
||||||
|
val measurement_unit_liquid_base: String
|
||||||
|
val measurement_unit_weight_base: String
|
||||||
|
val measurement_unit_weight_tiny: String
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.numberFormat = NumberFormat.getInstance()
|
||||||
|
val measurementSystem = LocaleData.getMeasurementSystem(ULocale.getDefault())
|
||||||
|
this.measurement_unit_liquid_base = context.getString(
|
||||||
|
if (measurementSystem == LocaleData. MeasurementSystem.SI)
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
R.string.measurement_unit_weight_tiny_metric
|
||||||
|
else
|
||||||
|
R.string.measurement_unit_weight_tiny_imperial
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatEventQuantity(item: LunaEvent): String {
|
||||||
|
return if ((item.quantity ?: 0) > 0) {
|
||||||
|
numberFormat.format(item.quantity) + " " + when (item.type) {
|
||||||
|
LunaEvent.TYPE_BABY_BOTTLE -> measurement_unit_liquid_base
|
||||||
|
LunaEvent.TYPE_WEIGHT -> measurement_unit_weight_base
|
||||||
|
LunaEvent.TYPE_MEDICINE -> measurement_unit_weight_tiny
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@
|
|||||||
<stroke
|
<stroke
|
||||||
android:width="2dp"
|
android:width="2dp"
|
||||||
android:color="@color/accent" />
|
android:color="@color/accent" />
|
||||||
|
<solid
|
||||||
|
android:color="@color/cardview_dark_background"/>
|
||||||
<corners android:radius="15dp" />
|
<corners android:radius="15dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="5dp"
|
android:bottom="5dp"
|
||||||
|
5
app/src/main/res/drawable/ic_more.xml
Normal file
5
app/src/main/res/drawable/ic_more.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
|
||||||
|
|
||||||
|
</vector>
|
12
app/src/main/res/drawable/textview_background.xml
Normal file
12
app/src/main/res/drawable/textview_background.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="@color/grey" />
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="10dp"
|
||||||
|
android:left="20dp"
|
||||||
|
android:right="20dp"
|
||||||
|
android:top="10dp" />
|
||||||
|
</shape>
|
@ -108,7 +108,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="10dp"
|
android:layout_margin="10dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="2"
|
||||||
android:background="@drawable/button_background"
|
android:background="@drawable/button_background"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:textSize="30dp"
|
android:textSize="30dp"
|
||||||
@ -119,12 +119,23 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="10dp"
|
android:layout_margin="10dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="2"
|
||||||
android:background="@drawable/button_background"
|
android:background="@drawable/button_background"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:textSize="30dp"
|
android:textSize="30dp"
|
||||||
android:text="🚼 💧"/>
|
android:text="🚼 💧"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/button_more"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/button_background"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:src="@drawable/ic_more"
|
||||||
|
app:tint="@android:color/darker_gray"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -68,7 +68,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="30dp"
|
android:layout_marginLeft="30dp"
|
||||||
android:hint="@string/settings_storage_dav_url_hint"
|
android:hint="@string/settings_storage_dav_url_hint"
|
||||||
android:inputType="textUri"/>
|
android:inputType="textUri"
|
||||||
|
android:background="@drawable/textview_background"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -83,7 +84,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="30dp"
|
android:layout_marginLeft="30dp"
|
||||||
android:inputType="textEmailAddress"/>
|
android:inputType="textEmailAddress"
|
||||||
|
android:background="@drawable/textview_background"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -98,7 +100,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="30dp"
|
android:layout_marginLeft="30dp"
|
||||||
android:inputType="textPassword"/>
|
android:inputType="textPassword"
|
||||||
|
android:background="@drawable/textview_background"/>
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
android:id="@+id/progress_indicator"
|
android:id="@+id/progress_indicator"
|
||||||
|
55
app/src/main/res/layout/dialog_event_detail.xml
Normal file
55
app/src/main/res/layout/dialog_event_detail.xml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_event_detail_type_emoji"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:textSize="60dp"
|
||||||
|
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:gravity="center_horizontal"
|
||||||
|
android:textColor="@color/accent"
|
||||||
|
android:textSize="24dp"
|
||||||
|
android:text="@string/event_diaperchange_pee_desc"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_event_detail_type_date"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="2020"/>
|
||||||
|
|
||||||
|
<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="2020"/>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:layout_marginTop="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_event_detail_type_notes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:text="Lorem ipsum"/>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
</LinearLayout>
|
27
app/src/main/res/layout/dialog_notes.xml
Normal file
27
app/src/main/res/layout/dialog_notes.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/notes_qty_edittext"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:lines="1"
|
||||||
|
android:inputType="number"
|
||||||
|
android:hint="@string/log_notes_dialog_qty_hint"
|
||||||
|
android:background="@drawable/textview_background"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/notes_edittext"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:gravity="top"
|
||||||
|
android:lines="10"
|
||||||
|
android:hint="@string/log_notes_dialog_note_hint"
|
||||||
|
android:background="@drawable/textview_background"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
62
app/src/main/res/layout/more_events_popup.xml
Normal file
62
app/src/main/res/layout/more_events_popup.xml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="20dp"
|
||||||
|
android:background="@color/transparent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_medicine"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="20dp"
|
||||||
|
android:background="@drawable/button_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/button_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:background="@drawable/button_background"
|
||||||
|
style="@style/OverflowMenuText"
|
||||||
|
android:text="@string/overflow_event_note"/>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/overflow_event_custom_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_custom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:padding="20dp"
|
||||||
|
android:background="@drawable/button_background"
|
||||||
|
style="@style/OverflowMenuText"
|
||||||
|
android:text="@string/overflow_event_custom"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
@ -11,7 +11,8 @@
|
|||||||
android:layout_width="150dp"
|
android:layout_width="150dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:hint="0"/>
|
android:hint="0"
|
||||||
|
android:background="@drawable/textview_background"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -3,21 +3,27 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/type"
|
android:id="@+id/type"
|
||||||
android:layout_width="0dp"
|
android:layout_width="90dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="2"
|
android:paddingLeft="10dp"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
android:text="Type"/>
|
android:lines="1"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/event_diaperchange_pee_type"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/description"
|
android:id="@+id/description"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="2"
|
android:layout_weight="2"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
android:text="Description"/>
|
android:text="Description"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -25,7 +31,11 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
|
android:lines="1"
|
||||||
|
android:maxLines="1"
|
||||||
android:text="Qty"/>
|
android:text="Qty"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -35,6 +45,8 @@
|
|||||||
android:layout_weight="2"
|
android:layout_weight="2"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
android:text="Time"/>
|
android:text="Time"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -10,14 +10,10 @@
|
|||||||
<string name="log_weight_dialog_title">Pesata</string>
|
<string name="log_weight_dialog_title">Pesata</string>
|
||||||
<string name="log_weight_dialog_description">Inserisci il peso rilevato</string>
|
<string name="log_weight_dialog_description">Inserisci il peso rilevato</string>
|
||||||
|
|
||||||
<string name="event_bottle_type">🍼</string>
|
<string name="overflow_event_medicine">💊 Medicina</string>
|
||||||
<string name="event_scale_type">⚖️</string>
|
<string name="overflow_event_enema">🪠 Clistere</string>
|
||||||
<string name="event_breastfeeding_left_type">🤱 ←</string>
|
<string name="overflow_event_note">📝 Nota</string>
|
||||||
<string name="event_breastfeeding_both_type">🤱 ↔</string>
|
<string name="overflow_event_custom">➕ Personalizzato</string>
|
||||||
<string name="event_breastfeeding_right_type">🤱 →</string>
|
|
||||||
<string name="event_diaperchange_poo_type">🚼 💩</string>
|
|
||||||
<string name="event_diaperchange_pee_type">🚼 💧</string>
|
|
||||||
<string name="event_unknown_type">-</string>
|
|
||||||
|
|
||||||
<string name="event_bottle_desc">Biberon</string>
|
<string name="event_bottle_desc">Biberon</string>
|
||||||
<string name="event_scale_desc">Pesata</string>
|
<string name="event_scale_desc">Pesata</string>
|
||||||
@ -26,9 +22,13 @@
|
|||||||
<string name="event_breastfeeding_right_desc">Allatt. al seno (dx)</string>
|
<string name="event_breastfeeding_right_desc">Allatt. al seno (dx)</string>
|
||||||
<string name="event_diaperchange_poo_desc">Cambio (con cacca)</string>
|
<string name="event_diaperchange_poo_desc">Cambio (con cacca)</string>
|
||||||
<string name="event_diaperchange_pee_desc">Cambio (con pipì)</string>
|
<string name="event_diaperchange_pee_desc">Cambio (con pipì)</string>
|
||||||
|
<string name="event_medicine_desc">Medicina</string>
|
||||||
|
<string name="event_enema_desc">Clistere</string>
|
||||||
|
<string name="event_note_desc">Nota</string>
|
||||||
<string name="event_unknown_desc"></string>
|
<string name="event_unknown_desc"></string>
|
||||||
|
|
||||||
<string name="toast_event_added">Evento aggiunto</string>
|
<string name="toast_event_added">Evento aggiunto</string>
|
||||||
|
<string name="toast_logbook_saved">Diario salvato</string>
|
||||||
<string name="toast_event_add_error">Impossibile aggiungere l\'evento</string>
|
<string name="toast_event_add_error">Impossibile aggiungere l\'evento</string>
|
||||||
<string name="toast_integer_error">Valore non valido. Inserire un numero intero.</string>
|
<string name="toast_integer_error">Valore non valido. Inserire un numero intero.</string>
|
||||||
|
|
||||||
@ -48,7 +48,7 @@
|
|||||||
<string name="settings_storage_local">Sul dispositivo</string>
|
<string name="settings_storage_local">Sul dispositivo</string>
|
||||||
<string name="settings_storage_local_desc">La soluzione più privacy-friendly: i dati non escono mai dal tuo dispositivo</string>
|
<string name="settings_storage_local_desc">La soluzione più privacy-friendly: i dati non escono mai dal tuo dispositivo</string>
|
||||||
<string name="settings_storage_dav">Su un server WebDAV</string>
|
<string name="settings_storage_dav">Su un server WebDAV</string>
|
||||||
<string name="settings_storage_dav_desc">Puoi usare un qualunque servizio che supporti WebDAV (come ad esempio Nextcloud, Owncloud, Dropbox, Box...) per salvare i dati. In questo modo puoi sincronizzarli tra più dispositivi, ad esempio quello del papà, della mamma, dei nonni... Ti servirà l\'url WebDAV, che trovi nella documentazione del tuo provider (ad es. in Nextcloud è nelle impostazioni di Files)</string>
|
<string name="settings_storage_dav_desc">Puoi usare un qualunque servizio che supporti WebDAV (come ad esempio Nextcloud, Owncloud, Dropbox, Box…) per salvare i dati. In questo modo puoi sincronizzarli tra più dispositivi, ad esempio quello del papà, della mamma, dei nonni… Ti servirà l\'url WebDAV, che trovi nella documentazione del tuo provider (ad es. in Nextcloud è nelle impostazioni di Files)</string>
|
||||||
<string name="settings_storage_dav_url">Url WebDAV</string>
|
<string name="settings_storage_dav_url">Url WebDAV</string>
|
||||||
<string name="settings_storage_dav_url_hint">https://</string>
|
<string name="settings_storage_dav_url_hint">https://</string>
|
||||||
<string name="settings_storage_dav_user">Username</string>
|
<string name="settings_storage_dav_user">Username</string>
|
||||||
@ -60,4 +60,18 @@
|
|||||||
<string name="settings_webdav_creation_ok">Connessione al server WebDAV avvenuta con successo</string>
|
<string name="settings_webdav_creation_ok">Connessione al server WebDAV avvenuta con successo</string>
|
||||||
<string name="settings_json_error">Sul server esiste un salvataggio, ma è corrotto o illeggibile. Cancellare il file </string>
|
<string name="settings_json_error">Sul server esiste un salvataggio, ma è corrotto o illeggibile. Cancellare il file </string>
|
||||||
<string name="settings_generic_error">Si è verificato un errore: </string>
|
<string name="settings_generic_error">Si è verificato un errore: </string>
|
||||||
|
|
||||||
|
<string name="trim_logbook_dialog_title">Il tuo diario è bello grande!</string>
|
||||||
|
<string name="trim_logbook_dialog_message_local">Il file del tuo diario sta crescendo molto. Ti suggeriamo di cancellare gli eventi più vecchi per evitare problemi di memoria.</string>
|
||||||
|
<string name="trim_logbook_dialog_message_dav">Il file del tuo diario sta crescendo molto. Ti suggeriamo di cancellare gli eventi più vecchi per evitare problemi di memoria. Se vuoi conservare tutto lo storico, fai un backup del file "lunatracker_logbook.json" sul tuo server WebDAV o rinominalo per mantenerlo così e cominciare un nuovo diario.</string>
|
||||||
|
<string name="trim_logbook_dialog_button_ok">Cancella i più vecchi</string>
|
||||||
|
<string name="trim_logbook_dialog_button_cancel">Ricordamelo dopo</string>
|
||||||
|
|
||||||
|
<string name="log_notes_dialog_description">Note:</string>
|
||||||
|
<string name="log_medicine_dialog_description">Nome della medicina, quantità, formato, note…:</string>
|
||||||
|
<string name="log_notes_dialog_qty_hint">Quantità, o vuoto</string>
|
||||||
|
<string name="log_notes_dialog_note_hint">Inserisci le note</string>
|
||||||
|
|
||||||
|
<string name="dialog_event_detail_title">Dettaglio evento</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -4,5 +4,8 @@
|
|||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
<color name="white">#FFFFFFFF</color>
|
||||||
<color name="translucent">#c000</color>
|
<color name="translucent">#c000</color>
|
||||||
|
<color name="transparent">#0000</color>
|
||||||
<color name="grey">#ccc</color>
|
<color name="grey">#ccc</color>
|
||||||
|
<color name="list_background_odd">#423B25</color>
|
||||||
|
<color name="list_background_even">@color/transparent</color>
|
||||||
</resources>
|
</resources>
|
@ -10,14 +10,17 @@
|
|||||||
<string name="log_weight_dialog_title">Weight</string>
|
<string name="log_weight_dialog_title">Weight</string>
|
||||||
<string name="log_weight_dialog_description">Insert the weight</string>
|
<string name="log_weight_dialog_description">Insert the weight</string>
|
||||||
|
|
||||||
<string name="event_bottle_type">🍼</string>
|
<string name="event_bottle_type" translatable="false">🍼</string>
|
||||||
<string name="event_scale_type">⚖️</string>
|
<string name="event_scale_type" translatable="false">⚖️</string>
|
||||||
<string name="event_breastfeeding_left_type">🤱 ←</string>
|
<string name="event_breastfeeding_left_type" translatable="false">🤱 ←</string>
|
||||||
<string name="event_breastfeeding_both_type">🤱 ↔</string>
|
<string name="event_breastfeeding_both_type" translatable="false">🤱 ↔</string>
|
||||||
<string name="event_breastfeeding_right_type">🤱 →</string>
|
<string name="event_breastfeeding_right_type" translatable="false">🤱 →</string>
|
||||||
<string name="event_diaperchange_poo_type">🚼 💩</string>
|
<string name="event_diaperchange_poo_type" translatable="false">🚼 💩</string>
|
||||||
<string name="event_diaperchange_pee_type">🚼 💧</string>
|
<string name="event_diaperchange_pee_type" translatable="false">🚼 💧</string>
|
||||||
<string name="event_unknown_type">-</string>
|
<string name="event_medicine_type" translatable="false">💊</string>
|
||||||
|
<string name="event_enema_type" translatable="false">🪠</string>
|
||||||
|
<string name="event_note_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>
|
||||||
<string name="event_scale_desc">Weight</string>
|
<string name="event_scale_desc">Weight</string>
|
||||||
@ -26,9 +29,18 @@
|
|||||||
<string name="event_breastfeeding_right_desc">Breastfeeding (right)</string>
|
<string name="event_breastfeeding_right_desc">Breastfeeding (right)</string>
|
||||||
<string name="event_diaperchange_poo_desc">Diaper chg (poo)</string>
|
<string name="event_diaperchange_poo_desc">Diaper chg (poo)</string>
|
||||||
<string name="event_diaperchange_pee_desc">Diaper chg (pee)</string>
|
<string name="event_diaperchange_pee_desc">Diaper chg (pee)</string>
|
||||||
|
<string name="event_medicine_desc">Medicine</string>
|
||||||
|
<string name="event_enema_desc">Enema</string>
|
||||||
|
<string name="event_note_desc">Note</string>
|
||||||
<string name="event_unknown_desc"></string>
|
<string name="event_unknown_desc"></string>
|
||||||
|
|
||||||
|
<string name="overflow_event_medicine">💊 Medicine</string>
|
||||||
|
<string name="overflow_event_enema">🪠 Enema</string>
|
||||||
|
<string name="overflow_event_note">📝 Note</string>
|
||||||
|
<string name="overflow_event_custom">➕ Add custom</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_event_add_error">Unable to log the event</string>
|
<string name="toast_event_add_error">Unable to log the event</string>
|
||||||
<string name="toast_integer_error">Invalid value. Insert an integer.</string>
|
<string name="toast_integer_error">Invalid value. Insert an integer.</string>
|
||||||
|
|
||||||
@ -48,7 +60,7 @@
|
|||||||
<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>
|
||||||
<string name="settings_storage_dav">On a WebDAV server</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_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">WebDAV url</string>
|
||||||
<string name="settings_storage_dav_url_hint">https://</string>
|
<string name="settings_storage_dav_url_hint">https://</string>
|
||||||
<string name="settings_storage_dav_user">Username</string>
|
<string name="settings_storage_dav_user">Username</string>
|
||||||
@ -60,4 +72,25 @@
|
|||||||
<string name="settings_webdav_creation_ok">Successfully connected with 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_json_error">There\'s a save file on the server, but is corrupted or unreadable. Please delete it </string>
|
||||||
<string name="settings_generic_error">Error: </string>
|
<string name="settings_generic_error">Error: </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_button_ok">Trim it now</string>
|
||||||
|
<string name="trim_logbook_dialog_button_cancel">Remind me later</string>
|
||||||
|
|
||||||
|
<string name="log_notes_dialog_description">Notes:</string>
|
||||||
|
<string name="log_medicine_dialog_description">Medicine name, quantity, type, notes…:</string>
|
||||||
|
<string name="log_notes_dialog_qty_hint">Quantity (or empty)</string>
|
||||||
|
<string name="log_notes_dialog_note_hint">Write some notes</string>
|
||||||
|
|
||||||
|
<string name="measurement_unit_liquid_base_metric" translatable="false">ml</string>
|
||||||
|
<string name="measurement_unit_weight_base_metric" translatable="false">g</string>
|
||||||
|
<string name="measurement_unit_weight_tiny_metric" translatable="false">mg</string>
|
||||||
|
<string name="measurement_unit_liquid_base_imperial" translatable="false">fl oz.</string>
|
||||||
|
<string name="measurement_unit_weight_base_imperial" translatable="false">oz</string>
|
||||||
|
<string name="measurement_unit_weight_tiny_imperial" translatable="false">gr</string>
|
||||||
|
|
||||||
|
<string name="dialog_event_detail_title">Event detail</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -1,5 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<style name="Theme.LunaTracker" parent="Theme.AppCompat.NoActionBar" />
|
<style name="Theme.LunaTracker" parent="Theme.AppCompat.NoActionBar">
|
||||||
|
<item name="colorAccent">@color/accent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="OverflowMenuText">
|
||||||
|
<item name="android:textSize">20sp</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
Reference in New Issue
Block a user