Compare commits
16 Commits
b1e26f8a96
...
efdb8f584a
Author | SHA1 | Date | |
---|---|---|---|
efdb8f584a | |||
1511764497 | |||
744bfef62b | |||
34ca6c1cd6 | |||
b4f47ea6bb | |||
99743288c5 | |||
03ec28f8ef | |||
ac9f74dbd7 | |||
36f52234a3 | |||
e23ab77274 | |||
e3aceaf133 | |||
0494a11538 | |||
ecbde64ca1 | |||
32fbeac079 | |||
0b0fd8f5af | |||
fe5da015cb |
1
.gitignore
vendored
1
.gitignore
vendored
@ -106,3 +106,4 @@ app/release/output-metadata.json
|
||||
|
||||
# Other
|
||||
app/src/main/java/it/danieleverducci/lunatracker/TemporaryHardcodedCredentials.kt
|
||||
.kotlin/sessions/*
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.danieleverducci.lunatracker
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
@ -7,13 +8,18 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.EditText
|
||||
import android.widget.NumberPicker
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.children
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
@ -24,6 +30,7 @@ import it.danieleverducci.lunatracker.entities.Logbook
|
||||
import it.danieleverducci.lunatracker.entities.LunaEvent
|
||||
import it.danieleverducci.lunatracker.repository.FileLogbookRepository
|
||||
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
|
||||
import it.danieleverducci.lunatracker.repository.LogbookListObtainedListener
|
||||
import it.danieleverducci.lunatracker.repository.LogbookLoadedListener
|
||||
import it.danieleverducci.lunatracker.repository.LogbookRepository
|
||||
import it.danieleverducci.lunatracker.repository.LogbookSavedListener
|
||||
@ -42,7 +49,7 @@ class MainActivity : AppCompatActivity() {
|
||||
val DEBUG_CHECK_LOGBOOK_CONSISTENCY = false
|
||||
}
|
||||
|
||||
lateinit var logbook: Logbook
|
||||
var logbook: Logbook? = null
|
||||
lateinit var adapter: LunaEventRecyclerAdapter
|
||||
lateinit var progressIndicator: LinearProgressIndicator
|
||||
lateinit var buttonsContainer: ViewGroup
|
||||
@ -50,7 +57,8 @@ class MainActivity : AppCompatActivity() {
|
||||
lateinit var handler: Handler
|
||||
var savingEvent = false
|
||||
val updateListRunnable: Runnable = Runnable {
|
||||
loadLogbook()
|
||||
if (logbook != null)
|
||||
loadLogbook(logbook!!.name)
|
||||
handler.postDelayed(updateListRunnable, 1000*60)
|
||||
}
|
||||
var logbookRepo: LogbookRepository? = null
|
||||
@ -77,6 +85,7 @@ class MainActivity : AppCompatActivity() {
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
// Set listeners
|
||||
findViewById<View>(R.id.logbooks_add_button).setOnClickListener { showAddLogbookDialog(true) }
|
||||
findViewById<View>(R.id.button_bottle).setOnClickListener { askBabyBottleContent() }
|
||||
findViewById<View>(R.id.button_scale).setOnClickListener { askWeightValue() }
|
||||
findViewById<View>(R.id.button_nipple_left).setOnClickListener { logEvent(
|
||||
@ -115,10 +124,11 @@ class MainActivity : AppCompatActivity() {
|
||||
showSettings()
|
||||
})
|
||||
findViewById<View>(R.id.button_no_connection_retry).setOnClickListener({
|
||||
loadLogbook()
|
||||
// This may happen at start, when logbook is still null: better ask the logbook list
|
||||
loadLogbookList()
|
||||
})
|
||||
findViewById<View>(R.id.button_sync).setOnClickListener({
|
||||
loadLogbook()
|
||||
loadLogbookList()
|
||||
})
|
||||
}
|
||||
|
||||
@ -129,8 +139,11 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
fun showLogbook() {
|
||||
// Show logbook
|
||||
if (logbook == null)
|
||||
Log.w(TAG, "showLogbook(): logbook is null!")
|
||||
|
||||
adapter.items.clear()
|
||||
adapter.items.addAll(logbook.logs)
|
||||
adapter.items.addAll(logbook?.logs ?: listOf())
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@ -155,8 +168,13 @@ class MainActivity : AppCompatActivity() {
|
||||
// Update list dates
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
// Reload data
|
||||
loadLogbook()
|
||||
if (logbook != null) {
|
||||
// Already running: reload data for currently selected logbook
|
||||
loadLogbook(logbook!!.name)
|
||||
} else {
|
||||
// First start: load logbook list
|
||||
loadLogbookList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
@ -276,7 +294,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
)
|
||||
d.setPositiveButton(R.string.trim_logbook_dialog_button_ok) { dialogInterface, i ->
|
||||
logbook.trim()
|
||||
logbook?.trim()
|
||||
saveLogbook()
|
||||
}
|
||||
d.setNegativeButton(R.string.trim_logbook_dialog_button_cancel) { dialogInterface, i ->
|
||||
@ -300,11 +318,154 @@ class MainActivity : AppCompatActivity() {
|
||||
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() }
|
||||
d.setNeutralButton(R.string.dialog_event_detail_delete_button) { dialogInterface, i -> deleteEvent(event) }
|
||||
val alertDialog = d.create()
|
||||
alertDialog.show()
|
||||
alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setTextColor(ContextCompat.getColor(this, R.color.danger))
|
||||
}
|
||||
|
||||
fun showAddLogbookDialog(requestedByUser: Boolean) {
|
||||
val d = AlertDialog.Builder(this)
|
||||
d.setTitle(R.string.dialog_add_logbook_title)
|
||||
val dialogView = layoutInflater.inflate(R.layout.dialog_add_logbook, null)
|
||||
dialogView.findViewById<TextView>(R.id.dialog_add_logbook_message).text = getString(
|
||||
if (requestedByUser) R.string.dialog_add_logbook_message else R.string.dialog_add_logbook_message_intro
|
||||
)
|
||||
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()) }
|
||||
if (requestedByUser) {
|
||||
d.setCancelable(true)
|
||||
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() }
|
||||
} else {
|
||||
d.setCancelable(false)
|
||||
}
|
||||
val alertDialog = d.create()
|
||||
alertDialog.show()
|
||||
}
|
||||
|
||||
fun loadLogbook() {
|
||||
fun loadLogbookList() {
|
||||
setLoading(true)
|
||||
logbookRepo?.listLogbooks(this, object: LogbookListObtainedListener {
|
||||
override fun onLogbookListObtained(logbooksNames: ArrayList<String>) {
|
||||
runOnUiThread({
|
||||
if (logbooksNames.isEmpty()) {
|
||||
// First run, no logbook: create one
|
||||
showAddLogbookDialog(false)
|
||||
return@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) {
|
||||
Log.e(TAG, "Unable to load logbooks list (IOError): $error")
|
||||
runOnUiThread({
|
||||
setLoading(false)
|
||||
onRepoError(getString(R.string.settings_network_error) + error.toString())
|
||||
})
|
||||
}
|
||||
|
||||
override fun onWebDAVError(error: SardineException) {
|
||||
Log.e(TAG, "Unable to load logbooks list (SardineException): $error")
|
||||
runOnUiThread({
|
||||
setLoading(false)
|
||||
onRepoError(
|
||||
if(error.toString().contains("401")) {
|
||||
getString(R.string.settings_webdav_error_denied)
|
||||
} else if(error.toString().contains("503")) {
|
||||
getString(R.string.settings_webdav_error_server_offline)
|
||||
} else {
|
||||
getString(R.string.settings_webdav_error_generic) + error.toString()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(error: Exception) {
|
||||
Log.e(TAG, "Unable to load logbooks list: $error")
|
||||
runOnUiThread({
|
||||
setLoading(false)
|
||||
onRepoError(getString(R.string.settings_generic_error) + error.toString())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun addLogbook(logbookName: String) {
|
||||
val newLogbook = Logbook(logbookName)
|
||||
setLoading(true)
|
||||
logbookRepo?.saveLogbook(this, newLogbook, object: LogbookSavedListener{
|
||||
override fun onLogbookSaved() {
|
||||
Log.d(TAG, "Logbook $logbookName created")
|
||||
runOnUiThread({
|
||||
setLoading(false)
|
||||
loadLogbookList()
|
||||
Toast.makeText(this@MainActivity, getString(R.string.logbook_created) + logbookName, Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
}
|
||||
|
||||
override fun onIOError(error: IOException) {
|
||||
runOnUiThread({
|
||||
onRepoError(getString(R.string.settings_network_error) + error.toString())
|
||||
})
|
||||
}
|
||||
|
||||
override fun onWebDAVError(error: SardineException) {
|
||||
runOnUiThread({
|
||||
onRepoError(
|
||||
if(error.toString().contains("401")) {
|
||||
getString(R.string.settings_webdav_error_denied)
|
||||
} else if(error.toString().contains("503")) {
|
||||
getString(R.string.settings_webdav_error_server_offline)
|
||||
} else {
|
||||
getString(R.string.settings_webdav_error_generic) + error.toString()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
override fun onJSONError(error: JSONException) {
|
||||
runOnUiThread({
|
||||
onRepoError(getString(R.string.settings_json_error) + error.toString())
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(error: Exception) {
|
||||
runOnUiThread({
|
||||
onRepoError(getString(R.string.settings_generic_error) + error.toString())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun loadLogbook(name: String) {
|
||||
if (savingEvent)
|
||||
return
|
||||
|
||||
@ -314,7 +475,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
// Load data
|
||||
setLoading(true)
|
||||
logbookRepo?.loadLogbook(this, object: LogbookLoadedListener{
|
||||
logbookRepo?.loadLogbook(this, name, object: LogbookLoadedListener{
|
||||
override fun onLogbookLoaded(lb: Logbook) {
|
||||
runOnUiThread({
|
||||
setLoading(false)
|
||||
@ -323,7 +484,7 @@ class MainActivity : AppCompatActivity() {
|
||||
showLogbook()
|
||||
|
||||
if (DEBUG_CHECK_LOGBOOK_CONSISTENCY) {
|
||||
for (e in logbook.logs) {
|
||||
for (e in logbook?.logs ?: listOf()) {
|
||||
val em = e.getTypeEmoji(this@MainActivity)
|
||||
if (em == getString(R.string.event_unknown_type)) {
|
||||
Log.e(TAG, "UNKNOWN: ${e.type}")
|
||||
@ -346,6 +507,8 @@ class MainActivity : AppCompatActivity() {
|
||||
onRepoError(
|
||||
if(error.toString().contains("401")) {
|
||||
getString(R.string.settings_webdav_error_denied)
|
||||
} else if(error.toString().contains("503")) {
|
||||
getString(R.string.settings_webdav_error_server_offline)
|
||||
} else {
|
||||
getString(R.string.settings_webdav_error_generic) + error.toString()
|
||||
}
|
||||
@ -385,21 +548,37 @@ class MainActivity : AppCompatActivity() {
|
||||
recyclerView.smoothScrollToPosition(0)
|
||||
|
||||
setLoading(true)
|
||||
logbook.logs.add(0, event)
|
||||
logbook?.logs?.add(0, event)
|
||||
saveLogbook(event)
|
||||
|
||||
// Check logbook size to avoid OOM errors
|
||||
if (logbook.isTooBig()) {
|
||||
if (logbook?.isTooBig() == true) {
|
||||
askToTrimLogbook()
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteEvent(event: LunaEvent) {
|
||||
// Update view
|
||||
savingEvent(true)
|
||||
adapter.items.remove(event)
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
// Update data
|
||||
setLoading(true)
|
||||
logbook?.logs?.remove(event)
|
||||
saveLogbook()
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the logbook. If saving while adding an event, please specify the event so in case
|
||||
* of error can be removed from the list.
|
||||
*/
|
||||
fun saveLogbook(lastEventAdded: LunaEvent? = null) {
|
||||
logbookRepo?.saveLogbook(this, logbook, object: LogbookSavedListener{
|
||||
if (logbook == null) {
|
||||
Log.e(TAG, "Trying to save logbook, but logbook is null!")
|
||||
return
|
||||
}
|
||||
logbookRepo?.saveLogbook(this, logbook!!, object: LogbookSavedListener{
|
||||
override fun onLogbookSaved() {
|
||||
Log.d(TAG, "Logbook saved")
|
||||
runOnUiThread({
|
||||
@ -432,6 +611,8 @@ class MainActivity : AppCompatActivity() {
|
||||
onRepoError(
|
||||
if(error.toString().contains("401")) {
|
||||
getString(R.string.settings_webdav_error_denied)
|
||||
} else if(error.toString().contains("503")) {
|
||||
getString(R.string.settings_webdav_error_server_offline)
|
||||
} else {
|
||||
getString(R.string.settings_webdav_error_generic) + error.toString()
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.thegrizzlylabs.sardineandroid.impl.SardineException
|
||||
import it.danieleverducci.lunatracker.repository.FileLogbookRepository
|
||||
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
|
||||
import it.danieleverducci.lunatracker.repository.LogbookListObtainedListener
|
||||
import it.danieleverducci.lunatracker.repository.LogbookRepository
|
||||
import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
|
||||
import okio.IOException
|
||||
import org.json.JSONException
|
||||
@ -72,13 +75,34 @@ open class SettingsActivity : AppCompatActivity() {
|
||||
textViewWebDAVPass.text.toString()
|
||||
)
|
||||
progressIndicator.visibility = View.VISIBLE
|
||||
webDAVLogbookRepo.createLogbook(this, object: WebDAVLogbookRepository.LogbookCreatedListener{
|
||||
override fun onLogbookCreated() {
|
||||
runOnUiThread({
|
||||
progressIndicator.visibility = View.INVISIBLE
|
||||
saveSettings()
|
||||
Toast.makeText(this@SettingsActivity, R.string.settings_webdav_creation_ok, Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
|
||||
webDAVLogbookRepo.listLogbooks(this, object: LogbookListObtainedListener{
|
||||
|
||||
override fun onLogbookListObtained(logbooksNames: ArrayList<String>) {
|
||||
if (logbooksNames.isEmpty()) {
|
||||
// TODO: Ask the user if he wants to upload the local ones or to create a new one
|
||||
copyLocalLogbooksToWebdav(webDAVLogbookRepo, object: OnCopyLocalLogbooksToWebdavFinishedListener {
|
||||
|
||||
override fun onCopyLocalLogbooksToWebdavFinished(errors: String?) {
|
||||
runOnUiThread({
|
||||
progressIndicator.visibility = View.INVISIBLE
|
||||
if (errors == null) {
|
||||
saveSettings()
|
||||
Toast.makeText(this@SettingsActivity, R.string.settings_webdav_creation_ok, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this@SettingsActivity, errors, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
} else {
|
||||
runOnUiThread({
|
||||
progressIndicator.visibility = View.INVISIBLE
|
||||
saveSettings()
|
||||
Toast.makeText(this@SettingsActivity, R.string.settings_webdav_creation_ok, Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onIOError(error: IOException) {
|
||||
@ -99,6 +123,16 @@ open class SettingsActivity : AppCompatActivity() {
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(error: Exception) {
|
||||
runOnUiThread({
|
||||
progressIndicator.visibility = View.INVISIBLE
|
||||
Toast.makeText(this@SettingsActivity, getString(R.string.settings_generic_error) + error.toString(), Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
/*webDAVLogbookRepo.createLogbook(this, LogbookRepository.DEFAULT_LOGBOOK_NAME, object: WebDAVLogbookRepository.LogbookCreatedListener{
|
||||
|
||||
override fun onJSONError(error: JSONException) {
|
||||
runOnUiThread({
|
||||
progressIndicator.visibility = View.INVISIBLE
|
||||
@ -106,14 +140,8 @@ open class SettingsActivity : AppCompatActivity() {
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(error: Exception) {
|
||||
runOnUiThread({
|
||||
progressIndicator.visibility = View.INVISIBLE
|
||||
Toast.makeText(this@SettingsActivity, getString(R.string.settings_generic_error) + error.toString(), Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
})*/
|
||||
}
|
||||
|
||||
fun saveSettings() {
|
||||
@ -129,4 +157,32 @@ open class SettingsActivity : AppCompatActivity() {
|
||||
finish()
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the local logbooks to webdav.
|
||||
* @return success
|
||||
*/
|
||||
private fun copyLocalLogbooksToWebdav(webDAVLogbookRepository: WebDAVLogbookRepository, listener: OnCopyLocalLogbooksToWebdavFinishedListener) {
|
||||
Thread(Runnable {
|
||||
var errors = StringBuilder()
|
||||
val fileLogbookRepo = FileLogbookRepository()
|
||||
val logbooks = fileLogbookRepo.getAllLogbooks(this)
|
||||
for (logbook in logbooks) {
|
||||
// Copy only if does not already exist
|
||||
val error = webDAVLogbookRepository.uploadLogbookIfNotExists(this, logbook.name)
|
||||
if (error != null) {
|
||||
if (errors.isNotEmpty())
|
||||
errors.append("\n")
|
||||
errors.append(String.format(getString(R.string.settings_webdav_upload_error), logbook.name, error))
|
||||
}
|
||||
}
|
||||
listener.onCopyLocalLogbooksToWebdavFinished(
|
||||
if (errors.isEmpty()) null else errors.toString()
|
||||
)
|
||||
}).start()
|
||||
}
|
||||
|
||||
private interface OnCopyLocalLogbooksToWebdavFinishedListener {
|
||||
fun onCopyLocalLogbooksToWebdavFinished(errors: String?)
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package it.danieleverducci.lunatracker.entities
|
||||
|
||||
class Logbook {
|
||||
class Logbook(val name: String) {
|
||||
companion object {
|
||||
val MAX_SAFE_LOGBOOK_SIZE = 30000
|
||||
}
|
||||
|
@ -9,26 +9,30 @@ import org.json.JSONException
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.FilenameFilter
|
||||
|
||||
class FileLogbookRepository: LogbookRepository {
|
||||
companion object {
|
||||
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 {
|
||||
listener.onLogbookLoaded(loadLogbook(context))
|
||||
listener.onLogbookLoaded(loadLogbook(context, name))
|
||||
} catch (e: FileNotFoundException) {
|
||||
Log.d(TAG, "No logbook file found, create one")
|
||||
val newLogbook = Logbook()
|
||||
val newLogbook = Logbook(name)
|
||||
saveLogbook(context, newLogbook)
|
||||
listener.onLogbookLoaded(newLogbook)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadLogbook(context: Context): Logbook {
|
||||
val logbook = Logbook()
|
||||
val file = File(context.getFilesDir(), "data.json")
|
||||
fun loadLogbook(context: Context, name: String): Logbook {
|
||||
val logbook = Logbook(name)
|
||||
val fileName = getFileName(name)
|
||||
val file = File(context.getFilesDir(), fileName)
|
||||
val json = FileInputStream(file).bufferedReader().use { it.readText() }
|
||||
val ja = JSONArray(json)
|
||||
for (i in 0 until ja.length()) {
|
||||
@ -53,11 +57,55 @@ class FileLogbookRepository: LogbookRepository {
|
||||
}
|
||||
|
||||
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()
|
||||
for (l in logbook.logs) {
|
||||
ja.put(l.toJson())
|
||||
}
|
||||
file.writeText(ja.toString())
|
||||
}
|
||||
|
||||
override fun listLogbooks(
|
||||
context: Context,
|
||||
listener: LogbookListObtainedListener
|
||||
) {
|
||||
listener.onLogbookListObtained(listLogbooks(context))
|
||||
}
|
||||
|
||||
fun getAllLogbooks(context: Context): List<Logbook> {
|
||||
val logbooks = arrayListOf<Logbook>()
|
||||
for (logbookName in listLogbooks(context)) {
|
||||
logbooks.add(loadLogbook(context, logbookName))
|
||||
}
|
||||
return logbooks
|
||||
}
|
||||
|
||||
private fun listLogbooks(context: Context): ArrayList<String> {
|
||||
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()) {
|
||||
return arrayListOf()
|
||||
}
|
||||
|
||||
val logbooksNames = arrayListOf<String>()
|
||||
logbooksFileNames.forEach { it ->
|
||||
logbooksNames.add(
|
||||
it.replace("${FILE_NAME_START}_", "").replace(FILE_NAME_START, "").replace(FILE_NAME_END, "")
|
||||
)
|
||||
}
|
||||
return 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
|
||||
|
||||
interface LogbookRepository {
|
||||
fun loadLogbook(context: Context, listener: LogbookLoadedListener)
|
||||
fun saveLogbook(context: Context, logbook: Logbook, listener: LogbookSavedListener)
|
||||
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 listLogbooks(context: Context, listener: LogbookListObtainedListener)
|
||||
}
|
||||
|
||||
interface LogbookLoadedListener {
|
||||
@ -25,4 +29,11 @@ interface LogbookSavedListener {
|
||||
fun onWebDAVError(error: SardineException)
|
||||
fun onJSONError(error: JSONException)
|
||||
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.util.Log
|
||||
import com.thegrizzlylabs.sardineandroid.DavResource
|
||||
import com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine
|
||||
import com.thegrizzlylabs.sardineandroid.impl.SardineException
|
||||
import it.danieleverducci.lunatracker.entities.Logbook
|
||||
@ -14,11 +15,13 @@ import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.net.SocketTimeoutException
|
||||
import kotlin.io.bufferedReader
|
||||
import kotlin.text.replace
|
||||
|
||||
class WebDAVLogbookRepository(val webDavURL: String, val username: String, val password: String): LogbookRepository {
|
||||
companion object {
|
||||
val TAG = "LogbookRepository"
|
||||
val FILE_NAME = "lunatracker_logbook.json"
|
||||
val FILE_NAME_START = "lunatracker_logbook"
|
||||
val FILE_NAME_END = ".json"
|
||||
}
|
||||
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 {
|
||||
try {
|
||||
val logbook = loadLogbook(context)
|
||||
val logbook = loadLogbook(name)
|
||||
listener.onLogbookLoaded(logbook)
|
||||
} catch (e: SardineException) {
|
||||
Log.e(TAG, e.toString())
|
||||
@ -52,12 +55,12 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
||||
}).start()
|
||||
}
|
||||
|
||||
private fun loadLogbook(context: Context): Logbook {
|
||||
val inputStream = sardine.get("$webDavURL/$FILE_NAME")
|
||||
private fun loadLogbook(name: String,): Logbook {
|
||||
val inputStream = sardine.get(getUrl(name))
|
||||
val json = inputStream.bufferedReader().use(BufferedReader::readText)
|
||||
inputStream.close()
|
||||
val ja = JSONArray(json)
|
||||
val logbook = Logbook()
|
||||
val logbook = Logbook(name)
|
||||
for (i in 0 until ja.length()) {
|
||||
try {
|
||||
val evt: LunaEvent = LunaEvent(ja.getJSONObject(i))
|
||||
@ -95,6 +98,40 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
||||
}).start()
|
||||
}
|
||||
|
||||
override fun listLogbooks(
|
||||
context: Context,
|
||||
listener: LogbookListObtainedListener
|
||||
) {
|
||||
Thread(Runnable {
|
||||
try {
|
||||
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)
|
||||
} catch (e: SardineException) {
|
||||
Log.e(TAG, e.toString())
|
||||
listener.onWebDAVError(e)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, e.toString())
|
||||
listener.onIOError(e)
|
||||
} catch (e: SocketTimeoutException) {
|
||||
Log.e(TAG, e.toString())
|
||||
listener.onIOError(e)
|
||||
} catch (e: Exception) {
|
||||
listener.onError(e)
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
|
||||
private fun saveLogbook(context: Context, logbook: Logbook) {
|
||||
// Lock logbook on WebDAV to avoid concurrent changes
|
||||
//sardine.lock(getUrl())
|
||||
@ -108,64 +145,56 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
||||
for (l in logbook.logs) {
|
||||
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.
|
||||
* 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.
|
||||
* @return error, or null if no error
|
||||
*/
|
||||
fun createLogbook(context: Context, listener: LogbookCreatedListener) {
|
||||
Thread(Runnable {
|
||||
try {
|
||||
loadLogbook(context)
|
||||
listener.onLogbookCreated()
|
||||
} catch (e: SardineException) {
|
||||
if (e.toString().contains("404")) {
|
||||
// Connection successful, but no existing save. Upload the local one.
|
||||
try {
|
||||
val flr = FileLogbookRepository()
|
||||
val logbook = flr.loadLogbook(context)
|
||||
saveLogbook(context, logbook)
|
||||
Log.d(TAG, "Local logbook file found, uploaded")
|
||||
listener.onLogbookCreated()
|
||||
} catch (e: FileNotFoundException) {
|
||||
Log.d(TAG, "No local logbook file found, uploading empty file")
|
||||
saveLogbook(context, Logbook())
|
||||
listener.onLogbookCreated()
|
||||
} catch (e: SardineException) {
|
||||
Log.e(TAG, "Unable to upload logbook: $e")
|
||||
listener.onWebDAVError(e)
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, e.toString())
|
||||
listener.onWebDAVError(e)
|
||||
fun uploadLogbookIfNotExists(context: Context, name: String): String? {
|
||||
val flr = FileLogbookRepository()
|
||||
try {
|
||||
loadLogbook(name)
|
||||
Log.d(TAG, "Logbook file $name already exist on the webDav share: will not overwrite it")
|
||||
return null
|
||||
} catch (e: SardineException) {
|
||||
if (e.toString().contains("404")) {
|
||||
// Connection successful, but logbook does not exist. Upload the local one.
|
||||
try {
|
||||
val logbook = flr.loadLogbook(context, name)
|
||||
saveLogbook(context, logbook)
|
||||
Log.d(TAG, "Local logbook file $name found, uploaded")
|
||||
return null
|
||||
} catch (e: FileNotFoundException) {
|
||||
Log.e(TAG, "No local logbook file found, this should not happen!")
|
||||
return "No local logbook file found, app is in inconsistent state, please delete and reinstall it"
|
||||
} catch (e: SardineException) {
|
||||
Log.e(TAG, "Unable to upload logbook: $e")
|
||||
return e.toString()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
} else {
|
||||
Log.e(TAG, e.toString())
|
||||
listener.onIOError(e)
|
||||
} catch (e: SocketTimeoutException) {
|
||||
Log.e(TAG, e.toString())
|
||||
listener.onIOError(e)
|
||||
} catch (e: JSONException) {
|
||||
Log.e(TAG, e.toString())
|
||||
listener.onJSONError(e)
|
||||
} catch (e: Exception) {
|
||||
listener.onError(e)
|
||||
return e.toString()
|
||||
}
|
||||
}).start()
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, e.toString())
|
||||
return e.toString()
|
||||
} catch (e: SocketTimeoutException) {
|
||||
Log.e(TAG, e.toString())
|
||||
return e.toString()
|
||||
} catch (e: JSONException) {
|
||||
Log.e(TAG, e.toString())
|
||||
return e.toString()
|
||||
} catch (e: Exception) {
|
||||
return e.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUrl(): String {
|
||||
return "$webDavURL/$FILE_NAME"
|
||||
}
|
||||
|
||||
|
||||
interface LogbookCreatedListener {
|
||||
fun onLogbookCreated()
|
||||
fun onIOError(error: okio.IOException)
|
||||
fun onWebDAVError(error: SardineException)
|
||||
fun onJSONError(error: JSONException)
|
||||
fun onError(error: Exception)
|
||||
private fun getUrl(name: String): String {
|
||||
val fileName = "${FILE_NAME_START}${if (name.isNotEmpty()) "_" else ""}${name}${FILE_NAME_END}"
|
||||
Log.d(TAG, fileName)
|
||||
return "$webDavURL/$fileName"
|
||||
}
|
||||
}
|
@ -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>
|
@ -7,16 +7,77 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="30dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/title"
|
||||
android:textSize="30dp"
|
||||
android:gravity="center_horizontal"/>
|
||||
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:textSize="26dp"
|
||||
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
|
||||
android:layout_width="match_parent"
|
||||
@ -164,24 +225,6 @@
|
||||
|
||||
</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
|
||||
android:id="@+id/no_connection_screen"
|
||||
android:layout_width="match_parent"
|
||||
|
25
app/src/main/res/layout/dialog_add_logbook.xml
Normal file
25
app/src/main/res/layout/dialog_add_logbook.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?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_add_logbook_message"
|
||||
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_height="match_parent"
|
||||
android:padding="20dp"
|
||||
android:background="@drawable/button_background"
|
||||
android:background="@drawable/dropdown_list_item_background"
|
||||
style="@style/OverflowMenuText"
|
||||
android:text="@string/overflow_event_medicine"/>
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="20dp"
|
||||
android:background="@drawable/button_background"
|
||||
android:background="@drawable/dropdown_list_item_background"
|
||||
style="@style/OverflowMenuText"
|
||||
android:text="@string/overflow_event_enema"/>
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="20dp"
|
||||
android:background="@drawable/button_background"
|
||||
android:background="@drawable/dropdown_list_item_background"
|
||||
style="@style/OverflowMenuText"
|
||||
android:text="@string/overflow_event_note"/>
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="20dp"
|
||||
android:background="@drawable/button_background"
|
||||
android:background="@drawable/dropdown_list_item_background"
|
||||
style="@style/OverflowMenuText"
|
||||
android:text="@string/overflow_event_temperature"/>
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="20dp"
|
||||
android:background="@drawable/button_background"
|
||||
android:background="@drawable/dropdown_list_item_background"
|
||||
style="@style/OverflowMenuText"
|
||||
android:text="@string/overflow_event_colic"/>
|
||||
|
||||
|
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"/>
|
@ -61,11 +61,13 @@
|
||||
<string name="settings_storage_dav_pass">Password</string>
|
||||
<string name="settings_network_error">Impossibile raggiungere il server: </string>
|
||||
<string name="settings_webdav_error_denied">Nome utente o password WebDAV sbagliati</string>
|
||||
<string name="settings_webdav_error_server_offline">Il server WebDAV non è al momento disponibile</string>
|
||||
<string name="settings_webdav_error_generic">Si è verificato un errore tentando di accedere al server WebDAV:</string>
|
||||
<string name="settings_webdav_creation_error_generic">Impossibile creare un file di salvataggio sul server WebDAV:</string>
|
||||
<string name="settings_webdav_creation_ok">Connessione al server WebDAV avvenuta con successo</string>
|
||||
<string name="settings_json_error">Sul server esiste un salvataggio, ma è corrotto o illeggibile. Cancellare il file </string>
|
||||
<string name="settings_generic_error">Si è verificato un errore: </string>
|
||||
<string name="settings_webdav_upload_error">Errore durante l\'upload del logbook locale %1$s su webdav: %2$s</string>
|
||||
|
||||
<string name="trim_logbook_dialog_title">Il tuo diario è bello grande!</string>
|
||||
<string name="trim_logbook_dialog_message_local">Il file del tuo diario sta crescendo molto. Ti suggeriamo di cancellare gli eventi più vecchi per evitare problemi di memoria.</string>
|
||||
@ -79,5 +81,15 @@
|
||||
<string name="log_notes_dialog_note_hint">Inserisci le note</string>
|
||||
|
||||
<string name="dialog_event_detail_title">Dettaglio evento</string>
|
||||
<string name="dialog_event_detail_save_button">Salva</string>
|
||||
<string name="dialog_event_detail_delete_button">Elimina</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.\nSe vuoi un\'icona, inserisci una emoji!</string>
|
||||
|
||||
<string name="dialog_add_logbook_message_intro">Benvenuto! Per usare quest\'app devi creare almeno un diario. Probabilmente vuoi chiamarlo col nome di tuo figlio.</string>
|
||||
<string name="default_logbook_name">👶 Il mio primo diario</string>
|
||||
<string name="logbook_created">Creato nuovo diario: </string>
|
||||
|
||||
</resources>
|
@ -75,11 +75,13 @@
|
||||
<string name="settings_storage_dav_pass">Password</string>
|
||||
<string name="settings_network_error">Unable to reach server: </string>
|
||||
<string name="settings_webdav_error_denied">Wrong WebDAV user or password</string>
|
||||
<string name="settings_webdav_error_server_offline">WebDAV server is currently unavailable</string>
|
||||
<string name="settings_webdav_error_generic">Error while trying to access WebDAV:</string>
|
||||
<string name="settings_webdav_creation_error_generic">Unable to save a file on the WebDAV server:</string>
|
||||
<string name="settings_webdav_creation_ok">Successfully connected with WebDAV server</string>
|
||||
<string name="settings_json_error">There\'s a save file on the server, but is corrupted or unreadable. Please delete it </string>
|
||||
<string name="settings_generic_error">Error: </string>
|
||||
<string name="settings_webdav_upload_error">Error while uploading local logbook %1$s to webdav: %2$s</string>
|
||||
|
||||
<string name="trim_logbook_dialog_title">Your logbook is pretty big!</string>
|
||||
<string name="trim_logbook_dialog_message_local">Your logbook file is growing a lot. We suggest trimming the oldest events to avoid crashes.</string>
|
||||
@ -102,5 +104,15 @@
|
||||
<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_save_button">Save</string>
|
||||
<string name="dialog_event_detail_delete_button">Delete</string>
|
||||
|
||||
<string name="dialog_add_logbook_title">Add logbook</string>
|
||||
<string name="dialog_add_logbook_logbookname">👶 Logbook name</string>
|
||||
<string name="dialog_add_logbook_message">Write a name to identify this logbook. This name will appear on top of the screen and, if you use WebDAV, will be in the save file name as well.</string>
|
||||
<string name="dialog_add_logbook_message_intro">Welcome! To use this app you need to create at least one logbook. You would probably want to call it with your child\'s name.</string>
|
||||
|
||||
<string name="default_logbook_name">👶 My first logbook</string>
|
||||
<string name="logbook_created">New logbook created: </string>
|
||||
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user