From fe5da015cb6524d7386bb33498f72e867102ab23 Mon Sep 17 00:00:00 2001 From: Daniele Verducci Date: Mon, 13 Jan 2025 08:16:06 +0100 Subject: [PATCH] Support for multiple logbook files --- .../lunatracker/MainActivity.kt | 2 +- .../lunatracker/SettingsActivity.kt | 2 +- .../lunatracker/entities/Logbook.kt | 2 +- .../repository/FileLogbookRepository.kt | 46 ++++++++++++++++--- .../repository/LogbookRepository.kt | 12 ++++- .../repository/WebDAVLogbookRepository.kt | 46 +++++++++++++------ 6 files changed, 85 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 8b47feb..4b3d073 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -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) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt index 239bc4e..9f12cd6 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt @@ -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 diff --git a/app/src/main/java/it/danieleverducci/lunatracker/entities/Logbook.kt b/app/src/main/java/it/danieleverducci/lunatracker/entities/Logbook.kt index 8017383..2915ddd 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/entities/Logbook.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/entities/Logbook.kt @@ -1,6 +1,6 @@ package it.danieleverducci.lunatracker.entities -class Logbook { +class Logbook(val name: String) { companion object { val MAX_SAFE_LOGBOOK_SIZE = 30000 } diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt index 0a5ff97..5d239da 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt @@ -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 { + 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() + logbooksFileNames.forEach { it -> + logbooksNames.add( + it.replace(FILE_NAME_START, "").replace("${FILE_NAME_START}_", "").replace(FILE_NAME_END, "") + ) + } + return logbooksNames + } } \ No newline at end of file diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/LogbookRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/LogbookRepository.kt index 316909e..d8cd6b2 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/LogbookRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/LogbookRepository.kt @@ -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 } 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) } \ No newline at end of file diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt index 7cb17ef..a2d4ce2 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt @@ -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 { + val logbooksNames = arrayListOf() + 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" }