Compare commits
3 Commits
v0.4
...
32fbeac079
Author | SHA1 | Date | |
---|---|---|---|
32fbeac079 | |||
0b0fd8f5af | |||
fe5da015cb |
@ -24,6 +24,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
|
||||
@ -156,7 +157,7 @@ class MainActivity : AppCompatActivity() {
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
// Reload data
|
||||
loadLogbook()
|
||||
loadLogbookList()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
@ -304,17 +305,89 @@ class MainActivity : AppCompatActivity() {
|
||||
alertDialog.show()
|
||||
}
|
||||
|
||||
fun loadLogbook() {
|
||||
fun showAddLogbookDialog() {
|
||||
val d = AlertDialog.Builder(this)
|
||||
d.setTitle(R.string.dialog_add_logbook_title)
|
||||
val dialogView = layoutInflater.inflate(R.layout.dialog_add_logbook, null)
|
||||
val logbookNameEditText = dialogView.findViewById<EditText>(R.id.dialog_add_logbook_logbookname)
|
||||
d.setView(dialogView)
|
||||
d.setPositiveButton(android.R.string.ok) { dialogInterface, i -> addLogbook(logbookNameEditText.text.toString()) }
|
||||
d.setNegativeButton(android.R.string.cancel) { dialogInterface, i -> dialogInterface.dismiss() }
|
||||
val alertDialog = d.create()
|
||||
alertDialog.show()
|
||||
}
|
||||
|
||||
fun loadLogbookList() {
|
||||
setLoading(true)
|
||||
logbookRepo?.listLogbooks(this, object: LogbookListObtainedListener {
|
||||
override fun onLogbookListObtained(logbooksNames: ArrayList<String>) {
|
||||
runOnUiThread({
|
||||
// Show logbooks buttons
|
||||
val logbooksButtonsContainer =
|
||||
findViewById<ViewGroup>(R.id.logbooks_buttons_container)
|
||||
logbooksButtonsContainer.removeAllViews()
|
||||
for (lbn in logbooksNames) {
|
||||
val lbnButton = layoutInflater.inflate(
|
||||
R.layout.logbook_button,
|
||||
logbooksButtonsContainer,
|
||||
false
|
||||
) as TextView
|
||||
lbnButton.setText(
|
||||
if (lbn.isEmpty()) getString(R.string.default_logbook_name) else lbn
|
||||
)
|
||||
lbnButton.setOnClickListener({
|
||||
loadLogbook(lbn)
|
||||
})
|
||||
logbooksButtonsContainer.addView(lbnButton)
|
||||
}
|
||||
// Show "Add logbook" button
|
||||
val addLogbookButton = layoutInflater.inflate(
|
||||
R.layout.logbook_button,
|
||||
logbooksButtonsContainer,
|
||||
false
|
||||
) as TextView
|
||||
addLogbookButton.setText("+")
|
||||
addLogbookButton.setOnClickListener({showAddLogbookDialog()})
|
||||
logbooksButtonsContainer.addView(addLogbookButton)
|
||||
|
||||
// Load logbook
|
||||
loadLogbook()
|
||||
})
|
||||
}
|
||||
|
||||
override fun onIOError(error: IOException) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun onWebDAVError(error: SardineException) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun onError(error: Exception) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun addLogbook(logbookName: String) {
|
||||
this.logbook = Logbook(logbookName)
|
||||
saveLogbook()
|
||||
loadLogbookList()
|
||||
}
|
||||
|
||||
fun loadLogbook(name: String = LogbookRepository.DEFAULT_LOGBOOK_NAME) {
|
||||
if (savingEvent)
|
||||
return
|
||||
|
||||
// TODO: Highlight logbook button
|
||||
|
||||
// Reset time counter
|
||||
handler.removeCallbacks(updateListRunnable)
|
||||
handler.postDelayed(updateListRunnable, UPDATE_EVERY_SECS*1000)
|
||||
|
||||
// Load data
|
||||
setLoading(true)
|
||||
logbookRepo?.loadLogbook(this, object: LogbookLoadedListener{
|
||||
logbookRepo?.loadLogbook(this, name, object: LogbookLoadedListener{
|
||||
override fun onLogbookLoaded(lb: Logbook) {
|
||||
runOnUiThread({
|
||||
setLoading(false)
|
||||
|
@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.thegrizzlylabs.sardineandroid.impl.SardineException
|
||||
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
|
||||
import it.danieleverducci.lunatracker.repository.LogbookRepository
|
||||
import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
|
||||
import okio.IOException
|
||||
import org.json.JSONException
|
||||
@ -72,7 +73,7 @@ open class SettingsActivity : AppCompatActivity() {
|
||||
textViewWebDAVPass.text.toString()
|
||||
)
|
||||
progressIndicator.visibility = View.VISIBLE
|
||||
webDAVLogbookRepo.createLogbook(this, object: WebDAVLogbookRepository.LogbookCreatedListener{
|
||||
webDAVLogbookRepo.createLogbook(this, LogbookRepository.DEFAULT_LOGBOOK_NAME, object: WebDAVLogbookRepository.LogbookCreatedListener{
|
||||
override fun onLogbookCreated() {
|
||||
runOnUiThread({
|
||||
progressIndicator.visibility = View.INVISIBLE
|
||||
|
@ -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,42 @@ 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
|
||||
) {
|
||||
val logbooksFileNames = context.getFilesDir().list(object: FilenameFilter {
|
||||
override fun accept(dir: File?, name: String?): Boolean {
|
||||
if (name == null)
|
||||
return false
|
||||
if (name.startsWith(FILE_NAME_START) && name.endsWith(FILE_NAME_END))
|
||||
return true
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
if (logbooksFileNames == null || logbooksFileNames.isEmpty())
|
||||
listener.onLogbookListObtained(arrayListOf())
|
||||
|
||||
val logbooksNames = arrayListOf<String>()
|
||||
logbooksFileNames.forEach { it ->
|
||||
logbooksNames.add(
|
||||
it.replace("${FILE_NAME_START}_", "").replace(FILE_NAME_START, "").replace(FILE_NAME_END, "")
|
||||
)
|
||||
}
|
||||
listener.onLogbookListObtained(logbooksNames)
|
||||
}
|
||||
|
||||
private fun getFileName(name: String): String {
|
||||
return "$FILE_NAME_START${if (name.isNotEmpty()) "_" else ""}${name}$FILE_NAME_END"
|
||||
}
|
||||
}
|
@ -7,8 +7,12 @@ import okio.IOException
|
||||
import org.json.JSONException
|
||||
|
||||
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 {
|
||||
@ -26,3 +30,10 @@ interface LogbookSavedListener {
|
||||
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,30 +132,30 @@ 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).
|
||||
*/
|
||||
fun createLogbook(context: Context, listener: LogbookCreatedListener) {
|
||||
fun createLogbook(context: Context, name: String, listener: LogbookCreatedListener) {
|
||||
Thread(Runnable {
|
||||
try {
|
||||
loadLogbook(context)
|
||||
loadLogbook(name)
|
||||
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)
|
||||
val logbook = flr.loadLogbook(context, name)
|
||||
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())
|
||||
saveLogbook(context, Logbook(name))
|
||||
listener.onLogbookCreated()
|
||||
} catch (e: SardineException) {
|
||||
Log.e(TAG, "Unable to upload logbook: $e")
|
||||
@ -156,8 +180,10 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
|
||||
}).start()
|
||||
}
|
||||
|
||||
private fun getUrl(): String {
|
||||
return "$webDavURL/$FILE_NAME"
|
||||
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"
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,6 +18,22 @@
|
||||
android:textSize="30dp"
|
||||
android:gravity="center_horizontal"/>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/logbooks_buttons_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
24
app/src/main/res/layout/dialog_add_logbook.xml
Normal file
24
app/src/main/res/layout/dialog_add_logbook.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_add_logbook_message"/>
|
||||
|
||||
|
||||
<EditText
|
||||
android:id="@+id/dialog_add_logbook_logbookname"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:lines="1"
|
||||
android:inputType="text"
|
||||
android:hint="@string/dialog_add_logbook_logbookname"
|
||||
android:background="@drawable/textview_background"/>
|
||||
|
||||
</LinearLayout>
|
13
app/src/main/res/layout/logbook_button.xml
Normal file
13
app/src/main/res/layout/logbook_button.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:background="@drawable/button_background"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/accent"/>
|
@ -80,4 +80,10 @@
|
||||
|
||||
<string name="dialog_event_detail_title">Dettaglio evento</string>
|
||||
|
||||
<string name="dialog_add_logbook_title">Aggiungi diario</string>
|
||||
<string name="dialog_add_logbook_logbookname">Nome del diario</string>
|
||||
<string name="dialog_add_logbook_message">Scrivi un nome per identificare questo diario. Comparirà in cima allo schermo, e se usi WebDAV sarà incluso anche nel nome del file di salvataggio.</string>
|
||||
|
||||
<string name="default_logbook_name">Senza nome</string>
|
||||
|
||||
</resources>
|
@ -103,4 +103,10 @@
|
||||
|
||||
<string name="dialog_event_detail_title">Event detail</string>
|
||||
|
||||
<string name="dialog_add_logbook_title">Add logbook</string>
|
||||
<string name="dialog_add_logbook_logbookname">Logbook name</string>
|
||||
<string name="dialog_add_logbook_message">Write a name to identify this logbook. This name will appear on top of the screen and, if you use WebDAV, will be in the save file name as well.</string>
|
||||
|
||||
<string name="default_logbook_name">Unnamed</string>
|
||||
|
||||
</resources>
|
Reference in New Issue
Block a user