Compare commits
14 Commits
v0.3
...
e23ab77274
Author | SHA1 | Date | |
---|---|---|---|
e23ab77274 | |||
e3aceaf133 | |||
0494a11538 | |||
ecbde64ca1 | |||
32fbeac079 | |||
0b0fd8f5af | |||
fe5da015cb | |||
b1e26f8a96 | |||
48017f70f6 | |||
524a121149 | |||
e1388f9bad | |||
90761afef2 | |||
b02a6c9aad | |||
0d331d90ff |
1
.gitignore
vendored
1
.gitignore
vendored
@ -106,3 +106,4 @@ app/release/output-metadata.json
|
|||||||
|
|
||||||
# Other
|
# Other
|
||||||
app/src/main/java/it/danieleverducci/lunatracker/TemporaryHardcodedCredentials.kt
|
app/src/main/java/it/danieleverducci/lunatracker/TemporaryHardcodedCredentials.kt
|
||||||
|
.kotlin/sessions/*
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
LunaTracker is a newborn baby tracking app.
|
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, even 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.
|
||||||
|
|
||||||
|
@ -7,22 +7,29 @@ import android.util.Log
|
|||||||
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.AdapterView
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.NumberPicker
|
import android.widget.NumberPicker
|
||||||
import android.widget.PopupWindow
|
import android.widget.PopupWindow
|
||||||
|
import android.widget.Spinner
|
||||||
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
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.children
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
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.google.android.material.slider.Slider
|
||||||
import com.thegrizzlylabs.sardineandroid.impl.SardineException
|
import com.thegrizzlylabs.sardineandroid.impl.SardineException
|
||||||
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.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.LogbookListObtainedListener
|
||||||
import it.danieleverducci.lunatracker.repository.LogbookLoadedListener
|
import it.danieleverducci.lunatracker.repository.LogbookLoadedListener
|
||||||
import it.danieleverducci.lunatracker.repository.LogbookRepository
|
import it.danieleverducci.lunatracker.repository.LogbookRepository
|
||||||
import it.danieleverducci.lunatracker.repository.LogbookSavedListener
|
import it.danieleverducci.lunatracker.repository.LogbookSavedListener
|
||||||
@ -30,7 +37,6 @@ 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 utils.NumericUtils
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
@ -39,6 +45,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
companion object {
|
companion object {
|
||||||
val TAG = "MainActivity"
|
val TAG = "MainActivity"
|
||||||
val UPDATE_EVERY_SECS: Long = 30
|
val UPDATE_EVERY_SECS: Long = 30
|
||||||
|
val DEBUG_CHECK_LOGBOOK_CONSISTENCY = false
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var logbook: Logbook
|
lateinit var logbook: Logbook
|
||||||
@ -53,6 +60,7 @@ 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)
|
||||||
@ -75,6 +83,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
|
|
||||||
// Set listeners
|
// Set listeners
|
||||||
|
findViewById<View>(R.id.logbooks_add_button).setOnClickListener { showAddLogbookDialog() }
|
||||||
findViewById<View>(R.id.button_bottle).setOnClickListener { askBabyBottleContent() }
|
findViewById<View>(R.id.button_bottle).setOnClickListener { askBabyBottleContent() }
|
||||||
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(
|
||||||
@ -154,7 +163,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
|
|
||||||
// Reload data
|
// Reload data
|
||||||
loadLogbook()
|
loadLogbookList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
@ -206,6 +215,30 @@ class MainActivity : AppCompatActivity() {
|
|||||||
alertDialog.show()
|
alertDialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun askTemperatureValue() {
|
||||||
|
// Show number picker dialog
|
||||||
|
val d = AlertDialog.Builder(this)
|
||||||
|
val dialogView = layoutInflater.inflate(R.layout.temperature_dialog, null)
|
||||||
|
d.setTitle(R.string.log_temperature_dialog_title)
|
||||||
|
d.setMessage(R.string.log_temperature_dialog_description)
|
||||||
|
d.setView(dialogView)
|
||||||
|
val tempSlider = dialogView.findViewById<Slider>(R.id.dialog_temperature_value)
|
||||||
|
val range = NumericUtils(this).getValidEventQuantityRange(LunaEvent.TYPE_TEMPERATURE)!!
|
||||||
|
tempSlider.valueFrom = range.first.toFloat()
|
||||||
|
tempSlider.valueTo = range.second.toFloat()
|
||||||
|
tempSlider.value = range.third.toFloat()
|
||||||
|
val tempTextView = dialogView.findViewById<TextView>(R.id.dialog_temperature_display)
|
||||||
|
tempTextView.text = range.third.toString()
|
||||||
|
tempSlider.addOnChangeListener({s, v, b -> tempTextView.text = v.toString()})
|
||||||
|
d.setPositiveButton(android.R.string.ok) { dialogInterface, i ->
|
||||||
|
val temperature = (tempSlider.value * 10).toInt() // In tenth of a grade
|
||||||
|
logEvent(LunaEvent(LunaEvent.TYPE_TEMPERATURE, temperature))
|
||||||
|
}
|
||||||
|
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() }
|
||||||
|
val alertDialog = d.create()
|
||||||
|
alertDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
fun askNotes(lunaEvent: LunaEvent) {
|
fun askNotes(lunaEvent: LunaEvent) {
|
||||||
val d = AlertDialog.Builder(this)
|
val d = AlertDialog.Builder(this)
|
||||||
val dialogView = layoutInflater.inflate(R.layout.dialog_notes, null)
|
val dialogView = layoutInflater.inflate(R.layout.dialog_notes, null)
|
||||||
@ -278,7 +311,74 @@ class MainActivity : AppCompatActivity() {
|
|||||||
alertDialog.show()
|
alertDialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadLogbook() {
|
fun showAddLogbookDialog() {
|
||||||
|
val d = AlertDialog.Builder(this)
|
||||||
|
d.setTitle(R.string.dialog_add_logbook_title)
|
||||||
|
val dialogView = layoutInflater.inflate(R.layout.dialog_add_logbook, null)
|
||||||
|
val logbookNameEditText = dialogView.findViewById<EditText>(R.id.dialog_add_logbook_logbookname)
|
||||||
|
d.setView(dialogView)
|
||||||
|
d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> addLogbook(logbookNameEditText.text.toString()) }
|
||||||
|
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() }
|
||||||
|
val alertDialog = d.create()
|
||||||
|
alertDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadLogbookList() {
|
||||||
|
setLoading(true)
|
||||||
|
logbookRepo?.listLogbooks(this, object: LogbookListObtainedListener {
|
||||||
|
override fun onLogbookListObtained(logbooksNames: ArrayList<String>) {
|
||||||
|
runOnUiThread({
|
||||||
|
// Show logbooks dropdown
|
||||||
|
val spinner = findViewById<Spinner>(R.id.logbooks_spinner)
|
||||||
|
val sAdapter = ArrayAdapter<String>(this@MainActivity, android.R.layout.simple_spinner_item)
|
||||||
|
sAdapter.setDropDownViewResource(R.layout.row_logbook_spinner)
|
||||||
|
for (ln in logbooksNames) {
|
||||||
|
sAdapter.add(
|
||||||
|
if (ln.isEmpty()) getString(R.string.default_logbook_name) else ln
|
||||||
|
)
|
||||||
|
}
|
||||||
|
spinner.adapter = sAdapter
|
||||||
|
spinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(
|
||||||
|
parent: AdapterView<*>?,
|
||||||
|
view: View?,
|
||||||
|
position: Int,
|
||||||
|
id: Long
|
||||||
|
) {
|
||||||
|
// Changed logbook: empty list
|
||||||
|
adapter.items.clear()
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
// Load logbook
|
||||||
|
loadLogbook(logbooksNames.get(position))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onIOError(error: IOException) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onWebDAVError(error: SardineException) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(error: Exception) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addLogbook(logbookName: String) {
|
||||||
|
this.logbook = Logbook(logbookName)
|
||||||
|
saveLogbook()
|
||||||
|
loadLogbookList() // TODO: Does not reload logbooks buttons on top, why?
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadLogbook(name: String = LogbookRepository.DEFAULT_LOGBOOK_NAME) {
|
||||||
if (savingEvent)
|
if (savingEvent)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -288,13 +388,22 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Load data
|
// Load data
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
logbookRepo?.loadLogbook(this, object: LogbookLoadedListener{
|
logbookRepo?.loadLogbook(this, name, object: LogbookLoadedListener{
|
||||||
override fun onLogbookLoaded(lb: Logbook) {
|
override fun onLogbookLoaded(lb: Logbook) {
|
||||||
runOnUiThread({
|
runOnUiThread({
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
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}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,6 +566,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showOverflowPopupWindow(anchor: View) {
|
private fun showOverflowPopupWindow(anchor: View) {
|
||||||
|
if (showingOverflowPopupWindow)
|
||||||
|
return
|
||||||
|
|
||||||
PopupWindow(anchor.context).apply {
|
PopupWindow(anchor.context).apply {
|
||||||
isOutsideTouchable = true
|
isOutsideTouchable = true
|
||||||
val inflater = LayoutInflater.from(anchor.context)
|
val inflater = LayoutInflater.from(anchor.context)
|
||||||
@ -473,12 +585,24 @@ class MainActivity : AppCompatActivity() {
|
|||||||
askNotes(LunaEvent(LunaEvent.TYPE_NOTE))
|
askNotes(LunaEvent(LunaEvent.TYPE_NOTE))
|
||||||
dismiss()
|
dismiss()
|
||||||
})
|
})
|
||||||
contentView.findViewById<View>(R.id.button_custom).setOnClickListener({
|
contentView.findViewById<View>(R.id.button_temperature).setOnClickListener({
|
||||||
Toast.makeText(anchor.context, "TODO: Implement custom events", Toast.LENGTH_SHORT).show()
|
askTemperatureValue()
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
|
contentView.findViewById<View>(R.id.button_colic).setOnClickListener({
|
||||||
|
logEvent(
|
||||||
|
LunaEvent(LunaEvent.TYPE_COLIC)
|
||||||
|
)
|
||||||
dismiss()
|
dismiss()
|
||||||
})
|
})
|
||||||
}.also { popupWindow ->
|
}.also { popupWindow ->
|
||||||
|
popupWindow.setOnDismissListener({
|
||||||
|
Handler(mainLooper).postDelayed({
|
||||||
|
showingOverflowPopupWindow = false
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
popupWindow.showAsDropDown(anchor)
|
popupWindow.showAsDropDown(anchor)
|
||||||
|
showingOverflowPopupWindow = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
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.repository.LocalSettingsRepository
|
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
|
||||||
|
import it.danieleverducci.lunatracker.repository.LogbookRepository
|
||||||
import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
|
import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
|
||||||
import okio.IOException
|
import okio.IOException
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
@ -72,7 +73,7 @@ open class SettingsActivity : AppCompatActivity() {
|
|||||||
textViewWebDAVPass.text.toString()
|
textViewWebDAVPass.text.toString()
|
||||||
)
|
)
|
||||||
progressIndicator.visibility = View.VISIBLE
|
progressIndicator.visibility = View.VISIBLE
|
||||||
webDAVLogbookRepo.createLogbook(this, object: WebDAVLogbookRepository.LogbookCreatedListener{
|
webDAVLogbookRepo.createLogbook(this, LogbookRepository.DEFAULT_LOGBOOK_NAME, object: WebDAVLogbookRepository.LogbookCreatedListener{
|
||||||
override fun onLogbookCreated() {
|
override fun onLogbookCreated() {
|
||||||
runOnUiThread({
|
runOnUiThread({
|
||||||
progressIndicator.visibility = View.INVISIBLE
|
progressIndicator.visibility = View.INVISIBLE
|
||||||
|
@ -5,6 +5,7 @@ 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
|
||||||
@ -16,18 +17,26 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
|
|||||||
val items = ArrayList<LunaEvent>()
|
val items = ArrayList<LunaEvent>()
|
||||||
val numericUtils: NumericUtils
|
val numericUtils: NumericUtils
|
||||||
var onItemClickListener: OnItemClickListener? = null
|
var onItemClickListener: OnItemClickListener? = null
|
||||||
|
val layoutRes: Int
|
||||||
|
|
||||||
constructor(context: Context) {
|
constructor(context: Context) {
|
||||||
this.context = context
|
this.context = context
|
||||||
this.numericUtils = NumericUtils(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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +49,7 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
|
|||||||
holder.root.setBackgroundResource(
|
holder.root.setBackgroundResource(
|
||||||
if (position % 2 == 0) R.color.list_background_even else R.color.list_background_odd
|
if (position % 2 == 0) R.color.list_background_even else R.color.list_background_odd
|
||||||
)
|
)
|
||||||
|
holder.quantity.setTextColor(ContextCompat.getColor(context, R.color.textColor))
|
||||||
// Contents
|
// Contents
|
||||||
holder.type.text = item.getTypeEmoji(context)
|
holder.type.text = item.getTypeEmoji(context)
|
||||||
holder.description.text = when(item.type) {
|
holder.description.text = when(item.type) {
|
||||||
@ -49,7 +59,23 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
|
|||||||
else -> item.getTypeDescription(context)
|
else -> item.getTypeDescription(context)
|
||||||
}
|
}
|
||||||
holder.time.text = DateUtils.formatTimeAgo(context, item.time)
|
holder.time.text = DateUtils.formatTimeAgo(context, item.time)
|
||||||
holder.quantity.text = numericUtils.formatEventQuantity(item)
|
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 = quantityText
|
||||||
|
|
||||||
// Listeners
|
// Listeners
|
||||||
if (onItemClickListener != null) {
|
if (onItemClickListener != null) {
|
||||||
holder.root.setOnClickListener({
|
holder.root.setOnClickListener({
|
||||||
@ -62,6 +88,18 @@ class LunaEventRecyclerAdapter: RecyclerView.Adapter<LunaEventRecyclerAdapter.Lu
|
|||||||
return items.size
|
return items.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getPreviousWeightEvent(startFromPosition: Int): LunaEvent? {
|
||||||
|
if (startFromPosition == items.size - 1)
|
||||||
|
return null
|
||||||
|
for (pos in startFromPosition + 1 until items.size) {
|
||||||
|
val item = items.get(pos)
|
||||||
|
if (item.type != LunaEvent.TYPE_WEIGHT)
|
||||||
|
continue
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
class LunaEventVH: RecyclerView.ViewHolder {
|
class LunaEventVH: RecyclerView.ViewHolder {
|
||||||
val root: View
|
val root: View
|
||||||
val type: TextView
|
val type: TextView
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package it.danieleverducci.lunatracker.entities
|
package it.danieleverducci.lunatracker.entities
|
||||||
|
|
||||||
class Logbook {
|
class Logbook(val name: String) {
|
||||||
companion object {
|
companion object {
|
||||||
val MAX_SAFE_LOGBOOK_SIZE = 30000
|
val MAX_SAFE_LOGBOOK_SIZE = 30000
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ class LunaEvent {
|
|||||||
val TYPE_ENEMA = "ENEMA"
|
val TYPE_ENEMA = "ENEMA"
|
||||||
val TYPE_NOTE = "NOTE"
|
val TYPE_NOTE = "NOTE"
|
||||||
val TYPE_CUSTOM = "CUSTOM"
|
val TYPE_CUSTOM = "CUSTOM"
|
||||||
|
val TYPE_COLIC = "COLIC"
|
||||||
|
val TYPE_TEMPERATURE = "TEMPERATURE"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val jo: JSONObject
|
private val jo: JSONObject
|
||||||
@ -84,6 +86,8 @@ class LunaEvent {
|
|||||||
TYPE_MEDICINE -> R.string.event_medicine_type
|
TYPE_MEDICINE -> R.string.event_medicine_type
|
||||||
TYPE_ENEMA -> R.string.event_enema_type
|
TYPE_ENEMA -> R.string.event_enema_type
|
||||||
TYPE_NOTE -> R.string.event_note_type
|
TYPE_NOTE -> R.string.event_note_type
|
||||||
|
TYPE_TEMPERATURE -> R.string.event_temperature_type
|
||||||
|
TYPE_COLIC -> R.string.event_colic_type
|
||||||
else -> R.string.event_unknown_type
|
else -> R.string.event_unknown_type
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -102,6 +106,8 @@ class LunaEvent {
|
|||||||
TYPE_MEDICINE -> R.string.event_medicine_desc
|
TYPE_MEDICINE -> R.string.event_medicine_desc
|
||||||
TYPE_ENEMA -> R.string.event_enema_desc
|
TYPE_ENEMA -> R.string.event_enema_desc
|
||||||
TYPE_NOTE -> R.string.event_note_desc
|
TYPE_NOTE -> R.string.event_note_desc
|
||||||
|
TYPE_TEMPERATURE -> R.string.event_temperature_desc
|
||||||
|
TYPE_COLIC -> R.string.event_colic_desc
|
||||||
else -> R.string.event_unknown_desc
|
else -> R.string.event_unknown_desc
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -9,26 +9,30 @@ 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
|
||||||
|
import java.io.FilenameFilter
|
||||||
|
|
||||||
class FileLogbookRepository: LogbookRepository {
|
class FileLogbookRepository: LogbookRepository {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "FileLogbookRepository"
|
val TAG = "FileLogbookRepository"
|
||||||
|
val FILE_NAME_START = "data"
|
||||||
|
val FILE_NAME_END = ".json"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLogbook(context: Context, listener: LogbookLoadedListener) {
|
override fun loadLogbook(context: Context, name: String, listener: LogbookLoadedListener) {
|
||||||
try {
|
try {
|
||||||
listener.onLogbookLoaded(loadLogbook(context))
|
listener.onLogbookLoaded(loadLogbook(context, name))
|
||||||
} catch (e: FileNotFoundException) {
|
} catch (e: FileNotFoundException) {
|
||||||
Log.d(TAG, "No logbook file found, create one")
|
Log.d(TAG, "No logbook file found, create one")
|
||||||
val newLogbook = Logbook()
|
val newLogbook = Logbook(name)
|
||||||
saveLogbook(context, newLogbook)
|
saveLogbook(context, newLogbook)
|
||||||
listener.onLogbookLoaded(newLogbook)
|
listener.onLogbookLoaded(newLogbook)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadLogbook(context: Context): Logbook {
|
fun loadLogbook(context: Context, name: String): Logbook {
|
||||||
val logbook = Logbook()
|
val logbook = Logbook(name)
|
||||||
val file = File(context.getFilesDir(), "data.json")
|
val fileName = getFileName(name)
|
||||||
|
val file = File(context.getFilesDir(), fileName)
|
||||||
val json = FileInputStream(file).bufferedReader().use { it.readText() }
|
val json = FileInputStream(file).bufferedReader().use { it.readText() }
|
||||||
val ja = JSONArray(json)
|
val ja = JSONArray(json)
|
||||||
for (i in 0 until ja.length()) {
|
for (i in 0 until ja.length()) {
|
||||||
@ -53,11 +57,42 @@ class FileLogbookRepository: LogbookRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun saveLogbook(context: Context, logbook: Logbook) {
|
fun saveLogbook(context: Context, logbook: Logbook) {
|
||||||
val file = File(context.getFilesDir(), "data.json")
|
val fileName = getFileName(logbook.name)
|
||||||
|
val file = File(context.getFilesDir(), fileName)
|
||||||
val ja = JSONArray()
|
val ja = JSONArray()
|
||||||
for (l in logbook.logs) {
|
for (l in logbook.logs) {
|
||||||
ja.put(l.toJson())
|
ja.put(l.toJson())
|
||||||
}
|
}
|
||||||
file.writeText(ja.toString())
|
file.writeText(ja.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun listLogbooks(
|
||||||
|
context: Context,
|
||||||
|
listener: LogbookListObtainedListener
|
||||||
|
) {
|
||||||
|
val logbooksFileNames = context.getFilesDir().list(object: FilenameFilter {
|
||||||
|
override fun accept(dir: File?, name: String?): Boolean {
|
||||||
|
if (name == null)
|
||||||
|
return false
|
||||||
|
if (name.startsWith(FILE_NAME_START) && name.endsWith(FILE_NAME_END))
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (logbooksFileNames == null || logbooksFileNames.isEmpty())
|
||||||
|
listener.onLogbookListObtained(arrayListOf())
|
||||||
|
|
||||||
|
val logbooksNames = arrayListOf<String>()
|
||||||
|
logbooksFileNames.forEach { it ->
|
||||||
|
logbooksNames.add(
|
||||||
|
it.replace("${FILE_NAME_START}_", "").replace(FILE_NAME_START, "").replace(FILE_NAME_END, "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
listener.onLogbookListObtained(logbooksNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFileName(name: String): String {
|
||||||
|
return "$FILE_NAME_START${if (name.isNotEmpty()) "_" else ""}${name}$FILE_NAME_END"
|
||||||
|
}
|
||||||
}
|
}
|
@ -7,8 +7,12 @@ import okio.IOException
|
|||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
|
|
||||||
interface LogbookRepository {
|
interface LogbookRepository {
|
||||||
fun loadLogbook(context: Context, listener: LogbookLoadedListener)
|
companion object {
|
||||||
|
val DEFAULT_LOGBOOK_NAME = "" // For compatibility with older app versions
|
||||||
|
}
|
||||||
|
fun loadLogbook(context: Context, name: String = "", listener: LogbookLoadedListener)
|
||||||
fun saveLogbook(context: Context,logbook: Logbook, listener: LogbookSavedListener)
|
fun saveLogbook(context: Context,logbook: Logbook, listener: LogbookSavedListener)
|
||||||
|
fun listLogbooks(context: Context, listener: LogbookListObtainedListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LogbookLoadedListener {
|
interface LogbookLoadedListener {
|
||||||
@ -26,3 +30,10 @@ interface LogbookSavedListener {
|
|||||||
fun onJSONError(error: JSONException)
|
fun onJSONError(error: JSONException)
|
||||||
fun onError(error: Exception)
|
fun onError(error: Exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LogbookListObtainedListener {
|
||||||
|
fun onLogbookListObtained(logbooksNames: ArrayList<String>)
|
||||||
|
fun onIOError(error: IOException)
|
||||||
|
fun onWebDAVError(error: SardineException)
|
||||||
|
fun onError(error: Exception)
|
||||||
|
}
|
@ -2,6 +2,7 @@ package it.danieleverducci.lunatracker.repository
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.thegrizzlylabs.sardineandroid.DavResource
|
||||||
import com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine
|
import com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine
|
||||||
import com.thegrizzlylabs.sardineandroid.impl.SardineException
|
import com.thegrizzlylabs.sardineandroid.impl.SardineException
|
||||||
import it.danieleverducci.lunatracker.entities.Logbook
|
import it.danieleverducci.lunatracker.entities.Logbook
|
||||||
@ -14,11 +15,13 @@ import java.io.FileNotFoundException
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import kotlin.io.bufferedReader
|
import kotlin.io.bufferedReader
|
||||||
|
import kotlin.text.replace
|
||||||
|
|
||||||
class WebDAVLogbookRepository(val webDavURL: String, val username: String, val password: String): LogbookRepository {
|
class WebDAVLogbookRepository(val webDavURL: String, val username: String, val password: String): LogbookRepository {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "LogbookRepository"
|
val TAG = "LogbookRepository"
|
||||||
val FILE_NAME = "lunatracker_logbook.json"
|
val FILE_NAME_START = "lunatracker_logbook"
|
||||||
|
val FILE_NAME_END = ".json"
|
||||||
}
|
}
|
||||||
val sardine: OkHttpSardine = OkHttpSardine()
|
val sardine: OkHttpSardine = OkHttpSardine()
|
||||||
|
|
||||||
@ -29,10 +32,10 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLogbook(context: Context, listener: LogbookLoadedListener) {
|
override fun loadLogbook(context: Context, name: String, listener: LogbookLoadedListener) {
|
||||||
Thread(Runnable {
|
Thread(Runnable {
|
||||||
try {
|
try {
|
||||||
val logbook = loadLogbook(context)
|
val logbook = loadLogbook(name)
|
||||||
listener.onLogbookLoaded(logbook)
|
listener.onLogbookLoaded(logbook)
|
||||||
} catch (e: SardineException) {
|
} catch (e: SardineException) {
|
||||||
Log.e(TAG, e.toString())
|
Log.e(TAG, e.toString())
|
||||||
@ -52,12 +55,12 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
|||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadLogbook(context: Context): Logbook {
|
private fun loadLogbook(name: String,): Logbook {
|
||||||
val inputStream = sardine.get("$webDavURL/$FILE_NAME")
|
val inputStream = sardine.get(getUrl(name))
|
||||||
val json = inputStream.bufferedReader().use(BufferedReader::readText)
|
val json = inputStream.bufferedReader().use(BufferedReader::readText)
|
||||||
inputStream.close()
|
inputStream.close()
|
||||||
val ja = JSONArray(json)
|
val ja = JSONArray(json)
|
||||||
val logbook = Logbook()
|
val logbook = Logbook(name)
|
||||||
for (i in 0 until ja.length()) {
|
for (i in 0 until ja.length()) {
|
||||||
try {
|
try {
|
||||||
val evt: LunaEvent = LunaEvent(ja.getJSONObject(i))
|
val evt: LunaEvent = LunaEvent(ja.getJSONObject(i))
|
||||||
@ -95,6 +98,27 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
|||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun listLogbooks(
|
||||||
|
context: Context,
|
||||||
|
listener: LogbookListObtainedListener
|
||||||
|
) {
|
||||||
|
Thread(Runnable {
|
||||||
|
val logbooksNames = arrayListOf<String>()
|
||||||
|
for (dr: DavResource in sardine.list(webDavURL)){
|
||||||
|
if(!dr.name.startsWith(FILE_NAME_START))
|
||||||
|
continue
|
||||||
|
if(!dr.name.endsWith(FILE_NAME_END))
|
||||||
|
continue
|
||||||
|
logbooksNames.add(
|
||||||
|
dr.name.replace("${FILE_NAME_START}_", "")
|
||||||
|
.replace(FILE_NAME_START, "")
|
||||||
|
.replace(FILE_NAME_END, "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
listener.onLogbookListObtained(logbooksNames)
|
||||||
|
}).start()
|
||||||
|
}
|
||||||
|
|
||||||
private fun saveLogbook(context: Context, logbook: Logbook) {
|
private fun saveLogbook(context: Context, logbook: Logbook) {
|
||||||
// Lock logbook on WebDAV to avoid concurrent changes
|
// Lock logbook on WebDAV to avoid concurrent changes
|
||||||
//sardine.lock(getUrl())
|
//sardine.lock(getUrl())
|
||||||
@ -108,30 +132,30 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
|||||||
for (l in logbook.logs) {
|
for (l in logbook.logs) {
|
||||||
ja.put(l.toJson())
|
ja.put(l.toJson())
|
||||||
}
|
}
|
||||||
sardine.put(getUrl(), ja.toString().toByteArray())
|
sardine.put(getUrl(logbook.name), ja.toString().toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to server and check if a logbook already exists.
|
* Connect to server and check if a logbook already exists.
|
||||||
* If it does not exist, try to upload the local one (or create a new one).
|
* If it does not exist, try to upload the local one (or create a new one).
|
||||||
*/
|
*/
|
||||||
fun createLogbook(context: Context, listener: LogbookCreatedListener) {
|
fun createLogbook(context: Context, name: String, listener: LogbookCreatedListener) {
|
||||||
Thread(Runnable {
|
Thread(Runnable {
|
||||||
try {
|
try {
|
||||||
loadLogbook(context)
|
loadLogbook(name)
|
||||||
listener.onLogbookCreated()
|
listener.onLogbookCreated()
|
||||||
} catch (e: SardineException) {
|
} catch (e: SardineException) {
|
||||||
if (e.toString().contains("404")) {
|
if (e.toString().contains("404")) {
|
||||||
// Connection successful, but no existing save. Upload the local one.
|
// Connection successful, but no existing save. Upload the local one.
|
||||||
try {
|
try {
|
||||||
val flr = FileLogbookRepository()
|
val flr = FileLogbookRepository()
|
||||||
val logbook = flr.loadLogbook(context)
|
val logbook = flr.loadLogbook(context, name)
|
||||||
saveLogbook(context, logbook)
|
saveLogbook(context, logbook)
|
||||||
Log.d(TAG, "Local logbook file found, uploaded")
|
Log.d(TAG, "Local logbook file found, uploaded")
|
||||||
listener.onLogbookCreated()
|
listener.onLogbookCreated()
|
||||||
} catch (e: FileNotFoundException) {
|
} catch (e: FileNotFoundException) {
|
||||||
Log.d(TAG, "No local logbook file found, uploading empty file")
|
Log.d(TAG, "No local logbook file found, uploading empty file")
|
||||||
saveLogbook(context, Logbook())
|
saveLogbook(context, Logbook(name))
|
||||||
listener.onLogbookCreated()
|
listener.onLogbookCreated()
|
||||||
} catch (e: SardineException) {
|
} catch (e: SardineException) {
|
||||||
Log.e(TAG, "Unable to upload logbook: $e")
|
Log.e(TAG, "Unable to upload logbook: $e")
|
||||||
@ -156,8 +180,10 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
|||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getUrl(): String {
|
private fun getUrl(name: String): String {
|
||||||
return "$webDavURL/$FILE_NAME"
|
val fileName = "${FILE_NAME_START}${if (name.isNotEmpty()) "_" else ""}${name}${FILE_NAME_END}"
|
||||||
|
Log.d(TAG, fileName)
|
||||||
|
return "$webDavURL/$fileName"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ class NumericUtils (val context: Context) {
|
|||||||
val measurement_unit_liquid_base: String
|
val measurement_unit_liquid_base: String
|
||||||
val measurement_unit_weight_base: String
|
val measurement_unit_weight_base: String
|
||||||
val measurement_unit_weight_tiny: String
|
val measurement_unit_weight_tiny: String
|
||||||
|
val measurement_unit_temperature_base: String
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.numberFormat = NumberFormat.getInstance()
|
this.numberFormat = NumberFormat.getInstance()
|
||||||
@ -34,18 +35,58 @@ class NumericUtils (val context: Context) {
|
|||||||
else
|
else
|
||||||
R.string.measurement_unit_weight_tiny_imperial
|
R.string.measurement_unit_weight_tiny_imperial
|
||||||
)
|
)
|
||||||
|
this.measurement_unit_temperature_base = context.getString(
|
||||||
|
if (measurementSystem == LocaleData. MeasurementSystem.SI)
|
||||||
|
R.string.measurement_unit_temperature_base_metric
|
||||||
|
else
|
||||||
|
R.string.measurement_unit_temperature_base_imperial
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatEventQuantity(item: LunaEvent): String {
|
fun formatEventQuantity(item: LunaEvent): String {
|
||||||
return if ((item.quantity ?: 0) > 0) {
|
val formatted = StringBuilder()
|
||||||
numberFormat.format(item.quantity) + " " + when (item.type) {
|
if ((item.quantity ?: 0) > 0) {
|
||||||
|
if (item.type == LunaEvent.TYPE_TEMPERATURE)
|
||||||
|
formatted.append((item.quantity / 10.0f).toString())
|
||||||
|
else
|
||||||
|
formatted.append(item.quantity)
|
||||||
|
|
||||||
|
formatted.append(" ")
|
||||||
|
formatted.append(
|
||||||
|
when (item.type) {
|
||||||
LunaEvent.TYPE_BABY_BOTTLE -> measurement_unit_liquid_base
|
LunaEvent.TYPE_BABY_BOTTLE -> measurement_unit_liquid_base
|
||||||
LunaEvent.TYPE_WEIGHT -> measurement_unit_weight_base
|
LunaEvent.TYPE_WEIGHT -> measurement_unit_weight_base
|
||||||
LunaEvent.TYPE_MEDICINE -> measurement_unit_weight_tiny
|
LunaEvent.TYPE_MEDICINE -> measurement_unit_weight_tiny
|
||||||
|
LunaEvent.TYPE_TEMPERATURE -> measurement_unit_temperature_base
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
} else {
|
)
|
||||||
""
|
}
|
||||||
|
return formatted.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a valid quantity range for the event type.
|
||||||
|
* @return min, max, normal
|
||||||
|
*/
|
||||||
|
fun getValidEventQuantityRange(lunaEventType: String): Triple<Int, Int, Int>? {
|
||||||
|
val measurementSystem = LocaleData.getMeasurementSystem(ULocale.getDefault())
|
||||||
|
return when (lunaEventType) {
|
||||||
|
LunaEvent.TYPE_TEMPERATURE -> {
|
||||||
|
if (measurementSystem == LocaleData. MeasurementSystem.SI)
|
||||||
|
Triple(
|
||||||
|
context.resources.getInteger(R.integer.human_body_temp_min_metric),
|
||||||
|
context.resources.getInteger(R.integer.human_body_temp_max_metric),
|
||||||
|
context.resources.getInteger(R.integer.human_body_temp_normal_metric)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Triple(
|
||||||
|
context.resources.getInteger(R.integer.human_body_temp_min_imperial),
|
||||||
|
context.resources.getInteger(R.integer.human_body_temp_max_imperial),
|
||||||
|
context.resources.getInteger(R.integer.human_body_temp_normal_imperial)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_pressed="true"
|
||||||
|
android:drawable="@drawable/dropdown_list_item_background_pressed"/>
|
||||||
|
<item android:drawable="@drawable/dropdown_list_item_background_released"/>
|
||||||
|
</selector>
|
@ -0,0 +1,14 @@
|
|||||||
|
<?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" />
|
||||||
|
<solid
|
||||||
|
android:color="@color/grey" />
|
||||||
|
<corners android:radius="15dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="5dp"
|
||||||
|
android:left="5dp"
|
||||||
|
android:right="5dp"
|
||||||
|
android:top="5dp" />
|
||||||
|
</shape>
|
@ -0,0 +1,14 @@
|
|||||||
|
<?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" />
|
||||||
|
<solid
|
||||||
|
android:color="@color/cardview_dark_background"/>
|
||||||
|
<corners android:radius="15dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="5dp"
|
||||||
|
android:left="5dp"
|
||||||
|
android:right="5dp"
|
||||||
|
android:top="5dp" />
|
||||||
|
</shape>
|
@ -6,18 +6,78 @@
|
|||||||
<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="10dp"
|
||||||
android:paddingLeft="15dp"
|
android:paddingLeft="15dp"
|
||||||
android:paddingRight="15dp">
|
android:paddingRight="15dp">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/button_settings"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:src="@drawable/ic_settings"
|
||||||
|
app:tint="@color/grey"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
android:text="@string/title"
|
android:text="@string/title"
|
||||||
android:textSize="30sp"
|
android:textSize="26dp"
|
||||||
android:gravity="center_horizontal"/>
|
android:gravity="center"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/button_sync"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:src="@drawable/ic_sync"
|
||||||
|
app:tint="@color/grey"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="38dp"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/button_background">
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/logbooks_spinner"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/logbooks_add_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/accent"
|
||||||
|
android:textSize="20dp"
|
||||||
|
android:text="+"
|
||||||
|
android:background="@drawable/button_background"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -152,7 +212,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"/>
|
||||||
|
|
||||||
@ -165,24 +225,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/button_settings"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="10dp"
|
|
||||||
android:layout_gravity="end"
|
|
||||||
android:src="@drawable/ic_settings"
|
|
||||||
app:tint="@color/grey"/>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/button_sync"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="10dp"
|
|
||||||
android:layout_gravity="start"
|
|
||||||
android:src="@drawable/ic_sync"
|
|
||||||
app:tint="@color/grey"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/no_connection_screen"
|
android:id="@+id/no_connection_screen"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -122,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
|
||||||
@ -130,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>
|
||||||
|
24
app/src/main/res/layout/dialog_add_logbook.xml
Normal file
24
app/src/main/res/layout/dialog_add_logbook.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/dialog_add_logbook_message"/>
|
||||||
|
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/dialog_add_logbook_logbookname"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:lines="1"
|
||||||
|
android:inputType="text"
|
||||||
|
android:hint="@string/dialog_add_logbook_logbookname"
|
||||||
|
android:background="@drawable/textview_background"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -15,7 +15,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="20dp"
|
android:padding="20dp"
|
||||||
android:background="@drawable/button_background"
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
style="@style/OverflowMenuText"
|
style="@style/OverflowMenuText"
|
||||||
android:text="@string/overflow_event_medicine"/>
|
android:text="@string/overflow_event_medicine"/>
|
||||||
|
|
||||||
@ -25,7 +25,7 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:padding="20dp"
|
android:padding="20dp"
|
||||||
android:background="@drawable/button_background"
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
style="@style/OverflowMenuText"
|
style="@style/OverflowMenuText"
|
||||||
android:text="@string/overflow_event_enema"/>
|
android:text="@string/overflow_event_enema"/>
|
||||||
|
|
||||||
@ -35,27 +35,29 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:padding="20dp"
|
android:padding="20dp"
|
||||||
android:background="@drawable/button_background"
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
style="@style/OverflowMenuText"
|
style="@style/OverflowMenuText"
|
||||||
android:text="@string/overflow_event_note"/>
|
android:text="@string/overflow_event_note"/>
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/overflow_event_custom_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/button_custom"
|
android:id="@+id/button_temperature"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:padding="20dp"
|
android:padding="20dp"
|
||||||
android:background="@drawable/button_background"
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
style="@style/OverflowMenuText"
|
style="@style/OverflowMenuText"
|
||||||
android:text="@string/overflow_event_custom"/>
|
android:text="@string/overflow_event_temperature"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_colic"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:padding="20dp"
|
||||||
|
android:background="@drawable/dropdown_list_item_background"
|
||||||
|
style="@style/OverflowMenuText"
|
||||||
|
android:text="@string/overflow_event_colic"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
11
app/src/main/res/layout/row_logbook_spinner.xml
Normal file
11
app/src/main/res/layout/row_logbook_spinner.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/text1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingEnd="20dp"
|
||||||
|
android:paddingTop="20dp"
|
||||||
|
android:paddingBottom="20dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"/>
|
@ -24,6 +24,7 @@
|
|||||||
android:layout_weight="2"
|
android:layout_weight="2"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
|
android:textColor="@color/accent"
|
||||||
android:text="Description"/>
|
android:text="Description"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -34,8 +35,6 @@
|
|||||||
android:layout_marginLeft="5dp"
|
android:layout_marginLeft="5dp"
|
||||||
android:layout_marginRight="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
|
||||||
|
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>
|
26
app/src/main/res/layout/temperature_dialog.xml
Normal file
26
app/src/main/res/layout/temperature_dialog.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.slider.Slider
|
||||||
|
android:id="@+id/dialog_temperature_value"
|
||||||
|
android:layout_width="250dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:valueFrom="@integer/human_body_temp_min_metric"
|
||||||
|
android:valueTo="@integer/human_body_temp_max_metric"
|
||||||
|
android:stepSize="0.1"
|
||||||
|
android:value="@integer/human_body_temp_normal_metric"
|
||||||
|
android:theme="@style/LTSlider"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_temperature_display"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="30sp"
|
||||||
|
android:textColor="@color/accent"/>
|
||||||
|
</LinearLayout>
|
@ -10,10 +10,14 @@
|
|||||||
<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="log_temperature_dialog_title">Temperatura</string>
|
||||||
|
<string name="log_temperature_dialog_description">Inserisci la temperatura</string>
|
||||||
|
|
||||||
<string name="overflow_event_medicine">💊 Medicina</string>
|
<string name="overflow_event_medicine">💊 Medicina</string>
|
||||||
<string name="overflow_event_enema">🪠 Clistere</string>
|
<string name="overflow_event_enema">🪠 Clistere</string>
|
||||||
<string name="overflow_event_note">📝 Nota</string>
|
<string name="overflow_event_note">📝 Nota</string>
|
||||||
<string name="overflow_event_custom">➕ Personalizzato</string>
|
<string name="overflow_event_temperature">🌡️ Temperatura</string>
|
||||||
|
<string name="overflow_event_colic">💨 Colichette</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>
|
||||||
@ -25,6 +29,8 @@
|
|||||||
<string name="event_medicine_desc">Medicina</string>
|
<string name="event_medicine_desc">Medicina</string>
|
||||||
<string name="event_enema_desc">Clistere</string>
|
<string name="event_enema_desc">Clistere</string>
|
||||||
<string name="event_note_desc">Nota</string>
|
<string name="event_note_desc">Nota</string>
|
||||||
|
<string name="event_temperature_desc">Temperatura</string>
|
||||||
|
<string name="event_colic_desc">Colichette</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>
|
||||||
@ -74,4 +80,10 @@
|
|||||||
|
|
||||||
<string name="dialog_event_detail_title">Dettaglio evento</string>
|
<string name="dialog_event_detail_title">Dettaglio evento</string>
|
||||||
|
|
||||||
|
<string name="dialog_add_logbook_title">Aggiungi diario</string>
|
||||||
|
<string name="dialog_add_logbook_logbookname">Nome del diario</string>
|
||||||
|
<string name="dialog_add_logbook_message">Scrivi un nome per identificare questo diario. Comparirà in cima allo schermo, e se usi WebDAV sarà incluso anche nel nome del file di salvataggio.</string>
|
||||||
|
|
||||||
|
<string name="default_logbook_name">👶 Il mio primo diario</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -8,4 +8,6 @@
|
|||||||
<color name="grey">#ccc</color>
|
<color name="grey">#ccc</color>
|
||||||
<color name="list_background_odd">#423B25</color>
|
<color name="list_background_odd">#423B25</color>
|
||||||
<color name="list_background_even">@color/transparent</color>
|
<color name="list_background_even">@color/transparent</color>
|
||||||
|
<color name="danger">#f00</color>
|
||||||
|
<color name="textColor">@color/grey</color>
|
||||||
</resources>
|
</resources>
|
@ -10,6 +10,9 @@
|
|||||||
<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="log_temperature_dialog_title">Temperature</string>
|
||||||
|
<string name="log_temperature_dialog_description">Insert the temperature</string>
|
||||||
|
|
||||||
<string name="event_bottle_type" translatable="false">🍼</string>
|
<string name="event_bottle_type" translatable="false">🍼</string>
|
||||||
<string name="event_scale_type" translatable="false">⚖️</string>
|
<string name="event_scale_type" translatable="false">⚖️</string>
|
||||||
<string name="event_breastfeeding_left_type" translatable="false">🤱 ←</string>
|
<string name="event_breastfeeding_left_type" translatable="false">🤱 ←</string>
|
||||||
@ -20,6 +23,8 @@
|
|||||||
<string name="event_medicine_type" translatable="false">💊</string>
|
<string name="event_medicine_type" translatable="false">💊</string>
|
||||||
<string name="event_enema_type" translatable="false">🪠</string>
|
<string name="event_enema_type" translatable="false">🪠</string>
|
||||||
<string name="event_note_type" translatable="false">📝</string>
|
<string name="event_note_type" translatable="false">📝</string>
|
||||||
|
<string name="event_temperature_type" translatable="false">🌡️</string>
|
||||||
|
<string name="event_colic_type" translatable="false">💨</string>
|
||||||
<string name="event_unknown_type" translatable="false">\?</string>
|
<string name="event_unknown_type" translatable="false">\?</string>
|
||||||
|
|
||||||
<string name="event_bottle_desc">Baby bottle</string>
|
<string name="event_bottle_desc">Baby bottle</string>
|
||||||
@ -32,12 +37,15 @@
|
|||||||
<string name="event_medicine_desc">Medicine</string>
|
<string name="event_medicine_desc">Medicine</string>
|
||||||
<string name="event_enema_desc">Enema</string>
|
<string name="event_enema_desc">Enema</string>
|
||||||
<string name="event_note_desc">Note</string>
|
<string name="event_note_desc">Note</string>
|
||||||
|
<string name="event_temperature_desc">Temperature</string>
|
||||||
|
<string name="event_colic_desc">Gaseous colic</string>
|
||||||
<string name="event_unknown_desc"></string>
|
<string name="event_unknown_desc"></string>
|
||||||
|
|
||||||
<string name="overflow_event_medicine">💊 Medicine</string>
|
<string name="overflow_event_medicine">💊 Medicine</string>
|
||||||
<string name="overflow_event_enema">🪠 Enema</string>
|
<string name="overflow_event_enema">🪠 Enema</string>
|
||||||
<string name="overflow_event_note">📝 Note</string>
|
<string name="overflow_event_note">📝 Note</string>
|
||||||
<string name="overflow_event_custom">➕ Add custom</string>
|
<string name="overflow_event_temperature">🌡️ Temperature</string>
|
||||||
|
<string name="overflow_event_colic">💨 Gaseous colic</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>
|
||||||
@ -90,7 +98,15 @@
|
|||||||
<string name="measurement_unit_liquid_base_imperial" translatable="false">fl oz.</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_base_imperial" translatable="false">oz</string>
|
||||||
<string name="measurement_unit_weight_tiny_imperial" translatable="false">gr</string>
|
<string name="measurement_unit_weight_tiny_imperial" translatable="false">gr</string>
|
||||||
|
<string name="measurement_unit_temperature_base_imperial" translatable="false">°F</string>
|
||||||
|
<string name="measurement_unit_temperature_base_metric" translatable="false">°C</string>
|
||||||
|
|
||||||
<string name="dialog_event_detail_title">Event detail</string>
|
<string name="dialog_event_detail_title">Event detail</string>
|
||||||
|
|
||||||
|
<string name="dialog_add_logbook_title">Add logbook</string>
|
||||||
|
<string name="dialog_add_logbook_logbookname">Logbook name</string>
|
||||||
|
<string name="dialog_add_logbook_message">Write a name to identify this logbook. This name will appear on top of the screen and, if you use WebDAV, will be in the save file name as well.</string>
|
||||||
|
|
||||||
|
<string name="default_logbook_name">👶 My first logbook</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -3,9 +3,15 @@
|
|||||||
|
|
||||||
<style name="Theme.LunaTracker" parent="Theme.AppCompat.NoActionBar">
|
<style name="Theme.LunaTracker" parent="Theme.AppCompat.NoActionBar">
|
||||||
<item name="colorAccent">@color/accent</item>
|
<item name="colorAccent">@color/accent</item>
|
||||||
|
<item name="android:textColor">@color/textColor</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="OverflowMenuText">
|
<style name="OverflowMenuText">
|
||||||
<item name="android:textSize">20sp</item>
|
<item name="android:textSize">20sp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="LTSlider" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||||
|
<item name="colorPrimary">@color/accent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
9
app/src/main/res/values/values.xml
Normal file
9
app/src/main/res/values/values.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<integer name="human_body_temp_min_imperial">91</integer>
|
||||||
|
<integer name="human_body_temp_min_metric">33</integer>
|
||||||
|
<integer name="human_body_temp_max_imperial">109</integer>
|
||||||
|
<integer name="human_body_temp_max_metric">43</integer>
|
||||||
|
<integer name="human_body_temp_normal_imperial">98</integer>
|
||||||
|
<integer name="human_body_temp_normal_metric">37</integer>
|
||||||
|
</resources>
|
@ -1,7 +1,5 @@
|
|||||||
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, even 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.
|
||||||
|
|
||||||
|
@ -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