Allow deleting imported dictionaries and voice input models

This commit is contained in:
Aleksandras Kostarevas 2024-04-01 16:48:39 -05:00
parent 626477a027
commit 31b5fe933e
4 changed files with 108 additions and 11 deletions

View File

@ -266,6 +266,18 @@ object ResourceHelper {
Dictionary.TYPE_MAIN 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() { class ImportResourceActivity : ComponentActivity() {
@ -314,7 +326,7 @@ class ImportResourceActivity : ComponentActivity() {
applicationContext.setSetting(key, outputFileName) applicationContext.setSetting(key, outputFileName)
} }
} }
LatinIMELegacy.mPendingDictionaryUpdate = true; LatinIMELegacy.mPendingDictionaryUpdate = true
finish() finish()
} }
} }

View File

@ -6,6 +6,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
@ -15,6 +16,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource 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.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope 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.getSetting
import org.futo.inputmethod.latin.uix.voiceinput.downloader.DownloadActivity import org.futo.inputmethod.latin.uix.voiceinput.downloader.DownloadActivity
import org.futo.inputmethod.latin.xlm.UserDictionaryObserver import org.futo.inputmethod.latin.xlm.UserDictionaryObserver
import org.futo.inputmethod.updates.openURI
import org.futo.voiceinput.shared.ModelDoesNotExistException import org.futo.voiceinput.shared.ModelDoesNotExistException
import org.futo.voiceinput.shared.RecognizerView import org.futo.voiceinput.shared.RecognizerView
import org.futo.voiceinput.shared.RecognizerViewListener import org.futo.voiceinput.shared.RecognizerViewListener
@ -224,15 +228,18 @@ private class VoiceInputNoModelWindow(val locale: Locale) : ActionWindow {
@Composable @Composable
override fun WindowContents(keyboardShown: Boolean) { override fun WindowContents(keyboardShown: Boolean) {
val context = LocalContext.current
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxSize() .fillMaxSize()
.clickable(enabled = true, .clickable(enabled = true,
onClickLabel = null, onClickLabel = null,
onClick = { TODO() }, onClick = {
context.openURI("https://keyboard.futo.org/voice-input-models", true)
},
role = null, role = null,
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() })) { 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)
} }
} }

View File

@ -1,7 +1,13 @@
package org.futo.inputmethod.latin.uix.settings.pages 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.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable 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.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource 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.ScrollableList
import org.futo.inputmethod.latin.uix.settings.Tip import org.futo.inputmethod.latin.uix.settings.Tip
import org.futo.inputmethod.latin.uix.settings.openLanguageSettings 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.utils.SubtypeLocaleUtils
import org.futo.inputmethod.latin.xlm.ModelPaths import org.futo.inputmethod.latin.xlm.ModelPaths
import org.futo.inputmethod.updates.openURI import org.futo.inputmethod.updates.openURI
@ -31,11 +38,69 @@ data class LanguageOptions(
val transformerModel: String? 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 @Preview
@Composable @Composable
fun LanguagesScreen(navController: NavHostController = rememberNavController()) { fun LanguagesScreen(navController: NavHostController = rememberNavController()) {
val context = LocalContext.current val context = LocalContext.current
val deleteDialogInfo: MutableState<DeleteInfo?> = 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 { ScrollableList {
ScreenTitle("Languages", showBack = true, navController) 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 voiceInputModelName = ResourceHelper.tryFindingVoiceInputModelForLocale(context, locale)?.name?.let { stringResource(it) }
val dictionaryName = runBlocking { ResourceHelper.findFileForKind(context, locale, FileKind.Dictionary) }?.let { val dictionaryName = runBlocking { ResourceHelper.findFileForKind(context, locale, FileKind.Dictionary) }?.let {
"Imported Dictionary" "Imported Dictionary"
} ?: if(BinaryDictionaryGetter.getDictionaryFiles(locale, context, false, false).let { } ?: if(BinaryDictionaryGetter.getDictionaryFiles(locale, context, false, false).isNotEmpty()) {
println("DICTIONARIES FOR ${locale.displayLanguage}: ${it.toList().map { it.mFilename }.joinToString(",")}")
it
}.isNotEmpty()) {
"Built-in Dictionary" "Built-in Dictionary"
} else { } else {
null null
@ -80,7 +142,11 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController())
title = options.voiceInputModel ?: "None", title = options.voiceInputModel ?: "None",
style = options.voiceInputModel?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow, style = options.voiceInputModel?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow,
navigate = { 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) icon = painterResource(id = R.drawable.mic_fill)
) )
@ -88,7 +154,14 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController())
title = options.dictionary ?: "None", title = options.dictionary ?: "None",
style = options.dictionary?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow, style = options.dictionary?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow,
navigate = { 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) icon = painterResource(id = R.drawable.book)
) )
@ -96,7 +169,11 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController())
title = options.transformerModel ?: "None", title = options.transformerModel ?: "None",
style = options.transformerModel?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow, style = options.transformerModel?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow,
navigate = { 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) icon = painterResource(id = R.drawable.cpu)
) )

View File

@ -25,6 +25,7 @@ import org.futo.inputmethod.latin.xlm.ModelInfo
import org.futo.inputmethod.latin.xlm.ModelPaths import org.futo.inputmethod.latin.xlm.ModelPaths
import org.futo.inputmethod.updates.openURI import org.futo.inputmethod.updates.openURI
import java.net.URLEncoder import java.net.URLEncoder
import java.util.Locale
@Composable @Composable
fun ModelNavigationItem(navController: NavHostController, name: String, isPrimary: Boolean, path: String) { fun ModelNavigationItem(navController: NavHostController, name: String, isPrimary: Boolean, path: String) {
@ -68,7 +69,7 @@ fun ModelListScreen(navController: NavHostController = rememberNavController())
modelsByLanguage.forEach { item -> modelsByLanguage.forEach { item ->
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
ScreenTitle(item.key) ScreenTitle(Locale(item.key).displayLanguage)
item.value.forEach { model -> item.value.forEach { model ->
val name = if (model.finetune_count > 0) { val name = if (model.finetune_count > 0) {