mirror of
https://github.com/florisboard/florisboard.git
synced 2024-09-19 19:42:20 +02:00
Remove AssetManager and switch to extension functions
This commit is contained in:
parent
f780ef0213
commit
e1550d813b
@ -40,7 +40,6 @@ import dev.patrickgold.florisboard.lib.devtools.Flog
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.io.AssetManager
|
||||
import dev.patrickgold.florisboard.lib.io.deleteContentsRecursively
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
@ -67,7 +66,6 @@ class FlorisApplication : Application() {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val mainHandler by lazy { Handler(mainLooper) }
|
||||
|
||||
val assetManager = lazy { AssetManager(this) }
|
||||
val cacheManager = lazy { CacheManager(this) }
|
||||
val clipboardManager = lazy { ClipboardManager(this) }
|
||||
val editorInstance = lazy { EditorInstance(this) }
|
||||
@ -144,8 +142,6 @@ private tailrec fun Context.florisApplication(): FlorisApplication {
|
||||
|
||||
fun Context.appContext() = lazyOf(this.florisApplication())
|
||||
|
||||
fun Context.assetManager() = this.florisApplication().assetManager
|
||||
|
||||
fun Context.cacheManager() = this.florisApplication().cacheManager
|
||||
|
||||
fun Context.clipboardManager() = this.florisApplication().clipboardManager
|
||||
|
@ -28,12 +28,12 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.assetManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.io.FlorisRef
|
||||
import dev.patrickgold.florisboard.lib.io.loadTextAsset
|
||||
|
||||
@Composable
|
||||
fun ProjectLicenseScreen() = FlorisScreen {
|
||||
@ -41,7 +41,6 @@ fun ProjectLicenseScreen() = FlorisScreen {
|
||||
scrollable = false
|
||||
|
||||
val context = LocalContext.current
|
||||
val assetManager by context.assetManager()
|
||||
|
||||
content {
|
||||
// Forcing LTR because the Apache 2.0 License shipped and displayed
|
||||
@ -54,8 +53,8 @@ fun ProjectLicenseScreen() = FlorisScreen {
|
||||
.florisVerticalScroll()
|
||||
.florisHorizontalScroll(),
|
||||
) {
|
||||
val licenseText = assetManager.loadTextAsset(
|
||||
FlorisRef.assets("license/project_license.txt")
|
||||
val licenseText = FlorisRef.assets("license/project_license.txt").loadTextAsset(
|
||||
context
|
||||
).getOrElse {
|
||||
stringRes(R.string.about__project_license__error_license_text_failed, "error_message" to (it.message ?: ""))
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ package dev.patrickgold.florisboard.ime.keyboard
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.assetManager
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.popup.PopupMapping
|
||||
@ -34,6 +33,7 @@ import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogWarning
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.florisboard.lib.io.loadJsonAsset
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -69,7 +69,6 @@ private data class CachedPopupMapping(
|
||||
class LayoutManager(context: Context) {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val appContext by context.appContext()
|
||||
private val assetManager by context.assetManager()
|
||||
private val extensionManager by context.extensionManager()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
|
||||
@ -101,7 +100,7 @@ class LayoutManager(context: Context) {
|
||||
val layout = async {
|
||||
runCatching {
|
||||
val jsonStr = ZipUtils.readFileFromArchive(appContext, ext.sourceRef!!, path).getOrThrow()
|
||||
val arrangement = assetManager.loadJsonAsset<LayoutArrangement>(jsonStr).getOrThrow()
|
||||
val arrangement = loadJsonAsset<LayoutArrangement>(jsonStr).getOrThrow()
|
||||
CachedLayout(ltn.type, ltn.name, meta, arrangement)
|
||||
}
|
||||
}
|
||||
@ -128,7 +127,7 @@ class LayoutManager(context: Context) {
|
||||
val popupMapping = async {
|
||||
runCatching {
|
||||
val jsonStr = ZipUtils.readFileFromArchive(appContext, ext.sourceRef!!, path).getOrThrow()
|
||||
val mapping = assetManager.loadJsonAsset<PopupMapping>(jsonStr).getOrThrow()
|
||||
val mapping = loadJsonAsset<PopupMapping>(jsonStr).getOrThrow()
|
||||
CachedPopupMapping(name, meta, mapping)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import android.os.FileObserver
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.LiveData
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.assetManager
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyboardExtension
|
||||
import dev.patrickgold.florisboard.ime.nlp.LanguagePackExtension
|
||||
import dev.patrickgold.florisboard.ime.text.composing.Appender
|
||||
@ -38,6 +37,10 @@ import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.io.FlorisRef
|
||||
import dev.patrickgold.florisboard.lib.io.FsFile
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.florisboard.lib.io.delete
|
||||
import dev.patrickgold.florisboard.lib.io.listDirs
|
||||
import dev.patrickgold.florisboard.lib.io.listFiles
|
||||
import dev.patrickgold.florisboard.lib.io.loadJsonAsset
|
||||
import dev.patrickgold.florisboard.lib.io.writeJson
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -88,7 +91,6 @@ class ExtensionManager(context: Context) {
|
||||
}
|
||||
|
||||
private val appContext by context.appContext()
|
||||
private val assetManager by context.assetManager()
|
||||
private val ioScope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
val keyboardExtensions = ExtensionIndex(KeyboardExtension.serializer(), IME_KEYBOARD_PATH)
|
||||
@ -149,7 +151,7 @@ class ExtensionManager(context: Context) {
|
||||
fun delete(ext: Extension) {
|
||||
check(canDelete(ext)) { "Cannot delete extension!" }
|
||||
ext.unload(appContext)
|
||||
assetManager.delete(ext.sourceRef!!)
|
||||
ext.sourceRef!!.delete(appContext)
|
||||
}
|
||||
|
||||
inner class ExtensionIndex<T : Extension>(
|
||||
@ -207,11 +209,11 @@ class ExtensionManager(context: Context) {
|
||||
|
||||
private fun indexAssetsModule(): List<T> {
|
||||
val list = mutableListOf<T>()
|
||||
assetManager.listDirs(assetsModuleRef).fold(
|
||||
assetsModuleRef.listDirs(appContext).fold(
|
||||
onSuccess = { extRefs ->
|
||||
for (extRef in extRefs) {
|
||||
val fileRef = extRef.subRef(ExtensionDefaults.MANIFEST_FILE_NAME)
|
||||
assetManager.loadJsonAsset(fileRef, serializer, ExtensionJsonConfig).fold(
|
||||
fileRef.loadJsonAsset(appContext, serializer, ExtensionJsonConfig).fold(
|
||||
onSuccess = { ext ->
|
||||
ext.sourceRef = extRef
|
||||
list.add(ext)
|
||||
@ -231,7 +233,7 @@ class ExtensionManager(context: Context) {
|
||||
|
||||
private fun indexInternalModule(): List<T> {
|
||||
val list = mutableListOf<T>()
|
||||
assetManager.listFiles(internalModuleRef).fold(
|
||||
internalModuleRef.listFiles(appContext).fold(
|
||||
onSuccess = { extRefs ->
|
||||
for (extRef in extRefs) {
|
||||
val fileRef = extRef.absoluteFile(appContext)
|
||||
@ -240,7 +242,7 @@ class ExtensionManager(context: Context) {
|
||||
}
|
||||
ZipUtils.readFileFromArchive(appContext, extRef, ExtensionDefaults.MANIFEST_FILE_NAME).fold(
|
||||
onSuccess = { metaStr ->
|
||||
assetManager.loadJsonAsset(metaStr, serializer, ExtensionJsonConfig).fold(
|
||||
loadJsonAsset(metaStr, serializer, ExtensionJsonConfig).fold(
|
||||
onSuccess = { ext ->
|
||||
ext.sourceRef = extRef
|
||||
list.add(ext)
|
||||
|
@ -17,7 +17,6 @@
|
||||
package dev.patrickgold.florisboard.lib.io
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.ime.keyboard.AbstractKeyData
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CaseSelector
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CharWidthSelector
|
||||
@ -66,122 +65,118 @@ val DefaultJsonConfig = Json {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fully deprecate and remove this class, should be substituted by extension funs on the actual file and stream
|
||||
// instances
|
||||
class AssetManager(context: Context) {
|
||||
val appContext by context.appContext()
|
||||
|
||||
fun delete(ref: FlorisRef) {
|
||||
when {
|
||||
ref.isCache || ref.isInternal -> {
|
||||
ref.absoluteFile(appContext).delete()
|
||||
}
|
||||
else -> error("Can not delete directory/file in location '${ref.scheme}'.")
|
||||
fun FlorisRef.delete(context: Context) {
|
||||
when {
|
||||
isCache || isInternal -> {
|
||||
absoluteFile(context).delete()
|
||||
}
|
||||
}
|
||||
|
||||
fun hasAsset(ref: FlorisRef): Boolean {
|
||||
return when {
|
||||
ref.isAssets -> {
|
||||
try {
|
||||
val file = File(ref.relativePath)
|
||||
val list = appContext.assets.list(file.parent?.toString() ?: "")
|
||||
list?.contains(file.name) == true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
ref.isCache || ref.isInternal -> {
|
||||
val file = File(ref.absolutePath(appContext))
|
||||
file.exists() && file.isFile
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun list(ref: FlorisRef) = list(ref, files = true, dirs = true)
|
||||
|
||||
fun listFiles(ref: FlorisRef) = list(ref, files = true, dirs = false)
|
||||
|
||||
fun listDirs(ref: FlorisRef) = list(ref, files = false, dirs = true)
|
||||
|
||||
private fun list(ref: FlorisRef, files: Boolean, dirs: Boolean) = runCatching<List<FlorisRef>> {
|
||||
when {
|
||||
!files && !dirs -> listOf()
|
||||
ref.isAssets -> {
|
||||
appContext.assets.list(ref.relativePath)?.mapNotNull { fileName ->
|
||||
val subList = appContext.assets.list("${ref.relativePath}/$fileName") ?: return@mapNotNull null
|
||||
when {
|
||||
files && dirs || files && subList.isEmpty() || dirs && subList.isNotEmpty() -> {
|
||||
ref.subRef(fileName)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
} ?: listOf()
|
||||
}
|
||||
ref.isCache || ref.isInternal -> {
|
||||
val dir = ref.absoluteFile(appContext)
|
||||
if (dir.isDirectory) {
|
||||
when {
|
||||
files && dirs -> dir.listFiles()?.toList()
|
||||
files -> dir.listFiles()?.filter { it.isFile }
|
||||
dirs -> dir.listFiles()?.filter { it.isDirectory }
|
||||
else -> null
|
||||
}!!.map { ref.subRef(it.name) }
|
||||
} else {
|
||||
listOf()
|
||||
}
|
||||
}
|
||||
else -> error("Unsupported FlorisRef source!")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> loadJsonAsset(
|
||||
ref: FlorisRef,
|
||||
serializer: KSerializer<T>,
|
||||
jsonConfig: Json = DefaultJsonConfig,
|
||||
) = runCatching<T> {
|
||||
val jsonStr = loadTextAsset(ref).getOrThrow()
|
||||
jsonConfig.decodeFromString(serializer, jsonStr)
|
||||
}
|
||||
|
||||
inline fun <reified T> loadJsonAsset(jsonStr: String, jsonConfig: Json = DefaultJsonConfig): Result<T> {
|
||||
return runCatching { jsonConfig.decodeFromString(jsonStr) }
|
||||
}
|
||||
|
||||
fun <T> loadJsonAsset(
|
||||
jsonStr: String,
|
||||
serializer: KSerializer<T>,
|
||||
jsonConfig: Json = DefaultJsonConfig,
|
||||
) = runCatching<T> {
|
||||
jsonConfig.decodeFromString(serializer, jsonStr)
|
||||
}
|
||||
|
||||
fun loadTextAsset(ref: FlorisRef): Result<String> {
|
||||
return when {
|
||||
ref.isAssets -> runCatching {
|
||||
appContext.assets.reader(ref.relativePath).use { it.readText() }
|
||||
}
|
||||
ref.isCache || ref.isInternal -> {
|
||||
val file = File(ref.absolutePath(appContext))
|
||||
val contents = readTextFile(file).getOrElse { return resultErr(it) }
|
||||
if (contents.isBlank()) {
|
||||
resultErrStr("File is blank!")
|
||||
} else {
|
||||
resultOk(contents)
|
||||
}
|
||||
}
|
||||
else -> resultErrStr("Unsupported asset ref!")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a given [file] and returns its content.
|
||||
*
|
||||
* @param file The file object.
|
||||
* @return The contents of the file or an empty string, if the file does not exist.
|
||||
*/
|
||||
private fun readTextFile(file: File) = runCatching {
|
||||
file.readText(Charsets.UTF_8)
|
||||
else -> error("Can not delete directory/file in location '${scheme}'.")
|
||||
}
|
||||
}
|
||||
|
||||
fun FlorisRef.hasAsset(context: Context): Boolean {
|
||||
return when {
|
||||
isAssets -> {
|
||||
try {
|
||||
val file = File(relativePath)
|
||||
val list = context.assets.list(file.parent?.toString() ?: "")
|
||||
list?.contains(file.name) == true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
isCache || isInternal -> {
|
||||
val file = File(absolutePath(context))
|
||||
file.exists() && file.isFile
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun FlorisRef.list(context: Context) = list(context, files = true, dirs = true)
|
||||
|
||||
fun FlorisRef.listFiles(context: Context) = list(context, files = true, dirs = false)
|
||||
|
||||
fun FlorisRef.listDirs(context: Context) = list(context, files = false, dirs = true)
|
||||
|
||||
private fun FlorisRef.list(appContext: Context, files: Boolean, dirs: Boolean) = runCatching<List<FlorisRef>> {
|
||||
when {
|
||||
!files && !dirs -> listOf()
|
||||
isAssets -> {
|
||||
appContext.assets.list(relativePath)?.mapNotNull { fileName ->
|
||||
val subList = appContext.assets.list("${relativePath}/$fileName") ?: return@mapNotNull null
|
||||
when {
|
||||
files && dirs || files && subList.isEmpty() || dirs && subList.isNotEmpty() -> {
|
||||
subRef(fileName)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
} ?: listOf()
|
||||
}
|
||||
isCache || isInternal -> {
|
||||
val dir = absoluteFile(appContext)
|
||||
if (dir.isDirectory) {
|
||||
when {
|
||||
files && dirs -> dir.listFiles()?.toList()
|
||||
files -> dir.listFiles()?.filter { it.isFile }
|
||||
dirs -> dir.listFiles()?.filter { it.isDirectory }
|
||||
else -> null
|
||||
}!!.map { subRef(it.name) }
|
||||
} else {
|
||||
listOf()
|
||||
}
|
||||
}
|
||||
else -> error("Unsupported FlorisRef source!")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> FlorisRef.loadJsonAsset(
|
||||
context: Context,
|
||||
serializer: KSerializer<T>,
|
||||
jsonConfig: Json = DefaultJsonConfig,
|
||||
) = runCatching<T> {
|
||||
val jsonStr = loadTextAsset(context).getOrThrow()
|
||||
jsonConfig.decodeFromString(serializer, jsonStr)
|
||||
}
|
||||
|
||||
inline fun <reified T> loadJsonAsset(jsonStr: String, jsonConfig: Json = DefaultJsonConfig): Result<T> {
|
||||
return runCatching { jsonConfig.decodeFromString(jsonStr) }
|
||||
}
|
||||
|
||||
fun <T> loadJsonAsset(
|
||||
jsonStr: String,
|
||||
serializer: KSerializer<T>,
|
||||
jsonConfig: Json = DefaultJsonConfig,
|
||||
) = runCatching<T> {
|
||||
jsonConfig.decodeFromString(serializer, jsonStr)
|
||||
}
|
||||
|
||||
fun FlorisRef.loadTextAsset(context: Context): Result<String> {
|
||||
return when {
|
||||
isAssets -> runCatching {
|
||||
context.assets.reader(relativePath).use { it.readText() }
|
||||
}
|
||||
isCache || isInternal -> {
|
||||
val file = File(absolutePath(context))
|
||||
val contents = readTextFile(file).getOrElse { return resultErr(it) }
|
||||
if (contents.isBlank()) {
|
||||
resultErrStr("File is blank!")
|
||||
} else {
|
||||
resultOk(contents)
|
||||
}
|
||||
}
|
||||
else -> resultErrStr("Unsupported asset ref!")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a given [file] and returns its content.
|
||||
*
|
||||
* @param file The file object.
|
||||
* @return The contents of the file or an empty string, if the file does not exist.
|
||||
*/
|
||||
private fun readTextFile(file: File) = runCatching {
|
||||
file.readText(Charsets.UTF_8)
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,7 +18,6 @@ package dev.patrickgold.florisboard.lib.io
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import dev.patrickgold.florisboard.assetManager
|
||||
import dev.patrickgold.florisboard.lib.android.copyRecursively
|
||||
import dev.patrickgold.florisboard.lib.android.write
|
||||
import java.io.FileOutputStream
|
||||
@ -28,10 +27,9 @@ import java.util.zip.ZipOutputStream
|
||||
|
||||
object ZipUtils {
|
||||
fun readFileFromArchive(context: Context, zipRef: FlorisRef, relPath: String) = runCatching<String> {
|
||||
val assetManager by context.assetManager()
|
||||
when {
|
||||
zipRef.isAssets -> {
|
||||
assetManager.loadTextAsset(zipRef.subRef(relPath)).getOrThrow()
|
||||
zipRef.subRef(relPath).loadTextAsset(context).getOrThrow()
|
||||
}
|
||||
zipRef.isCache || zipRef.isInternal -> {
|
||||
val flexHandle = FsFile(zipRef.absolutePath(context))
|
||||
|
Loading…
Reference in New Issue
Block a user