diff --git a/java/src/org/futo/inputmethod/latin/uix/ImportResourceActivity.kt b/java/src/org/futo/inputmethod/latin/uix/ImportResourceActivity.kt index 83ee61047..9e61b4a72 100644 --- a/java/src/org/futo/inputmethod/latin/uix/ImportResourceActivity.kt +++ b/java/src/org/futo/inputmethod/latin/uix/ImportResourceActivity.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.remember 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.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.datastore.preferences.core.Preferences @@ -42,7 +43,7 @@ import org.futo.inputmethod.latin.Subtypes import org.futo.inputmethod.latin.SubtypesSetting import org.futo.inputmethod.latin.uix.settings.NavigationItem import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle -import org.futo.inputmethod.latin.uix.settings.ScreenTitle +import org.futo.inputmethod.latin.uix.settings.ScreenTitleWithIcon import org.futo.inputmethod.latin.uix.settings.ScrollableList import org.futo.inputmethod.latin.uix.settings.Tip import org.futo.inputmethod.latin.uix.settings.useDataStore @@ -98,7 +99,7 @@ fun ImportScreen(fileKind: FileKindAndInfo, file: String?, onApply: (FileKindAnd val importing = remember { mutableStateOf(false) } val importingLanguage = remember { mutableStateOf("") } ScrollableList { - ScreenTitle(title = "Resource Importer") + ScreenTitleWithIcon(title = "Import ${fileKind.kind.kindTitle()}", painter = painterResource(id = fileKind.kind.icon())) if(fileKind.kind == FileKind.Invalid) { Text("This file does not appear to be a dictionary, voice input or transformer model. It may be an invalid file or corrupted. Please try a different file.") @@ -111,9 +112,10 @@ fun ImportScreen(fileKind: FileKindAndInfo, file: String?, onApply: (FileKindAnd } ) } else { - Text("You are importing a ${fileKind.kind.youAreImporting()}. ${fileKind.name?.let { "Info: $it" } ?: ""}", modifier = Modifier.padding(8.dp)) - - Spacer(modifier = Modifier.height(32.dp)) + fileKind.name?.let { + Text("Info: $it", modifier = Modifier.padding(16.dp, 8.dp)) + Spacer(modifier = Modifier.height(32.dp)) + } if(importing.value) { Box(modifier = Modifier @@ -124,8 +126,8 @@ fun ImportScreen(fileKind: FileKindAndInfo, file: String?, onApply: (FileKindAnd Text("Importing for ${importingLanguage.value}", textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth()) } else { Text( - "Which language would you like to set the ${fileKind.kind.youAreImporting()} for?", - modifier = Modifier.padding(8.dp) + "Select the language to import for:", + modifier = Modifier.padding(16.dp, 8.dp) ) val languages = getActiveLanguages(context).let { @@ -163,7 +165,16 @@ enum class FileKind { VoiceInput, Transformer, Dictionary, - Invalid + Invalid; + + fun getAddonUrlForLocale(locale: Locale?): String { + return when(this) { + VoiceInput -> "https://keyboard.futo.org/voice-input-models?locale=${locale?.toLanguageTag() ?: ""}" + Transformer -> "https://keyboard.futo.org/models?locale=${locale?.toLanguageTag() ?: ""}" + Dictionary -> "https://keyboard.futo.org/dictionaries?locale=${locale?.toLanguageTag() ?: ""}" + Invalid -> "https://keyboard.futo.org/" + } + } } fun FileKind.youAreImporting(): String { @@ -175,6 +186,25 @@ fun FileKind.youAreImporting(): String { } } + +fun FileKind.kindTitle(): String { + return when(this) { + FileKind.VoiceInput -> "Voice Input" + FileKind.Transformer -> "Transformer" + FileKind.Dictionary -> "Dictionary" + FileKind.Invalid -> "(invalid)" + } +} + +fun FileKind.icon(): Int { + return when(this) { + FileKind.VoiceInput -> R.drawable.mic_fill + FileKind.Transformer -> R.drawable.cpu + FileKind.Dictionary -> R.drawable.book + FileKind.Invalid -> R.drawable.close + } +} + fun FileKind.extension(): String { return when(this) { FileKind.VoiceInput -> ".bin" diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/Components.kt b/java/src/org/futo/inputmethod/latin/uix/settings/Components.kt index 4f20b6bc9..7cea04857 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/Components.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/Components.kt @@ -90,6 +90,19 @@ fun ScreenTitle(title: String, showBack: Boolean = false, navController: NavHost } } +@Composable +fun ScreenTitleWithIcon(title: String, painter: Painter) { + Row(modifier = Modifier.fillMaxWidth()) { + Spacer(modifier = Modifier.width(16.dp)) + + Icon(painter, contentDescription = "", modifier = Modifier.align(CenterVertically)) + Spacer(modifier = Modifier.width(18.dp)) + Text(title, style = Typography.titleLarge, modifier = Modifier + .align(CenterVertically) + .padding(0.dp, 16.dp)) + } +} + @Composable @Preview fun Tip(text: String = "This is an example tip") { diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/SettingsActivity.kt b/java/src/org/futo/inputmethod/latin/uix/settings/SettingsActivity.kt index d8ff7deb9..149fad817 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/SettingsActivity.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/SettingsActivity.kt @@ -30,7 +30,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import org.futo.inputmethod.latin.R +import org.futo.inputmethod.latin.uix.ImportResourceActivity import org.futo.inputmethod.latin.uix.THEME_KEY import org.futo.inputmethod.latin.uix.USE_SYSTEM_VOICE_INPUT import org.futo.inputmethod.latin.uix.deferGetSetting @@ -40,7 +40,6 @@ import org.futo.inputmethod.latin.uix.theme.ThemeOption import org.futo.inputmethod.latin.uix.theme.ThemeOptions import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme -import org.futo.inputmethod.latin.uix.urlEncode import org.futo.inputmethod.latin.xlm.ModelPaths import org.futo.inputmethod.updates.checkForUpdateAndSaveToPreferences import java.io.File @@ -222,14 +221,13 @@ class SettingsActivity : ComponentActivity() { if(requestCode == IMPORT_GGUF_MODEL_REQUEST && resultCode == Activity.RESULT_OK) { data?.data?.also { uri -> - try { - val model = ModelPaths.importModel(this, uri) - navController.navigate("model/${model.absolutePath.urlEncode()}") - }catch(error: IllegalArgumentException) { - navController.navigateToError(getString(R.string.model_import_failed), error.message ?: getString( - R.string.failed_to_import_the_selected_model - )) - } + val intent = Intent() + intent.setClass(this, ImportResourceActivity::class.java) + intent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP + ) + intent.setData(uri) + startActivity(intent) } } else if(requestCode == EXPORT_GGUF_MODEL_REQUEST && resultCode == Activity.RESULT_OK && fileBeingSaved != null) { data?.data?.also { uri -> 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 ce38e3478..7525558df 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 @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn @@ -39,12 +40,14 @@ import org.futo.inputmethod.latin.common.Constants import org.futo.inputmethod.latin.uix.FileKind import org.futo.inputmethod.latin.uix.ResourceHelper import org.futo.inputmethod.latin.uix.getSetting +import org.futo.inputmethod.latin.uix.icon +import org.futo.inputmethod.latin.uix.kindTitle import org.futo.inputmethod.latin.uix.namePreferenceKeyFor import org.futo.inputmethod.latin.uix.settings.NavigationItem import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle import org.futo.inputmethod.latin.uix.settings.ScreenTitle -import org.futo.inputmethod.latin.uix.settings.ScrollableList import org.futo.inputmethod.latin.uix.settings.SettingItem +import org.futo.inputmethod.latin.uix.settings.pages.modelmanager.openModelImporter import org.futo.inputmethod.latin.uix.settings.useDataStoreValueBlocking import org.futo.inputmethod.latin.uix.theme.Typography import org.futo.inputmethod.latin.uix.youAreImporting @@ -59,41 +62,56 @@ data class LanguageOptions( ) @Composable -fun ConfirmDeleteResourceDialog( +fun ConfirmResourceActionDialog( onDismissRequest: () -> Unit, - onConfirmation: () -> Unit, - dialogTitle: String, - dialogText: String, + + onExplore: () -> Unit, + onDelete: () -> Unit, + onImport: () -> Unit, + + resourceKind: FileKind, + isCurrentlySet: Boolean, + locale: Locale ) { AlertDialog( icon = { - Icon(painterResource(id = R.drawable.delete), contentDescription = "Example Icon") + Icon(painterResource(id = resourceKind.icon()), contentDescription = "Action") }, title = { - Text(text = dialogTitle) + Text(text = "${locale.displayLanguage} - ${resourceKind.kindTitle()}") }, text = { - Text(text = dialogText) + if(isCurrentlySet) { + Text(text = "Would you like to delete ${resourceKind.youAreImporting()} for ${locale.displayLanguage}, or replace it with another file?") + } else { + Text(text = "No ${resourceKind.youAreImporting()} override is set for ${locale.displayLanguage}. You can explore downloads online, or import an existing file.") + } }, onDismissRequest = { onDismissRequest() }, confirmButton = { - TextButton( - onClick = { - onConfirmation() - } - ) { - Text("Delete") + TextButton(onClick = { + onImport() + }) { + Text( + if(isCurrentlySet) { + "Replace" + } else { + "Import" + } + ) } }, dismissButton = { - TextButton( - onClick = { - onDismissRequest() + if(isCurrentlySet) { + TextButton(onClick = { onDelete() }) { + Text("Delete") + } + } else { + TextButton(onClick = { onExplore() }) { + Text("Explore") } - ) { - Text("Cancel") } } ) @@ -119,14 +137,24 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController()) if(deleteDialogInfo.value != null) { val info = deleteDialogInfo.value!! - ConfirmDeleteResourceDialog( + ConfirmResourceActionDialog( onDismissRequest = { deleteDialogInfo.value = null }, - onConfirmation = { + onExplore = { + context.openURI(info.kind.getAddonUrlForLocale(info.locale), true) + deleteDialogInfo.value = null + }, + onDelete = { 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." + onImport = { + openModelImporter(context) + deleteDialogInfo.value = null + }, + + resourceKind = info.kind, + locale = info.locale, + isCurrentlySet = runBlocking { ResourceHelper.findFileForKind(context, info.locale, info.kind)?.exists() == true } ) } LazyColumn { @@ -209,37 +237,25 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController()) title = options.voiceInputModel ?: "None", style = options.voiceInputModel?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow, navigate = { - 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) - } + deleteDialogInfo.value = DeleteInfo(locale, FileKind.VoiceInput) }, - icon = painterResource(id = R.drawable.mic_fill) + icon = painterResource(FileKind.VoiceInput.icon()) ) NavigationItem( title = options.dictionary ?: "None", style = options.dictionary?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow, navigate = { - if(runBlocking { ResourceHelper.findFileForKind(context, locale, FileKind.Dictionary) } == null) { - context.openURI("https://keyboard.futo.org/dictionaries", true) - } else { - deleteDialogInfo.value = DeleteInfo(locale, FileKind.Dictionary) - } + deleteDialogInfo.value = DeleteInfo(locale, FileKind.Dictionary) }, - icon = painterResource(id = R.drawable.book) + icon = painterResource(FileKind.Dictionary.icon()) ) NavigationItem( title = options.transformerModel ?: "None", style = options.transformerModel?.let { NavigationItemStyle.HomeTertiary } ?: NavigationItemStyle.MiscNoArrow, navigate = { - if(options.transformerModel == null) { - context.openURI("https://keyboard.futo.org/models", true) - } else { - navController.navigate("models") - } + navController.navigate("models") }, - icon = painterResource(id = R.drawable.cpu) + icon = painterResource(FileKind.Transformer.icon()) ) if(subtypes.size > 1) { subtypes.forEach { @@ -260,7 +276,48 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController()) } } } + } + item { + Spacer(modifier = Modifier.height(32.dp)) + ScreenTitle("Other options") + NavigationItem( + title = "Import resource file", + style = NavigationItemStyle.Misc, + navigate = { + openModelImporter(context) + }, + ) + NavigationItem( + title = "Explore voice input models", + style = NavigationItemStyle.Misc, + navigate = { + context.openURI( + FileKind.VoiceInput.getAddonUrlForLocale(null), + true + ) + }, + ) + NavigationItem( + title = "Explore dictionaries", + style = NavigationItemStyle.Misc, + navigate = { + context.openURI( + FileKind.Dictionary.getAddonUrlForLocale(null), + true + ) + }, + ) + NavigationItem( + title = "Explore transformer models", + style = NavigationItemStyle.Misc, + navigate = { + context.openURI( + FileKind.Transformer.getAddonUrlForLocale(null), + true + ) + }, + ) } } } \ No newline at end of file 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 abe2324d1..99e8e99af 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 @@ -100,12 +100,7 @@ fun ModelListScreen(navController: NavHostController = rememberNavController()) title = "Import from file", style = NavigationItemStyle.Misc, navigate = { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { - addCategory(Intent.CATEGORY_OPENABLE) - type = "application/octet-stream" - } - - (context as Activity).startActivityForResult(intent, IMPORT_GGUF_MODEL_REQUEST) + openModelImporter(context) } ) } diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/pages/modelmanager/Utils.kt b/java/src/org/futo/inputmethod/latin/uix/settings/pages/modelmanager/Utils.kt index 8a8666f87..b55d19896 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/pages/modelmanager/Utils.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/pages/modelmanager/Utils.kt @@ -1,5 +1,6 @@ package org.futo.inputmethod.latin.uix.settings.pages.modelmanager +import android.app.Activity import android.content.Context import android.content.Intent import android.view.ContextThemeWrapper @@ -24,6 +25,7 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import org.futo.inputmethod.latin.uix.settings.EXPORT_GGUF_MODEL_REQUEST +import org.futo.inputmethod.latin.uix.settings.IMPORT_GGUF_MODEL_REQUEST import org.futo.inputmethod.latin.uix.settings.SettingsActivity import org.futo.inputmethod.latin.xlm.ModelInfo import org.futo.inputmethod.latin.xlm.ModelInfoLoader @@ -141,3 +143,13 @@ fun ModelPicker( } } } + + +fun openModelImporter(context: Context) { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "application/octet-stream" + } + + (context as Activity).startActivityForResult(intent, IMPORT_GGUF_MODEL_REQUEST) +} \ No newline at end of file