HeliBoard/app/src/main/java/helium314/keyboard/settings/screens/DictionaryScreen.kt
Helium314 f0689b00f6 remove "use system locales"
now behavior is as it was disabled
if no subtypes are explicitly enabled, it behaves the same as if the setting was enabled (same as before, actually)
2025-02-25 22:41:54 +01:00

140 lines
6.5 KiB
Kotlin

// SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.settings.screens
import android.content.Context
import android.content.Intent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
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.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import helium314.keyboard.latin.Dictionary
import helium314.keyboard.latin.R
import helium314.keyboard.latin.common.Links
import helium314.keyboard.latin.common.LocaleUtils
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
import helium314.keyboard.latin.common.LocaleUtils.localizedDisplayName
import helium314.keyboard.latin.common.splitOnWhitespace
import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX
import helium314.keyboard.latin.utils.DictionaryInfoUtils
import helium314.keyboard.latin.utils.SubtypeLocaleUtils
import helium314.keyboard.latin.utils.SubtypeSettings
import helium314.keyboard.latin.utils.appendLink
import helium314.keyboard.latin.utils.getDictionaryLocales
import helium314.keyboard.latin.utils.locale
import helium314.keyboard.settings.SearchScreen
import helium314.keyboard.settings.dialogs.ConfirmationDialog
import helium314.keyboard.settings.dialogs.DictionaryDialog
import helium314.keyboard.settings.dictionaryFilePicker
import java.io.File
import java.util.Locale
@Composable
fun DictionaryScreen(
onClickBack: () -> Unit,
) {
val ctx = LocalContext.current
val enabledLanguages = SubtypeSettings.getEnabledSubtypes(true).map { it.locale().language }
val cachedDictFolders = DictionaryInfoUtils.getCachedDirectoryList(ctx).orEmpty().map { it.name }
val comparer = compareBy<Locale>({ it.language !in enabledLanguages }, { it.toLanguageTag() !in cachedDictFolders}, { it.displayName })
val dictionaryLocales = getDictionaryLocales(ctx).sortedWith(comparer).toMutableList()
dictionaryLocales.add(0, Locale(SubtypeLocaleUtils.NO_LANGUAGE))
var selectedLocale: Locale? by remember { mutableStateOf(null) }
var showAddDictDialog by remember { mutableStateOf(false) }
val dictPicker = dictionaryFilePicker(selectedLocale)
SearchScreen(
onClickBack = onClickBack,
title = { Text(stringResource(R.string.dictionary_settings_category)) },
filteredItems = { term ->
if (term.isBlank()) dictionaryLocales
else dictionaryLocales.filter { loc ->
loc.language != SubtypeLocaleUtils.NO_LANGUAGE
&& loc.localizedDisplayName(ctx).replace("(", "")
.splitOnWhitespace().any { it.startsWith(term, true) }
}
},
itemContent = { locale ->
if (locale.language == SubtypeLocaleUtils.NO_LANGUAGE) {
Text(stringResource(R.string.add_new_dictionary_title), Modifier.clickable { showAddDictDialog = true })
} else {
Column(
Modifier.clickable { selectedLocale = locale }
.padding(vertical = 6.dp, horizontal = 16.dp)
.fillMaxWidth()
) {
val (dicts, hasInternal) = getUserAndInternalDictionaries(ctx, locale)
val types = dicts.mapTo(mutableListOf()) { it.name.substringBefore("_${USER_DICTIONARY_SUFFIX}") }
if (hasInternal && !types.contains(Dictionary.TYPE_MAIN))
types.add(0, stringResource(R.string.internal_dictionary_summary))
Text(locale.localizedDisplayName(ctx))
Text(
types.joinToString(", "),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
)
if (showAddDictDialog) {
ConfirmationDialog(
onDismissRequest = { showAddDictDialog = false },
onConfirmed = {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.setType("application/octet-stream")
dictPicker.launch(intent)
},
title = { Text(stringResource(R.string.add_new_dictionary_title)) },
content = {
// addDictString contains "%s" since we didn't supply a formatArg
val addDictString = stringResource(R.string.add_dictionary)
val annotated = buildAnnotatedString {
append(addDictString.substringBefore("%s"))
appendLink(stringResource(R.string.dictionary_link_text), Links.DICTIONARY_URL)
append(addDictString.substringAfter("%s"))
}
Text(annotated)
}
)
}
if (selectedLocale != null) {
DictionaryDialog(
onDismissRequest = { selectedLocale = null },
locale = selectedLocale!!
)
}
}
/** @return list of user dictionary files and whether an internal dictionary exists */
fun getUserAndInternalDictionaries(context: Context, locale: Locale): Pair<List<File>, Boolean> {
val userDicts = mutableListOf<File>()
var hasInternalDict = false
val userLocaleDir = File(DictionaryInfoUtils.getCacheDirectoryForLocale(locale, context))
if (userLocaleDir.exists() && userLocaleDir.isDirectory) {
userLocaleDir.listFiles()?.forEach {
if (it.name.endsWith(USER_DICTIONARY_SUFFIX))
userDicts.add(it)
else if (it.name.startsWith(DictionaryInfoUtils.MAIN_DICT_PREFIX))
hasInternalDict = true
}
}
if (hasInternalDict)
return userDicts to true
val internalDicts = DictionaryInfoUtils.getAssetsDictionaryList(context) ?: return userDicts to false
val best = LocaleUtils.getBestMatch(locale, internalDicts.toList()) {
DictionaryInfoUtils.extractLocaleFromAssetsDictionaryFile(it)?.constructLocale() ?: SubtypeLocaleUtils.NO_LANGUAGE.constructLocale()
}
return userDicts to (best != null)
}