Support for multiple logbook files

This commit is contained in:
Daniele Verducci 2025-01-13 08:16:06 +01:00
parent b1e26f8a96
commit fe5da015cb
6 changed files with 85 additions and 25 deletions

View File

@ -314,7 +314,7 @@ class MainActivity : AppCompatActivity() {
// Load data
setLoading(true)
logbookRepo?.loadLogbook(this, object: LogbookLoadedListener{
logbookRepo?.loadLogbook(this, "", object: LogbookLoadedListener{
override fun onLogbookLoaded(lb: Logbook) {
runOnUiThread({
setLoading(false)

View File

@ -72,7 +72,7 @@ open class SettingsActivity : AppCompatActivity() {
textViewWebDAVPass.text.toString()
)
progressIndicator.visibility = View.VISIBLE
webDAVLogbookRepo.createLogbook(this, object: WebDAVLogbookRepository.LogbookCreatedListener{
webDAVLogbookRepo.createLogbook(this, "", object: WebDAVLogbookRepository.LogbookCreatedListener{
override fun onLogbookCreated() {
runOnUiThread({
progressIndicator.visibility = View.INVISIBLE

View File

@ -1,6 +1,6 @@
package it.danieleverducci.lunatracker.entities
class Logbook {
class Logbook(val name: String) {
companion object {
val MAX_SAFE_LOGBOOK_SIZE = 30000
}

View File

@ -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 = "$FILE_NAME_START{${if (name.isNotEmpty()) "_" else ""}{$name}$FILE_NAME_END"
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,39 @@ class FileLogbookRepository: LogbookRepository {
}
fun saveLogbook(context: Context, logbook: Logbook) {
val file = File(context.getFilesDir(), "data.json")
val name = logbook.name
val fileName = "$FILE_NAME_START${if (name.isNotEmpty()) "_" else ""}${name}$FILE_NAME_END"
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
): 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
}
}

View File

@ -7,8 +7,9 @@ import okio.IOException
import org.json.JSONException
interface LogbookRepository {
fun loadLogbook(context: Context, listener: LogbookLoadedListener)
fun saveLogbook(context: Context, logbook: Logbook, listener: LogbookSavedListener)
fun loadLogbook(context: Context, name: String = "", listener: LogbookLoadedListener)
fun saveLogbook(context: Context,logbook: Logbook, listener: LogbookSavedListener)
fun listLogbooks(context: Context, listener: LogbookListObtainedListener): ArrayList<String>
}
interface LogbookLoadedListener {
@ -25,4 +26,11 @@ interface LogbookSavedListener {
fun onWebDAVError(error: SardineException)
fun onJSONError(error: JSONException)
fun onError(error: Exception)
}
interface LogbookListObtainedListener {
fun onLogbookListObtained()
fun onIOError(error: IOException)
fun onWebDAVError(error: SardineException)
fun onError(error: Exception)
}

View File

@ -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,21 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
}).start()
}
override fun listLogbooks(
context: Context,
listener: LogbookListObtainedListener
): ArrayList<String> {
val logbooksNames = arrayListOf<String>()
for (dr: DavResource in sardine.list(webDavURL)){
logbooksNames.add(
dr.name.replace(FileLogbookRepository.Companion.FILE_NAME_START, "")
.replace("${FileLogbookRepository.Companion.FILE_NAME_START}_", "")
.replace(FileLogbookRepository.Companion.FILE_NAME_END, "")
)
}
return logbooksNames
}
private fun saveLogbook(context: Context, logbook: Logbook) {
// Lock logbook on WebDAV to avoid concurrent changes
//sardine.lock(getUrl())
@ -108,30 +126,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 +174,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"
}