HeliBoard/app/src/main/java/helium314/keyboard/settings/screens/LanguageScreen.kt

186 lines
8 KiB
Kotlin
Raw Normal View History

2025-02-25 20:35:30 +01:00
// SPDX-License-Identifier: GPL-3.0-only
2025-02-16 22:58:19 +01:00
package helium314.keyboard.settings.screens
import android.content.Context
import android.os.Build
import android.view.inputmethod.InputMethodSubtype
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
2025-02-16 22:58:19 +01:00
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
2025-02-16 22:58:19 +01:00
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
2025-02-16 22:58:19 +01:00
import androidx.compose.ui.unit.dp
import helium314.keyboard.latin.R
import helium314.keyboard.latin.common.Constants.Separators
import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
2025-02-23 13:41:30 +01:00
import helium314.keyboard.latin.common.LocaleUtils.localizedDisplayName
2025-02-16 22:58:19 +01:00
import helium314.keyboard.latin.common.splitOnWhitespace
import helium314.keyboard.latin.settings.Defaults
import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX
import helium314.keyboard.latin.utils.DictionaryInfoUtils
import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.MissingDictionaryDialog
2025-02-16 22:58:19 +01:00
import helium314.keyboard.latin.utils.SettingsSubtype.Companion.toSettingsSubtype
import helium314.keyboard.latin.utils.SubtypeLocaleUtils
2025-02-16 22:58:19 +01:00
import helium314.keyboard.latin.utils.SubtypeSettings
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
import helium314.keyboard.latin.utils.displayName
import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.locale
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.SearchScreen
import helium314.keyboard.settings.SettingsActivity
import helium314.keyboard.settings.Theme
2025-02-16 22:58:19 +01:00
import helium314.keyboard.settings.dialogs.SubtypeDialog
import helium314.keyboard.settings.initPreview
import helium314.keyboard.settings.previewDark
import java.util.Locale
2025-02-16 22:58:19 +01:00
@Composable
fun LanguageScreen(
onClickBack: () -> Unit,
) {
val ctx = LocalContext.current
var sortedSubtypes by remember { mutableStateOf(getSortedSubtypes(ctx)) }
val prefs = ctx.prefs()
val b = (LocalContext.current.getActivity() as? SettingsActivity)?.prefChanged?.collectAsState()
if ((b?.value ?: 0) < 0)
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
var selectedSubtype: String? by rememberSaveable { mutableStateOf(null) }
val enabledSubtypes = SubtypeSettings.getEnabledSubtypes()
2025-02-16 22:58:19 +01:00
SearchScreen(
onClickBack = onClickBack,
title = {
Column {
Text(stringResource(R.string.language_and_layouts_title))
Text(stringResource(
R.string.text_tap_languages),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
},
filteredItems = { term ->
sortedSubtypes.filter {
it.displayName(ctx).replace("(", "")
.splitOnWhitespace().any { it.startsWith(term, true) }
}
},
itemContent = { item ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable { selectedSubtype = item.toSettingsSubtype().toPref() }
2025-02-16 22:58:19 +01:00
.padding(vertical = 6.dp, horizontal = 16.dp)
) {
var showNoDictDialog by remember { mutableStateOf(false) }
2025-02-16 22:58:19 +01:00
Column(modifier = Modifier.weight(1f)) {
Text(item.displayName(ctx), style = MaterialTheme.typography.bodyLarge)
val description = if (SubtypeSettings.isAdditionalSubtype(item)) {
val secondaryLocales = item.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)?.split(Separators.KV)
?.joinToString(", ") { it.constructLocale().localizedDisplayName(ctx) }
stringResource(R.string.custom_subtype) + (secondaryLocales?.let { "\n$it" } ?: "")
} else null
if (description != null)
2025-02-16 22:58:19 +01:00
Text(
text = description,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
Switch(
checked = item in enabledSubtypes,
2025-02-16 22:58:19 +01:00
onCheckedChange = {
if (it && !dictsAvailable(item.locale(), ctx))
showNoDictDialog = true
2025-02-16 22:58:19 +01:00
if (it) SubtypeSettings.addEnabledSubtype(prefs, item)
else SubtypeSettings.removeEnabledSubtype(ctx, item)
2025-02-16 22:58:19 +01:00
}
)
if (showNoDictDialog)
MissingDictionaryDialog({ showNoDictDialog = false }, item.locale())
2025-02-16 22:58:19 +01:00
}
}
)
if (selectedSubtype != null) {
val oldSubtype = selectedSubtype!!.toSettingsSubtype()
2025-02-16 22:58:19 +01:00
SubtypeDialog(
onDismissRequest = {
selectedSubtype = null
2025-02-23 19:16:39 +01:00
sortedSubtypes = getSortedSubtypes(ctx)
},
2025-02-16 22:58:19 +01:00
onConfirmed = {
SubtypeUtilsAdditional.changeAdditionalSubtype(oldSubtype, it, ctx)
2025-02-16 22:58:19 +01:00
},
initialSubtype = oldSubtype
2025-02-16 22:58:19 +01:00
)
}
}
private fun dictsAvailable(locale: Locale, context: Context): Boolean {
val (dicts, hasInternal) = getUserAndInternalDictionaries(context, locale)
return hasInternal || dicts.isNotEmpty()
}
// sorting by display name is still slow, even with the cache... but probably good enough
2025-02-16 22:58:19 +01:00
private fun getSortedSubtypes(context: Context): List<InputMethodSubtype> {
val systemLocales = SubtypeSettings.getSystemLocales()
val enabledSubtypes = SubtypeSettings.getEnabledSubtypes(true)
2025-02-16 22:58:19 +01:00
val localesWithDictionary = DictionaryInfoUtils.getCachedDirectoryList(context)?.mapNotNull { dir ->
if (!dir.isDirectory)
return@mapNotNull null
if (dir.list()?.any { it.endsWith(USER_DICTIONARY_SUFFIX) } == true)
dir.name.constructLocale()
else null
}.orEmpty()
val defaultAdditionalSubtypes = Defaults.PREF_ADDITIONAL_SUBTYPES.split(Separators.SETS).map {
it.substringBefore(Separators.SET) to (it.substringAfter(Separators.SET) + ",AsciiCapable,EmojiCapable,isAdditionalSubtype")
}
fun isDefaultSubtype(subtype: InputMethodSubtype): Boolean =
defaultAdditionalSubtypes.any { it.first == subtype.locale().language && it.second == subtype.extraValue }
val subtypeSortComparator = compareBy<InputMethodSubtype>(
{ it !in enabledSubtypes },
{ it.locale() !in localesWithDictionary },
{ it.locale() !in systemLocales},
{ !(SubtypeSettings.isAdditionalSubtype(it) && !isDefaultSubtype(it) ) },
{
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) it.languageTag == SubtypeLocaleUtils.NO_LANGUAGE
else it.locale == SubtypeLocaleUtils.NO_LANGUAGE
2025-02-16 22:58:19 +01:00
},
{ it.displayName(context) }
)
return SubtypeSettings.getAllAvailableSubtypes().sortedWith(subtypeSortComparator)
}
@Preview
@Composable
private fun Preview() {
initPreview(LocalContext.current)
Theme(previewDark) {
Surface {
LanguageScreen { }
}
}
}