Compare commits
17 Commits
v0.2
...
524a121149
Author | SHA1 | Date | |
---|---|---|---|
524a121149 | |||
e1388f9bad | |||
90761afef2 | |||
b02a6c9aad | |||
0d331d90ff | |||
5d17ebf46b | |||
3cbc4139ae | |||
f31c68193a | |||
de5b1d4ead | |||
de75ed584b | |||
a4b43ffb10 | |||
37b68450da | |||
841da9e28e | |||
876c68ce81 | |||
35a3b42a32 | |||
d5cb30cdd7 | |||
57f997e6f1 |
@ -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 = 2
|
||||||
versionName = "0.2"
|
versionName = "0.3"
|
||||||
|
|
||||||
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
|
||||||
@ -27,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.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
|
||||||
|
val DEBUG_CHECK_LOGBOOK_CONSISTENCY = false
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var logbook: Logbook
|
lateinit var logbook: Logbook
|
||||||
@ -45,12 +53,18 @@ class MainActivity : AppCompatActivity() {
|
|||||||
handler.postDelayed(updateListRunnable, 1000*60)
|
handler.postDelayed(updateListRunnable, 1000*60)
|
||||||
}
|
}
|
||||||
var logbookRepo: LogbookRepository? = null
|
var logbookRepo: LogbookRepository? = null
|
||||||
|
var showingOverflowPopupWindow = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
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)
|
||||||
@ -89,6 +103,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
LunaEvent.TYPE_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()
|
||||||
})
|
})
|
||||||
@ -138,7 +156,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Reload data
|
// Reload data
|
||||||
loadLogbook()
|
loadLogbook()
|
||||||
handler.postDelayed(updateListRunnable, 1000*30)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
@ -177,7 +194,7 @@ 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)
|
||||||
@ -190,6 +207,40 @@ 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() {
|
fun askToTrimLogbook() {
|
||||||
val d = AlertDialog.Builder(this)
|
val d = AlertDialog.Builder(this)
|
||||||
d.setTitle(R.string.trim_logbook_dialog_title)
|
d.setTitle(R.string.trim_logbook_dialog_title)
|
||||||
@ -210,10 +261,32 @@ class MainActivity : AppCompatActivity() {
|
|||||||
alertDialog.show()
|
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{
|
||||||
@ -223,6 +296,15 @@ class MainActivity : AppCompatActivity() {
|
|||||||
findViewById<View>(R.id.no_connection_screen).visibility = View.GONE
|
findViewById<View>(R.id.no_connection_screen).visibility = View.GONE
|
||||||
logbook = lb
|
logbook = lb
|
||||||
showLogbook()
|
showLogbook()
|
||||||
|
|
||||||
|
if (DEBUG_CHECK_LOGBOOK_CONSISTENCY) {
|
||||||
|
for (e in logbook.logs) {
|
||||||
|
val em = e.getTypeEmoji(this@MainActivity)
|
||||||
|
if (em == getString(R.string.event_unknown_type)) {
|
||||||
|
Log.e(TAG, "UNKNOWN: ${e.type}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,4 +466,38 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showOverflowPopupWindow(anchor: View) {
|
||||||
|
if (showingOverflowPopupWindow)
|
||||||
|
return
|
||||||
|
|
||||||
|
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.setOnDismissListener({
|
||||||
|
Handler(mainLooper).postDelayed({
|
||||||
|
showingOverflowPopupWindow = false
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
popupWindow.showAsDropDown(anchor)
|
||||||
|
showingOverflowPopupWindow = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,42 @@
|
|||||||
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.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.core.content.ContextCompat
|
||||||
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.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
|
||||||
|
val layoutRes: Int
|
||||||
|
|
||||||
constructor(context: Context) {
|
constructor(context: Context) {
|
||||||
this.context = context
|
this.context = context
|
||||||
|
this.numericUtils = NumericUtils(context)
|
||||||
|
|
||||||
|
val fontScale = context.resources.configuration.fontScale
|
||||||
|
val screenSize = context.resources.configuration.screenWidthDp
|
||||||
|
this.layoutRes =
|
||||||
|
if(fontScale > 1.2 || screenSize < 320 || (fontScale > 1 && screenSize < 400))
|
||||||
|
R.layout.row_luna_event_vertical
|
||||||
|
else
|
||||||
|
R.layout.row_luna_event
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
override fun onCreateViewHolder(
|
||||||
parent: ViewGroup,
|
parent: ViewGroup,
|
||||||
viewType: Int
|
viewType: Int
|
||||||
): LunaEventVH {
|
): LunaEventVH {
|
||||||
|
val view = LayoutInflater.from(context).inflate(layoutRes, parent, false)
|
||||||
val view = LayoutInflater.from(context).inflate(R.layout.row_luna_event, parent, false)
|
|
||||||
return LunaEventVH(view)
|
return LunaEventVH(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,86 +45,78 @@ 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(
|
||||||
LunaEvent.TYPE_BABY_BOTTLE -> R.string.event_bottle_type
|
if (position % 2 == 0) R.color.list_background_even else R.color.list_background_odd
|
||||||
LunaEvent.TYPE_WEIGHT -> R.string.event_scale_type
|
|
||||||
LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_type
|
|
||||||
LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_type
|
|
||||||
LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_type
|
|
||||||
LunaEvent.TYPE_DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_type
|
|
||||||
LunaEvent.TYPE_DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_type
|
|
||||||
else -> R.string.event_unknown_type
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
holder.description.text = context.getString(
|
holder.quantity.setTextColor(ContextCompat.getColor(context, R.color.textColor))
|
||||||
when (item.type) {
|
// Contents
|
||||||
LunaEvent.TYPE_BABY_BOTTLE -> R.string.event_bottle_desc
|
holder.type.text = item.getTypeEmoji(context)
|
||||||
LunaEvent.TYPE_WEIGHT -> R.string.event_scale_desc
|
holder.description.text = when(item.type) {
|
||||||
LunaEvent.TYPE_BREASTFEEDING_LEFT_NIPPLE -> R.string.event_breastfeeding_left_desc
|
LunaEvent.TYPE_MEDICINE -> item.notes
|
||||||
LunaEvent.TYPE_BREASTFEEDING_BOTH_NIPPLE -> R.string.event_breastfeeding_both_desc
|
LunaEvent.TYPE_NOTE -> item.notes
|
||||||
LunaEvent.TYPE_BREASTFEEDING_RIGHT_NIPPLE -> R.string.event_breastfeeding_right_desc
|
LunaEvent.TYPE_CUSTOM -> item.notes
|
||||||
LunaEvent.TYPE_DIAPERCHANGE_POO -> R.string.event_diaperchange_poo_desc
|
else -> item.getTypeDescription(context)
|
||||||
LunaEvent.TYPE_DIAPERCHANGE_PEE -> R.string.event_diaperchange_pee_desc
|
}
|
||||||
else -> R.string.event_unknown_desc
|
holder.time.text = DateUtils.formatTimeAgo(context, item.time)
|
||||||
|
var quantityText = numericUtils.formatEventQuantity(item)
|
||||||
|
|
||||||
|
// if the event is weight, show difference with the last one
|
||||||
|
if (item.type == LunaEvent.TYPE_WEIGHT) {
|
||||||
|
val lastWeight = getPreviousWeightEvent(position)
|
||||||
|
if (lastWeight != null) {
|
||||||
|
val differenceInWeight = item.quantity - lastWeight.quantity
|
||||||
|
val sign = if (differenceInWeight > 0) "+" else ""
|
||||||
|
quantityText += "\n($sign$differenceInWeight)"
|
||||||
|
if (differenceInWeight < 0) {
|
||||||
|
holder.quantity.setTextColor(ContextCompat.getColor(context, R.color.danger))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
holder.quantity.text = if ((item.quantity ?: 0) > 0) item.quantity.toString() else ""
|
|
||||||
holder.time.text = formatTimeAgo(context, item.time)
|
holder.quantity.text = quantityText
|
||||||
|
|
||||||
|
// Listeners
|
||||||
|
if (onItemClickListener != null) {
|
||||||
|
holder.root.setOnClickListener({
|
||||||
|
onItemClickListener?.onItemClick(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return items.size
|
return items.size
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun getPreviousWeightEvent(startFromPosition: Int): LunaEvent? {
|
||||||
* Formats the provided unix timestamp in a string like "3 hours, 26 minutes ago)
|
if (startFromPosition == items.size - 1)
|
||||||
*/
|
return null
|
||||||
fun formatTimeAgo(context: Context, unixTime: Long): String {
|
for (pos in startFromPosition + 1 until items.size) {
|
||||||
val secondsDiff = (System.currentTimeMillis() / 1000) - unixTime
|
val item = items.get(pos)
|
||||||
val minutesDiff = secondsDiff / 60
|
if (item.type != LunaEvent.TYPE_WEIGHT)
|
||||||
|
continue
|
||||||
if (minutesDiff < 1)
|
return item
|
||||||
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())
|
return null
|
||||||
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,7 @@
|
|||||||
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
|
||||||
|
|
||||||
@ -19,6 +21,10 @@ class LunaEvent {
|
|||||||
val TYPE_BREASTFEEDING_RIGHT_NIPPLE = "BREASTFEEDING_RIGHT_NIPPLE"
|
val TYPE_BREASTFEEDING_RIGHT_NIPPLE = "BREASTFEEDING_RIGHT_NIPPLE"
|
||||||
val TYPE_DIAPERCHANGE_POO = "DIAPERCHANGE_POO"
|
val TYPE_DIAPERCHANGE_POO = "DIAPERCHANGE_POO"
|
||||||
val TYPE_DIAPERCHANGE_PEE = "DIAPERCHANGE_PEE"
|
val TYPE_DIAPERCHANGE_PEE = "DIAPERCHANGE_PEE"
|
||||||
|
val TYPE_MEDICINE = "MEDICINE"
|
||||||
|
val TYPE_ENEMA = "ENEMA"
|
||||||
|
val TYPE_NOTE = "NOTE"
|
||||||
|
val TYPE_CUSTOM = "CUSTOM"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val jo: JSONObject
|
private val jo: JSONObject
|
||||||
@ -39,6 +45,11 @@ class LunaEvent {
|
|||||||
if (value > 0)
|
if (value > 0)
|
||||||
jo.put("quantity", value)
|
jo.put("quantity", value)
|
||||||
}
|
}
|
||||||
|
var notes: String
|
||||||
|
get(): String = jo.optString("notes")
|
||||||
|
set(value) {
|
||||||
|
jo.put("notes", value)
|
||||||
|
}
|
||||||
|
|
||||||
constructor(jo: JSONObject) {
|
constructor(jo: JSONObject) {
|
||||||
this.jo = jo
|
this.jo = jo
|
||||||
@ -60,6 +71,42 @@ class LunaEvent {
|
|||||||
this.quantity = quantity
|
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 {
|
||||||
return jo
|
return jo
|
||||||
}
|
}
|
||||||
|
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>
|
@ -6,7 +6,6 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingTop="30dp"
|
android:paddingTop="30dp"
|
||||||
android:paddingLeft="15dp"
|
android:paddingLeft="15dp"
|
||||||
@ -16,7 +15,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/title"
|
android:text="@string/title"
|
||||||
android:textSize="30sp"
|
android:textSize="30dp"
|
||||||
android:gravity="center_horizontal"/>
|
android:gravity="center_horizontal"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -108,7 +107,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 +118,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>
|
||||||
@ -141,7 +151,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Diario di bordo"
|
android:text="@string/logbook"
|
||||||
android:textColor="@color/accent"
|
android:textColor="@color/accent"
|
||||||
android:textStyle="bold"/>
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
@ -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"
|
||||||
@ -119,6 +122,9 @@
|
|||||||
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_marginRight="20dp"
|
||||||
|
android:background="@drawable/button_background"
|
||||||
|
android:textColor="@color/accent"
|
||||||
android:text="@android:string/cancel"/>
|
android:text="@android:string/cancel"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -127,6 +133,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:textColor="@color/accent"
|
android:textColor="@color/accent"
|
||||||
|
android:background="@drawable/button_background"
|
||||||
android:text="@android:string/ok"/>
|
android:text="@android:string/ok"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
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,28 @@
|
|||||||
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:textColor="@color/accent"
|
||||||
android:text="Description"/>
|
android:text="Description"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -25,6 +32,8 @@
|
|||||||
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:text="Qty"/>
|
android:text="Qty"/>
|
||||||
|
|
||||||
@ -35,6 +44,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>
|
||||||
|
59
app/src/main/res/layout/row_luna_event_vertical.xml
Normal file
59
app/src/main/res/layout/row_luna_event_vertical.xml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/type"
|
||||||
|
android:layout_width="90dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:lines="1"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/event_diaperchange_pee_type"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:textColor="@color/accent"
|
||||||
|
android:text="Description"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/quantity"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:text="Qty"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
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,6 +22,9 @@
|
|||||||
<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>
|
||||||
@ -49,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>
|
||||||
@ -68,4 +67,11 @@
|
|||||||
<string name="trim_logbook_dialog_button_ok">Cancella i più vecchi</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="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,10 @@
|
|||||||
<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>
|
||||||
|
<color name="danger">#f00</color>
|
||||||
|
<color name="textColor">@color/grey</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,8 +29,16 @@
|
|||||||
<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_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>
|
||||||
@ -49,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>
|
||||||
@ -68,4 +79,18 @@
|
|||||||
<string name="trim_logbook_dialog_button_ok">Trim it now</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="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,12 @@
|
|||||||
<?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>
|
||||||
|
<item name="android:textColor">@color/textColor</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="OverflowMenuText">
|
||||||
|
<item name="android:textSize">20sp</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
@ -1,5 +1,3 @@
|
|||||||
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.
|
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, event if it's 4 AM and you have no idea why is crying.
|
You have to react fast, event if it's 4 AM and you have no idea why is crying.
|
||||||
|
|
||||||
@ -10,4 +8,4 @@ Dedicated to my daughter Luna.
|
|||||||
|
|
||||||
|
|
||||||
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
|
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
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
LunaTracker è un'app di tracciamento neonatale
|
|
||||||
|
|
||||||
Essere genitori può essere dura. Arrivate a casa dall'ospedale, esausti, con questo cosino fragile e sconosciuto che non ha un manuale d'uso, e ha un solo modo per segnalare che c'è qualcosa che non va: piangere.
|
Essere genitori può essere dura. Arrivate a casa dall'ospedale, esausti, con questo cosino fragile e sconosciuto che non ha un manuale d'uso, e ha un solo modo per segnalare che c'è qualcosa che non va: piangere.
|
||||||
Dovete essere sempre pronti a reagire, anche se sono le 4 di mattina e non avete idea del perché stia piangendo.
|
Dovete essere sempre pronti a reagire, anche se sono le 4 di mattina e non avete idea del perché stia piangendo.
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user