Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions app/src/main/java/be/scri/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import be.scri.ui.screens.ThirdPartyScreen
import be.scri.ui.screens.WikimediaScreen
import be.scri.ui.screens.about.AboutScreen
import be.scri.ui.screens.download.CheckUpdateActions
import be.scri.ui.screens.download.ConjugateDataDownloadViewModel
import be.scri.ui.screens.download.ConjugateDownloadDataScreen
import be.scri.ui.screens.download.DataDownloadViewModel
import be.scri.ui.screens.download.DownloadActions
Expand Down Expand Up @@ -85,6 +86,7 @@ fun ScribeApp(
isIncreaseTextSize: Boolean,
modifier: Modifier = Modifier,
downloadViewModel: DataDownloadViewModel = viewModel(),
conjugateDownloadViewModel: ConjugateDataDownloadViewModel = viewModel(),
) {
val coroutineScope = rememberCoroutineScope()
val navBackStackEntry by navController.currentBackStackEntryAsState()
Expand All @@ -107,6 +109,26 @@ fun ScribeApp(
cancelCheckForNewData = downloadViewModel::cancelCheckForNewData,
)

// Conjugate-specific download actions
val conjugateDownloadStates = conjugateDownloadViewModel.downloadStates
val onConjugateDownloadAction = conjugateDownloadViewModel::handleDownloadAction
val onConjugateDownloadAll = conjugateDownloadViewModel::handleDownloadAllLanguages
val initializeConjugateStates = conjugateDownloadViewModel::initializeStates
val conjugateDownloadActions =
DownloadActions(
downloadStates = conjugateDownloadStates,
onDownloadAction = onConjugateDownloadAction,
onDownloadAll = onConjugateDownloadAll,
initializeStates = initializeConjugateStates,
)
val conjugateCheckUpdateState by conjugateDownloadViewModel.checkUpdateState.collectAsState()
val conjugateCheckUpdateActions =
CheckUpdateActions(
checkUpdateState = conjugateCheckUpdateState,
checkForNewData = conjugateDownloadViewModel::checkForNewData,
cancelCheckForNewData = conjugateDownloadViewModel::cancelCheckForNewData,
)

val screens = remember(context) { BottomBarScreen.getScreens() }
ScribeTheme(
useDarkTheme = isDarkTheme,
Expand Down Expand Up @@ -265,8 +287,8 @@ fun ScribeApp(
navController.popBackStack()
},
isDarkTheme = isDarkTheme,
downloadActions = downloadActions,
checkUpdateActions = checkUpdateActions,
downloadActions = conjugateDownloadActions,
checkUpdateActions = conjugateCheckUpdateActions,
modifier = Modifier.padding(innerPadding),
)
}
Expand Down
82 changes: 82 additions & 0 deletions app/src/main/java/be/scri/data/remote/ConjugateDynamicDbHelper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package be.scri.data.remote

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteException
import android.database.sqlite.SQLiteOpenHelper
import android.util.Log
import be.scri.data.model.DataResponse

/**
* Helper class for managing dynamic SQLite databases for conjugate data (verbs only).
* It creates only the verbs table and inserts verb data according to the provided DataResponse.
*/
class ConjugateDynamicDbHelper(
context: Context,
language: String,
) : SQLiteOpenHelper(context, "${language.uppercase()}ConjugateData.sqlite", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
// Tables are created dynamically via syncConjugateDatabase from API contract.
}

override fun onUpgrade(
db: SQLiteDatabase,
old: Int,
new: Int,
) {
// Dynamic schema updates are handled via syncConjugateDatabase.
}

/**
* Synchronizes the conjugate database schema and data based on the provided DataResponse.
* Only creates the verbs table and inserts verb data, ignoring other data types.
* @param response The data response containing the contract and data to be inserted.
*/
fun syncConjugateDatabase(response: DataResponse) {
val db = writableDatabase
try {
db.beginTransaction()

// Check if verbs table exists in the contract
val verbsColumns = response.contract.fields["verbs"]
if (verbsColumns != null) {
// Create verbs table
val colDefinition = verbsColumns.keys.joinToString(", ") { "$it TEXT" }
db.execSQL("DROP TABLE IF EXISTS verbs")
db.execSQL(
"CREATE TABLE verbs " +
"(id INTEGER PRIMARY KEY AUTOINCREMENT, $colDefinition)",
)

// Insert verbs data
val verbsData = response.data["verbs"]
if (verbsData != null) {
val cv = ContentValues()
verbsData.forEach { row ->
cv.clear()
row.forEach { (key, value) ->
cv.put(key, value?.toString() ?: "")
}
db.insert("verbs", null, cv)
}
Log.i("CONJUGATE_DB", "Successfully synced ${verbsData.size} verb records for ${response.language}")
} else {
Log.w("CONJUGATE_DB", "No verbs data found in response for ${response.language}")
}
} else {
Log.e("CONJUGATE_DB", "No verbs table found in contract for ${response.language}")
}

db.setTransactionSuccessful()
} catch (e: SQLiteException) {
Log.e("CONJUGATE_DB", "Error during conjugate database sync: ${e.message}")
throw e
} finally {
db.endTransaction()
db.close()
}
}
}
25 changes: 25 additions & 0 deletions app/src/main/java/be/scri/helpers/DatabaseFileManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,31 @@ class DatabaseFileManager(
return getDatabase(dbName, "data/$dbName")
}

/**
* Retrieves a read-only [SQLiteDatabase] instance for conjugate data (verbs only).
* This database is created specifically for the Conjugate app and contains only verb data.
*
* @param language The language code (e.g., "DE", "FR") used to determine the database filename.
*
* @return An open, read-only [SQLiteDatabase] instance, or `null` on failure.
*/
fun getConjugateDatabase(language: String): SQLiteDatabase? {
val dbName = "${language}ConjugateData.sqlite"
val dbFile = context.getDatabasePath(dbName)

if (!dbFile.exists()) {
Log.w(TAG, "Conjugate database $dbName not found. User needs to download conjugate data first")
return null
}

return try {
SQLiteDatabase.openDatabase(dbFile.path, null, SQLiteDatabase.OPEN_READONLY)
} catch (e: SQLiteException) {
Log.e(TAG, "Failed to open conjugate database $dbName", e)
null
}
}

/**
* A generic function to get a database. It ensures the database file exists in the app's
* private storage (copying it from assets if necessary) and then opens a read-only connection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class ConjugateDataManager(
language: String,
): String {
if (form.isNullOrEmpty()) return ""
return fileManager.getLanguageDatabase(language)?.use { db ->
return fileManager.getConjugateDatabase(language)?.use { db ->
if (!db.tableExists("verbs")) {
return ""
}
Expand Down Expand Up @@ -170,7 +170,7 @@ class ConjugateDataManager(

val targetForm = words.first()

val db = fileManager.getLanguageDatabase(language = language)
val db = fileManager.getConjugateDatabase(language = language)
var auxResult = ""

val auxCursor =
Expand Down
Loading
Loading