diff --git a/app/src/main/java/helium314/keyboard/latin/settings/LanguageSettingsDialog.kt b/app/src/main/java/helium314/keyboard/latin/settings/LanguageSettingsDialog.kt index 6bba6288c..c14ab261a 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/LanguageSettingsDialog.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/LanguageSettingsDialog.kt @@ -389,7 +389,7 @@ class LanguageSettingsDialog( } /** @return list of user dictionary files and whether an internal dictionary exists */ -fun getUserAndInternalDictionaries(context: Context, locale: Locale): Pair, Boolean> { +private fun getUserAndInternalDictionaries(context: Context, locale: Locale): Pair, Boolean> { val userDicts = mutableListOf() var hasInternalDict = false val userLocaleDir = File(DictionaryInfoUtils.getCacheDirectoryForLocale(locale, context)) diff --git a/app/src/main/java/helium314/keyboard/latin/utils/DictionaryUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/DictionaryUtils.kt index 01dfc41cb..7a114b6c6 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/DictionaryUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/DictionaryUtils.kt @@ -15,8 +15,7 @@ import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.settings.Defaults import helium314.keyboard.latin.settings.Settings import java.io.File -import java.util.* -import kotlin.collections.HashSet +import java.util.Locale fun getDictionaryLocales(context: Context): MutableSet { val locales = HashSet() diff --git a/app/src/main/java/helium314/keyboard/settings/FilePicker.kt b/app/src/main/java/helium314/keyboard/settings/FilePicker.kt index 8330544d4..503a23b23 100644 --- a/app/src/main/java/helium314/keyboard/settings/FilePicker.kt +++ b/app/src/main/java/helium314/keyboard/settings/FilePicker.kt @@ -16,9 +16,13 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import helium314.keyboard.latin.R +import helium314.keyboard.latin.common.FileUtils import helium314.keyboard.latin.utils.LayoutUtilsCustom import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.settings.dialogs.InfoDialog +import helium314.keyboard.settings.dialogs.NewDictionaryDialog +import java.io.File +import java.util.Locale val layoutIntent = Intent(Intent.ACTION_OPEN_DOCUMENT) .addCategory(Intent.CATEGORY_OPENABLE) @@ -59,3 +63,22 @@ fun layoutFilePicker( return loadFilePicker } +@Composable +fun dictionaryFilePicker(mainLocale: Locale?): ManagedActivityResultLauncher { + val ctx = LocalContext.current + val cachedDictionaryFile = File(ctx.cacheDir.path + File.separator + "temp_dict") + var done by remember { mutableStateOf(false) } + val picker = filePicker { uri -> + cachedDictionaryFile.delete() + FileUtils.copyContentUriToNewFile(uri, ctx, cachedDictionaryFile) + done = true + } + if (done) + NewDictionaryDialog( + onDismissRequest = { done = false }, + cachedDictionaryFile, + mainLocale + ) + + return picker +} diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt b/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt index 9bc2c2bb4..cf6f5ee21 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt @@ -13,6 +13,8 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.Column import androidx.compose.material3.Surface +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.platform.ComposeView import androidx.core.view.ViewCompat import androidx.core.view.isGone @@ -26,6 +28,7 @@ import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.ExecutorUtils import helium314.keyboard.latin.utils.cleanUnusedMainDicts import helium314.keyboard.latin.utils.prefs +import helium314.keyboard.settings.dialogs.NewDictionaryDialog import kotlinx.coroutines.flow.MutableStateFlow import java.io.BufferedOutputStream import java.io.File @@ -43,6 +46,8 @@ import java.util.zip.ZipOutputStream class SettingsActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener { private val prefs by lazy { this.prefs() } val prefChanged = MutableStateFlow(0) // simple counter, as the only relevant information is that something changed + private val dictUriFlow = MutableStateFlow(null) + private val cachedDictionaryFile by lazy { File(this.cacheDir.path + File.separator + "temp_dict") } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -55,7 +60,7 @@ class SettingsActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferen askAboutCrashReports() // with this the layout edit dialog is not covered by the keyboard - // alterative of Modifier.imePadding() and properties = DialogProperties(decorFitsSystemWindows = false) has other weird side effects + // alternative of Modifier.imePadding() and properties = DialogProperties(decorFitsSystemWindows = false) has other weird side effects ViewCompat.setOnApplyWindowInsetsListener(window.decorView.rootView) { _, insets -> insets } settingsContainer = SettingsContainer(this) @@ -73,13 +78,14 @@ class SettingsActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferen findViewById(R.id.navHost).setContent { Theme { Surface { + val dictUri by dictUriFlow.collectAsState() if (spellchecker) Column { // lazy way of implementing spell checker settings settingsContainer[Settings.PREF_USE_CONTACTS]!!.Preference() settingsContainer[Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE]!!.Preference() } else - SettingsNavHost( + SettingsNavHost( onClickBack = { // this.finish() // todo: when removing old settings if (supportFragmentManager.findFragmentById(R.id.settingsFragmentContainer) == null) @@ -87,9 +93,25 @@ class SettingsActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferen else supportFragmentManager.popBackStack() } ) + if (dictUri != null) { + NewDictionaryDialog( + onDismissRequest = { dictUriFlow.value = null }, + cachedFile = cachedDictionaryFile, + mainLocale = null + ) + } } } } + + if (intent?.action == Intent.ACTION_VIEW) { + intent?.data?.let { + cachedDictionaryFile.delete() + FileUtils.copyContentUriToNewFile(it, this, cachedDictionaryFile) + dictUriFlow.value = it + } + intent = null + } } private fun updateContainerVisibility() { // todo: remove when removing old settings diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt b/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt index 875480563..087f25976 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsNavHost.kt @@ -15,6 +15,7 @@ import helium314.keyboard.settings.screens.AdvancedSettingsScreen import helium314.keyboard.settings.screens.AppearanceScreen import helium314.keyboard.settings.screens.ColorsScreen import helium314.keyboard.settings.screens.DebugScreen +import helium314.keyboard.settings.screens.DictionaryScreen import helium314.keyboard.settings.screens.GestureTypingScreen import helium314.keyboard.settings.screens.LanguageScreen import helium314.keyboard.settings.screens.MainSettingsScreen @@ -60,6 +61,7 @@ fun SettingsNavHost( onClickAppearance = { navController.navigate(SettingsDestination.Appearance) }, onClickLanguage = { navController.navigate(SettingsDestination.Languages) }, onClickLayouts = { navController.navigate(SettingsDestination.Layouts) }, + onClickDictionaries = { navController.navigate(SettingsDestination.Dictionaries) }, onClickBack = ::goBack, ) } @@ -95,6 +97,9 @@ fun SettingsNavHost( composable(SettingsDestination.Languages) { LanguageScreen(onClickBack = ::goBack) } + composable(SettingsDestination.Dictionaries) { + DictionaryScreen(onClickBack = ::goBack) + } composable(SettingsDestination.Layouts) { SecondaryLayoutScreen(onClickBack = ::goBack) } @@ -124,6 +129,7 @@ object SettingsDestination { const val PersonalDictionary = "personal_dictionary" const val Languages = "languages" const val Layouts = "layouts" + const val Dictionaries = "dictionaries" val navTarget = MutableStateFlow(Settings) private val navScope = CoroutineScope(Dispatchers.Default) diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt new file mode 100644 index 000000000..03b79dbde --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt @@ -0,0 +1,91 @@ +package helium314.keyboard.settings.dialogs + +import android.content.Intent +import androidx.compose.foundation.layout.Arrangement +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.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +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.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import helium314.keyboard.compat.locale +import helium314.keyboard.latin.Dictionary +import helium314.keyboard.latin.R +import helium314.keyboard.latin.common.LocaleUtils +import helium314.keyboard.latin.utils.DictionaryInfoUtils +import helium314.keyboard.settings.dictionaryFilePicker +import helium314.keyboard.settings.screens.getUserAndInternalDictionaries +import java.util.Locale + +@Composable +fun DictionaryDialog( + onDismissRequest: () -> Unit, + locale: Locale, +) { + val ctx = LocalContext.current + val (dictionaries, hasInternal) = getUserAndInternalDictionaries(ctx, locale) + val picker = dictionaryFilePicker(locale) + ThreeButtonAlertDialog( + onDismissRequest = onDismissRequest, + onConfirmed = {}, + confirmButtonText = null, + cancelButtonText = stringResource(R.string.dialog_close), + title = { Text(LocaleUtils.getLocaleDisplayNameInSystemLocale(locale, ctx)) }, + text = { + Column { + if (hasInternal) { + val color = if (dictionaries.none { it.startsWith(Dictionary.TYPE_MAIN + ":") }) MaterialTheme.colorScheme.onSurface + else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) // for disabled look + Text(stringResource(R.string.internal_dictionary_summary), color = color, modifier = Modifier.fillMaxWidth()) + } + dictionaries.forEach { + val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(it) + val type = header?.mIdString?.substringBefore(":") + var showDeleteDialog by remember { mutableStateOf(false) } + if (header != null) { + HorizontalDivider() + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp) + ) { + Column { + Text(header.info(LocalContext.current.resources.configuration.locale()), style = MaterialTheme.typography.bodyMedium) + } + IconButton( + onClick = { showDeleteDialog = true } + ) { Icon(painterResource(R.drawable.ic_bin), stringResource(R.string.delete)) } + } + } + if (showDeleteDialog) + ConfirmationDialog( + onDismissRequest = { showDeleteDialog = false }, + onConfirmed = { it.delete() }, + text = { Text(stringResource(R.string.remove_dictionary_message, type ?: ""))} + ) + } + } + }, + neutralButtonText = stringResource(R.string.add_new_dictionary_title), + onNeutral = { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) + .addCategory(Intent.CATEGORY_OPENABLE) + .setType("application/octet-stream") + picker.launch(intent) + } + ) +} diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/NewDictionaryDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/NewDictionaryDialog.kt new file mode 100644 index 000000000..16267d394 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/NewDictionaryDialog.kt @@ -0,0 +1,93 @@ +package helium314.keyboard.settings.dialogs + +import android.content.Intent +import androidx.compose.foundation.layout.Column +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.platform.LocalContext +import androidx.compose.ui.res.stringResource +import helium314.keyboard.compat.locale +import helium314.keyboard.dictionarypack.DictionaryPackConstants +import helium314.keyboard.latin.Dictionary +import helium314.keyboard.latin.R +import helium314.keyboard.latin.ReadOnlyBinaryDictionary +import helium314.keyboard.latin.common.LocaleUtils +import helium314.keyboard.latin.common.LocaleUtils.constructLocale +import helium314.keyboard.latin.makedict.DictionaryHeader +import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX +import helium314.keyboard.latin.utils.DictionaryInfoUtils +import helium314.keyboard.latin.utils.ScriptUtils.script +import helium314.keyboard.latin.utils.SubtypeSettings +import java.io.File +import java.util.Locale + +@Composable +fun NewDictionaryDialog( + onDismissRequest: () -> Unit, + cachedFile: File, + mainLocale: Locale? +) { + val (error, header) = checkDict(cachedFile) + if (error != null) { + InfoDialog(stringResource(error), onDismissRequest) + cachedFile.delete() + } else if (header != null) { + val ctx = LocalContext.current + val dictLocale = header.mLocaleString.constructLocale() + var locale by remember { mutableStateOf(mainLocale ?: dictLocale) } + val comparer = compareBy({ it != mainLocale}, { it != dictLocale }, { it.script() != dictLocale.script() }) + val locales = SubtypeSettings.getAvailableSubtypeLocales().sortedWith(comparer) + val cacheDir = DictionaryInfoUtils.getCacheDirectoryForLocale(locale, ctx) + val dictFile = File(cacheDir, header.mIdString.substringBefore(":") + "_" + USER_DICTIONARY_SUFFIX) + ThreeButtonAlertDialog( + onDismissRequest = { onDismissRequest(); cachedFile.delete() }, + onConfirmed = { + dictFile.parentFile?.mkdirs() + dictFile.delete() + cachedFile.renameTo(dictFile) + if (header.mIdString.substringBefore(":") == Dictionary.TYPE_MAIN) { + // replaced main dict, remove the one created from internal data + val internalMainDictFile = File(cacheDir, DictionaryInfoUtils.getExtractedMainDictFilename()) + internalMainDictFile.delete() + } + val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION) + ctx.sendBroadcast(newDictBroadcast) + }, + text = { + Column { + Text(header.info(LocalContext.current.resources.configuration.locale())) + // todo: dropdown takes very long to load, should be lazy! + // but can't be lazy because of measurements (has width of widest element) + // -> what do? + DropDownField( + selectedItem = locale, + onSelected = { locale = it }, + items = locales + ) { Text(LocaleUtils.getLocaleDisplayNameInSystemLocale(it, ctx)) } + if (locale.script() != dictLocale.script()) + Text("wrong script", color = MaterialTheme.colorScheme.error) // todo: string resource + if (dictFile.exists()) + Text("will overwrite existing dictionary", color = MaterialTheme.colorScheme.error) // todo: string resource + } + } + ) + } +} + +private fun checkDict(file: File): Pair { + val newHeader = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file, 0, file.length()) + ?: return R.string.dictionary_file_error to null + + val locale = newHeader.mLocaleString.constructLocale() + val dict = ReadOnlyBinaryDictionary(file.absolutePath, 0, file.length(), false, locale, "test") + if (!dict.isValidDictionary) { + dict.close() + return R.string.dictionary_load_error to null + } + return null to newHeader +} diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/SubtypeDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/SubtypeDialog.kt index f62322d0b..0aeb92aec 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/SubtypeDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/SubtypeDialog.kt @@ -295,6 +295,7 @@ fun SubtypeDialog( currentSubtype = if (newValue.isEmpty()) currentSubtype.without(ExtraValue.SECONDARY_LOCALES) else currentSubtype.with(ExtraValue.SECONDARY_LOCALES, newValue) }, + title = { Text("languages with dictionaries") }, // todo: string resource items = availableLocalesForScript, initialSelection = currentSubtype.getExtraValueOf(ExtraValue.SECONDARY_LOCALES) ?.split(Separators.KV)?.map { it.constructLocale() }.orEmpty(), @@ -392,7 +393,7 @@ private fun WithSmallTitle( } @Composable -private fun DropDownField( +fun DropDownField( items: List, selectedItem: T, onSelected: (T) -> Unit, diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt index 62a5c888a..55b573277 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt @@ -86,7 +86,7 @@ fun ThreeButtonAlertDialog( if (confirmButtonText != null) TextButton( enabled = checkOk(), - onClick = { onDismissRequest(); onConfirmed() }, + onClick = { onConfirmed(); onDismissRequest() }, ) { Text(confirmButtonText) } } } diff --git a/app/src/main/java/helium314/keyboard/settings/screens/DictionaryScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/DictionaryScreen.kt new file mode 100644 index 000000000..ef0e05d68 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/screens/DictionaryScreen.kt @@ -0,0 +1,131 @@ +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.unit.dp +import helium314.keyboard.latin.Dictionary +import helium314.keyboard.latin.R +import helium314.keyboard.latin.common.LocaleUtils +import helium314.keyboard.latin.common.LocaleUtils.constructLocale +import helium314.keyboard.latin.common.splitOnWhitespace +import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX +import helium314.keyboard.latin.utils.DICTIONARY_URL +import helium314.keyboard.latin.utils.DictionaryInfoUtils +import helium314.keyboard.latin.utils.SubtypeLocaleUtils +import helium314.keyboard.latin.utils.SubtypeSettings +import helium314.keyboard.latin.utils.getDictionaryLocales +import helium314.keyboard.latin.utils.locale +import helium314.keyboard.latin.utils.prefs +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(ctx.prefs(), true).map { it.locale().language } + val comparer = compareBy({ it.language !in enabledLanguages }, { it.displayName }) // todo: could also prefer if there is a user-added dict + 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 { + it.language != SubtypeLocaleUtils.NO_LANGUAGE && + LocaleUtils.getLocaleDisplayNameInSystemLocale(it, ctx).replace("(", "") + .splitOnWhitespace().any { it.startsWith(term, true) } + } + }, + itemContent = { + if (it.language == SubtypeLocaleUtils.NO_LANGUAGE) { + Text(stringResource(R.string.add_new_dictionary_title), Modifier.clickable { showAddDictDialog = true }) + } else { + Column( + Modifier.clickable { selectedLocale = it } + .padding(vertical = 6.dp, horizontal = 16.dp) + .fillMaxWidth() + ) { + val (dicts, hasInternal) = getUserAndInternalDictionaries(ctx, it) + 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(LocaleUtils.getLocaleDisplayNameInSystemLocale(it, 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)) }, + text = { + // todo: no html in compose + val dictLink = "" + ctx.getString(R.string.dictionary_link_text) + "" + Text(stringResource(R.string.add_dictionary, dictLink)) + } + ) + } + 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, Boolean> { + val userDicts = mutableListOf() + 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) +} diff --git a/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt index cd596c64b..713cc6ec9 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/MainSettingsScreen.kt @@ -44,6 +44,7 @@ fun MainSettingsScreen( onClickAppearance: () -> Unit, onClickLanguage: () -> Unit, onClickLayouts: () -> Unit, + onClickDictionaries: () -> Unit, onClickBack: () -> Unit, ) { val ctx = LocalContext.current @@ -133,6 +134,17 @@ fun MainSettingsScreen( contentDescription = null ) } + Preference( + name = stringResource(R.string.dictionary_settings_category), + onClick = onClickDictionaries, + icon = R.drawable.ic_dictionary + ) { + Icon( + painter = painterResource(R.drawable.ic_arrow_left), + modifier = Modifier.scale(-1f, 1f), + contentDescription = null + ) + } Preference( name = stringResource(R.string.settings_screen_advanced), onClick = onClickAdvanced, @@ -198,7 +210,7 @@ fun MainSettingsScreen( private fun PreviewScreen() { Theme(true) { Surface { - MainSettingsScreen({}, {}, {}, {}, {}, {}, {}, {}, {}, {}) + MainSettingsScreen({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}) } } } diff --git a/app/src/main/res/drawable/ic_dictionary.xml b/app/src/main/res/drawable/ic_dictionary.xml new file mode 100644 index 000000000..8ff4c607c --- /dev/null +++ b/app/src/main/res/drawable/ic_dictionary.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + +