Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package org.futo.inputmethod.v2keyboard
import android.content.Context
import android.content.res.AssetManager
import android.util.Log
import com.charleskorn.kaml.PolymorphismStyle
import com.charleskorn.kaml.Yaml
import com.charleskorn.kaml.YamlConfiguration
import kotlinx.serialization.modules.EmptySerializersModule
import org.futo.inputmethod.latin.uix.actions.BugInfo
import org.futo.inputmethod.latin.uix.actions.BugViewerState
import java.util.Locale
object LayoutManager {
private var layoutsById: Map<String, Keyboard>? = null
private var initialized = false
private fun listFilesRecursively(assetManager: AssetManager, path: String): List<String> {
val files = assetManager.list(path)
return if(files.isNullOrEmpty()) {
listOf(path)
} else {
files.flatMap { listFilesRecursively(assetManager, "$path/$it") }
}
}
private fun getAllLayoutPaths(assetManager: AssetManager): List<String> {
return listFilesRecursively(assetManager, "layouts").filter {
it.endsWith(".yml") || it.endsWith(".yaml")
}
}
fun init(context: Context) {
if(initialized) return
initialized = true
val assetManager = context.assets
val layoutPaths = getAllLayoutPaths(assetManager)
layoutsById = layoutPaths.associate { path ->
val filename = path.split("/").last().split(".yaml").first()
val keyboard = try {
parseKeyboardYaml(context, path).apply { id = filename }
} catch(e: Exception) {
BugViewerState.pushBug(BugInfo(
"LayoutManager",
"Failed to parse layout $filename\nMessage: ${e.message}, cause: ${e.cause?.message}"
))
e.printStackTrace()
parseKeyboardYaml(context, "layouts/Special/error.yaml").apply { id = filename }
}
filename to keyboard
}
}
fun getLayout(context: Context, name: String): Keyboard {
init(context)
return layoutsById?.get(name) ?: throw IllegalArgumentException("Failed to find keyboard layout $name. Available layouts: ${layoutsById?.keys}")
}
fun queryLayoutsForLocale(locale: Locale): List<Keyboard> {
val language = locale.language
Aleksandras Kostarevas
committed
//val script = locale.getKeyboardScript()
return layoutsById!!.values.filter { it.languages.contains(language) }
}
fun getAllLayoutNames(context: Context): List<String> {
init(context)
return getAllLayoutPaths(context.assets).map {
it.split("/").last().split(".yaml").first()
}
}
}
private fun parseKeyboardYaml(context: Context, layoutPath: String): Keyboard {
val yaml = Yaml(
EmptySerializersModule(),
YamlConfiguration(
polymorphismStyle = PolymorphismStyle.Property,
allowAnchorsAndAliases = true
)
)
return context.assets.open(layoutPath).use { inputStream ->
val yamlString = inputStream.bufferedReader().use { it.readText() }
try {
yaml.decodeFromString(Keyboard.serializer(), yamlString)
} catch(e: Throwable) {
Log.e("KeyboardParser", "Failed to parse $layoutPath")
throw Exception("Error while parsing layout [$layoutPath]", e)
}
}
}