From 31b5fe933e9b1b89c60656c1b4f8c1c694a83797 Mon Sep 17 00:00:00 2001 From: Aleksandras Kostarevas Date: Mon, 1 Apr 2024 16:48:39 -0500 Subject: [PATCH] Allow deleting imported dictionaries and voice input models --- .../latin/uix/ImportResourceActivity.kt | 14 ++- .../latin/uix/actions/VoiceInputAction.kt | 11 ++- .../latin/uix/settings/pages/Languages.kt | 91 +++++++++++++++++-- .../settings/pages/modelmanager/ModelList.kt | 3 +- 4 files changed, 108 insertions(+), 11 deletions(-) diff --git a/java/src/org/futo/inputmethod/latin/uix/ImportResourceActivity.kt b/java/src/org/futo/inputmethod/latin/uix/ImportResourceActivity.kt index 370548976..632a77866 100644 --- a/java/src/org/futo/inputmethod/latin/uix/ImportResourceActivity.kt +++ b/java/src/org/futo/inputmethod/latin/uix/ImportResourceActivity.kt @@ -266,6 +266,18 @@ object ResourceHelper { Dictionary.TYPE_MAIN ) } + + fun deleteResourceForLanguage(context: Context, kind: FileKind, locale: Locale) { + val setting = kind.preferencesKeyFor(locale.toString()) + val value = runBlocking { context.getSetting(setting, "") } + if(value.isNotBlank()) { + runBlocking { context.setSetting(setting, "") } + val file = File(context.getExternalFilesDir(null), value) + file.delete() + } + + LatinIMELegacy.mPendingDictionaryUpdate = true + } } class ImportResourceActivity : ComponentActivity() { @@ -314,7 +326,7 @@ class ImportResourceActivity : ComponentActivity() { applicationContext.setSetting(key, outputFileName) } } - LatinIMELegacy.mPendingDictionaryUpdate = true; + LatinIMELegacy.mPendingDictionaryUpdate = true finish() } } diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/VoiceInputAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/VoiceInputAction.kt index 140c24637..bb54455cb 100644 --- a/java/src/org/futo/inputmethod/latin/uix/actions/VoiceInputAction.kt +++ b/java/src/org/futo/inputmethod/latin/uix/actions/VoiceInputAction.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState @@ -15,6 +16,8 @@ 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.text.style.TextAlign +import androidx.compose.ui.unit.dp import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope @@ -33,6 +36,7 @@ import org.futo.inputmethod.latin.uix.VERBOSE_PROGRESS import org.futo.inputmethod.latin.uix.getSetting import org.futo.inputmethod.latin.uix.voiceinput.downloader.DownloadActivity import org.futo.inputmethod.latin.xlm.UserDictionaryObserver +import org.futo.inputmethod.updates.openURI import org.futo.voiceinput.shared.ModelDoesNotExistException import org.futo.voiceinput.shared.RecognizerView import org.futo.voiceinput.shared.RecognizerViewListener @@ -224,15 +228,18 @@ private class VoiceInputNoModelWindow(val locale: Locale) : ActionWindow { @Composable override fun WindowContents(keyboardShown: Boolean) { + val context = LocalContext.current Box(modifier = Modifier .fillMaxSize() .clickable(enabled = true, onClickLabel = null, - onClick = { TODO() }, + onClick = { + context.openURI("https://keyboard.futo.org/voice-input-models", true) + }, role = null, indication = null, interactionSource = remember { MutableInteractionSource() })) { - Text("No model available for ${locale.displayLanguage}, tap to check options?", modifier = Modifier.align(Alignment.Center)) + Text("No voice input model installed for ${locale.displayLanguage}, tap to check options?", modifier = Modifier.align(Alignment.Center).padding(8.dp), textAlign = TextAlign.Center) } } diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/pages/Languages.kt b/java/src/org/futo/inputmethod/latin/uix/settings/pages/Languages.kt index 9bc58e166..a927aa3df 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/pages/Languages.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/pages/Languages.kt @@ -1,7 +1,13 @@ package org.futo.inputmethod.latin.uix.settings.pages +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -20,6 +26,7 @@ import org.futo.inputmethod.latin.uix.settings.ScreenTitle import org.futo.inputmethod.latin.uix.settings.ScrollableList import org.futo.inputmethod.latin.uix.settings.Tip import org.futo.inputmethod.latin.uix.settings.openLanguageSettings +import org.futo.inputmethod.latin.uix.youAreImporting import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils import org.futo.inputmethod.latin.xlm.ModelPaths import org.futo.inputmethod.updates.openURI @@ -31,11 +38,69 @@ data class LanguageOptions( val transformerModel: String? ) +@Composable +fun ConfirmDeleteResourceDialog( + onDismissRequest: () -> Unit, + onConfirmation: () -> Unit, + dialogTitle: String, + dialogText: String, +) { + AlertDialog( + icon = { + Icon(painterResource(id = R.drawable.delete), contentDescription = "Example Icon") + }, + title = { + Text(text = dialogTitle) + }, + text = { + Text(text = dialogText) + }, + onDismissRequest = { + onDismissRequest() + }, + confirmButton = { + TextButton( + onClick = { + onConfirmation() + } + ) { + Text("Delete") + } + }, + dismissButton = { + TextButton( + onClick = { + onDismissRequest() + } + ) { + Text("Cancel") + } + } + ) +} + +data class DeleteInfo( + val locale: Locale, + val kind: FileKind +) @Preview @Composable fun LanguagesScreen(navController: NavHostController = rememberNavController()) { val context = LocalContext.current + val deleteDialogInfo: MutableState = remember { mutableStateOf(null) } + if(deleteDialogInfo.value != null) { + val info = deleteDialogInfo.value!! + ConfirmDeleteResourceDialog( + onDismissRequest = { deleteDialogInfo.value = null }, + onConfirmation = { + ResourceHelper.deleteResourceForLanguage(context, info.kind, info.locale) + deleteDialogInfo.value = null + }, + dialogTitle = "Delete ${info.kind.youAreImporting()} for ${info.locale.displayLanguage}?", + dialogText = "If deleted, the imported ${info.kind.youAreImporting()} file for ${info.locale.displayLanguage} will be deleted. If there is no built-in fallback for this language, the feature may cease to function. You can always download and re-import a different ${info.kind.youAreImporting()} file." + ) + } ScrollableList { ScreenTitle("Languages", showBack = true, navController) @@ -55,10 +120,7 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController()) val voiceInputModelName = ResourceHelper.tryFindingVoiceInputModelForLocale(context, locale)?.name?.let { stringResource(it) } val dictionaryName = runBlocking { ResourceHelper.findFileForKind(context, locale, FileKind.Dictionary) }?.let { "Imported Dictionary" - } ?: if(BinaryDictionaryGetter.getDictionaryFiles(locale, context, false, false).let { - println("DICTIONARIES FOR ${locale.displayLanguage}: ${it.toList().map { it.mFilename }.joinToString(",")}") - it - }.isNotEmpty()) { + } ?: if(BinaryDictionaryGetter.getDictionaryFiles(locale, context, false, false).isNotEmpty()) { "Built-in Dictionary" } else { null @@ -80,7 +142,11 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController()) title = options.voiceInputModel ?: "None", style = options.voiceInputModel?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow, navigate = { - context.openURI("https://keyboard.futo.org/voice-input-models", true) + if(runBlocking { ResourceHelper.findFileForKind(context, locale, FileKind.VoiceInput) } == null) { + context.openURI("https://keyboard.futo.org/voice-input-models", true) + } else { + deleteDialogInfo.value = DeleteInfo(locale, FileKind.VoiceInput) + } }, icon = painterResource(id = R.drawable.mic_fill) ) @@ -88,7 +154,14 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController()) title = options.dictionary ?: "None", style = options.dictionary?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow, navigate = { - context.openURI("https://codeberg.org/Helium314/aosp-dictionaries#dictionaries", true) + if(runBlocking { ResourceHelper.findFileForKind(context, locale, FileKind.Dictionary) } == null) { + context.openURI( + "https://codeberg.org/Helium314/aosp-dictionaries#dictionaries", + true + ) + } else { + deleteDialogInfo.value = DeleteInfo(locale, FileKind.Dictionary) + } }, icon = painterResource(id = R.drawable.book) ) @@ -96,7 +169,11 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController()) title = options.transformerModel ?: "None", style = options.transformerModel?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow, navigate = { - context.openURI("https://keyboard.futo.org/models", true) + if(options.transformerModel == null) { + context.openURI("https://keyboard.futo.org/models", true) + } else { + navController.navigate("models") + } }, icon = painterResource(id = R.drawable.cpu) ) diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/pages/modelmanager/ModelList.kt b/java/src/org/futo/inputmethod/latin/uix/settings/pages/modelmanager/ModelList.kt index 4151b3ae7..abe2324d1 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/pages/modelmanager/ModelList.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/pages/modelmanager/ModelList.kt @@ -25,6 +25,7 @@ import org.futo.inputmethod.latin.xlm.ModelInfo import org.futo.inputmethod.latin.xlm.ModelPaths import org.futo.inputmethod.updates.openURI import java.net.URLEncoder +import java.util.Locale @Composable fun ModelNavigationItem(navController: NavHostController, name: String, isPrimary: Boolean, path: String) { @@ -68,7 +69,7 @@ fun ModelListScreen(navController: NavHostController = rememberNavController()) modelsByLanguage.forEach { item -> Spacer(modifier = Modifier.height(32.dp)) - ScreenTitle(item.key) + ScreenTitle(Locale(item.key).displayLanguage) item.value.forEach { model -> val name = if (model.finetune_count > 0) {