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
1 change: 1 addition & 0 deletions app/src/main/java/be/scri/data/remote/DynamicDbHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class DynamicDbHelper(
db.setTransactionSuccessful()
} catch (e: SQLiteException) {
Log.e("SCRIBE_DB", "Error during insert: ${e.message}")
throw e
} finally {
db.endTransaction()
db.close()
Expand Down
23 changes: 23 additions & 0 deletions app/src/main/java/be/scri/helpers/StringUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,27 @@ object StringUtils {
}
return result
}

/**
* Replaces placeholder variables in a template string with provided parameters.
*
* This helper function works with template strings that use {variable_name} placeholder syntax,
* replacing them in order with the provided parameters.
*
* @param template The template string (e.g., "Network error: {error}")
* @param params Variable number of string parameters to replace placeholders with.
* Placeholders are replaced in the order they appear in the string.
*
* @return The formatted string with all placeholders replaced by the provided parameters.
*/
fun formatStringWithParams(
template: String,
vararg params: String,
): String {
var result = template
params.forEach { param ->
result = result.replaceFirst(Regex("""\{[^}]+\}"""), param)
}
return result
}
}
18 changes: 13 additions & 5 deletions app/src/main/java/be/scri/ui/screens/SelectLanguageScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.content.edit
import be.scri.R
import be.scri.helpers.StringUtils
import be.scri.ui.common.ScribeBaseScreen
import be.scri.ui.common.appcomponents.ConfirmationDialog

Expand Down Expand Up @@ -129,13 +130,20 @@ fun SelectTranslationSourceLanguageScreen(
}

