mirror of
https://gitlab.futo.org/keyboard/latinime.git
synced 2024-09-28 14:54:30 +01:00
Display imported dictionary name
This commit is contained in:
parent
31b5fe933e
commit
c4bd35c921
@ -39,7 +39,6 @@ import org.futo.inputmethod.latin.LatinIMELegacy
|
|||||||
import org.futo.inputmethod.latin.R
|
import org.futo.inputmethod.latin.R
|
||||||
import org.futo.inputmethod.latin.ReadOnlyBinaryDictionary
|
import org.futo.inputmethod.latin.ReadOnlyBinaryDictionary
|
||||||
import org.futo.inputmethod.latin.RichInputMethodManager
|
import org.futo.inputmethod.latin.RichInputMethodManager
|
||||||
import org.futo.inputmethod.latin.uix.settings.DataStoreItem
|
|
||||||
import org.futo.inputmethod.latin.uix.settings.NavigationItem
|
import org.futo.inputmethod.latin.uix.settings.NavigationItem
|
||||||
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
|
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
|
||||||
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
|
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
|
||||||
@ -56,6 +55,7 @@ import org.futo.voiceinput.shared.BUILTIN_ENGLISH_MODEL
|
|||||||
import org.futo.voiceinput.shared.types.ModelFileFile
|
import org.futo.voiceinput.shared.types.ModelFileFile
|
||||||
import org.futo.voiceinput.shared.types.ModelLoader
|
import org.futo.voiceinput.shared.types.ModelLoader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@ -77,18 +77,18 @@ fun getActiveLanguages(context: Context): List<InputLanguage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun FileKind.preferencesKeyFor(locale: String): Preferences.Key<String> {
|
fun FileKind.preferenceKeyFor(locale: String): Preferences.Key<String> {
|
||||||
assert(this != FileKind.Invalid)
|
assert(this != FileKind.Invalid)
|
||||||
return stringPreferencesKey("resource_${name}_${locale}")
|
return stringPreferencesKey("resource_${name}_${locale}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
fun FileKind.namePreferenceKeyFor(locale: String): Preferences.Key<String> {
|
||||||
fun resourceOption(language: InputMethodSubtype, kind: FileKind): DataStoreItem<String> {
|
assert(this != FileKind.Invalid)
|
||||||
return useDataStore(key = kind.preferencesKeyFor(language.locale), default = "")
|
return stringPreferencesKey("resourcename_${name}_${locale}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ImportScreen(fileKind: FileKindAndInfo, file: String?, onApply: (FileKind, InputMethodSubtype) -> Unit, onCancel: () -> Unit) {
|
fun ImportScreen(fileKind: FileKindAndInfo, file: String?, onApply: (FileKindAndInfo, InputMethodSubtype) -> Unit, onCancel: () -> Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val importing = remember { mutableStateOf(false) }
|
val importing = remember { mutableStateOf(false) }
|
||||||
val importingLanguage = remember { mutableStateOf("") }
|
val importingLanguage = remember { mutableStateOf("") }
|
||||||
@ -106,7 +106,7 @@ fun ImportScreen(fileKind: FileKindAndInfo, file: String?, onApply: (FileKind, I
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Text("You are importing a ${fileKind.kind.youAreImporting()}.", modifier = Modifier.padding(8.dp))
|
Text("You are importing a ${fileKind.kind.youAreImporting()}. ${fileKind.name?.let { "Info: $it" } ?: ""}", modifier = Modifier.padding(8.dp))
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
|
||||||
@ -124,10 +124,10 @@ fun ImportScreen(fileKind: FileKindAndInfo, file: String?, onApply: (FileKind, I
|
|||||||
)
|
)
|
||||||
|
|
||||||
val languages = getActiveLanguages(context).let {
|
val languages = getActiveLanguages(context).let {
|
||||||
if(fileKind.guessedLanguage != null) {
|
if(fileKind.locale != null) {
|
||||||
it.filter { it.tag.lowercase() == fileKind.guessedLanguage.lowercase() || it.tag.split("_")[0].lowercase() == fileKind.guessedLanguage.split("_")[0].lowercase() }.let {
|
it.filter { it.tag.lowercase() == fileKind.locale.lowercase() || it.tag.split("_")[0].lowercase() == fileKind.locale.split("_")[0].lowercase() }.let {
|
||||||
if(it.isEmpty()) {
|
if(it.isEmpty()) {
|
||||||
Text("Warning: This file appears to be intended for a language (${fileKind.guessedLanguage}) which is not active", modifier = Modifier.padding(8.dp))
|
Text("Warning: This file appears to be intended for a language (${fileKind.locale}) which is not active", modifier = Modifier.padding(8.dp))
|
||||||
getActiveLanguages(context)
|
getActiveLanguages(context)
|
||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
@ -145,7 +145,7 @@ fun ImportScreen(fileKind: FileKindAndInfo, file: String?, onApply: (FileKind, I
|
|||||||
navigate = {
|
navigate = {
|
||||||
importing.value = true
|
importing.value = true
|
||||||
importingLanguage.value = it.name
|
importingLanguage.value = it.name
|
||||||
onApply(fileKind.kind, it.inputMethodSubtype)
|
onApply(fileKind, it.inputMethodSubtype)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -181,9 +181,77 @@ fun FileKind.extension(): String {
|
|||||||
|
|
||||||
data class FileKindAndInfo(
|
data class FileKindAndInfo(
|
||||||
val kind: FileKind,
|
val kind: FileKind,
|
||||||
val guessedLanguage: String?
|
val name: String?,
|
||||||
|
val locale: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun parseDictionaryMetadataKV(inputStream: InputStream): Map<String, String>? {
|
||||||
|
while (inputStream.available() > 0) {
|
||||||
|
val v = inputStream.read()
|
||||||
|
if(v == -1) {
|
||||||
|
return null
|
||||||
|
} else if (v == 'd'.code) {
|
||||||
|
if (inputStream.read() == 'a'.code
|
||||||
|
&& inputStream.read() == 't'.code
|
||||||
|
&& inputStream.read() == 'e'.code
|
||||||
|
&& inputStream.read() == 0x1F
|
||||||
|
) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val readUntilSeparator = {
|
||||||
|
val codes: MutableList<Int> = mutableListOf()
|
||||||
|
while(true) {
|
||||||
|
val v = inputStream.read()
|
||||||
|
if(v == -1) {
|
||||||
|
break
|
||||||
|
} else if(v == 0x1F) {
|
||||||
|
break
|
||||||
|
} else if(v == 0) {
|
||||||
|
// 3 byte character
|
||||||
|
// not 100% sure it's correct to compare to 0 here, but seems to work usually
|
||||||
|
val v1 = v
|
||||||
|
val v2 = inputStream.read()
|
||||||
|
val v3 = inputStream.read()
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if(v2 == -1 || v3 == -1 || v2 == 0x1F || v3 == 0x1F) break
|
||||||
|
|
||||||
|
codes.add((v1 shl 16) or (v2 shl 8) or (v3))
|
||||||
|
} else {
|
||||||
|
codes.add(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String(codes.toIntArray(), 0, codes.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
val keyValueList = mutableMapOf(
|
||||||
|
"date" to readUntilSeparator()
|
||||||
|
)
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
val key = readUntilSeparator()
|
||||||
|
val value = readUntilSeparator()
|
||||||
|
|
||||||
|
if(key.isBlank() || value.isBlank()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
keyValueList[key] = value
|
||||||
|
|
||||||
|
if(key == "version") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyValueList
|
||||||
|
}
|
||||||
|
|
||||||
fun determineFileKind(context: Context, file: Uri): FileKindAndInfo {
|
fun determineFileKind(context: Context, file: Uri): FileKindAndInfo {
|
||||||
val contentResolver = context.contentResolver
|
val contentResolver = context.contentResolver
|
||||||
|
|
||||||
@ -198,42 +266,42 @@ fun determineFileKind(context: Context, file: Uri): FileKindAndInfo {
|
|||||||
val magic = ByteBuffer.wrap(array).getInt().toUInt()
|
val magic = ByteBuffer.wrap(array).getInt().toUInt()
|
||||||
|
|
||||||
when(magic) {
|
when(magic) {
|
||||||
voiceInputMagic -> FileKindAndInfo(FileKind.VoiceInput, null)
|
voiceInputMagic -> FileKindAndInfo(FileKind.VoiceInput, null, null)
|
||||||
transformerMagic -> FileKindAndInfo(FileKind.Transformer, null)
|
transformerMagic -> FileKindAndInfo(FileKind.Transformer, null, null)
|
||||||
dictionaryMagic -> {
|
dictionaryMagic -> {
|
||||||
while(array[0] != 0x3A.toByte()) {
|
val metadata = parseDictionaryMetadataKV(inputStream)
|
||||||
inputStream.read(array, 0, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
val chars: MutableList<Char> = mutableListOf()
|
FileKindAndInfo(
|
||||||
while(array[0] != 0x1F.toByte()) {
|
FileKind.Dictionary,
|
||||||
inputStream.read(array, 0, 1)
|
name = metadata?.get("description"),
|
||||||
if(array[0] == 0x1F.toByte()) break
|
locale = metadata?.get("locale")
|
||||||
|
)
|
||||||
chars.add(array[0].toInt().toChar())
|
|
||||||
}
|
|
||||||
|
|
||||||
val language = String(chars.toCharArray())
|
|
||||||
|
|
||||||
FileKindAndInfo(FileKind.Dictionary, language)
|
|
||||||
}
|
}
|
||||||
else -> FileKindAndInfo(FileKind.Invalid, null)
|
else -> FileKindAndInfo(FileKind.Invalid, null, null)
|
||||||
}
|
}
|
||||||
} ?: FileKindAndInfo(FileKind.Invalid, null)
|
} ?: FileKindAndInfo(FileKind.Invalid, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
object ResourceHelper {
|
object ResourceHelper {
|
||||||
suspend fun findFileForKind(context: Context, locale: Locale, kind: FileKind): File? {
|
suspend fun findKeyForLocaleAndKind(context: Context, locale: Locale, kind: FileKind): String? {
|
||||||
val keysToTry = listOf(
|
val keysToTry = listOf(
|
||||||
locale.language,
|
locale.language,
|
||||||
"${locale.language}_${locale.country}",
|
"${locale.language}_${locale.country}",
|
||||||
"${locale.language.lowercase()}_${locale.country.uppercase()}",
|
"${locale.language.lowercase()}_${locale.country.uppercase()}",
|
||||||
)
|
)
|
||||||
|
|
||||||
val settingValue: String = keysToTry.firstNotNullOfOrNull { key ->
|
val key: String = keysToTry.firstNotNullOfOrNull { key ->
|
||||||
context.getSetting(kind.preferencesKeyFor(key), "").ifEmpty { null }
|
context.getSetting(kind.preferenceKeyFor(key), "").ifEmpty { null }?.let { key }
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun findFileForKind(context: Context, locale: Locale, kind: FileKind): File? {
|
||||||
|
val key = findKeyForLocaleAndKind(context, locale, kind) ?: return null
|
||||||
|
|
||||||
|
val settingValue: String = context.getSetting(kind.preferenceKeyFor(key), "")
|
||||||
|
|
||||||
val file = File(context.getExternalFilesDir(null), settingValue)
|
val file = File(context.getExternalFilesDir(null), settingValue)
|
||||||
|
|
||||||
if(!file.exists()) {
|
if(!file.exists()) {
|
||||||
@ -268,14 +336,16 @@ object ResourceHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun deleteResourceForLanguage(context: Context, kind: FileKind, locale: Locale) {
|
fun deleteResourceForLanguage(context: Context, kind: FileKind, locale: Locale) {
|
||||||
val setting = kind.preferencesKeyFor(locale.toString())
|
val setting = kind.preferenceKeyFor(locale.toString())
|
||||||
val value = runBlocking { context.getSetting(setting, "") }
|
val value = runBlocking { context.getSetting(setting, "") }
|
||||||
if(value.isNotBlank()) {
|
if(value.isNotBlank()) {
|
||||||
runBlocking { context.setSetting(setting, "") }
|
|
||||||
val file = File(context.getExternalFilesDir(null), value)
|
val file = File(context.getExternalFilesDir(null), value)
|
||||||
file.delete()
|
file.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runBlocking { context.setSetting(kind.preferenceKeyFor(locale.toString()), "") }
|
||||||
|
runBlocking { context.setSetting(kind.namePreferenceKeyFor(locale.toString()), "") }
|
||||||
|
|
||||||
LatinIMELegacy.mPendingDictionaryUpdate = true
|
LatinIMELegacy.mPendingDictionaryUpdate = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,16 +353,16 @@ object ResourceHelper {
|
|||||||
class ImportResourceActivity : ComponentActivity() {
|
class ImportResourceActivity : ComponentActivity() {
|
||||||
private val themeOption: MutableState<ThemeOption?> = mutableStateOf(null)
|
private val themeOption: MutableState<ThemeOption?> = mutableStateOf(null)
|
||||||
private val fileBeingImported: MutableState<String?> = mutableStateOf(null)
|
private val fileBeingImported: MutableState<String?> = mutableStateOf(null)
|
||||||
private val fileKind: MutableState<FileKindAndInfo> = mutableStateOf(FileKindAndInfo(FileKind.Invalid, null))
|
private val fileKind: MutableState<FileKindAndInfo> = mutableStateOf(FileKindAndInfo(FileKind.Invalid, null, null))
|
||||||
private var uri: Uri? = null
|
private var uri: Uri? = null
|
||||||
|
|
||||||
private fun applySetting(fileKind: FileKind, inputMethodSubtype: InputMethodSubtype) {
|
private fun applySetting(fileKind: FileKindAndInfo, inputMethodSubtype: InputMethodSubtype) {
|
||||||
val outputFileName = "${fileKind.name.lowercase()}_${inputMethodSubtype.locale}${fileKind.extension()}"
|
val outputFileName = "${fileKind.kind.name.lowercase()}_${inputMethodSubtype.locale}${fileKind.kind.extension()}"
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
// This is a special case for now
|
// This is a special case for now
|
||||||
if (fileKind == FileKind.Transformer) {
|
if (fileKind.kind == FileKind.Transformer) {
|
||||||
// 1. Copy file
|
// 1. Copy file
|
||||||
val contentResolver = applicationContext.contentResolver
|
val contentResolver = applicationContext.contentResolver
|
||||||
val outDirectory = ModelPaths.getModelDirectory(applicationContext)
|
val outDirectory = ModelPaths.getModelDirectory(applicationContext)
|
||||||
@ -322,8 +392,16 @@ class ImportResourceActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Update reference
|
// 2. Update reference
|
||||||
val key = fileKind.preferencesKeyFor(inputMethodSubtype.locale)
|
applicationContext.setSetting(
|
||||||
applicationContext.setSetting(key, outputFileName)
|
fileKind.kind.preferenceKeyFor(inputMethodSubtype.locale),
|
||||||
|
outputFileName
|
||||||
|
)
|
||||||
|
fileKind.name?.let {
|
||||||
|
applicationContext.setSetting(
|
||||||
|
fileKind.kind.namePreferenceKeyFor(inputMethodSubtype.locale),
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LatinIMELegacy.mPendingDictionaryUpdate = true
|
LatinIMELegacy.mPendingDictionaryUpdate = true
|
||||||
|
@ -20,6 +20,8 @@ import org.futo.inputmethod.latin.R
|
|||||||
import org.futo.inputmethod.latin.RichInputMethodManager
|
import org.futo.inputmethod.latin.RichInputMethodManager
|
||||||
import org.futo.inputmethod.latin.uix.FileKind
|
import org.futo.inputmethod.latin.uix.FileKind
|
||||||
import org.futo.inputmethod.latin.uix.ResourceHelper
|
import org.futo.inputmethod.latin.uix.ResourceHelper
|
||||||
|
import org.futo.inputmethod.latin.uix.getSetting
|
||||||
|
import org.futo.inputmethod.latin.uix.namePreferenceKeyFor
|
||||||
import org.futo.inputmethod.latin.uix.settings.NavigationItem
|
import org.futo.inputmethod.latin.uix.settings.NavigationItem
|
||||||
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
|
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
|
||||||
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
|
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
|
||||||
@ -118,8 +120,8 @@ fun LanguagesScreen(navController: NavHostController = rememberNavController())
|
|||||||
val locale = Locale.forLanguageTag(it.locale.replace("_", "-"))
|
val locale = Locale.forLanguageTag(it.locale.replace("_", "-"))
|
||||||
|
|
||||||
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.findKeyForLocaleAndKind(context, locale, FileKind.Dictionary) }?.let {
|
||||||
"Imported Dictionary"
|
runBlocking { context.getSetting(FileKind.Dictionary.namePreferenceKeyFor(it), "Dictionary") } + " (Imported)"
|
||||||
} ?: if(BinaryDictionaryGetter.getDictionaryFiles(locale, context, false, false).isNotEmpty()) {
|
} ?: if(BinaryDictionaryGetter.getDictionaryFiles(locale, context, false, false).isNotEmpty()) {
|
||||||
"Built-in Dictionary"
|
"Built-in Dictionary"
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user