From e6c2526529f009ca886afa8aff13c148ecfbb35f Mon Sep 17 00:00:00 2001 From: oxidiert Date: Fri, 15 Aug 2025 10:11:32 +0200 Subject: [PATCH] Big commit. Managed to do upload and download right. No Race conditions. --- .../main/java/com/dano/test1/AppDatabase.kt | 4 +- app/src/main/java/com/dano/test1/Daos.kt | 7 ++++ .../java/com/dano/test1/DatabaseUploader.kt | 17 +++++--- .../com/dano/test1/HandlerOpeningScreen.kt | 40 ++++++++++++++++++- app/src/main/java/com/dano/test1/MyApp.kt | 11 +++++ 5 files changed, 71 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/dano/test1/AppDatabase.kt b/app/src/main/java/com/dano/test1/AppDatabase.kt index 2cbc0ee..e1f51be 100644 --- a/app/src/main/java/com/dano/test1/AppDatabase.kt +++ b/app/src/main/java/com/dano/test1/AppDatabase.kt @@ -11,7 +11,8 @@ import androidx.room.RoomDatabase Answer::class, CompletedQuestionnaire::class ], - version = 10 + version = 1, + exportSchema = false ) abstract class AppDatabase : RoomDatabase() { abstract fun clientDao(): ClientDao @@ -20,3 +21,4 @@ abstract class AppDatabase : RoomDatabase() { abstract fun answerDao(): AnswerDao abstract fun completedQuestionnaireDao(): CompletedQuestionnaireDao } + diff --git a/app/src/main/java/com/dano/test1/Daos.kt b/app/src/main/java/com/dano/test1/Daos.kt index 702934b..11c818f 100644 --- a/app/src/main/java/com/dano/test1/Daos.kt +++ b/app/src/main/java/com/dano/test1/Daos.kt @@ -12,8 +12,15 @@ interface ClientDao { @Delete suspend fun deleteClient(client: Client) + + @Query("SELECT COUNT(*) FROM clients") + suspend fun getClientCount(): Int + + @Query("SELECT * FROM clients") + suspend fun getAllClients(): List } + @Dao interface QuestionnaireDao { @Insert(onConflict = OnConflictStrategy.IGNORE) diff --git a/app/src/main/java/com/dano/test1/DatabaseUploader.kt b/app/src/main/java/com/dano/test1/DatabaseUploader.kt index 70912cb..e928f8a 100644 --- a/app/src/main/java/com/dano/test1/DatabaseUploader.kt +++ b/app/src/main/java/com/dano/test1/DatabaseUploader.kt @@ -18,7 +18,8 @@ import kotlin.system.exitProcess object DatabaseUploader { private const val DB_NAME = "questionnaire_database" - private const val SERVER_DELTA_URL = "http://49.13.157.44/uploadDelta.php" + // TODO entferne uploadDeltaTest.php + private const val SERVER_DELTA_URL = "http://49.13.157.44/uploadDeltaTest.php" private const val SERVER_CHECK_URL = "http://49.13.157.44/checkDatabaseExists.php" private const val API_TOKEN = "MEIN_SUPER_GEHEIMES_TOKEN_12345" @@ -56,10 +57,10 @@ object DatabaseUploader { val exists = checkDatabaseExists() if (exists) { Log.d("UPLOAD", "Server-Datenbank vorhanden → Delta-Upload") - uploadDelta(dbFile) + uploadPseudoDelta(dbFile) } else { Log.d("UPLOAD", "Keine Server-Datenbank → Delta-Upload") - uploadDelta(dbFile) + uploadPseudoDelta(dbFile) } } catch (e: Exception) { @@ -94,7 +95,7 @@ object DatabaseUploader { } } - private fun uploadDelta(file: File) { + private fun uploadPseudoDelta(file: File) { try { val db = SQLiteDatabase.openDatabase(file.absolutePath, null, SQLiteDatabase.OPEN_READONLY) @@ -138,12 +139,18 @@ object DatabaseUploader { } if (response.isSuccessful) { Log.d("UPLOAD", "Delta-Upload erfolgreich: $body") + // Lösche Hauptdatenbank if (file.delete()) { Log.d("UPLOAD", "Lokale DB gelöscht.") - exitProcess(0) } else { Log.e("UPLOAD", "Löschen der lokalen DB fehlgeschlagen.") } + // Lösche Journal-Datei + val journalFile = File(file.parent, file.name + "-journal") + if (journalFile.exists() && journalFile.delete()) { + Log.d("UPLOAD", "Journal-Datei gelöscht.") + } + exitProcess(0) } else { Log.e("UPLOAD", "Delta-Upload fehlgeschlagen: ${response.code} $body") } diff --git a/app/src/main/java/com/dano/test1/HandlerOpeningScreen.kt b/app/src/main/java/com/dano/test1/HandlerOpeningScreen.kt index f11ccad..e592d4f 100644 --- a/app/src/main/java/com/dano/test1/HandlerOpeningScreen.kt +++ b/app/src/main/java/com/dano/test1/HandlerOpeningScreen.kt @@ -9,6 +9,7 @@ import android.widget.* import kotlinx.coroutines.* import org.json.JSONArray import android.util.Log +import java.io.File var INTEGRATION_INDEX_POINTS: Int? = null @@ -35,6 +36,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) { activity.setContentView(R.layout.opening_screen) bindViews() + loadQuestionnaireOrder() createQuestionnaireButtons() restorePreviousClientCode() @@ -46,11 +48,22 @@ class HandlerOpeningScreen(private val activity: MainActivity) { setupUploadButton() setupDownloadButton() + val dbPath = "/data/data/com.dano.test1/databases/questionnaire_database" + val pathExists = File(dbPath).exists() + if (pathExists) { + updateMainButtonsState(true) + } + else{ + updateMainButtonsState(false) + } + if (!editText.text.isNullOrBlank()) { buttonLoad.performClick() } + } + private fun bindViews() { editText = activity.findViewById(R.id.editText) spinner = activity.findViewById(R.id.string_spinner1) @@ -183,13 +196,12 @@ class HandlerOpeningScreen(private val activity: MainActivity) { } withContext(Dispatchers.Main) { + updateMainButtonsState(true) // Datenbank vorhanden -> Buttons aktivieren handleNormalLoad(clientCode) } } } - - private suspend fun handleNormalLoad(clientCode: String) { val completedIds = withContext(Dispatchers.IO) { MyApp.database.completedQuestionnaireDao().getCompletedQuestionnairesForClient(clientCode) @@ -559,6 +571,22 @@ class HandlerOpeningScreen(private val activity: MainActivity) { } } + // --- Füge diese Funktion in deine Klasse ein --- + private fun isDatabasePopulated(): Boolean { + return try { + // Wir prüfen, ob die Datenbank mindestens eine nicht-interne Tabelle enthält. + // Das ist robust gegenüber verschiedenen Tabellennamen. + val db = MyApp.database.openHelper.readableDatabase + val cursor = db.query( + "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name != 'room_master_table'" + ) + cursor.use { it.count > 0 } + } catch (e: Exception) { + // Falls etwas schiefgeht (z.B. DB noch nicht vorhanden), gilt: nicht vorhanden + false + } + } + private fun setupDownloadButton() { downloadButton.text = "Download" downloadButton.setOnClickListener { @@ -568,7 +596,15 @@ class HandlerOpeningScreen(private val activity: MainActivity) { Toast.makeText(activity, "Datenbank wird heruntergeladen...", Toast.LENGTH_SHORT).show() DatabaseDownloader.downloadAndReplaceDatabase(activity) + updateMainButtonsState(true) } } + private fun updateMainButtonsState(isDatabaseAvailable: Boolean) { + val buttons = listOf(buttonLoad, saveButton, editButton) + buttons.forEach { button -> + button.isEnabled = isDatabaseAvailable + button.alpha = if (isDatabaseAvailable) 1.0f else 0.5f + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/dano/test1/MyApp.kt b/app/src/main/java/com/dano/test1/MyApp.kt index 75362da..f26e934 100644 --- a/app/src/main/java/com/dano/test1/MyApp.kt +++ b/app/src/main/java/com/dano/test1/MyApp.kt @@ -1,7 +1,9 @@ package com.dano.test1 import android.app.Application +import android.util.Log import androidx.room.Room +import androidx.room.RoomDatabase import com.dano.test1.data.AppDatabase class MyApp : Application() { @@ -13,12 +15,21 @@ class MyApp : Application() { override fun onCreate() { super.onCreate() + + // Room Datenbank bauen: nur die Hauptdatei, ohne WAL und Journal database = Room.databaseBuilder( applicationContext, AppDatabase::class.java, "questionnaire_database" ) .fallbackToDestructiveMigration() + .setJournalMode(RoomDatabase.JournalMode.TRUNCATE) // TRUNCATE = keine -wal Datei, nur Hauptdatei + .addCallback(object : RoomDatabase.Callback() { + override fun onOpen(db: androidx.sqlite.db.SupportSQLiteDatabase) { + super.onOpen(db) + Log.d("DB", "Datenbank geöffnet. Nur questionnaire_database wird genutzt.") + } + }) .build() } }