HeliBoard/app/src/main/java/helium314/keyboard/settings/screens/LanguageScreen.kt
Helium314 6bbce0b5ca change subtype dialog to screen
mostly just moved the content and adjusted necessary calls
still needs some minor tweaking
2025-03-30 12:40:06 +02:00

170 lines
7.4 KiB
Kotlin

// SPDX-License-Identifier: GPL-3.0-only
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
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.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
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
import helium314.keyboard.latin.common.LocaleUtils.localizedDisplayName
import helium314.keyboard.latin.common.splitOnWhitespace
import helium314.keyboard.latin.settings.Defaults
import helium314.keyboard.latin.settings.SettingsSubtype.Companion.toSettingsSubtype
import helium314.keyboard.latin.utils.DictionaryInfoUtils
import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.MissingDictionaryDialog
import helium314.keyboard.latin.utils.SubtypeLocaleUtils
import helium314.keyboard.latin.utils.SubtypeSettings
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.SettingsDestination
import helium314.keyboard.settings.Theme
import helium314.keyboard.settings.initPreview
import helium314.keyboard.settings.previewDark
import java.util.Locale
@Composable
fun LanguageScreen(
onClickBack: () -> Unit,
) {
val ctx = LocalContext.current
val 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")
val enabledSubtypes = SubtypeSettings.getEnabledSubtypes()
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 {
SettingsDestination.navigateTo(SettingsDestination.Subtype + item.toSettingsSubtype().toPref())
}
.padding(vertical = 6.dp, horizontal = 16.dp)
) {
var showNoDictDialog by remember { mutableStateOf(false) }
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)
Text(
text = description,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
Switch(
checked = item in enabledSubtypes,
onCheckedChange = {
if (it && !dictsAvailable(item.locale(), ctx))
showNoDictDialog = true
if (it) SubtypeSettings.addEnabledSubtype(prefs, item)
else SubtypeSettings.removeEnabledSubtype(ctx, item)
}
)
if (showNoDictDialog)
MissingDictionaryDialog({ showNoDictDialog = false }, item.locale())
}
}
)
}
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
private fun getSortedSubtypes(context: Context): List<InputMethodSubtype> {
val systemLocales = SubtypeSettings.getSystemLocales()
val enabledSubtypes = SubtypeSettings.getEnabledSubtypes(true)
val localesWithDictionary = DictionaryInfoUtils.getCachedDirectoryList(context)?.mapNotNull { dir ->
if (!dir.isDirectory)
return@mapNotNull null
if (dir.list()?.any { it.endsWith(DictionaryInfoUtils.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
},
{ it.displayName(context) }
)
return SubtypeSettings.getAllAvailableSubtypes().sortedWith(subtypeSortComparator)
}
@Preview
@Composable
private fun Preview() {
initPreview(LocalContext.current)
Theme(previewDark) {
Surface {
LanguageScreen { }
}
}
}