if (showDialog.value) {
val localizedSelectedLang = getDisplayLanguageName(selectedLanguage.value)
val localizedSavedLang = getDisplayLanguageName(savedLanguage.value)
ConfirmationDialog(
text =
"You've changed your source translation language. " +
"Would you like to download new data so that you can translate " +
"from ${selectedLanguage.value}?",
textConfirm = "Download data",
textChange = "Keep ${savedLanguage.value}",
StringUtils.stringResourceWithParams(
R.string.i18n_app_settings_keyboard_translation_change_source_tooltip_download_warning,
localizedSelectedLang,
),
textConfirm = stringResource(R.string.i18n_app__global_download_data),
textChange =
StringUtils.stringResourceWithParams(
R.string.i18n_app_settings_keyboard_translation_change_source_tooltip_keep_source_language,
localizedSavedLang,
),
onConfirm = {
// User confirmed - save the new selection permanently.
savedLanguage.value = selectedLanguage.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import android.widget.Toast
import androidx.compose.runtime.mutableStateMapOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import be.scri.R
import be.scri.data.remote.ConjugateDynamicDbHelper
import be.scri.data.remote.RetrofitClient
import be.scri.helpers.LanguageMappingConstants
import be.scri.helpers.StringUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.TimeoutCancellationException
Expand Down Expand Up @@ -106,7 +108,12 @@ class ConjugateDataDownloadViewModel(
}

if (currentState == DownloadState.Completed) {
Toast.makeText(getApplication(), "$displayLang conjugate data is already up to date", Toast.LENGTH_SHORT).show()
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_menu_ui_conjugate_data_already_up_to_date,
)
val msg = StringUtils.formatStringWithParams(template, displayLang)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
return
}
}
Expand Down Expand Up @@ -144,23 +151,51 @@ class ConjugateDataDownloadViewModel(

withContext(Dispatchers.Main) {
downloadStates[key] = DownloadState.Completed
Toast.makeText(getApplication(), "Download $displayLang conjugate data finished!", Toast.LENGTH_SHORT).show()
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_menu_ui_conjugate_data_download_success,
)
val msg = StringUtils.formatStringWithParams(template, displayLang)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
}
} else {
// Already up to date: Skip the DB work.
withContext(Dispatchers.Main) {
downloadStates[key] = DownloadState.Completed
Toast.makeText(getApplication(), "Already up to date!", Toast.LENGTH_SHORT).show()
val msg =
getApplication<Application>().getString(
R.string.i18n_app_download_menu_ui_download_data_generic_already_up_to_date,
)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
}
}
} catch (e: IOException) {
updateErrorState(key, "Network Error: ${e.message}")
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_error_network,
)
val errorMsg = StringUtils.formatStringWithParams(template, e.message ?: "")
updateErrorState(key, errorMsg)
} catch (e: SQLiteException) {
updateErrorState(key, "Database Error: ${e.message}")
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_error_database,
)
val errorMsg = StringUtils.formatStringWithParams(template, e.message ?: "")
updateErrorState(key, errorMsg)
} catch (e: HttpException) {
updateErrorState(key, "Server Error: ${e.code()}")
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_error_server,
)
val errorMsg = StringUtils.formatStringWithParams(template, e.code().toString())
updateErrorState(key, errorMsg)
} catch (e: TimeoutCancellationException) {
updateErrorState(key, "Download timed out")
val errorMsg =
getApplication<Application>().getString(
R.string.i18n_app_download_error_timeout,
)
updateErrorState(key, errorMsg)
throw e
} finally {
// Clean up the job reference when done.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ fun ConjugateDownloadDataScreen(
) {
Column(Modifier.padding(vertical = 10.dp, horizontal = 4.dp)) {
Text(
text = "Update all",
text = stringResource(R.string.i18n_app_download_menu_ui_download_data_update_all),
color = colorResource(R.color.dark_scribe_blue),
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ private fun LanguagesListSection(
) {
Column(Modifier.padding(vertical = 10.dp, horizontal = 4.dp)) {
Text(
text = "Update all",
text = stringResource(R.string.i18n_app_download_menu_ui_download_data_update_all),
color = colorResource(R.color.dark_scribe_blue),
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import android.widget.Toast
import androidx.compose.runtime.mutableStateMapOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import be.scri.R
import be.scri.data.remote.DynamicDbHelper
import be.scri.data.remote.RetrofitClient
import be.scri.helpers.LanguageMappingConstants
import be.scri.helpers.StringUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.TimeoutCancellationException
Expand Down Expand Up @@ -106,7 +108,12 @@ class DataDownloadViewModel(
}

if (currentState == DownloadState.Completed) {
Toast.makeText(getApplication(), "$displayLang data is already up to date", Toast.LENGTH_SHORT).show()
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_menu_ui_download_data_already_up_to_date,
)
val msg = StringUtils.formatStringWithParams(template, displayLang)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
return
}
}
Expand Down Expand Up @@ -144,23 +151,51 @@ class DataDownloadViewModel(

withContext(Dispatchers.Main) {
downloadStates[key] = DownloadState.Completed
Toast.makeText(getApplication(), "Download $displayLang data finished!", Toast.LENGTH_SHORT).show()
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_menu_ui_download_data_download_success,
)
val msg = StringUtils.formatStringWithParams(template, displayLang)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
}
} else {
// Already up to date: Skip the DB work.
withContext(Dispatchers.Main) {
downloadStates[key] = DownloadState.Completed
Toast.makeText(getApplication(), "Already up to date!", Toast.LENGTH_SHORT).show()
val msg =
getApplication<Application>().getString(
R.string.i18n_app_download_menu_ui_download_data_generic_already_up_to_date,
)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
}
}
} catch (e: IOException) {
updateErrorState(key, "Network Error: ${e.message}")
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_error_network,
)
val errorMsg = StringUtils.formatStringWithParams(template, e.message ?: "")
updateErrorState(key, errorMsg)
} catch (e: SQLiteException) {
updateErrorState(key, "Database Error: ${e.message}")
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_error_database,
)
val errorMsg = StringUtils.formatStringWithParams(template, e.message ?: "")
updateErrorState(key, errorMsg)
} catch (e: HttpException) {
updateErrorState(key, "Server Error: ${e.code()}")
val template =
getApplication<Application>().getString(
R.string.i18n_app_download_error_server,
)
val errorMsg = StringUtils.formatStringWithParams(template, e.code().toString())
updateErrorState(key, errorMsg)
} catch (e: TimeoutCancellationException) {
updateErrorState(key, "Download timed out")
val errorMsg =
getApplication<Application>().getString(
R.string.i18n_app_download_error_timeout,
)
updateErrorState(key, errorMsg)
throw e
} finally {
// Clean up the job reference when done.
Expand Down
15 changes: 15 additions & 0 deletions app/src/test/kotlin/be/scri/helpers/StringUtilsTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package be.scri.helpers

import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
Expand Down Expand Up @@ -35,4 +36,18 @@ class StringUtilsTest {
fun isWordCapitalized_returnsFalse_forNumberStartingString() {
assertFalse(StringUtils.isWordCapitalized("1hello"))
}

@Test
fun formatStringWithParams_replacesPlaceholdersInOrder() {
val template = "Hello {name}, welcome to {place}."
val result = StringUtils.formatStringWithParams(template, "Alice", "Wonderland")
assertEquals("Hello Alice, welcome to Wonderland.", result)
}

@Test
fun formatStringWithParams_doesNotChangeTemplate_ifNoPlaceholders() {
val template = "Hello World"
val result = StringUtils.formatStringWithParams(template, "Alice")
assertEquals("Hello World", result)
}
}
Loading