diff --git a/app/src/main/java/be/scri/App.kt b/app/src/main/java/be/scri/App.kt
index a578966d..e7c2430d 100644
--- a/app/src/main/java/be/scri/App.kt
+++ b/app/src/main/java/be/scri/App.kt
@@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@@ -30,7 +31,6 @@ import be.scri.navigation.Screen
import be.scri.ui.common.appcomponents.HintDialog
import be.scri.ui.common.bottombar.ScribeBottomBar
import be.scri.ui.screens.DefaultCurrencySymbolScreen
-import be.scri.ui.screens.DownloadDataScreen
import be.scri.ui.screens.InstallationScreen
import be.scri.ui.screens.LanguageSettingsScreen
import be.scri.ui.screens.PrivacyPolicyScreen
@@ -38,6 +38,8 @@ import be.scri.ui.screens.SelectTranslationSourceLanguageScreen
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.DataDownloadViewModel
+import be.scri.ui.screens.download.DownloadDataScreen
import be.scri.ui.screens.settings.SettingsScreen
import be.scri.ui.theme.ScribeTheme
import kotlinx.coroutines.CoroutineScope
@@ -60,7 +62,8 @@ import kotlinx.coroutines.launch
* @param isDarkTheme Flag to indicate if dark theme is enabled.
* @param modifier Optional layout modifier for UI customization.
*/
-@SuppressLint("ComposeModifierMissing")
+@Suppress("LongParameterList")
+@SuppressLint("ComposeModifierMissing", "LongParameterList")
@Composable
fun ScribeApp(
pagerState: PagerState,
@@ -72,9 +75,12 @@ fun ScribeApp(
context: Context,
isDarkTheme: Boolean,
modifier: Modifier = Modifier,
+ downloadViewModel: DataDownloadViewModel = viewModel(),
) {
val coroutineScope = rememberCoroutineScope()
val navBackStackEntry by navController.currentBackStackEntryAsState()
+ val downloadStates = downloadViewModel.downloadStates
+ val onDownloadAction = downloadViewModel::handleDownloadAction
ScribeTheme(
useDarkTheme = isDarkTheme,
@@ -199,6 +205,13 @@ fun ScribeApp(
onBackNavigation = {
navController.popBackStack()
},
+ onNavigateToTranslation = { language ->
+ navController.navigate(
+ "translation_language_detail/$language",
+ )
+ },
+ downloadStates = downloadStates,
+ onDownloadAction = onDownloadAction,
modifier = Modifier.padding(innerPadding),
)
}
@@ -229,6 +242,10 @@ fun ScribeApp(
onBackNavigation = {
navController.popBackStack()
},
+ onNavigateToDownloadData = {
+ navController.popBackStack()
+ },
+ onDownloadAction = onDownloadAction,
modifier = Modifier.padding(innerPadding),
currentLanguage = language,
)
diff --git a/app/src/main/java/be/scri/extensions/CommonsContext.kt b/app/src/main/java/be/scri/extensions/CommonsContext.kt
index f633ab21..8b257452 100644
--- a/app/src/main/java/be/scri/extensions/CommonsContext.kt
+++ b/app/src/main/java/be/scri/extensions/CommonsContext.kt
@@ -9,15 +9,15 @@ import be.scri.helpers.PREFS_KEY
/**
* Retrieves the shared preferences using the predefined PREFS_KEY.
*
- * @receiver Context used to access shared preferences
- * @return SharedPreferences instance
+ * @receiver Context used to access shared preferences.
+ * @return SharedPreferences instance.
*/
fun Context.getSharedPrefs() = getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE)
/**
* Provides an instance of BaseConfig associated with the context.
*
- * @receiver Context used to create BaseConfig instance
- * @return BaseConfig instance
+ * @receiver Context used to create BaseConfig instance.
+ * @return BaseConfig instance.
*/
val Context.baseConfig: BaseConfig get() = BaseConfig.newInstance(this)
diff --git a/app/src/main/java/be/scri/extensions/ContextStyling.kt b/app/src/main/java/be/scri/extensions/ContextStyling.kt
index 4b908a6b..43ae324d 100644
--- a/app/src/main/java/be/scri/extensions/ContextStyling.kt
+++ b/app/src/main/java/be/scri/extensions/ContextStyling.kt
@@ -13,8 +13,8 @@ import be.scri.helpers.DARK_GREY
/**
* Retrieves the appropriate text color based on the user's theme settings.
*
- * @receiver Context used to access resources and configuration
- * @return Int representing the text color
+ * @receiver Context used to access resources and configuration.
+ * @return Int representing the text color.
*/
fun Context.getProperTextColor() =
if (baseConfig.isUsingSystemTheme) {
@@ -26,8 +26,8 @@ fun Context.getProperTextColor() =
/**
* Retrieves the appropriate key color based on the user's theme settings.
*
- * @receiver Context used to access resources and configuration
- * @return Int representing the key color
+ * @receiver Context used to access resources and configuration.
+ * @return Int representing the key color.
*/
fun Context.getProperKeyColor() =
@@ -40,8 +40,8 @@ fun Context.getProperKeyColor() =
/**
* Retrieves the appropriate background color based on the user's theme settings.
*
- * @receiver Context used to access resources and configuration
- * @return Int representing the background color
+ * @receiver Context used to access resources and configuration.
+ * @return Int representing the background color.
*/
fun Context.getProperBackgroundColor() =
if (baseConfig.isUsingSystemTheme) {
@@ -53,8 +53,8 @@ fun Context.getProperBackgroundColor() =
/**
* Retrieves the appropriate primary color based on the user's theme settings.
*
- * @receiver Context used to access resources and configuration
- * @return Int representing the primary color
+ * @receiver Context used to access resources and configuration.
+ * @return Int representing the primary color.
*/
fun Context.getProperPrimaryColor() =
when {
@@ -66,8 +66,8 @@ fun Context.getProperPrimaryColor() =
/**
* Determines if the current theme is black and white.
*
- * @receiver Context used to access configuration
- * @return Boolean indicating if the theme is black and white
+ * @receiver Context used to access configuration.
+ * @return Boolean indicating if the theme is black and white.
*/
fun Context.isBlackAndWhiteTheme() =
baseConfig.textColor == Color.WHITE &&
@@ -77,8 +77,8 @@ fun Context.isBlackAndWhiteTheme() =
/**
* Determines if the current theme is white.
*
- * @receiver Context used to access configuration
- * @return Boolean indicating if the theme is white
+ * @receiver Context used to access configuration.
+ * @return Boolean indicating if the theme is white.
*/
fun Context.isWhiteTheme() =
@@ -89,7 +89,7 @@ fun Context.isWhiteTheme() =
/**
* Determines if the system is using a dark theme.
*
- * @receiver Context used to access configuration
- * @return Boolean indicating if the system is using a dark theme
+ * @receiver Context used to access configuration.
+ * @return Boolean indicating if the system is using a dark theme.
*/
fun Context.isUsingSystemDarkTheme() = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES != 0
diff --git a/app/src/main/java/be/scri/extensions/Drawable.kt b/app/src/main/java/be/scri/extensions/Drawable.kt
index e1b1478f..23db6014 100644
--- a/app/src/main/java/be/scri/extensions/Drawable.kt
+++ b/app/src/main/java/be/scri/extensions/Drawable.kt
@@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable
* Applies a color filter to the drawable using the specified color.
*
* @param color The color to apply using the SRC_IN mode.
+ *
* @return The mutated [Drawable] with the color filter applied.
*/
fun Drawable.applyColorFilter(color: Int) = mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN)
diff --git a/app/src/main/java/be/scri/extensions/Int.kt b/app/src/main/java/be/scri/extensions/Int.kt
index 7ecc77c4..787e1ae9 100644
--- a/app/src/main/java/be/scri/extensions/Int.kt
+++ b/app/src/main/java/be/scri/extensions/Int.kt
@@ -34,6 +34,7 @@ fun Int.getContrastColor(): Int {
*
* @receiver Int The color to adjust.
* @param factor Float The factor to multiply the alpha by (0f = fully transparent, 1f = original alpha).
+ *
* @return Int The color with modified alpha.
*/
fun Int.adjustAlpha(factor: Float): Int {
@@ -62,6 +63,7 @@ private const val FACTOR_DIVIDER = 100
*
* @receiver Int The original color.
* @param factor Int The factor to darken the color by (default is 8).
+ *
* @return Int The darkened color.
*/
@@ -87,6 +89,7 @@ fun Int.darkenColor(factor: Int = DEFAULT_DARKEN_FACTOR): Int {
*
* @receiver Int The original color.
* @param factor Int The factor to lighten the color by (default is 8).
+ *
* @return Int The lightened color.
*/
@@ -113,6 +116,7 @@ private const val LIGHTNESS_THRESHOLD = 0.5f
* Converts a color from HSL to HSV.
*
* @param hsl FloatArray The color in HSL format.
+ *
* @return FloatArray The converted color in HSV format.
*/
private fun hsl2hsv(hsl: FloatArray): FloatArray {
@@ -127,6 +131,7 @@ private fun hsl2hsv(hsl: FloatArray): FloatArray {
* Converts a color from HSV to HSL.
*
* @param hsv FloatArray The color in HSV format.
+ *
* @return FloatArray The converted color in HSL format.
*/
diff --git a/app/src/main/java/be/scri/helpers/AlphanumericComparator.kt b/app/src/main/java/be/scri/helpers/AlphanumericComparator.kt
index 4cdaf020..bd8c1228 100644
--- a/app/src/main/java/be/scri/helpers/AlphanumericComparator.kt
+++ b/app/src/main/java/be/scri/helpers/AlphanumericComparator.kt
@@ -16,6 +16,7 @@ class AlphanumericComparator {
*
* @param string1 The first string to compare.
* @param string2 The second string to compare.
+ *
* @return A negative integer if `string1` comes before `string2`,
* a positive integer if `string1` comes after `string2`,
* and zero if they are equal.
@@ -67,6 +68,7 @@ class AlphanumericComparator {
* @param string The input string.
* @param length The total length of the string.
* @param marker The current position in the string from where extraction starts.
+ *
* @return A substring representing a numeric or alphabetic chunk.
*/
private fun getChunk(
@@ -105,7 +107,8 @@ class AlphanumericComparator {
* Checks if the given character is a numeric digit.
*
* @param ch The character to check.
- * @return `true` if the character is a digit (0-9), `false` otherwise.
+ *
+ * @return true if the character is a digit (0-9), false otherwise.
*/
private fun isDigit(ch: Char) = ch in '0'..'9'
}
diff --git a/app/src/main/java/be/scri/helpers/AnnotationTextUtils.kt b/app/src/main/java/be/scri/helpers/AnnotationTextUtils.kt
index e158286d..17d8e918 100644
--- a/app/src/main/java/be/scri/helpers/AnnotationTextUtils.kt
+++ b/app/src/main/java/be/scri/helpers/AnnotationTextUtils.kt
@@ -17,7 +17,9 @@ import be.scri.helpers.LanguageMappingConstants.prepAnnotationConversionDict
object AnnotationTextUtils {
/**
* Maps a case annotation string (e.g., "genitive case") to a displayable text and color.
+ *
* @param nounType The case annotation string.
+ *
* @return A pair containing the color resource ID and the display text.
*/
fun handleTextForCaseAnnotation(
@@ -40,7 +42,9 @@ object AnnotationTextUtils {
/**
* Maps a noun type string (e.g., "masculine") to a displayable text and color.
+ *
* @param nounType The noun type or gender string.
+ *
* @return A pair containing the color resource ID and the display text.
*/
fun handleColorAndTextForNounType(
@@ -62,8 +66,10 @@ object AnnotationTextUtils {
/**
* Processes a noun gender abbreviation for display, converting it based on language-specific conventions.
+ *
* @param language The current keyboard language.
* @param text The gender abbreviation (e.g., "M", "F", "N").
+ *
* @return The language-specific display text (e.g., "М" for Russian masculine).
*/
fun processValueForNouns(
@@ -73,8 +79,10 @@ object AnnotationTextUtils {
/**
* Processes a preposition case abbreviation for display, converting it based on language-specific conventions.
+ *
* @param language The current keyboard language.
* @param text The case abbreviation (e.g., "Acc", "Dat").
+ *
* @return The language-specific display text (e.g., "Akk" for German accusative).
*/
fun processValuesForPreposition(
diff --git a/app/src/main/java/be/scri/helpers/BaseConfig.kt b/app/src/main/java/be/scri/helpers/BaseConfig.kt
index 9281cece..cae9e465 100644
--- a/app/src/main/java/be/scri/helpers/BaseConfig.kt
+++ b/app/src/main/java/be/scri/helpers/BaseConfig.kt
@@ -8,10 +8,8 @@ import be.scri.extensions.getSharedPrefs
/**
* A configuration helper class for managing app settings.
- *
* This class provides access to various UI-related preferences such as colors, language settings,
* and theme preferences. Preferences are stored using `SharedPreferences`.
- *
*
* @param context The application context used to retrieve resources and shared preferences.
*/
@@ -28,6 +26,7 @@ open class BaseConfig(
* Creates a new instance of `BaseConfig`.
*
* @param context The application context.
+ *
* @return A new instance of `BaseConfig`.
*/
fun newInstance(context: Context) = BaseConfig(context)
diff --git a/app/src/main/java/be/scri/helpers/DatabaseFileManager.kt b/app/src/main/java/be/scri/helpers/DatabaseFileManager.kt
index bff3743d..52b12167 100644
--- a/app/src/main/java/be/scri/helpers/DatabaseFileManager.kt
+++ b/app/src/main/java/be/scri/helpers/DatabaseFileManager.kt
@@ -12,6 +12,7 @@ import java.io.IOException
/**
* Manages access to all SQLite database files.
* Ensures DB files are copied from assets and provides read-only connections.
+ *
* @param context The application context.
*/
class DatabaseFileManager(
@@ -31,6 +32,7 @@ class DatabaseFileManager(
* It handles copying the database from assets if it doesn't exist locally.
*
* @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 getLanguageDatabase(language: String): SQLiteDatabase? {
@@ -56,6 +58,7 @@ class DatabaseFileManager(
* @param dbName The filename of the database (e.g., "ENLanguageData.sqlite").
* @param assetPath The path to the database file within the app's assets folder
* (e.g., "data/ENLanguageData.sqlite").
+ *
* @return An open, read-only [SQLiteDatabase], or `null` if copying or opening fails.
*/
private fun getDatabase(
@@ -85,7 +88,8 @@ class DatabaseFileManager(
*
* @param dbFile The destination [File] in the app's database directory.
* @param assetPath The path to the source file within the assets folder.
- * @return `true` if the copy was successful, `false` otherwise.
+ *
+ * @return true if the copy was successful, false otherwise.
*/
private fun copyDatabaseFromAssets(
dbFile: File,
diff --git a/app/src/main/java/be/scri/helpers/DatabaseManagers.kt b/app/src/main/java/be/scri/helpers/DatabaseManagers.kt
index f2cc61fa..d9325f0e 100644
--- a/app/src/main/java/be/scri/helpers/DatabaseManagers.kt
+++ b/app/src/main/java/be/scri/helpers/DatabaseManagers.kt
@@ -41,6 +41,7 @@ class DatabaseManagers(
* It delegates the loading and parsing logic to the [ContractDataLoader].
*
* @param language The language code (e.g., "DE", "FR") for which to load the contract.
+ *
* @return A [DataContract] object containing the language's structural metadata, or `null`
* if not found or on error.
*/
diff --git a/app/src/main/java/be/scri/helpers/EmojiUtils.kt b/app/src/main/java/be/scri/helpers/EmojiUtils.kt
index 7ce4188c..8b333b01 100644
--- a/app/src/main/java/be/scri/helpers/EmojiUtils.kt
+++ b/app/src/main/java/be/scri/helpers/EmojiUtils.kt
@@ -13,8 +13,10 @@ object EmojiUtils {
/**
* Checks if the end of a string is likely an emoji.
* This is a heuristic check based on common emoji Unicode ranges.
+ *
* @param word The string to check.
- * @return `true` if the end of the string contains an emoji character, `false` otherwise.
+ *
+ * @return true if the end of the string contains an emoji character, false otherwise.
*/
fun isEmoji(word: String?): Boolean {
if (word.isNullOrEmpty() || word.length < DATA_SIZE_2) {
@@ -38,6 +40,7 @@ object EmojiUtils {
/**
* Inserts an emoji into the text field, replacing the keyword that triggered it if found.
+ *
* @param emoji The emoji character to insert.
*/
fun insertEmoji(
diff --git a/app/src/main/java/be/scri/helpers/KeyHandler.kt b/app/src/main/java/be/scri/helpers/KeyHandler.kt
index 4ffa26e0..0f0e148a 100644
--- a/app/src/main/java/be/scri/helpers/KeyHandler.kt
+++ b/app/src/main/java/be/scri/helpers/KeyHandler.kt
@@ -69,7 +69,8 @@ class KeyHandler(
* @param language The current keyboard language.
* @param inputConnection The current input connection.
* @param previousWasLastKeySpace The previous state of wasLastKeySpace.
- * @return True to reset wasLastKeySpace, false to preserve it.
+ *
+ * @return true to reset wasLastKeySpace, false to preserve it.
*/
private fun processKeyCode(
code: Int,
@@ -122,6 +123,7 @@ class KeyHandler(
* Handles the shift key press and returns whether to reset wasLastKeySpace at the end.
*
* @param previousWasLastKeySpace The previous state of wasLastKeySpace.
+ *
* @return False to preserve wasLastKeySpace state, true to reset it.
*/
private fun handleShiftKeyPress(previousWasLastKeySpace: Boolean): Boolean {
@@ -134,6 +136,7 @@ class KeyHandler(
* Handles the space key press and returns whether to reset wasLastKeySpace at the end.
*
* @param previousWasLastKeySpace The previous state of wasLastKeySpace.
+ *
* @return False to preserve wasLastKeySpace state, true to reset it.
*/
private fun handleSpaceKeyPress(previousWasLastKeySpace: Boolean): Boolean {
@@ -146,7 +149,8 @@ class KeyHandler(
* A valid state requires a non-null keyboard instance and an active input connection.
*
* @param inputConnection The current input connection.
- * @return `true` if the state is valid, `false` otherwise.
+ *
+ * @return true if the state is valid, false otherwise.
*/
private fun isValidState(inputConnection: InputConnection?): Boolean =
ime.keyboard != null &&
@@ -252,6 +256,7 @@ class KeyHandler(
/**
* Handles navigation keys (left/right arrows).
+ *
* @param code The key code, used to determine direction.
*/
private fun handleNavigationKey(code: Int) {
@@ -272,6 +277,7 @@ class KeyHandler(
/**
* Handles all special keys related to the Scribe command views (conjugation, etc.).
+ *
* @param code The key code of the pressed key.
* @param language The current keyboard language.
*/
diff --git a/app/src/main/java/be/scri/helpers/KeyboardBase.kt b/app/src/main/java/be/scri/helpers/KeyboardBase.kt
index b53a7e0b..bfc98dcf 100644
--- a/app/src/main/java/be/scri/helpers/KeyboardBase.kt
+++ b/app/src/main/java/be/scri/helpers/KeyboardBase.kt
@@ -134,11 +134,12 @@ class KeyboardBase {
/**
* Retrieves the dimension or fraction value from the attributes, adjusting the base value if necessary.
*
- * @param a the TypedArray containing the attributes
- * @param index the index of the desired attribute
- * @param base the base value for the fraction calculation
- * @param defValue the default value to return if no valid dimension is found
- * @return the calculated dimension or fraction value
+ * @param a The TypedArray containing the attributes.
+ * @param index The index of the desired attribute.
+ * @param base The base value for the fraction calculation.
+ * @param defValue The default value to return if no valid dimension is found.
+ *
+ * @return The calculated dimension or fraction value.
*/
fun getDimensionOrFraction(
a: TypedArray,
@@ -301,11 +302,11 @@ class KeyboardBase {
var repeatable = false
/** Create a key with the given top-left coordinate and extract its attributes from the XML parser.
- * @param res resources associated with the caller's context
- * @param parent the row that this key belongs to. The row must already be attached to a [KeyboardBase].
- * @param x the x coordinate of the top-left
- * @param y the y coordinate of the top-left
- * @param parser the XML parser containing the attributes for this key
+ * @param res Resources associated with the caller's context.
+ * @param parent The row that this key belongs to. The row must already be attached to a [KeyboardBase].
+ * @param x The x coordinate of the top-left.
+ * @param y The y coordinate of the top-left.
+ * @param parser The XML parser containing the attributes for this key.
*/
constructor(res: Resources, parent: Row, x: Int, y: Int, parser: XmlResourceParser?) : this(parent) {
this.x = x
@@ -364,8 +365,9 @@ class KeyboardBase {
/**
* Detects if a point falls inside this key.
- * @param x the x-coordinate of the point
- * @param y the y-coordinate of the point
+ * @param x The x-coordinate of the point.
+ * @param y The y-coordinate of the point.
+ *
* @return whether or not the point falls inside the key.
* If the key is attached to an edge, it will assume that all points between the key and
* the edge are considered to be inside the key.
@@ -387,10 +389,11 @@ class KeyboardBase {
/**
* Creates a keyboard from the given xml key layout file.
- * Weeds out rows that have a keyboard mode defined but don't match the specified mode.
- * @param context the application or service context
- * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
- * @param enterKeyType determines what icon should we show on Enter key
+ * Removes rows that have a keyboard mode defined but don't match the specified mode.
+ *
+ * @param context The application or service context.
+ * @param xmlLayoutResId The resource file that contains the keyboard layout and keys.
+ * @param enterKeyType Determines what icon should we show on Enter key.
*/
@JvmOverloads
constructor(
@@ -412,10 +415,11 @@ class KeyboardBase {
* populates it with the specified characters in left-to-right, top-to-bottom fashion,
* using the specified number of columns. If the specified number of columns is -1,
* then the keyboard will fit as many keys as possible in each row.
- * @param context the application or service context
- * @param layoutTemplateResId the layout template file, containing no keys.
- * @param characters the list of characters to display on the keyboard. One key will be created for each character.
- * @param keyWidth the width of the popup key, make sure it is the same as the key itself
+ *
+ * @param context The application or service context.
+ * @param layoutTemplateResId The layout template file, containing no keys.
+ * @param characters The list of characters to display on the keyboard. One key will be created for each character.
+ * @param keyWidth The width of the popup key, make sure it is the same as the key itself.
*/
constructor(context: Context, layoutTemplateResId: Int, characters: CharSequence, keyWidth: Int) :
this(context, layoutTemplateResId, 0) {
@@ -458,8 +462,9 @@ class KeyboardBase {
/**
* Sets the keyboard shift state.
*
- * @param shiftState the new shift state to apply
- * @return true if the shift state was changed; false otherwise
+ * @param shiftState The new shift state to apply.
+ *
+ * @return true if the shift state was changed, false otherwise.
*/
fun setShifted(shiftState: Int): Boolean {
if (mShiftState != shiftState) {
@@ -476,9 +481,10 @@ class KeyboardBase {
/**
* Creates a Row object from the XML resource parser.
*
- * @param res the resources associated with the context
- * @param parser the XML resource parser
- * @return the created Row object
+ * @param res The resources associated with the context.
+ * @param parser The XML resource parser.
+ *
+ * @return the created Row object.
*/
private fun createRowFromXml(
res: Resources,
@@ -489,12 +495,13 @@ class KeyboardBase {
/**
* Creates a Key object from the XML resource parser and the specified coordinates.
*
- * @param res the resources associated with the context
- * @param parent the parent Row that this key belongs to
- * @param x the x-coordinate of the key
- * @param y the y-coordinate of the key
- * @param parser the XML resource parser
- * @return the created Key object
+ * @param res The resources associated with the context.
+ * @param parent The parent Row that this key belongs to.
+ * @param x The x-coordinate of the key.
+ * @param y The y-coordinate of the key.
+ * @param parser the XML resource parser.
+ *
+ * @return the created Key object.
*/
private fun createKeyFromXml(
res: Resources,
@@ -508,8 +515,8 @@ class KeyboardBase {
* Loads the keyboard configuration from the provided XML parser, populating the rows and keys.
* This method also handles edge cases like custom icons for the Enter key based on its type.
*
- * @param context the application context
- * @param parser the XML resource parser
+ * @param context The application context.
+ * @param parser The XML resource parser.
*/
@SuppressLint("UseCompatLoadingForDrawables")
private fun loadKeyboard(
@@ -646,8 +653,8 @@ class KeyboardBase {
/**
* Parses the keyboard attributes such as key width, height, and horizontal gap from the XML resource.
*
- * @param res the resources associated with the context
- * @param parser the XML resource parser
+ * @param res The resources associated with the context.
+ * @param parser The XML resource parser.
*/
private fun parseKeyboardAttributes(
res: Resources,
diff --git a/app/src/main/java/be/scri/helpers/LanguageMappingConstants.kt b/app/src/main/java/be/scri/helpers/LanguageMappingConstants.kt
index b6dfb4f9..5826aed9 100644
--- a/app/src/main/java/be/scri/helpers/LanguageMappingConstants.kt
+++ b/app/src/main/java/be/scri/helpers/LanguageMappingConstants.kt
@@ -73,7 +73,9 @@ object LanguageMappingConstants {
/**
* Converts a full language name (e.g., "English") to its two-letter ISO alias (e.g., "EN").
+ *
* @param language The full name of the language.
+ *
* @return The two-letter alias.
*/
fun getLanguageAlias(language: String): String =
diff --git a/app/src/main/java/be/scri/helpers/PreferencesHelper.kt b/app/src/main/java/be/scri/helpers/PreferencesHelper.kt
index c23076bd..f5da00b1 100644
--- a/app/src/main/java/be/scri/helpers/PreferencesHelper.kt
+++ b/app/src/main/java/be/scri/helpers/PreferencesHelper.kt
@@ -50,6 +50,7 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to get the translation source.
+ *
* @return The translation source language.
*/
fun getTranslationSourceLanguage(
@@ -68,6 +69,7 @@ object PreferencesHelper {
*
* @param key The base key.
* @param language The language for which to generate the preference key.
+ *
* @return The generated language-specific preference key.
*/
fun getLanguageSpecificPreferenceKey(
@@ -297,6 +299,7 @@ object PreferencesHelper {
* Retrieves the user's dark mode preference.
*
* @param context The application context.
+ *
* @return The dark mode setting as an integer value (AppCompatDelegate.MODE_NIGHT_YES or MODE_NIGHT_NO).
*/
fun getUserDarkModePreference(context: Context): Int {
@@ -319,7 +322,8 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to check the preference.
- * @return True if accent characters are disabled, false otherwise.
+ *
+ * @return true if accent characters are disabled, false otherwise.
*/
fun getIsAccentCharacterDisabled(
context: Context,
@@ -336,7 +340,8 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to check the preference.
- * @return True if the preview feature is enabled, false otherwise.
+ *
+ * @return true if the preview feature is enabled, false otherwise.
*/
fun isShowPopupOnKeypressEnabled(
context: Context,
@@ -349,11 +354,12 @@ object PreferencesHelper {
}
/**
- * Retrieves whether vibration on keypress is enabled for a given language.
+ * Retrieves whether vibration on key press is enabled for a given language.
*
* @param context The application context.
* @param language The language for which to check the preference.
- * @return True if vibration on keypress is enabled, false otherwise.
+ *
+ * @return true if vibration on key press is enabled, false otherwise.
*/
fun getIsVibrateEnabled(
context: Context,
@@ -380,7 +386,8 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to check the preference.
- * @return True if period and comma are enabled, false otherwise.
+ *
+ * @return true if period and comma are enabled, false otherwise.
*/
fun getEnablePeriodAndCommaABC(
context: Context,
@@ -397,7 +404,8 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to check the preference.
- * @return True if double tap on space enabled for a given language, false otherwise.
+ *
+ * @return true if double tap on space enabled for a given language, false otherwise.
*/
fun getEnablePeriodOnSpaceBarDoubleTap(
context: Context,
@@ -411,7 +419,8 @@ object PreferencesHelper {
* Retrieves whether dark mode is enabled based on user preferences or system settings.
*
* @param context The application context.
- * @return True if dark mode is enabled, false otherwise.
+ *
+ * @return true if dark mode is enabled, false otherwise.
*/
fun getIsDarkModeOrNot(context: Context): Boolean {
val sharedPref = context.getSharedPreferences(SCRIBE_PREFS, MODE_PRIVATE)
@@ -426,7 +435,8 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to check the preference.
- * @return True if emoji suggestions are enabled, false otherwise.
+ *
+ * @return true if emoji suggestions are enabled, false otherwise.
*/
fun getIsEmojiSuggestionsEnabled(
context: Context,
@@ -442,7 +452,8 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to check the preference.
- * @return True if word-by-word deletion is enabled, false otherwise.
+ *
+ * @return true if word-by-word deletion is enabled, false otherwise.
*/
fun getIsWordByWordDeletionEnabled(
context: Context,
@@ -495,6 +506,7 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to get the currency preference.
+ *
* @return The currency symbol (e.g., "$", "€").
*/
fun getDefaultCurrencySymbol(
@@ -529,6 +541,7 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to get the currency preference.
+ *
* @return The currency name (e.g., "Dollar", "Euro").
*/
fun getDefaultCurrencyName(
@@ -569,6 +582,7 @@ object PreferencesHelper {
*
* @param context The application context.
* @param language The language for which to get the currency preference.
+ *
* @return The state of hold key style.
*/
fun getHoldKeyStyle(
diff --git a/app/src/main/java/be/scri/helpers/SpaceKeyProcessor.kt b/app/src/main/java/be/scri/helpers/SpaceKeyProcessor.kt
index 439d5355..eaaef455 100644
--- a/app/src/main/java/be/scri/helpers/SpaceKeyProcessor.kt
+++ b/app/src/main/java/be/scri/helpers/SpaceKeyProcessor.kt
@@ -23,6 +23,7 @@ class SpaceKeyProcessor(
* If in command bar mode, it treats space as a regular character input.
*
* @param currentWasLastKeySpace The state of whether the previous key was a space.
+ *
* @return The new state for `wasLastKeySpace` after processing the space key.
*/
fun processKeycodeSpace(currentWasLastKeySpace: Boolean): Boolean {
@@ -56,7 +57,8 @@ class SpaceKeyProcessor(
* Handles space key press when not in command bar mode.
* This includes the "period on double tap" logic if enabled and applicable,
* otherwise commits a normal space. Updates word suggestions.
- * @param wasLastKeySpace True if the previous key pressed was a space.
+ *
+ * @param wasLastKeySpace true if the previous key pressed was a space.
*/
private fun handleSpaceOutsideCommandBar(wasLastKeySpace: Boolean) {
val periodOnDoubleTapEnabled = PreferencesHelper.getEnablePeriodOnSpaceBarDoubleTap(context = ime, ime.language)
@@ -129,8 +131,10 @@ class SpaceKeyProcessor(
* on a double space when the text before is two characters long.
* Criteria: not null, length is 2, starts with a space, and does not end with " .".
* This typically matches patterns like " X" (where X is not '.') or " ".
+ *
* @param textBefore The two characters of text immediately before the cursor.
- * @return True if the conditions are met, false otherwise.
+ *
+ * @return true if the conditions are met, false otherwise.
*/
private fun meetsTwoCharDoubleSpacePeriodCondition(textBefore: String?): Boolean =
textBefore != null &&
diff --git a/app/src/main/java/be/scri/helpers/StringUtils.kt b/app/src/main/java/be/scri/helpers/StringUtils.kt
index 541bf7b8..4e7f8ca0 100644
--- a/app/src/main/java/be/scri/helpers/StringUtils.kt
+++ b/app/src/main/java/be/scri/helpers/StringUtils.kt
@@ -7,8 +7,10 @@ package be.scri.helpers
object StringUtils {
/**
* Checks if a word is capitalized (i.e., starts with an uppercase letter).
+ *
* @param word The word to check.
- * @return `true` if the word is capitalized, `false` otherwise.
+ *
+ * @return true if the word is capitalized, false otherwise.
*/
fun isWordCapitalized(word: String): Boolean {
if (word.isEmpty()) return false
diff --git a/app/src/main/java/be/scri/helpers/data/AutocompletionDataManager.kt b/app/src/main/java/be/scri/helpers/data/AutocompletionDataManager.kt
index c37c1bbf..7be20938 100644
--- a/app/src/main/java/be/scri/helpers/data/AutocompletionDataManager.kt
+++ b/app/src/main/java/be/scri/helpers/data/AutocompletionDataManager.kt
@@ -36,6 +36,7 @@ class AutocompletionDataManager(
*
* @param prefix The starting text to search for (e.g. "ap").
* @param limit The maximum number of suggestions to return (default: 3).
+ *
* @return A list of matching words that begin with the prefix.
*/
fun getAutocompletions(
diff --git a/app/src/main/java/be/scri/helpers/data/ConjugateDataManager.kt b/app/src/main/java/be/scri/helpers/data/ConjugateDataManager.kt
index 8c2ab629..d0678fba 100644
--- a/app/src/main/java/be/scri/helpers/data/ConjugateDataManager.kt
+++ b/app/src/main/java/be/scri/helpers/data/ConjugateDataManager.kt
@@ -22,6 +22,7 @@ class ConjugateDataManager(
* @param language The language code (e.g., "EN", "SV") to determine the correct database.
* @param jsonData The data contract for the language, which defines the structure of conjugations.
* @param word The specific verb to look up conjugations for.
+ *
* @return A nested map where the outer key is the tense group title
* (e.g., "Indicative"), the inner key is the
* conjugation category title (e.g., "Present"), and the value is a collection of the conjugated forms.
@@ -56,6 +57,7 @@ class ConjugateDataManager(
*
* @param jsonData The data contract containing the conjugation structure.
* @param word The base word, which is also added to the set.
+ *
* @return A `Set` of unique strings representing all possible conjugation form identifiers.
*/
fun extractConjugateHeadings(
@@ -78,6 +80,7 @@ class ConjugateDataManager(
* @param word The base word (verb) to look up.
* @param form The specific conjugation form identifier (e.g., "1ps", "past_participle").
* @param language The language code to select the correct database.
+ *
* @return The conjugated word as a [String], or an empty string if not found.
*/
private fun getTheValueForTheConjugateWord(
@@ -99,6 +102,7 @@ class ConjugateDataManager(
*
* @param cursor The database cursor positioned at the correct row for the verb.
* @param form The form identifier, which can be a simple column name or a complex string.
+ *
* @return The conjugated value, or an empty string on failure.
*/
private fun getConjugatedValueFromCursor(
@@ -124,6 +128,7 @@ class ConjugateDataManager(
*
* @param cursor The database cursor positioned at the correct row.
* @param form The complex form string to parse.
+ *
* @return The combined string (e.g., "have walked"), or an empty string on failure.
*/
private fun parseComplexForm(
@@ -208,6 +213,7 @@ class ConjugateDataManager(
* @param db The SQLite database instance to query.
* @param word The verb to search for.
* @param language The language code, used for special query conditions.
+ *
* @return A [Cursor] positioned at the verb's row, or null if the verb is not found.
* The caller is responsible for closing the cursor.
*/
diff --git a/app/src/main/java/be/scri/helpers/data/ContractDataLoader.kt b/app/src/main/java/be/scri/helpers/data/ContractDataLoader.kt
index 48d1349c..085abc8b 100644
--- a/app/src/main/java/be/scri/helpers/data/ContractDataLoader.kt
+++ b/app/src/main/java/be/scri/helpers/data/ContractDataLoader.kt
@@ -20,6 +20,7 @@ class ContractDataLoader(
* It gracefully handles file-not-found and JSON parsing errors by returning null.
*
* @param language The language code (e.g., "DE", "EN") used to determine the filename (e.g., "de.json").
+ *
* @return The decoded [DataContract] object if successful, or `null`
* if the file does not exist or cannot be parsed.
*/
diff --git a/app/src/main/java/be/scri/helpers/data/EmojiDataManager.kt b/app/src/main/java/be/scri/helpers/data/EmojiDataManager.kt
index a7b239f9..0c02bcfc 100644
--- a/app/src/main/java/be/scri/helpers/data/EmojiDataManager.kt
+++ b/app/src/main/java/be/scri/helpers/data/EmojiDataManager.kt
@@ -6,6 +6,7 @@ import be.scri.helpers.DatabaseFileManager
/**
* Manages emoji keywords by querying an SQLite database.
+ *
* @param fileManager The central manager for database file access.
*/
class EmojiDataManager(
@@ -20,6 +21,7 @@ class EmojiDataManager(
* As a side effect, it also calculates and stores the maximum length of any keyword found.
*
* @param language The language code (e.g., "DE", "FR") to select the correct database.
+ *
* @return A [HashMap] where keys are lowercase words and values are a list of associated emoji strings.
*/
fun getEmojiKeywords(language: String): HashMap> {
diff --git a/app/src/main/java/be/scri/helpers/data/GenderDataManager.kt b/app/src/main/java/be/scri/helpers/data/GenderDataManager.kt
index d36c5378..ebed9cd4 100644
--- a/app/src/main/java/be/scri/helpers/data/GenderDataManager.kt
+++ b/app/src/main/java/be/scri/helpers/data/GenderDataManager.kt
@@ -19,6 +19,7 @@ class GenderDataManager(
*
* @param language The language code (e.g., "DE", "FR") to select the correct database.
* @param contract The data contract for the language, which defines the gender-related database columns.
+ *
* @return A [HashMap] where keys are lowercase nouns and values are a list of their gender(s) (e.g., "masculine").
*/
fun findGenderOfWord(
@@ -37,6 +38,7 @@ class GenderDataManager(
*
* @param db The SQLite database instance.
* @param contract The data contract defining how gender is stored for this language.
+ *
* @return A [HashMap] of nouns to their genders.
*/
private fun processGenderData(
@@ -74,8 +76,10 @@ class GenderDataManager(
/**
* Checks if the data contract defines a single, canonical gender column.
+ *
* @param contract The data contract to check.
- * @return `true` if a canonical gender column is specified, `false` otherwise.
+ *
+ * @return true if a canonical gender column is specified, false otherwise.
*/
private fun hasCanonicalGender(contract: DataContract): Boolean =
contract.genders.canonical
@@ -84,8 +88,10 @@ class GenderDataManager(
/**
* Checks if the data contract defines separate columns for masculine and feminine genders.
+ *
* @param contract The data contract to check.
- * @return `true` if both masculine and feminine columns are specified, `false` otherwise.
+ *
+ * @return true if both masculine and feminine columns are specified, false otherwise.
*/
private fun hasMasculineFeminine(contract: DataContract): Boolean {
val masculineList = contract.genders.masculines
diff --git a/app/src/main/java/be/scri/helpers/data/PluralFormsManager.kt b/app/src/main/java/be/scri/helpers/data/PluralFormsManager.kt
index b5bff7f1..eb1c4d0a 100644
--- a/app/src/main/java/be/scri/helpers/data/PluralFormsManager.kt
+++ b/app/src/main/java/be/scri/helpers/data/PluralFormsManager.kt
@@ -18,6 +18,7 @@ class PluralFormsManager(
*
* @param language The language code (e.g., "EN", "DE") to select the correct database.
* @param jsonData The data contract, which specifies the names of the columns containing plural forms.
+ *
* @return A [List] of all plural word forms, or `null`
* if the operation fails or no plural columns are defined.
*/
@@ -37,6 +38,7 @@ class PluralFormsManager(
* @param language The language code to select the correct database.
* @param jsonData The data contract, which specifies the singular and plural column names.
* @param noun The singular noun to find the plural for.
+ *
* @return A [Map] containing the singular noun as the key and
* its plural form as the value, or an empty map if not found.
*/
@@ -79,6 +81,7 @@ class PluralFormsManager(
* @param singularCol The name of the column containing singular nouns.
* @param pluralCol The name of the column containing the corresponding plural nouns.
* @param noun The specific singular noun to search for.
+ *
* @return A map of the singular noun to its plural, or an empty map if not found.
*/
private fun querySpecificPlural(
@@ -109,6 +112,7 @@ class PluralFormsManager(
*
* @param db The SQLite database to query.
* @param pluralColumns A list of column names that contain plural forms.
+ *
* @return A [List] of all plural words found in the specified columns.
*/
private fun queryAllPluralForms(
diff --git a/app/src/main/java/be/scri/helpers/data/PrepositionDataManager.kt b/app/src/main/java/be/scri/helpers/data/PrepositionDataManager.kt
index 60716476..1750e154 100644
--- a/app/src/main/java/be/scri/helpers/data/PrepositionDataManager.kt
+++ b/app/src/main/java/be/scri/helpers/data/PrepositionDataManager.kt
@@ -7,6 +7,7 @@ import be.scri.helpers.DatabaseFileManager
/**
* Manages preposition data and case annotations from the database.
+ *
* @param fileManager The central manager for database file access.
*/
class PrepositionDataManager(
@@ -17,6 +18,7 @@ class PrepositionDataManager(
* This functionality is currently only supported for German ("DE") and Russian ("RU").
*
* @param language The language code.
+ *
* @return A [HashMap] where keys are prepositions and values are a list of required cases
* (e.g., "accusative case").
* Returns an empty map for unsupported languages or on failure.
@@ -36,6 +38,7 @@ class PrepositionDataManager(
* Iterates through a database cursor from the `prepositions` table and populates a map with the results.
*
* @param cursor The cursor containing the preposition and grammatical case data.
+ *
* @return A [HashMap] mapping prepositions to a list of their cases.
*/
private fun processCursor(cursor: Cursor): HashMap> {
diff --git a/app/src/main/java/be/scri/helpers/data/TranslationDataManager.kt b/app/src/main/java/be/scri/helpers/data/TranslationDataManager.kt
index 15897c82..58c1f73c 100644
--- a/app/src/main/java/be/scri/helpers/data/TranslationDataManager.kt
+++ b/app/src/main/java/be/scri/helpers/data/TranslationDataManager.kt
@@ -9,6 +9,7 @@ import be.scri.helpers.StringUtils.isWordCapitalized
/**
* Manages translations from a local SQLite database.
+ *
* @param context The application context.
* @param fileManager The central manager for database file access.
*/
@@ -21,6 +22,7 @@ class TranslationDataManager(
* The source is derived from user preferences, and the destination is the current keyboard language.
*
* @param language The current keyboard language name (e.g., "English").
+ *
* @return A [Pair] containing the source and destination ISO codes (e.g., "en" to "fr").
*/
fun getSourceAndDestinationLanguage(language: String): Pair {
@@ -34,6 +36,7 @@ class TranslationDataManager(
*
* @param sourceAndDestination A [Pair] of source and destination ISO language codes.
* @param word The word to be translated.
+ *
* @return The translated word as a [String], or an empty string if no translation is found.
*/
fun getTranslationDataForAWord(
@@ -96,6 +99,7 @@ class TranslationDataManager(
* @param destColumn The name of the column containing the translation
* (derived from the destination language ISO code, e.g., "fr").
* @param word The word to search for in the 'word' column of the source table.
+ *
* @return The translated word, or an empty string if not found.
*/
private fun queryForTranslation(
@@ -124,6 +128,7 @@ class TranslationDataManager(
* Converts a full language name (e.g., "english") to its corresponding two-letter ISO 639-1 code.
*
* @param languageName The full name of the language.
+ *
* @return The two-letter ISO code as a [String]. Defaults to "en".
*/
private fun generateISOCodeForLanguage(languageName: String): String =
@@ -143,6 +148,7 @@ class TranslationDataManager(
* Converts a two-letter ISO 639-1 code to its corresponding full language name used for table lookups.
*
* @param isoCode The two-letter ISO code.
+ *
* @return The full language name as a [String]. Defaults to "english".
*/
private fun generateLanguageNameForISOCode(isoCode: String): String =
diff --git a/app/src/main/java/be/scri/helpers/data/Trie.kt b/app/src/main/java/be/scri/helpers/data/Trie.kt
index 6db24a81..611bb252 100644
--- a/app/src/main/java/be/scri/helpers/data/Trie.kt
+++ b/app/src/main/java/be/scri/helpers/data/Trie.kt
@@ -44,6 +44,7 @@ class Trie {
*
* @param prefix The prefix to search for.
* @param limit The maximum number of results to return (default = 10).
+ *
* @return A list of words that begin with the prefix, up to [limit].
*
* Example:
diff --git a/app/src/main/java/be/scri/helpers/ui/HintUtils.kt b/app/src/main/java/be/scri/helpers/ui/HintUtils.kt
index b1e691c9..6a95c60e 100644
--- a/app/src/main/java/be/scri/helpers/ui/HintUtils.kt
+++ b/app/src/main/java/be/scri/helpers/ui/HintUtils.kt
@@ -43,6 +43,7 @@ object HintUtils {
*
* @param currentState The current state of the keyboard.
* @param language The language code (e.g., "English", "French").
+ *
* @return The appropriate hint message for the given state and language.
*/
fun getCommandBarHint(
@@ -59,6 +60,7 @@ object HintUtils {
* Maps the current state of the keyboard to its corresponding hints.
*
* @param currentState The current state of the keyboard.
+ *
* @return A map of language codes to hint strings for the current state.
*/
private fun getHintForState(currentState: GeneralKeyboardIME.ScribeState): Map =
@@ -160,6 +162,7 @@ object HintUtils {
*
* @param currentState The current state of the keyboard.
* @param language The language code (e.g., "English", "French").
+ *
* @return The appropriate prompt text for the given state and language.
*/
fun getPromptText(
@@ -180,6 +183,7 @@ object HintUtils {
* Retrieves the translation prompt text for the given language.
*
* @param language The language code (e.g., "English", "French").
+ *
* @return The translation prompt text for the given language.
*/
private fun getTranslationPrompt(
@@ -211,6 +215,7 @@ object HintUtils {
* Retrieves the conjugation prompt text for the given language.
*
* @param language The language code (e.g., "English", "French").
+ *
* @return The conjugation prompt text for the given language.
*/
private fun getConjugationPrompt(language: String): String =
@@ -230,6 +235,7 @@ object HintUtils {
* Retrieves the plural prompt text for the given language.
*
* @param language The language code (e.g., "English", "French").
+ *
* @return The plural prompt text for the given language.
*/
private fun getPluralPrompt(language: String): String =
diff --git a/app/src/main/java/be/scri/helpers/ui/RatingHelper.kt b/app/src/main/java/be/scri/helpers/ui/RatingHelper.kt
index 8234af72..87823929 100644
--- a/app/src/main/java/be/scri/helpers/ui/RatingHelper.kt
+++ b/app/src/main/java/be/scri/helpers/ui/RatingHelper.kt
@@ -23,10 +23,10 @@ object RatingHelper {
/**
* Gets the package name of the app that installed this app.
- *
* For example, "com.android.vending" for Google Play Store.
*
* @param context App context.
+ *
* @return Installer package name, or null if unknown or on error. Logs errors.
*/
private fun getInstallSource(context: Context): String? =
diff --git a/app/src/main/java/be/scri/helpers/ui/ShareHelper.kt b/app/src/main/java/be/scri/helpers/ui/ShareHelper.kt
index 51bd3f68..9031a106 100644
--- a/app/src/main/java/be/scri/helpers/ui/ShareHelper.kt
+++ b/app/src/main/java/be/scri/helpers/ui/ShareHelper.kt
@@ -11,6 +11,7 @@ import android.util.Log
interface ShareHelperInterface {
/**
* Shares scribe.
+ *
* @param context The Android context used to perform the share action.
*/
fun shareScribe(context: Context)
diff --git a/app/src/main/java/be/scri/services/GeneralKeyboardIME.kt b/app/src/main/java/be/scri/services/GeneralKeyboardIME.kt
index 42b771c7..a50b90c9 100644
--- a/app/src/main/java/be/scri/services/GeneralKeyboardIME.kt
+++ b/app/src/main/java/be/scri/services/GeneralKeyboardIME.kt
@@ -180,7 +180,7 @@ abstract class GeneralKeyboardIME(
* 2. The input type variation for URIs (common in address bars).
* 3. The hint text for keywords like "search" or "address".
*
- * @return `true` if the current input field is likely a search or address bar, `false` otherwise.
+ * @return true if the current input field is likely a search or address bar, false otherwise.
*/
fun isSearchBar(): Boolean {
val editorInfo = currentInputEditorInfo
@@ -212,7 +212,8 @@ abstract class GeneralKeyboardIME(
/**
* Returns whether the current conjugation state requires a subsequent selection view.
* This is used, for example, when a conjugation form has multiple options (e.g., "am/is/are" in English).
- * @return `true` if a subsequent selection screen is needed, `false` otherwise.
+ *
+ * @return true if a subsequent selection screen is needed, false otherwise.
*/
internal fun returnIsSubsequentRequired(): Boolean = subsequentAreaRequired
@@ -231,6 +232,7 @@ abstract class GeneralKeyboardIME(
/**
* Creates the main view for the input method, inflating it from XML and setting up the keyboard.
+ *
* @return The root View of the input method.
*/
override fun onCreateInputView(): View {
@@ -316,8 +318,9 @@ abstract class GeneralKeyboardIME(
}
/**
- * Called when the input view is finished. Resets the keyboard state to IDLE.
- * @param finishingInput `true` if we are finishing for good,
+ * Called when the input view is finished. Resets the keyboard state to idle.
+ *
+ * @param finishingInput true if we are finishing for good,
* `false` if just switching to another app.
*/
override fun onFinishInputView(finishingInput: Boolean) {
@@ -338,7 +341,8 @@ abstract class GeneralKeyboardIME(
/**
* Overrides the default implementation to check if there is any
* non-whitespace text before the cursor.
- * @return `true` if there is meaningful text before the cursor, `false` otherwise.
+ *
+ * @return true if there is meaningful text before the cursor, false otherwise.
*/
override fun hasTextBeforeCursor(): Boolean {
val ic = currentInputConnection ?: return false
@@ -348,6 +352,7 @@ abstract class GeneralKeyboardIME(
/**
* Called when a key is pressed down. Triggers haptic feedback if enabled.
+ *
* @param primaryCode The integer code of the key that was pressed.
*/
override fun onPress(primaryCode: Int) {
@@ -406,8 +411,9 @@ abstract class GeneralKeyboardIME(
/**
* Called when the IME is starting to interact with a new input field.
* It initializes the keyboard based on the input type and loads all language-specific data.
+ *
* @param attribute The editor information for the new input field.
- * @param restarting `true` if we are restarting the input with the same editor.
+ * @param restarting true if we are restarting the input with the same editor.
*/
override fun onStartInput(
attribute: EditorInfo?,
@@ -457,8 +463,9 @@ abstract class GeneralKeyboardIME(
/**
* Called when the input view is starting. It sets up the UI theme, emoji settings,
* and initial keyboard state.
+ *
* @param editorInfo The editor information for the input field.
- * @param restarting `true` if we are restarting the input with the same editor.
+ * @param restarting true if we are restarting the input with the same editor.
*/
override fun onStartInputView(
editorInfo: EditorInfo?,
@@ -526,6 +533,7 @@ abstract class GeneralKeyboardIME(
/**
* Updates the color of the Enter key based on the current Scribe state and theme (dark/light mode).
+ *
* @param isDarkMode The current dark mode status. If null, it will be determined from context.
*/
private fun updateEnterKeyColor(isDarkMode: Boolean?) {
@@ -547,6 +555,7 @@ abstract class GeneralKeyboardIME(
/**
* Updates the hint and prompt text displayed in the command bar area based on the current state.
+ *
* @param isUserDarkMode The current dark mode status. If null, it will be determined from context.
* @param text A specific text to be displayed in the prompt, often used for conjugation titles.
* @param word A word to be included in the hint text.
@@ -1070,9 +1079,11 @@ abstract class GeneralKeyboardIME(
/**
* Determines which keyboard layout XML to use based on the current [ScribeState].
+ *
* @param state The current state of the Scribe keyboard.
- * @param isSubsequentArea `true` if this is for a secondary conjugation view.
+ * @param isSubsequentArea true if this is for a secondary conjugation view.
* @param dataSize The number of items to display, used to select an appropriate layout.
+ *
* @return The resource ID of the keyboard layout XML.
*/
private fun getKeyboardLayoutForState(
@@ -1103,6 +1114,7 @@ abstract class GeneralKeyboardIME(
/**
* Initializes or re-initializes the keyboard with a new layout.
+ *
* @param xmlId The resource ID of the keyboard layout XML.
*/
private fun initializeKeyboard(xmlId: Int) {
@@ -1127,6 +1139,7 @@ abstract class GeneralKeyboardIME(
/**
* Retrieves and validates the stored index for the current conjugation view.
* Ensures the index is within the bounds of available conjugation types.
+ *
* @return A valid, zero-based index for the conjugation type.
*/
private fun getValidatedConjugateIndex(): Int {
@@ -1140,8 +1153,9 @@ abstract class GeneralKeyboardIME(
/**
* A wrapper to set up the conjugation key labels for the current language and index.
+ *
* @param conjugateIndex The index of the conjugation tense/mood to display.
- * @param isSubsequentArea `true` if setting up a secondary view.
+ * @param isSubsequentArea true if setting up a secondary view.
*/
internal fun setupConjugateKeysByLanguage(
conjugateIndex: Int,
@@ -1155,8 +1169,9 @@ abstract class GeneralKeyboardIME(
/**
* Sets the labels for the special conjugation keys based on the selected tense/mood.
+ *
* @param startIndex The index of the conjugation tense/mood from the loaded data.
- * @param isSubsequentArea `true` if this is for a secondary conjugation view.
+ * @param isSubsequentArea true if this is for a secondary conjugation view.
*/
private fun setUpConjugateKeys(
startIndex: Int,
@@ -1186,6 +1201,7 @@ abstract class GeneralKeyboardIME(
/**
* Sets up conjugation key labels for non-English languages, which typically follow a 3x2 grid layout.
+ *
* @param languageOutput The map of conjugation forms for the selected tense.
* @param conjugateLabel The list of labels for each person/number (e.g., "1ps", "2ps").
* @param title The title of the current tense/mood.
@@ -1227,8 +1243,9 @@ abstract class GeneralKeyboardIME(
/**
* Sets up conjugation key labels for English, which has a more complex structure,
* potentially requiring a subsequent selection view.
+ *
* @param languageOutput The map of conjugation forms for the selected tense.
- * @param isSubsequentArea `true` if this is for a secondary view.
+ * @param isSubsequentArea true if this is for a secondary view.
*/
private fun setUpEnglishConjugateKeys(
languageOutput: Map>,
@@ -1269,6 +1286,7 @@ abstract class GeneralKeyboardIME(
/**
* Sets up a secondary "sub-view" for conjugation when a single key has multiple options.
+ *
* @param data The full dataset of subsequent options.
* @param word The specific word selected from the primary view, used to filter the data.
*/
@@ -1304,8 +1322,9 @@ abstract class GeneralKeyboardIME(
/**
* Saves the type of conjugation layout being used (e.g., "2x2", "3x2") to shared preferences.
+ *
* @param language The current keyboard language.
- * @param isSubsequentArea `true` if this is for a secondary view.
+ * @param isSubsequentArea true if this is for a secondary view.
*/
internal fun saveConjugateModeType(
language: String,
@@ -1328,7 +1347,8 @@ abstract class GeneralKeyboardIME(
/**
* Updates the visibility of the suggestion buttons based on device type (phone/tablet)
* and whether auto-suggestions are currently active.
- * @param isAutoSuggestEnabled `true` if emoji or linguistic suggestions are available.
+ *
+ * @param isAutoSuggestEnabled true if emoji or linguistic suggestions are available.
*/
internal fun updateButtonVisibility(isAutoSuggestEnabled: Boolean) {
if (currentState != ScribeState.IDLE) {
@@ -1367,6 +1387,7 @@ abstract class GeneralKeyboardIME(
/**
* Handles the logic for showing/hiding suggestion buttons specifically on tablet layouts.
+ *
* @param emojiCount The number of available emoji suggestions.
*/
private fun updateTabletButtonVisibility(emojiCount: Int) {
@@ -1417,6 +1438,7 @@ abstract class GeneralKeyboardIME(
/**
* Handles the logic for showing/hiding suggestion buttons specifically on phone layouts.
+ *
* @param emojiCount The number of available emoji suggestions.
*/
private fun updatePhoneButtonVisibility(emojiCount: Int) {
@@ -1458,20 +1480,24 @@ abstract class GeneralKeyboardIME(
/**
* Retrieves the text immediately preceding the cursor.
+ *
* @return The text before the cursor, up to a defined maximum length.
*/
fun getText(): String? = currentInputConnection?.getTextBeforeCursor(TEXT_LENGTH, 0)?.toString()
/**
* Extracts the last word from the text immediately preceding the cursor.
+ *
* @return The last word as a [String], or null if no word is found.
*/
fun getLastWordBeforeCursor(): String? = getText()?.trim()?.split("\\s+".toRegex())?.lastOrNull()
/**
* Finds associated emojis for the last typed word.
+ *
* @param emojiKeywords The map of keywords to emojis.
* @param lastWord The word to look up.
+ *
* @return A mutable list of emoji suggestions, or null if none are found.
*/
fun findEmojisForLastWord(
@@ -1484,8 +1510,10 @@ abstract class GeneralKeyboardIME(
/**
* Finds the grammatical gender(s) for the last typed word.
+ *
* @param nounKeywords The map of nouns to their genders.
* @param lastWord The word to look up.
+ *
* @return A list of gender strings (e.g., "masculine", "neuter"), or null if not a known noun.
*/
fun findGenderForLastWord(
@@ -1504,8 +1532,10 @@ abstract class GeneralKeyboardIME(
/**
* Finds the next suggestions for the last typed word.
+ *
* @param wordSuggestions The map of words to their suggestions.
* @param lastWord The word to look up.
+ *
* @return A list of gender strings (e.g., "masculine", "neuter"), or null if not a known noun.
*/
fun getNextWordSuggestions(
@@ -1523,9 +1553,11 @@ abstract class GeneralKeyboardIME(
/**
* Checks if the last word is a known plural form.
+ *
* @param pluralWords The set of all known plural words.
* @param lastWord The word to check.
- * @return `true` if the word is in the plural set, `false` otherwise.
+ *
+ * @return true if the word is in the plural set, false otherwise.
*/
fun findWhetherWordIsPlural(
pluralWords: Set?,
@@ -1534,8 +1566,10 @@ abstract class GeneralKeyboardIME(
/**
* Finds the required grammatical case(s) for a preposition.
+ *
* @param caseAnnotation The map of prepositions to their required cases.
* @param lastWord The word to look up (which should be a preposition).
+ *
* @return A mutable list of case suggestions (e.g., "accusative case"), or null if not found.
*/
fun getCaseAnnotationForPreposition(
@@ -1548,7 +1582,8 @@ abstract class GeneralKeyboardIME(
/**
* Updates the text of the suggestion buttons, primarily for displaying emoji suggestions.
- * @param isAutoSuggestEnabled `true` if suggestions are active.
+ *
+ * @param isAutoSuggestEnabled true if suggestions are active.
* @param autoSuggestEmojis The list of emojis to display.
*/
fun updateEmojiSuggestion(
@@ -1600,8 +1635,9 @@ abstract class GeneralKeyboardIME(
/**
* The main dispatcher for displaying linguistic auto-suggestions (gender, case, plurality).
+ *
* @param nounTypeSuggestion The detected gender(s) of the last word.
- * @param isPlural `true` if the last word is plural.
+ * @param isPlural true if the last word is plural.
* @param caseAnnotationSuggestion The detected case(s) required by the last word.
*/
fun updateAutoSuggestText(
@@ -1686,8 +1722,10 @@ abstract class GeneralKeyboardIME(
/**
* A helper function to specifically trigger the plural suggestion UI if needed.
- * @param isPlural `true` if the word is plural.
- * @return `true` if the plural suggestion was handled, `false` otherwise.
+ *
+ * @param isPlural true if the word is plural.
+ *
+ * @return true if the plural suggestion was handled, false otherwise.
*/
private fun handlePluralIfNeeded(isPlural: Boolean): Boolean {
if (isPlural) {
@@ -1702,8 +1740,10 @@ abstract class GeneralKeyboardIME(
/**
* A helper function to handle displaying a single noun gender suggestion.
+ *
* @param nounTypeSuggestion A list containing a single gender string.
- * @return `true` if a suggestion was displayed, `false` otherwise.
+ *
+ * @return true if a suggestion was displayed, false otherwise.
*/
private fun handleSingleNounSuggestion(nounTypeSuggestion: List?): Boolean {
if (nounTypeSuggestion?.size == 1 && !isSingularAndPlural) {
@@ -1718,8 +1758,10 @@ abstract class GeneralKeyboardIME(
/**
* A helper function to handle displaying a single preposition case suggestion.
+ *
* @param caseAnnotationSuggestion A list containing a single case annotation string.
- * @return `true` if a suggestion was displayed, `false` otherwise.
+ *
+ * @return true if a suggestion was displayed, false otherwise.
*/
private fun handleSingleCaseSuggestion(caseAnnotationSuggestion: List?): Boolean {
if (caseAnnotationSuggestion?.size == 1) {
@@ -1739,8 +1781,10 @@ abstract class GeneralKeyboardIME(
/**
* A helper function to handle displaying multiple preposition case suggestions.
+ *
* @param caseAnnotationSuggestion A list containing multiple case annotation strings.
- * @return `true` if suggestions were displayed, `false` otherwise.
+ *
+ * @return true if suggestions were displayed, false otherwise.
*/
private fun handleMultipleCases(caseAnnotationSuggestion: List?): Boolean {
if ((caseAnnotationSuggestion?.size ?: 0) > 1) {
@@ -1753,9 +1797,11 @@ abstract class GeneralKeyboardIME(
/**
* Handles fallback logic when multiple suggestions are available but only one can be shown,
* or when the primary suggestion type isn't displayable.
+ *
* @param nounTypeSuggestion The list of noun suggestions.
* @param caseAnnotationSuggestion The list of case suggestions.
- * @return `true` if a fallback suggestion was applied, `false` otherwise.
+ *
+ * @return true if a fallback suggestion was applied, false otherwise.
*/
private fun handleFallbackSuggestions(
nounTypeSuggestion: List?,
@@ -1909,6 +1955,7 @@ abstract class GeneralKeyboardIME(
/**
* Configures a single suggestion button with the appropriate text and color based on the suggestion type.
+ *
* @param singleTypeSuggestion The list containing the single suggestion to display.
* @param type The type of suggestion, either "noun" or "preposition".
*/
@@ -1950,8 +1997,10 @@ abstract class GeneralKeyboardIME(
/**
* Determines the left and right suggestion types to display for dual suggestions.
+ *
* @param type The suggestion type ("noun" or "preposition").
* @param suggestions The list of suggestion strings.
+ *
* @return A pair of strings representing the left and right suggestion.
*/
private fun getSuggestionTypes(
@@ -1966,8 +2015,10 @@ abstract class GeneralKeyboardIME(
/**
* Creates pairs of (color, text) for dual suggestion buttons.
+ *
* @param type The suggestion type ("noun" or "preposition").
* @param suggestions The list of suggestion strings.
+ *
* @return A pair of pairs, each containing a color resource ID and a text string, or null on failure.
*/
private fun getSuggestionPairs(
@@ -1988,6 +2039,7 @@ abstract class GeneralKeyboardIME(
/**
* Applies a specific style to a suggestion button, including text, color, and a custom background.
+ *
* @param button The Button to style.
* @param colorRes The color resource ID for the background.
* @param text The text to display on the button.
@@ -2028,6 +2080,7 @@ abstract class GeneralKeyboardIME(
/**
* Sets up the UI for two side-by-side suggestion buttons.
+ *
* @param leftSuggestion A pair containing the color and text for the left button.
* @param rightSuggestion A pair containing the color and text for the right button.
*/
@@ -2059,6 +2112,7 @@ abstract class GeneralKeyboardIME(
/**
* Handles the logic when a word has multiple possible genders or
* cases but only one suggestion slot is available.
+ *
* It picks the first valid suggestion to display.
* @param multipleTypeSuggestion The list of noun suggestions.
*/
@@ -2092,6 +2146,7 @@ abstract class GeneralKeyboardIME(
/**
* Handles the UI logic for displaying multiple suggestions simultaneously,
* typically for words with multiple genders.
+ *
* @param multipleTypeSuggestion The list of suggestions to display.
* @param type The type of suggestion, either "noun" or "preposition".
*/
@@ -2153,6 +2208,7 @@ abstract class GeneralKeyboardIME(
/**
* Sets the text size and color for a default, non-active suggestion button.
+ *
* @param button The button to style.
*/
@@ -2164,7 +2220,9 @@ abstract class GeneralKeyboardIME(
/**
* Retrieves the plural form of a word from the database.
+ *
* @param word The singular word to find the plural for.
+ *
* @return The plural form as a string, or null if not found.
*/
private fun getPluralRepresentation(word: String?): String? {
@@ -2186,7 +2244,8 @@ abstract class GeneralKeyboardIME(
/**
* Moves the cursor in the input field.
- * @param moveRight `true` to move right, `false` to move left.
+ *
+ * @param moveRight true to move right, false to move left.
*/
private fun moveCursor(moveRight: Boolean) {
val extractedText = currentInputConnection?.getExtractedText(ExtractedTextRequest(), 0) ?: return
@@ -2196,8 +2255,10 @@ abstract class GeneralKeyboardIME(
/**
* Retrieves the translation for a given word.
+ *
* @param language The current keyboard language (destination language).
* @param commandBarInput The word to be translated (source word).
+ *
* @return The translated word as a string.
*/
private fun getTranslation(
@@ -2210,6 +2271,7 @@ abstract class GeneralKeyboardIME(
/**
* Gets the IME action ID (e.g., Go, Search, Done) from the current editor info.
+ *
* @return The IME action ID, or `IME_ACTION_NONE`.
*/
private fun getImeOptionsActionId(): Int =
@@ -2252,7 +2314,8 @@ abstract class GeneralKeyboardIME(
}
/**
- * Handles the Enter key press when in the `PLURAL` or `TRANSLATE` state.
+ * Handles the Enter key press when in the plural or translate state.
+ *
* @param rawInput The text from the command bar.
* @param inputConnection The current input connection.
*/
@@ -2288,6 +2351,7 @@ abstract class GeneralKeyboardIME(
/**
* Handles the Enter key press when in the `CONJUGATE` state. It fetches the
* conjugation data for the entered verb and transitions to the selection view.
+ *
* @param rawInput The verb entered in the command bar.
*/
private fun handleConjugateState(rawInput: String) {
@@ -2328,7 +2392,9 @@ abstract class GeneralKeyboardIME(
/**
* Handles the default behavior of the Enter key when not in a special Scribe command mode.
+ *
* It performs the editor action or sends a standard Enter key event.
+ *
* @param inputConnection The current input connection.
*/
private fun handleDefaultEnter(inputConnection: InputConnection) {
@@ -2346,6 +2412,7 @@ abstract class GeneralKeyboardIME(
/**
* Commits the output of a Scribe command (like translation or pluralization) to the input field.
+ *
* @param commandModeOutput The string result of the command.
* @param inputConnection The current input connection.
*/
@@ -2364,6 +2431,7 @@ abstract class GeneralKeyboardIME(
/**
* Handles switching between the letter and symbol keyboards.
+ *
* @param keyboardMode The current keyboard mode (letters or symbols).
* @param keyboardView The instance of the keyboard view.
* @param context The application context.
@@ -2394,6 +2462,7 @@ abstract class GeneralKeyboardIME(
/**
* Handles the logic for the Shift key. It cycles through shift states (off, on-for-one-char, caps lock)
* on the letter keyboard, and toggles between symbol pages on the symbol keyboard.
+ *
* @param keyboardMode The current keyboard mode.
* @param keyboardView The instance of the keyboard view.
*/
@@ -2475,8 +2544,10 @@ abstract class GeneralKeyboardIME(
/**
* Handles a key press on one of the special conjugation keys.
* It either commits the text directly or prepares for a subsequent selection view.
+ *
* @param code The key code of the pressed key.
- * @param isSubsequentRequired `true` if a sub-view is needed for more options.
+ * @param isSubsequentRequired true if a sub-view is needed for more options.
+ *
* @return The label of the key that was pressed.
*/
fun handleConjugateKeys(
@@ -2496,8 +2567,9 @@ abstract class GeneralKeyboardIME(
/**
* Handles the logic for the Delete/Backspace key. It deletes characters from either
* the main input field or the command bar, depending on the context.
- * @param isCommandBar `true` if the deletion should happen in the command bar.
- * @param isLongPress `true` if this is a long press/repeat action, `false` for single tap.
+ *
+ * @param isCommandBar true` if the deletion should happen in the command bar.
+ * @param isLongPress true` if this is a long press/repeat action, false for single tap.
*/
fun handleDelete(
isCommandBar: Boolean = false,
@@ -2535,6 +2607,7 @@ abstract class GeneralKeyboardIME(
/**
* Deletes an entire word, including any trailing whitespace.
+ *
* @param inputConnection The current input connection.
*/
private fun deleteWordByWord(inputConnection: InputConnection) {
@@ -2606,9 +2679,10 @@ abstract class GeneralKeyboardIME(
/**
* Handles the input of any non-special character key (e.g., letters, numbers, punctuation).
* It commits the character to the main input field or the command bar.
+ *
* @param code The character code of the key.
* @param keyboardMode The current keyboard mode.
- * @param commandBarState `true` if input should go to the command bar.
+ * @param commandBarState true if input should go to the command bar.
*/
fun handleElseCondition(
code: Int,
@@ -2661,6 +2735,7 @@ abstract class GeneralKeyboardIME(
/**
* Gets the current text in the command bar without the cursor.
+ *
* @return The text content without the trailing cursor character.
*/
private fun getCommandBarTextWithoutCursor(): String {
@@ -2674,6 +2749,7 @@ abstract class GeneralKeyboardIME(
/**
* Sets the command bar text and ensures it ends with the custom cursor.
+ *
* @param text The text to set (without cursor).
* @param cursorAtStart The flag to check if the text in the EditText is empty to determine the position of the cursor
*/
diff --git a/app/src/main/java/be/scri/ui/common/appcomponents/ConfirmationDialog.kt b/app/src/main/java/be/scri/ui/common/appcomponents/ConfirmationDialog.kt
new file mode 100644
index 00000000..110599ad
--- /dev/null
+++ b/app/src/main/java/be/scri/ui/common/appcomponents/ConfirmationDialog.kt
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+package be.scri.ui.common.appcomponents
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonColors
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.em
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogProperties
+import be.scri.R
+import be.scri.helpers.PreferencesHelper.getIsDarkModeOrNot
+
+/**
+ * A confirmation dialog with customizable text and actions.
+ *
+ * @param text The main text to display in the dialog.
+ * @param textChange The text for the change button.
+ * @param textConfirm The text for the confirm button.
+ * @param modifier Modifier for layout and styling.
+ * @param onConfirm Callback triggered when the confirm button is clicked.
+ * @param onChange Callback triggered when the change button is clicked.
+ * @param onDismiss Callback triggered when the dialog is dismissed.
+ */
+@Composable
+fun ConfirmationDialog(
+ text: String,
+ textChange: String,
+ textConfirm: String,
+ modifier: Modifier = Modifier,
+ onConfirm: () -> Unit = {},
+ onChange: () -> Unit = {},
+ onDismiss: () -> Unit = {},
+) {
+ val context = LocalContext.current
+ val isUserDarkMode = remember { getIsDarkModeOrNot(context.applicationContext) }
+
+ ConfirmationDialogContent(
+ text = text,
+ textConfirm = textConfirm,
+ textChange = textChange,
+ onConfirm = onConfirm,
+ onChange = onChange,
+ isUserDarkMode = isUserDarkMode,
+ modifier = modifier.padding(top = 24.dp),
+ onDismiss = onDismiss,
+ )
+}
+
+/**
+ * The content of the confirmation dialog.
+ *
+ * @param text The main text to display in the dialog.
+ * @param textConfirm The text for the confirm button.
+ * @param textChange The text for the change button.
+ * @param isUserDarkMode Whether the dark mode is enabled.
+ * @param modifier Modifier for layout and styling.
+ * @param onConfirm Callback triggered when the confirm button is clicked.
+ * @param onChange Callback triggered when the change button is clicked.
+ * @param onDismiss Callback triggered when the dialog is dismissed.
+ */
+@Composable
+fun ConfirmationDialogContent(
+ text: String,
+ onConfirm: () -> Unit,
+ onChange: () -> Unit,
+ isUserDarkMode: Boolean,
+ textConfirm: String,
+ textChange: String,
+ modifier: Modifier = Modifier,
+ onDismiss: () -> Unit,
+) {
+ val buttonConfirmColor: Color
+ val buttonChangeColor: Color
+ val buttonTextColor: Color
+ val textFontSize = 16.sp
+ val buttonPadding = 8.dp
+ if (isUserDarkMode) {
+ buttonConfirmColor = colorResource(R.color.dark_scribe_blue)
+ buttonChangeColor = colorResource(R.color.dark_special_key_color)
+ buttonTextColor = Color.White
+ } else {
+ buttonConfirmColor = colorResource(R.color.light_scribe_blue)
+ buttonChangeColor = colorResource(R.color.light_special_key_color)
+ buttonTextColor = Color.Black
+ }
+
+ val shadowColor = colorResource(R.color.light_key_shadow_color)
+
+ Dialog(
+ onDismissRequest = {
+ onDismiss()
+ },
+ properties =
+ DialogProperties(
+ usePlatformDefaultWidth = false,
+ ),
+ ) {
+ Box(
+ modifier =
+ modifier
+ .fillMaxWidth(0.95f)
+ .padding(horizontal = 4.dp)
+ .background(
+ brush =
+ Brush.verticalGradient(
+ colors =
+ listOf(
+ Color.Transparent,
+ shadowColor,
+ ),
+ ),
+ shape = RoundedCornerShape(10.dp),
+ ),
+ ) {
+ Surface(
+ shape = RoundedCornerShape(10.dp),
+ color = MaterialTheme.colorScheme.surface,
+ shadowElevation = 4.dp,
+ modifier = Modifier.padding(bottom = 4.dp),
+ ) {
+ Column(
+ modifier = Modifier.padding(10.dp),
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.padding(vertical = 8.dp, horizontal = 6.dp),
+ ) {
+ Icon(
+ painter = painterResource(R.drawable.ic_info_vector),
+ contentDescription = "Confirmation",
+ tint = Color(0xFFFDAD0D),
+ modifier =
+ Modifier
+ .padding(start = 0.dp, end = 10.dp)
+ .size(60.dp),
+ )
+
+ Text(
+ text = text,
+ color = MaterialTheme.colorScheme.onSurface,
+ fontSize = textFontSize,
+ style =
+ MaterialTheme.typography.bodyMedium.merge(
+ TextStyle(lineHeight = 1.5.em),
+ ),
+ modifier = Modifier.weight(0.85f),
+ )
+ }
+ Row(
+ horizontalArrangement = Arrangement.End,
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ Box(
+ modifier =
+ Modifier
+ .padding(end = 2.dp)
+ .background(Color.Transparent)
+ .drawBehind {
+ drawRoundRect(
+ color = shadowColor,
+ topLeft = Offset.Zero.copy(y = size.height - 16.dp.toPx()),
+ size = size.copy(height = 14.dp.toPx()),
+ cornerRadius = CornerRadius(8.dp.toPx()),
+ )
+ },
+ ) {
+ Button(
+ onClick = onChange,
+ colors =
+ ButtonColors(
+ containerColor = buttonChangeColor,
+ contentColor = buttonTextColor,
+ disabledContainerColor = MaterialTheme.colorScheme.secondary,
+ disabledContentColor = Color.White,
+ ),
+ contentPadding = PaddingValues(buttonPadding),
+ shape = RoundedCornerShape(8.dp),
+ ) {
+ Text(
+ text = textChange,
+ fontSize = textFontSize,
+ modifier = Modifier,
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.width(10.dp))
+
+ Box(
+ modifier =
+ Modifier
+ .background(Color.Transparent)
+ .drawBehind {
+ drawRoundRect(
+ color = shadowColor,
+ topLeft = Offset.Zero.copy(y = size.height - 16.dp.toPx()),
+ size = size.copy(height = 14.dp.toPx()),
+ cornerRadius = CornerRadius(8.dp.toPx()),
+ )
+ },
+ ) {
+ Button(
+ onClick = onConfirm,
+ colors =
+ ButtonColors(
+ containerColor = buttonConfirmColor,
+ contentColor = buttonTextColor,
+ disabledContainerColor = MaterialTheme.colorScheme.secondary,
+ disabledContentColor = Color.White,
+ ),
+ contentPadding = PaddingValues(buttonPadding),
+ shape = RoundedCornerShape(8.dp),
+ ) {
+ Text(
+ text = textConfirm,
+ fontSize = textFontSize,
+ modifier = Modifier,
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/be/scri/ui/common/components/DownloadDataOptionComp.kt b/app/src/main/java/be/scri/ui/common/components/DownloadDataOptionComp.kt
index 446fa13f..650c4a84 100644
--- a/app/src/main/java/be/scri/ui/common/components/DownloadDataOptionComp.kt
+++ b/app/src/main/java/be/scri/ui/common/components/DownloadDataOptionComp.kt
@@ -16,10 +16,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -28,7 +24,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import be.scri.R
-import java.time.LocalDate
+import be.scri.ui.screens.download.DownloadState
/**
* A button component that reflects the state of a data download.
@@ -40,10 +36,11 @@ import java.time.LocalDate
*/
@Composable
fun DownloadDataOptionComp(
+ onClick: () -> Unit,
isDarkTheme: Boolean,
modifier: Modifier = Modifier,
+ downloadState: DownloadState = DownloadState.Ready,
) {
- var downloadState by remember { mutableStateOf(DownloadState.Ready) }
val colorScheme = MaterialTheme.colorScheme
val backgroundColor = getBackgroundColor(downloadState, isDarkTheme, colorScheme)
@@ -51,20 +48,7 @@ fun DownloadDataOptionComp(
val iconColor = getIconColor(isDarkTheme, textColor)
Button(
- onClick = {
- downloadState =
- when (downloadState) {
- DownloadState.Ready -> DownloadState.Downloading
- DownloadState.Downloading -> DownloadState.Completed
- DownloadState.Completed ->
- if (isUpdateAvailable(PLACEBO_LOCAL_UPDATED_AT, PLACEBO_SERVER_UPDATED_AT)) {
- DownloadState.Update
- } else {
- DownloadState.Completed
- }
- DownloadState.Update -> DownloadState.Downloading
- }
- },
+ onClick = onClick,
enabled = true,
colors =
ButtonDefaults.buttonColors(
@@ -191,29 +175,3 @@ private fun DownloadStateIcon(
}
}
}
-
-/**
- * @return true if server data is newer than local data
- */
-private const val PLACEBO_SERVER_UPDATED_AT = "2025-01-10"
-private const val PLACEBO_LOCAL_UPDATED_AT = "2025-01-01"
-
-private fun isUpdateAvailable(
- localUpdatedAt: String,
- serverUpdatedAt: String,
-): Boolean {
- val localDate = LocalDate.parse(localUpdatedAt)
- val serverDate = LocalDate.parse(serverUpdatedAt)
-
- return serverDate.isAfter(localDate)
-}
-
-/**
- * Represents the state of the download button.
- */
-enum class DownloadState {
- Ready,
- Downloading,
- Completed,
- Update,
-}
diff --git a/app/src/main/java/be/scri/ui/common/components/LanguageItemComp.kt b/app/src/main/java/be/scri/ui/common/components/LanguageItemComp.kt
index 9810211b..168d3faf 100644
--- a/app/src/main/java/be/scri/ui/common/components/LanguageItemComp.kt
+++ b/app/src/main/java/be/scri/ui/common/components/LanguageItemComp.kt
@@ -19,6 +19,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import be.scri.ui.screens.download.DownloadState
/**
* Simple language item with a title on the left and a download button on the right.
@@ -27,10 +28,12 @@ import androidx.compose.ui.unit.sp
fun LanguageItemComp(
title: String,
onClick: () -> Unit,
+ onButtonClick: () -> Unit,
modifier: Modifier = Modifier,
titleFontWeight: FontWeight = FontWeight.Normal,
titleFontSize: androidx.compose.ui.unit.TextUnit = 16.sp,
isDarkTheme: Boolean = false,
+ buttonState: DownloadState? = null,
) {
Row(
modifier =
@@ -52,7 +55,9 @@ fun LanguageItemComp(
)
Spacer(modifier = Modifier.width(100.dp))
DownloadDataOptionComp(
+ onClick = onButtonClick,
isDarkTheme = isDarkTheme,
+ downloadState = buttonState ?: DownloadState.Ready,
modifier = Modifier.widthIn(min = 50.dp),
)
}
diff --git a/app/src/main/java/be/scri/ui/screens/LanguageSettingsScreen.kt b/app/src/main/java/be/scri/ui/screens/LanguageSettingsScreen.kt
index 1ae39df7..58f19de0 100644
--- a/app/src/main/java/be/scri/ui/screens/LanguageSettingsScreen.kt
+++ b/app/src/main/java/be/scri/ui/screens/LanguageSettingsScreen.kt
@@ -395,6 +395,7 @@ private fun getLayoutListData(
* If the specified language is not recognized, defaults to the English string resource.
*
* @param language The name of the language (e.g., "German", "French").
+ *
* @return The string resource ID corresponding to the localized name.
*/
fun getLanguageStringFromi18n(language: String): Int {
@@ -418,6 +419,7 @@ fun getLanguageStringFromi18n(language: String): Int {
* When the user selects the source language item, the provided [onTranslationLanguageSelect] callback is triggered.
*
* @param onTranslationLanguageSelect A lambda function invoked when the user clicks the "Select Source Language" item.
+ *
* @return A list of [ScribeItem]s to be displayed in the UI.
*/
@Composable
diff --git a/app/src/main/java/be/scri/ui/screens/SelectLanguageScreen.kt b/app/src/main/java/be/scri/ui/screens/SelectLanguageScreen.kt
index 83c3b7c3..40e2504f 100644
--- a/app/src/main/java/be/scri/ui/screens/SelectLanguageScreen.kt
+++ b/app/src/main/java/be/scri/ui/screens/SelectLanguageScreen.kt
@@ -30,27 +30,38 @@ import androidx.compose.ui.unit.dp
import androidx.core.content.edit
import be.scri.R
import be.scri.ui.common.ScribeBaseScreen
+import be.scri.ui.common.appcomponents.ConfirmationDialog
/**
* The Select Languages subpage is for selecting the translation source language.
+ * @param currentLanguage The current main/destination language.
+ * @param onBackNavigation Callback for back navigation action.
+ * @param onNavigateToDownloadData Callback for navigating to the data download screen.
+ * @param modifier Modifier for layout and styling.
+ * @param onDownloadAction Callback for download action when a new source language is selected and confirmed.
*/
@Composable
fun SelectTranslationSourceLanguageScreen(
currentLanguage: String,
onBackNavigation: () -> Unit,
+ onNavigateToDownloadData: () -> Unit,
modifier: Modifier = Modifier,
+ onDownloadAction: (String) -> Unit = {},
) {
val context = LocalContext.current
val sharedPref = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
- var selectedLanguage =
+ var savedLanguage =
remember {
mutableStateOf(sharedPref.getString("translation_source_$currentLanguage", "English") ?: "English")
}
+ var selectedLanguage = remember { mutableStateOf(savedLanguage.value) }
+ var showDialog = remember { mutableStateOf(false) }
val scrollState = rememberScrollState()
val options =
listOf("English", "French", "German", "Italian", "Portuguese", "Russian", "Spanish", "Swedish")
.filterNot { it == getDisplayLanguageName(currentLanguage) }
+
ScribeBaseScreen(
pageTitle = stringResource(R.string.app_settings_keyboard_translation_select_source_title),
lastPage = stringResource(id = getLanguageStringFromi18n(currentLanguage)),
@@ -84,8 +95,11 @@ fun SelectTranslationSourceLanguageScreen(
Modifier
.fillMaxWidth()
.clickable {
- selectedLanguage.value = option
- sharedPref.edit { putString("translation_source_$currentLanguage", option) }
+ // Only show dialog if the selection is different
+ if (option != savedLanguage.value) {
+ selectedLanguage.value = option
+ showDialog.value = true
+ }
}.padding(vertical = 5.dp, horizontal = 8.dp),
) {
Text(
@@ -96,8 +110,11 @@ fun SelectTranslationSourceLanguageScreen(
RadioButton(
selected = (option == selectedLanguage.value),
onClick = {
- selectedLanguage.value = option
- sharedPref.edit { putString("translation_source_$currentLanguage", option) }
+ // Only show dialog if the selection is different
+ if (option != savedLanguage.value) {
+ selectedLanguage.value = option
+ showDialog.value = true
+ }
},
)
}
@@ -110,6 +127,39 @@ fun SelectTranslationSourceLanguageScreen(
}
}
}
+
+ if (showDialog.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}",
+ onConfirm = {
+ // User confirmed - save the new selection permanently.
+ savedLanguage.value = selectedLanguage.value
+ sharedPref.edit { putString("translation_source_$currentLanguage", selectedLanguage.value) }
+
+ val downloadKey = currentLanguage.lowercase()
+ // trigger the download action in the ViewModel.
+ onDownloadAction(downloadKey)
+ showDialog.value = false
+ // Navigate to the download data screen.
+ onNavigateToDownloadData()
+ },
+ onChange = {
+ // User cancelled - revert back to old selection.
+ selectedLanguage.value = savedLanguage.value
+ showDialog.value = false
+ },
+ onDismiss = {
+ // Dialog dismissed - revert back to old selection.
+ selectedLanguage.value = savedLanguage.value
+ showDialog.value = false
+ },
+ )
+ }
}
@Composable
diff --git a/app/src/main/java/be/scri/ui/screens/about/AboutUtil.kt b/app/src/main/java/be/scri/ui/screens/about/AboutUtil.kt
index ed09bd38..8ccfe669 100644
--- a/app/src/main/java/be/scri/ui/screens/about/AboutUtil.kt
+++ b/app/src/main/java/be/scri/ui/screens/about/AboutUtil.kt
@@ -207,6 +207,7 @@ object AboutUtil {
* @param onWikimediaAndScribeClick Callback invoked when Wikimedia link is clicked.
* @param onShareScribeClick Callback invoked when Share Scribe link is clicked.
* @param context Android context to open URLs.
+ *
* @return A [ScribeItemList] wrapping community external links.
*/
@Composable
@@ -228,6 +229,7 @@ object AboutUtil {
* @param onMailClick Callback to open email intent.
* @param onResetHintsClick Callback to reset onboarding hints.
* @param context Android context used to launch external intents.
+ *
* @return A [ScribeItemList] wrapping feedback and support options.
*/
@Composable
@@ -254,6 +256,7 @@ object AboutUtil {
*
* @param onPrivacyPolicyClick Callback invoked when Privacy Policy is selected.
* @param onThirdPartyLicensesClick Callback invoked when Third-Party Licenses is selected.
+ *
* @return A [ScribeItemList] wrapping legal information items.
*/
@Composable
diff --git a/app/src/main/java/be/scri/ui/screens/DataDownloadScreen.kt b/app/src/main/java/be/scri/ui/screens/download/DataDownloadScreen.kt
similarity index 73%
rename from app/src/main/java/be/scri/ui/screens/DataDownloadScreen.kt
rename to app/src/main/java/be/scri/ui/screens/download/DataDownloadScreen.kt
index 6023d742..63f99a0a 100644
--- a/app/src/main/java/be/scri/ui/screens/DataDownloadScreen.kt
+++ b/app/src/main/java/be/scri/ui/screens/download/DataDownloadScreen.kt
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-package be.scri.ui.screens
+package be.scri.ui.screens.download
+import android.content.Context
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@@ -28,20 +29,35 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import be.scri.R
import be.scri.ui.common.ScribeBaseScreen
+import be.scri.ui.common.appcomponents.ConfirmationDialog
import be.scri.ui.common.components.CircleClickableItemComp
import be.scri.ui.common.components.LanguageItemComp
import be.scri.ui.common.components.SwitchableItemComp
import be.scri.ui.screens.settings.SettingsUtil
+/**
+ * Screen for downloading and managing language data.
+ *
+ * @param onBackNavigation Callback for back navigation action.
+ * @param onNavigateToTranslation Callback for navigating to translation language selection.
+ * @param modifier Modifier for layout and styling.
+ * @param downloadStates Map of language keys to their download states.
+ * @param onDownloadAction Callback for download action when a language is selected and confirmed.
+ */
@Composable
fun DownloadDataScreen(
onBackNavigation: () -> Unit,
+ onNavigateToTranslation: (String) -> Unit,
modifier: Modifier = Modifier,
+ downloadStates: Map = emptyMap(),
+ onDownloadAction: (String) -> Unit = {},
) {
val scrollState = rememberScrollState()
val checkForNewData = remember { mutableStateOf(false) }
val regularlyUpdateData = remember { mutableStateOf(true) }
+ val selectedLanguage = remember { mutableStateOf?>(null) }
val context = LocalContext.current
+ val sharedPref = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
val installedKeyboardLanguages =
remember {
SettingsUtil.getKeyboardLanguages(context)
@@ -140,12 +156,22 @@ fun DownloadDataScreen(
color = MaterialTheme.colorScheme.surface,
) {
Column(Modifier.padding(vertical = 10.dp, horizontal = 4.dp)) {
- languages.forEachIndexed { index, (key, title, isDark) ->
+ languages.forEachIndexed { index, lang ->
+ val (key, title, isDark) = lang
+ val currentStatus = downloadStates[key] ?: DownloadState.Ready
+
LanguageItemComp(
title = title,
- onClick = {
+ onClick = { },
+ onButtonClick = {
+ if (currentStatus == DownloadState.Ready) {
+ selectedLanguage.value = lang
+ } else {
+ onDownloadAction(key)
+ }
},
isDarkTheme = isDark,
+ buttonState = currentStatus,
)
if (index < languages.lastIndex) {
HorizontalDivider(
@@ -160,6 +186,25 @@ fun DownloadDataScreen(
}
Spacer(modifier = Modifier.height(10.dp))
+
+ selectedLanguage.value?.let { lang ->
+ val (key, title, _) = lang
+ val languageId = key.replaceFirstChar { it.uppercase() }
+ val sourceLang = sharedPref.getString("translation_source_$languageId", "English") ?: "English"
+ ConfirmationDialog(
+ text =
+ "The data you will download will allow you to translate from $sourceLang to $title." +
+ " Do you want to change the language you'll translate from?",
+ textConfirm = "Use $sourceLang",
+ textChange = "Change language",
+ onConfirm = {
+ onDownloadAction(key)
+ selectedLanguage.value = null
+ },
+ onChange = { onNavigateToTranslation(languageId) },
+ onDismiss = { selectedLanguage.value = null },
+ )
+ }
}
}
}
diff --git a/app/src/main/java/be/scri/ui/screens/download/DataDownloadViewModel.kt b/app/src/main/java/be/scri/ui/screens/download/DataDownloadViewModel.kt
new file mode 100644
index 00000000..e3230e5d
--- /dev/null
+++ b/app/src/main/java/be/scri/ui/screens/download/DataDownloadViewModel.kt
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package be.scri.ui.screens.download
+
+import androidx.compose.runtime.mutableStateMapOf
+import androidx.lifecycle.ViewModel
+import java.time.LocalDate
+
+private const val PLACEBO_SERVER_UPDATED_AT = "2025-01-10"
+private const val PLACEBO_LOCAL_UPDATED_AT = "2025-01-01"
+
+/** ViewModel to manage data download states and actions. */
+class DataDownloadViewModel : ViewModel() {
+ val downloadStates = mutableStateMapOf()
+
+ /**
+ * @return true if server data is newer than local data.
+ */
+ private fun isUpdateAvailable(
+ localUpdatedAt: String,
+ serverUpdatedAt: String,
+ ): Boolean {
+ val localDate = LocalDate.parse(localUpdatedAt)
+ val serverDate = LocalDate.parse(serverUpdatedAt)
+
+ return serverDate.isAfter(localDate)
+ }
+
+ /**
+ * Handles the download action based on the current state.
+ *
+ * @param key The key identifying the download item.
+ */
+ fun handleDownloadAction(key: String) {
+ val currentState = downloadStates[key] ?: DownloadState.Ready
+ downloadStates[key] =
+ when (currentState) {
+ DownloadState.Ready -> DownloadState.Downloading
+ DownloadState.Downloading -> DownloadState.Completed
+ DownloadState.Completed ->
+ if (isUpdateAvailable(PLACEBO_LOCAL_UPDATED_AT, PLACEBO_SERVER_UPDATED_AT)) {
+ DownloadState.Update
+ } else {
+ DownloadState.Completed
+ }
+ DownloadState.Update -> DownloadState.Downloading
+ }
+ }
+}
+
+/**
+ * Represents the state of the download button.
+ */
+enum class DownloadState {
+ Ready,
+ Downloading,
+ Completed,
+ Update,
+}
diff --git a/app/src/main/java/be/scri/ui/screens/settings/SettingsUtil.kt b/app/src/main/java/be/scri/ui/screens/settings/SettingsUtil.kt
index a1cdcdeb..17359777 100644
--- a/app/src/main/java/be/scri/ui/screens/settings/SettingsUtil.kt
+++ b/app/src/main/java/be/scri/ui/screens/settings/SettingsUtil.kt
@@ -20,7 +20,8 @@ object SettingsUtil {
* Checks whether the custom keyboard is already installed and enabled.
*
* @param context The context to access system services.
- * @return True if the keyboard is installed, false otherwise.
+ *
+ * @return true if the keyboard is installed, false otherwise.
*/
fun checkKeyboardInstallation(context: Context): Boolean {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
@@ -31,7 +32,7 @@ object SettingsUtil {
/**
* Sets the app theme to light or dark mode and saves the user's preference.
*
- * @param isDarkMode True to enable dark mode, false for light mode.
+ * @param isDarkMode true to enable dark mode, false for light mode.
* @param context The context used to save preferences.
*/
fun setLightDarkMode(
@@ -87,6 +88,7 @@ object SettingsUtil {
* Retrieves the list of available keyboard languages based on enabled input methods.
*
* @param context The context to access input methods.
+ *
* @return A list of language names.
*/
fun getKeyboardLanguages(context: Context): List {
@@ -110,6 +112,7 @@ object SettingsUtil {
* Maps a language name to its corresponding localized string resource ID.
*
* @param language The name of the language.
+ *
* @return The string resource ID for the localized name.
*/
fun getLocalizedLanguageName(language: String): Int {
diff --git a/app/src/main/java/be/scri/ui/screens/settings/SettingsViewModel.kt b/app/src/main/java/be/scri/ui/screens/settings/SettingsViewModel.kt
index 2c4437d3..e631e55a 100644
--- a/app/src/main/java/be/scri/ui/screens/settings/SettingsViewModel.kt
+++ b/app/src/main/java/be/scri/ui/screens/settings/SettingsViewModel.kt
@@ -52,7 +52,7 @@ class SettingsViewModel(
/**
* Updates the UI theme mode based on the user's preference.
*
- * @param value True to enable dark mode, false for light mode.
+ * @param value true to enable dark mode, false for light mode.
*/
fun setLightDarkMode(value: Boolean) {
_isUserDarkMode.value = value
diff --git a/app/src/main/java/be/scri/views/KeyboardView.kt b/app/src/main/java/be/scri/views/KeyboardView.kt
index b09661be..085fe80d 100644
--- a/app/src/main/java/be/scri/views/KeyboardView.kt
+++ b/app/src/main/java/be/scri/views/KeyboardView.kt
@@ -92,14 +92,16 @@ class KeyboardView
/**
* Called when the user presses a key. This is sent before the [.onKey] is called.
* For keys that repeat, this is only called once.
- * @param primaryCode the unicode of the key being pressed.
+ *
+ * @param primaryCode The unicode of the key being pressed.
* If the touch is not on a valid key, the value will be zero.
*/
fun onPress(primaryCode: Int)
/**
* Send a key press to the listener.
- * @param code this is the key that was pressed
+ *
+ * @param code The key that was pressed
*/
fun onKey(code: Int)
@@ -120,12 +122,14 @@ class KeyboardView
/**
* Sends a sequence of characters to the listener.
- * @param text the string to be displayed.
+ *
+ * @param text The string to be displayed.
*/
fun onText(text: String)
/**
* Checks if there is text before the current cursor position.
+ *
* @return true if there is text before the cursor and false otherwise.
*/
fun hasTextBeforeCursor(): Boolean
@@ -331,6 +335,7 @@ class KeyboardView
/**
* Sets the color of the Enter key based on a specific color or theme mode.
+ *
* @param color The optional color to apply.
* @param isDarkMode Whether the dark mode is enabled (optional).
*/
@@ -357,8 +362,10 @@ class KeyboardView
/**
* Sets the icon of the Enter key based on current state.
+ *
* @param state The current keyboard state.
* @param earlierValue Previously assigned Enter key value (optional).
+ *
* @return The updated Enter key value.
*/
fun setEnterKeyIcon(
@@ -455,6 +462,7 @@ class KeyboardView
* Returns the label for a key with the given code.
*
* @param code The code of the key.
+ *
* @return The label for the key, or null if the key code is not recognized.
*/
fun getKeyLabel(code: Int): String? =
@@ -624,6 +632,7 @@ class KeyboardView
/**
* Attaches a keyboard to this view.
* The keyboard can be switched at any time and the view will re-layout itself to accommodate the keyboard.
+ *
* @param keyboard the keyboard to display in this view
*/
fun setKeyboard(keyboard: KeyboardBase) {
@@ -674,8 +683,10 @@ class KeyboardView
/**
* Sets the state of the shift key of the keyboard, if any.
- * @param shifted whether or not to enable the state of the shift key
- * @return true if the shift key state changed, false if there was no change
+ *
+ * @param shifted Whether or not to enable the state of the shift key
+ *
+ * @return true if the shift key state changed, false if there was no change.
*/
private fun setShifted(shiftState: Int) {
if (mKeyboard?.setShifted(shiftState) == true) {
@@ -685,7 +696,7 @@ class KeyboardView
/**
* Returns the state of the shift key of the keyboard, if any.
- * @return true if the shift is in a pressed state, false otherwise
+ * @return true if the shift is in a pressed state, false otherwise.
*/
private fun isShifted(): Boolean = mKeyboard?.mShiftState ?: SHIFT_OFF > SHIFT_OFF
@@ -738,7 +749,8 @@ class KeyboardView
* Compute the average distance between adjacent keys (horizontally and vertically)
* and square it to get the proximity threshold.
* We use a square here and in computing the touch distance from a key's center to avoid taking a square root.
- * @param keyboard
+ *
+ * @param keyboard The base class for the keyboard UI.
*/
private fun computeProximityThreshold(keyboard: KeyboardBase?) {
if (keyboard == null) {
@@ -1348,7 +1360,8 @@ class KeyboardView
* Invalidates a key so that it will be redrawn on the next repaint.
* Use this method if only one key is changing it's content. Any changes that
* affect the position or size of the key may not be honored.
- * @param keyIndex the index of the key in the attached [KeyboardBase].
+ *
+ * @param keyIndex The index of the key in the attached [KeyboardBase].
*/
private fun invalidateKey(keyIndex: Int) {
if (keyIndex < 0 || keyIndex >= mKeys.size) {
@@ -1390,7 +1403,9 @@ class KeyboardView
* Called when a key is long pressed.
* By default this will open any popup keyboard associated with this key through the attributes
* popupLayout and popupCharacters.
- * @param popupKey the key that was long pressed
+ *
+ * @param popupKey The key that was long pressed.
+ *
* @return true if the long press is handled, false otherwise.
* Subclasses should call the method on the base class if the subclass doesn't wish to
* handle the call.