From 17b455a60a0eb10b0e8c166cbf642b7d1b0e9cdd Mon Sep 17 00:00:00 2001 From: oxidiert Date: Wed, 13 Aug 2025 12:06:07 +0200 Subject: [PATCH] added deltaUpload but not working 100% in combination with fullUpload. Changed download, it is now deleting the existing database before downloading. --- .../java/com/dano/test1/DatabaseDownloader.kt | 13 +- .../java/com/dano/test1/DatabaseUploader.kt | 153 +++++++++++++++--- 2 files changed, 142 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/dano/test1/DatabaseDownloader.kt b/app/src/main/java/com/dano/test1/DatabaseDownloader.kt index 64060ae..5fdb063 100644 --- a/app/src/main/java/com/dano/test1/DatabaseDownloader.kt +++ b/app/src/main/java/com/dano/test1/DatabaseDownloader.kt @@ -21,6 +21,16 @@ object DatabaseDownloader { fun downloadAndReplaceDatabase(context: Context) { CoroutineScope(Dispatchers.IO).launch { try { + // Alte DB sofort löschen, bevor irgendwas heruntergeladen wird + val dbFile = context.getDatabasePath(DB_NAME) + if (dbFile.exists()) { + if (dbFile.delete()) { + Log.d("DOWNLOAD", "Bestehende lokale DB gelöscht.") + } else { + Log.e("DOWNLOAD", "Konnte bestehende DB nicht löschen.") + } + } + Log.d("DOWNLOAD", "Download gestartet: $SERVER_DOWNLOAD_URL") val request = Request.Builder() @@ -41,10 +51,7 @@ object DatabaseDownloader { } Log.d("DOWNLOAD", "Datei gespeichert: ${downloadedFile.absolutePath}") - val dbFile = context.getDatabasePath(DB_NAME) - if (dbFile.exists()) dbFile.delete() downloadedFile.copyTo(dbFile, overwrite = true) - Log.d("DOWNLOAD", "Neue DB erfolgreich eingesetzt") } catch (e: Exception) { diff --git a/app/src/main/java/com/dano/test1/DatabaseUploader.kt b/app/src/main/java/com/dano/test1/DatabaseUploader.kt index f08c5ce..6de2364 100644 --- a/app/src/main/java/com/dano/test1/DatabaseUploader.kt +++ b/app/src/main/java/com/dano/test1/DatabaseUploader.kt @@ -2,13 +2,17 @@ package com.dano.test1 import android.content.Context import android.database.sqlite.SQLiteDatabase +import android.util.Base64 import android.util.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import android.database.Cursor import okhttp3.* import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.asRequestBody +import org.json.JSONArray +import org.json.JSONObject import java.io.File import java.io.IOException import kotlin.system.exitProcess @@ -16,7 +20,9 @@ import kotlin.system.exitProcess object DatabaseUploader { private const val DB_NAME = "questionnaire_database" - private const val SERVER_UPLOAD_URL = "http://49.13.157.44/uploadFull.php" + private const val SERVER_FULL_URL = "http://49.13.157.44/uploadFull.php" + private const val SERVER_DELTA_URL = "http://49.13.157.44/uploadDelta.php" + private const val SERVER_CHECK_URL = "http://49.13.157.44/checkDatabaseExists.php" private const val API_TOKEN = "MEIN_SUPER_GEHEIMES_TOKEN_12345" private val client = OkHttpClient() @@ -30,21 +36,34 @@ object DatabaseUploader { return@launch } - // WAL-Daten in die Hauptdatei schreiben + // WAL-Checkpoint try { val db = SQLiteDatabase.openDatabase( dbFile.absolutePath, null, SQLiteDatabase.OPEN_READWRITE ) - db.execSQL("PRAGMA wal_checkpoint(FULL);") + db.rawQuery("PRAGMA wal_checkpoint(FULL);", null).use { cursor -> + if (cursor.moveToFirst()) { + try { + Log.d("UPLOAD", "WAL-Checkpoint result: ${cursor.getInt(0)}") + } catch (_: Exception) {} + } + } db.close() - Log.d("UPLOAD", "WAL-Checkpoint erfolgreich durchgeführt.") + Log.d("UPLOAD", "WAL-Checkpoint erfolgreich.") } catch (e: Exception) { Log.e("UPLOAD", "Fehler beim WAL-Checkpoint", e) } - uploadFile(dbFile) + val exists = checkDatabaseExists() + if (exists) { + Log.d("UPLOAD", "Server-Datenbank vorhanden → Delta-Upload") + uploadDelta(dbFile) + } else { + Log.d("UPLOAD", "Keine Server-Datenbank → Full-Upload") + uploadFull(dbFile) + } } catch (e: Exception) { Log.e("UPLOAD", "Fehler beim Hochladen der DB", e) @@ -52,7 +71,33 @@ object DatabaseUploader { } } - private fun uploadFile(file: File) { + private fun checkDatabaseExists(): Boolean { + return try { + val request = Request.Builder() + .url(SERVER_CHECK_URL) + .get() + .build() + + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) { + Log.e("UPLOAD", "checkDatabaseExists HTTP error: ${response.code}") + return false + } + val body = response.body?.string() ?: return false + try { + val j = JSONObject(body) + j.optBoolean("exists", false) + } catch (e: Exception) { + body.contains("exists", ignoreCase = true) + } + } + } catch (e: Exception) { + Log.e("UPLOAD", "Fehler bei Server-Prüfung", e) + false + } + } + + private fun uploadFull(file: File) { val requestBody = MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart( @@ -63,28 +108,94 @@ object DatabaseUploader { .build() val request = Request.Builder() - .url(SERVER_UPLOAD_URL) + .url(SERVER_FULL_URL) .post(requestBody) .build() - client.newCall(request).enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - Log.e("UPLOAD", "Upload fehlgeschlagen: ${e.message}") + client.newCall(request).enqueue(serverCallback(file)) + } + + private fun uploadDelta(file: File) { + try { + val db = SQLiteDatabase.openDatabase(file.absolutePath, null, SQLiteDatabase.OPEN_READONLY) + + val data = JSONObject().apply { + put("clients", queryToJsonArray(db, "SELECT clientCode FROM clients")) + put("questionnaires", queryToJsonArray(db, "SELECT id FROM questionnaires")) + put("questions", queryToJsonArray(db, "SELECT questionId, questionnaireId, question FROM questions")) + put("answers", queryToJsonArray(db, "SELECT clientCode, questionId, answerValue FROM answers")) + put("completed_questionnaires", + queryToJsonArray(db, "SELECT clientCode, questionnaireId, timestamp, isDone, sumPoints FROM completed_questionnaires") + ) } - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful) { - Log.d("UPLOAD", "Upload erfolgreich: ${response.message}") - if (file.delete()) { - Log.d("UPLOAD", "Lokale DB gelöscht.") - exitProcess(0) - } else { - Log.e("UPLOAD", "Löschen der lokalen DB fehlgeschlagen.") + db.close() + + val requestBody = MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("token", API_TOKEN) + .addFormDataPart("data", data.toString()) + .build() + + val request = Request.Builder() + .url(SERVER_DELTA_URL) + .post(requestBody) + .build() + + client.newCall(request).enqueue(serverCallback(file)) + + } catch (e: Exception) { + Log.e("UPLOAD", "Fehler beim Delta-Upload", e) + } + } + + private fun queryToJsonArray(db: SQLiteDatabase, query: String): JSONArray { + val cursor = db.rawQuery(query, null) + val jsonArray = JSONArray() + cursor.use { + val columnNames = it.columnNames + while (it.moveToNext()) { + val obj = JSONObject() + for (col in columnNames) { + val idx = it.getColumnIndex(col) + if (idx >= 0) { + when (it.getType(idx)) { + Cursor.FIELD_TYPE_INTEGER -> obj.put(col, it.getLong(idx)) + Cursor.FIELD_TYPE_FLOAT -> obj.put(col, it.getDouble(idx)) + Cursor.FIELD_TYPE_STRING -> obj.put(col, it.getString(idx)) + Cursor.FIELD_TYPE_NULL -> obj.put(col, JSONObject.NULL) + Cursor.FIELD_TYPE_BLOB -> { + val blob = it.getBlob(idx) + obj.put(col, Base64.encodeToString(blob, Base64.NO_WRAP)) + } + else -> obj.put(col, it.getString(idx)) + } } - } else { - Log.e("UPLOAD", "Upload fehlgeschlagen: ${response.code} ${response.message}") } + jsonArray.put(obj) } - }) + } + return jsonArray + } + + private fun serverCallback(file: File) = object : Callback { + override fun onFailure(call: Call, e: IOException) { + Log.e("UPLOAD", "Upload fehlgeschlagen: ${e.message}") + } + + override fun onResponse(call: Call, response: Response) { + val body = try { response.body?.string() ?: "Keine Response" } catch (e: Exception) { "Fehler beim Lesen der Response: ${e.message}" } + if (response.isSuccessful) { + Log.d("UPLOAD", "Upload erfolgreich: $body") + if (file.delete()) { + Log.d("UPLOAD", "Lokale DB gelöscht.") + exitProcess(0) + } else { + Log.e("UPLOAD", "Löschen der lokalen DB fehlgeschlagen.") + } + } else { + Log.e("UPLOAD", "Upload fehlgeschlagen: ${response.code} $body") + } + } } }