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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions app/src/keyboards/java/be/scri/helpers/KeyboardDataHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package be.scri.helpers

import DataContract
import android.content.Context
import android.database.sqlite.SQLiteException
import android.util.Log
import be.scri.helpers.LanguageMappingConstants.getLanguageAlias
import be.scri.helpers.data.AutocompletionDataManager
import be.scri.helpers.english.ENInterfaceVariables.ALREADY_PLURAL_MSG

/**
* Manages database initializations, data contract loading, dictionary queries,
* translations, plurals, and autocompletion data for the keyboard system.
*/
class KeyboardDataHandler {
lateinit var dbManagers: DatabaseManagers
private set

lateinit var autocompletionManager: AutocompletionDataManager
private set

var dataContract: DataContract? = null
internal set

var emojiKeywords: HashMap<String, MutableList<String>>? = null
internal set

var emojiMaxKeywordLength: Int = 0
internal set

var pluralWords: Set<String>? = null
internal set

lateinit var nounKeywords: HashMap<String, List<String>>
internal set

lateinit var suggestionWords: HashMap<String, List<String>>
internal set

lateinit var caseAnnotation: HashMap<String, MutableList<String>>
internal set

var conjugateOutput: MutableMap<String, MutableMap<String, Collection<String>>>? = null
internal set

var conjugateLabels: Set<String> = emptySet()
internal set

val isInitialized: Boolean
get() = this::dbManagers.isInitialized

/**
* Initializes the database managers using the given [context].
*/
fun initialize(context: Context) {
dbManagers = DatabaseManagers(context)
autocompletionManager = dbManagers.autocompletionManager
}

/**
* Loads dictionary, emoji, plural, gender, preposition, and conjugate data for [language].
*/
fun loadLanguageData(language: String) {
val languageAlias = getLanguageAlias(language)
dataContract = dbManagers.getLanguageContract(languageAlias)
emojiKeywords = dbManagers.emojiManager.getEmojiKeywords(languageAlias)
emojiMaxKeywordLength = dbManagers.emojiManager.maxKeywordLength
pluralWords =
dbManagers.pluralManager
.getAllPluralForms(languageAlias, dataContract)
?.map { it.lowercase() }
?.toSet()
nounKeywords = dbManagers.genderManager.findGenderOfWord(languageAlias, dataContract)
suggestionWords = dbManagers.suggestionManager.getSuggestions(languageAlias)
val numbersColumns =
dataContract?.numbers?.let { map ->
(map.keys + map.values).distinct()
} ?: emptyList()
autocompletionManager.loadWords(languageAlias, numbersColumns)
caseAnnotation = dbManagers.prepositionManager.getCaseAnnotations(languageAlias)

val tempConjugateOutput = dbManagers.conjugateDataManager.getTheConjugateLabels(languageAlias, dataContract, "describe")
conjugateOutput = if (tempConjugateOutput?.isEmpty() == true) null else tempConjugateOutput
conjugateLabels = dbManagers.conjugateDataManager.extractConjugateHeadings(dataContract, "coacha")
}

/**
* Queries autocompletion suggestions for [prefix] up to [limit].
*/
fun getAutocompletions(
prefix: String,
limit: Int = 3,
): List<String> =
try {
autocompletionManager.getAutocompletions(prefix, limit)
} catch (e: SQLiteException) {
Log.e("KeyboardDataHandler", "Database error in autocompletion", e)
emptyList()
} catch (e: IllegalStateException) {
Log.e("KeyboardDataHandler", "Illegal state in autocompletion", e)
emptyList()
}

/**
* Retrieves the plural form of [word] for [language].
*/
fun getPluralRepresentation(
language: String,
word: String?,
): String? {
if (word.isNullOrEmpty()) return null
val langAlias = getLanguageAlias(language)
val lowercaseWord = word.lowercase()
if (pluralWords?.contains(lowercaseWord) == true) return ALREADY_PLURAL_MSG
return dbManagers.pluralManager
.getPluralRepresentation(langAlias, dataContract, word)
.values
.firstOrNull()
}

/**
* Retrieves the translation for [commandBarInput] in [language].
*/
fun getTranslation(
language: String,
commandBarInput: String,
): String {
val sourceDest = dbManagers.translationDataManager.getSourceAndDestinationLanguage(language)
return dbManagers.translationDataManager.getTranslationDataForAWord(sourceDest, commandBarInput)
}

/**
* Queries conjugate labels and headings for [searchInput] in [language].
*/
fun queryConjugateData(
language: String,
searchInput: String,
): Pair<MutableMap<String, MutableMap<String, Collection<String>>>?, Set<String>> {
val languageAlias = getLanguageAlias(language)
val tempOutput = dbManagers.conjugateDataManager.getTheConjugateLabels(languageAlias, dataContract, searchInput)
val output =
if (tempOutput?.isEmpty() == true || tempOutput?.values?.all { it.isEmpty() } == true) {
null
} else {
tempOutput
}
val labels = dbManagers.conjugateDataManager.extractConjugateHeadings(dataContract, searchInput)
return Pair(output, labels)
}
}
125 changes: 66 additions & 59 deletions app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import android.R.color.white
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.database.sqlite.SQLiteException
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.GradientDrawable
Expand All @@ -20,7 +19,6 @@ import android.text.InputType.TYPE_CLASS_DATETIME
import android.text.InputType.TYPE_CLASS_NUMBER
import android.text.InputType.TYPE_CLASS_PHONE
import android.text.InputType.TYPE_MASK_CLASS
import android.util.Log
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.EditorInfo
Expand Down Expand Up @@ -50,6 +48,7 @@ import be.scri.helpers.DatabaseManagers
import be.scri.helpers.EmojiUtils.insertEmoji
import be.scri.helpers.FloatingKeyboardHandler
import be.scri.helpers.KeyboardBase
import be.scri.helpers.KeyboardDataHandler
import be.scri.helpers.KeyboardLanguageMappingConstants
import be.scri.helpers.KeyboardStateManager
import be.scri.helpers.LanguageMappingConstants.getLanguageAlias
Expand Down Expand Up @@ -154,29 +153,78 @@ abstract class GeneralKeyboardIME(

private val shiftPermToggleSpeed: Int = DEFAULT_SHIFT_PERM_TOGGLE_SPEED

private lateinit var dbManagers: DatabaseManagers
internal val dataHandler = KeyboardDataHandler()

internal val dbManagers: DatabaseManagers
get() = dataHandler.dbManagers

internal val autocompletionManager: AutocompletionDataManager
get() = dataHandler.autocompletionManager

private lateinit var nativeSuggestionEngine: NativeSuggestionEngine
internal lateinit var suggestionHandler: SuggestionHandler
internal lateinit var autocompletionHandler: AutocompletionHandler
private lateinit var autocompletionManager: AutocompletionDataManager
internal val floatingKeyboardHandler by lazy { FloatingKeyboardHandler(this) }
private var dataContract: DataContract? = null

internal var dataContract: DataContract?
get() = dataHandler.dataContract
set(value) {
dataHandler.dataContract = value
}

internal val isUiManagerInitialized: Boolean get() = this::uiManager.isInitialized

internal fun recreateKeyboardPublic() = recreateKeyboard()

internal fun applyNavBarColorPublic() = applyNavBarColor()

var emojiKeywords: HashMap<String, MutableList<String>>? = null
private var conjugateOutput: MutableMap<String, MutableMap<String, Collection<String>>>? = null
private var conjugateLabels: Set<String> = emptySet()
var emojiKeywords: HashMap<String, MutableList<String>>?
get() = dataHandler.emojiKeywords
set(value) {
dataHandler.emojiKeywords = value
}

private var conjugateOutput: MutableMap<String, MutableMap<String, Collection<String>>>?
get() = dataHandler.conjugateOutput
set(value) {
dataHandler.conjugateOutput = value
}

private var conjugateLabels: Set<String>
get() = dataHandler.conjugateLabels
set(value) {
dataHandler.conjugateLabels = value
}

private var emojiMaxKeywordLength: Int
get() = dataHandler.emojiMaxKeywordLength
set(value) {
dataHandler.emojiMaxKeywordLength = value
}

internal var nounKeywords: HashMap<String, List<String>>
get() = dataHandler.nounKeywords
set(value) {
dataHandler.nounKeywords = value
}

internal var suggestionWords: HashMap<String, List<String>>
get() = dataHandler.suggestionWords
set(value) {
dataHandler.suggestionWords = value
}

var pluralWords: Set<String>?
get() = dataHandler.pluralWords
set(value) {
dataHandler.pluralWords = value
}

private var emojiMaxKeywordLength: Int = 0
internal lateinit var nounKeywords: HashMap<String, List<String>>
internal lateinit var suggestionWords: HashMap<String, List<String>>
var pluralWords: Set<String>? = null
internal lateinit var caseAnnotation: HashMap<String, MutableList<String>>
internal var caseAnnotation: HashMap<String, MutableList<String>>
get() = dataHandler.caseAnnotation
set(value) {
dataHandler.caseAnnotation = value
}

var emojiAutoSuggestionEnabled: Boolean = false
var lastWord: String? = null
Expand Down Expand Up @@ -258,10 +306,9 @@ abstract class GeneralKeyboardIME(
*/
override fun onCreate() {
super.onCreate()
dbManagers = DatabaseManagers(this)
dataHandler.initialize(this)
nativeSuggestionEngine = NativeSuggestionEngine(this)
suggestionHandler = SuggestionHandler(this)
autocompletionManager = dbManagers.autocompletionManager
autocompletionHandler = AutocompletionHandler(this)
clipboardHandler.initClipboardMonitor()
}
Expand Down Expand Up @@ -667,27 +714,7 @@ abstract class GeneralKeyboardIME(
}

private fun loadLanguageData() {
val languageAlias = getLanguageAlias(language)
dataContract = dbManagers.getLanguageContract(languageAlias)
emojiKeywords = dbManagers.emojiManager.getEmojiKeywords(languageAlias)
emojiMaxKeywordLength = dbManagers.emojiManager.maxKeywordLength
pluralWords =
dbManagers.pluralManager
.getAllPluralForms(languageAlias, dataContract)
?.map { it.lowercase() }
?.toSet()
nounKeywords = dbManagers.genderManager.findGenderOfWord(languageAlias, dataContract)
suggestionWords = dbManagers.suggestionManager.getSuggestions(languageAlias)
val numbersColumns =
dataContract?.numbers?.let { map ->
(map.keys + map.values).distinct()
} ?: emptyList()
autocompletionManager.loadWords(languageAlias, numbersColumns)
caseAnnotation = dbManagers.prepositionManager.getCaseAnnotations(languageAlias)

val tempConjugateOutput = dbManagers.conjugateDataManager.getTheConjugateLabels(languageAlias, dataContract, "describe")
conjugateOutput = if (tempConjugateOutput?.isEmpty() == true) null else tempConjugateOutput
conjugateLabels = dbManagers.conjugateDataManager.extractConjugateHeadings(dataContract, "coacha")
dataHandler.loadLanguageData(language)
}

private fun isLightColor(color: Int): Boolean {
Expand Down Expand Up @@ -1216,15 +1243,7 @@ abstract class GeneralKeyboardIME(
return nativeCompletions
}
}
return try {
dbManagers.autocompletionManager.getAutocompletions(prefix, limit)
} catch (e: SQLiteException) {
Log.e("GeneralKeyboardIME", "Database error in autocompletion", e)
emptyList()
} catch (e: IllegalStateException) {
Log.e("GeneralKeyboardIME", "Illegal state in autocompletion", e)
emptyList()
}
return dataHandler.getAutocompletions(prefix, limit)
}

/**
Expand Down Expand Up @@ -1280,16 +1299,7 @@ abstract class GeneralKeyboardIME(
*
* @return The plural form as a string, or null if not found.
*/
private fun getPluralRepresentation(word: String?): String? {
if (word.isNullOrEmpty()) return null
val langAlias = getLanguageAlias(language)
val lowercaseWord = word.lowercase()
if (pluralWords?.contains(lowercaseWord) == true) return ALREADY_PLURAL_MSG
return dbManagers.pluralManager
.getPluralRepresentation(langAlias, dataContract, word)
.values
.firstOrNull()
}
private fun getPluralRepresentation(word: String?): String? = dataHandler.getPluralRepresentation(language, word)

/**
* Retrieves the translation for a given word.
Expand All @@ -1302,10 +1312,7 @@ abstract class GeneralKeyboardIME(
private fun getTranslation(
language: String,
commandBarInput: String,
): String {
val sourceDest = dbManagers.translationDataManager.getSourceAndDestinationLanguage(language)
return dbManagers.translationDataManager.getTranslationDataForAWord(sourceDest, commandBarInput)
}
): String = dataHandler.getTranslation(language, commandBarInput)

/**
* Applies capitalization to all conjugated forms in the output map.
Expand Down
Loading
Loading