Compare commits
	
		
			11 Commits
		
	
	
		
			v0.4
			...
			multiple-c
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 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/*
 | 
			
		||||
 
 | 
			
		||||
@@ -7,13 +7,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 +29,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
 | 
			
		||||
@@ -50,7 +56,7 @@ class MainActivity : AppCompatActivity() {
 | 
			
		||||
    lateinit var handler: Handler
 | 
			
		||||
    var savingEvent = false
 | 
			
		||||
    val updateListRunnable: Runnable = Runnable {
 | 
			
		||||
        loadLogbook()
 | 
			
		||||
        loadLogbook(logbook.name)
 | 
			
		||||
        handler.postDelayed(updateListRunnable, 1000*60)
 | 
			
		||||
    }
 | 
			
		||||
    var logbookRepo: LogbookRepository? = null
 | 
			
		||||
@@ -77,6 +83,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 +122,10 @@ class MainActivity : AppCompatActivity() {
 | 
			
		||||
            showSettings()
 | 
			
		||||
        })
 | 
			
		||||
        findViewById<View>(R.id.button_no_connection_retry).setOnClickListener({
 | 
			
		||||
            loadLogbook()
 | 
			
		||||
            loadLogbook(logbook.name)
 | 
			
		||||
        })
 | 
			
		||||
        findViewById<View>(R.id.button_sync).setOnClickListener({
 | 
			
		||||
            loadLogbook()
 | 
			
		||||
            loadLogbook(logbook.name)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -156,7 +163,7 @@ class MainActivity : AppCompatActivity() {
 | 
			
		||||
        adapter.notifyDataSetChanged()
 | 
			
		||||
 | 
			
		||||
        // Reload data
 | 
			
		||||
        loadLogbook()
 | 
			
		||||
        loadLogbookList()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onStop() {
 | 
			
		||||
@@ -304,7 +311,126 @@ class MainActivity : AppCompatActivity() {
 | 
			
		||||
        alertDialog.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun loadLogbook() {
 | 
			
		||||
    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 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) {
 | 
			
		||||
                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) {
 | 
			
		||||
        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 {
 | 
			
		||||
                            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 +440,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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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,27 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
 | 
			
		||||
        }).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) {
 | 
			
		||||
        // Lock logbook on WebDAV to avoid concurrent changes
 | 
			
		||||
        //sardine.lock(getUrl())
 | 
			
		||||
@@ -108,64 +132,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"/>
 | 
			
		||||
@@ -66,6 +66,7 @@
 | 
			
		||||
    <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>
 | 
			
		||||
@@ -80,4 +81,11 @@
 | 
			
		||||
 | 
			
		||||
    <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.\nSe vuoi un\'icona, inserisci una emoji!</string>
 | 
			
		||||
 | 
			
		||||
    <string name="default_logbook_name">👶 Il mio primo diario</string>
 | 
			
		||||
    <string name="logbook_created">Creato nuovo diario: </string>
 | 
			
		||||
 | 
			
		||||
</resources>
 | 
			
		||||
@@ -80,6 +80,7 @@
 | 
			
		||||
    <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>
 | 
			
		||||
@@ -103,4 +104,12 @@
 | 
			
		||||
 | 
			
		||||
    <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="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>
 | 
			
		||||
		Reference in New Issue
	
	Block a user