refactor: file handle manager

This commit is contained in:
rhunk
2024-05-28 23:41:52 +02:00
parent 84fc7c6ee3
commit c31e6fd0c2
25 changed files with 329 additions and 303 deletions

View File

@@ -0,0 +1,87 @@
package me.rhunk.snapenhance
import android.os.ParcelFileDescriptor
import me.rhunk.snapenhance.bridge.storage.FileHandle
import me.rhunk.snapenhance.bridge.storage.FileHandleManager
import me.rhunk.snapenhance.common.bridge.FileHandleScope
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper
import me.rhunk.snapenhance.common.logger.AbstractLogger
import me.rhunk.snapenhance.common.util.ktx.toParcelFileDescriptor
import java.io.File
class LocalFileHandle(
private val file: File
): FileHandle.Stub() {
override fun exists() = file.exists()
override fun create() = file.createNewFile()
override fun delete() = file.delete()
override fun open(mode: Int): ParcelFileDescriptor? {
return runCatching {
ParcelFileDescriptor.open(file, mode)
}.onFailure {
AbstractLogger.directError("Failed to open file handle: ${it.message}", it)
}.getOrNull()
}
}
class AssetFileHandle(
private val context: RemoteSideContext,
private val assetPath: String
): FileHandle.Stub() {
override fun exists() = true
override fun create() = false
override fun delete() = false
override fun open(mode: Int): ParcelFileDescriptor? {
return runCatching {
context.androidContext.assets.open(assetPath).toParcelFileDescriptor(context.coroutineScope)
}.onFailure {
AbstractLogger.directError("Failed to open asset handle: ${it.message}", it)
}.getOrNull()
}
}
class RemoteFileHandleManager(
private val context: RemoteSideContext
): FileHandleManager.Stub() {
override fun getFileHandle(scope: String, name: String): FileHandle? {
val fileHandleScope = FileHandleScope.fromValue(scope) ?: run {
context.log.error("invalid file handle scope: $scope", "FileHandleManager")
return null
}
when (fileHandleScope) {
FileHandleScope.INTERNAL -> {
val fileHandleType = InternalFileHandleType.fromValue(name) ?: run {
context.log.error("invalid file handle name: $name", "FileHandleManager")
return null
}
return LocalFileHandle(
fileHandleType.resolve(context.androidContext)
)
}
FileHandleScope.LOCALE -> {
val foundLocale = context.androidContext.resources.assets.list("lang")?.firstOrNull {
it.startsWith(name)
}?.substringBefore(".") ?: return null
if (name == LocaleWrapper.DEFAULT_LOCALE) {
return AssetFileHandle(
context,
"lang/${LocaleWrapper.DEFAULT_LOCALE}.json"
)
}
return AssetFileHandle(
context,
"lang/$foundLocale.json"
)
}
else -> return null
}
}
}

View File

@@ -23,7 +23,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import me.rhunk.snapenhance.bridge.BridgeService
import me.rhunk.snapenhance.common.BuildConfig
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper
import me.rhunk.snapenhance.common.bridge.wrapper.LoggerWrapper
import me.rhunk.snapenhance.common.bridge.wrapper.MappingsWrapper
@@ -60,9 +59,10 @@ class RemoteSideContext(
set(value) { _activity?.clear(); _activity = WeakReference(value) }
val sharedPreferences: SharedPreferences get() = androidContext.getSharedPreferences("prefs", 0)
val config = ModConfig(androidContext)
val translation = LocaleWrapper()
val mappings = MappingsWrapper()
val fileHandleManager = RemoteFileHandleManager(this)
val config = ModConfig(androidContext, fileHandleManager)
val translation = LocaleWrapper(fileHandleManager)
val mappings = MappingsWrapper(fileHandleManager)
val taskManager = TaskManager(this)
val database = AppDatabase(this)
val streaksReminder = StreaksReminder(this)
@@ -70,7 +70,7 @@ class RemoteSideContext(
val scriptManager = RemoteScriptManager(this)
val settingsOverlay = SettingsOverlay(this)
val e2eeImplementation = E2EEImplementation(this)
val messageLogger by lazy { LoggerWrapper(androidContext.getDatabasePath(BridgeFileType.MESSAGE_LOGGER_DATABASE.fileName)) }
val messageLogger by lazy { LoggerWrapper(androidContext) }
val tracker = RemoteTracker(this)
val accountStorage = RemoteAccountStorage(this)
@@ -99,16 +99,15 @@ class RemoteSideContext(
runBlocking(Dispatchers.IO) {
log.init()
log.verbose("Loading RemoteSideContext")
config.loadFromContext(androidContext)
config.load()
launch {
mappings.apply {
loadFromContext(androidContext)
init(androidContext)
}
}
translation.apply {
userLocale = config.locale
loadFromContext(androidContext)
load()
}
database.init()
streaksReminder.init()

View File

@@ -8,9 +8,6 @@ import kotlinx.coroutines.runBlocking
import me.rhunk.snapenhance.RemoteSideContext
import me.rhunk.snapenhance.SharedContextHolder
import me.rhunk.snapenhance.bridge.snapclient.MessagingBridge
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
import me.rhunk.snapenhance.common.bridge.types.FileActionType
import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper
import me.rhunk.snapenhance.common.data.MessagingFriendInfo
import me.rhunk.snapenhance.common.data.MessagingGroupInfo
import me.rhunk.snapenhance.common.data.SocialScope
@@ -84,54 +81,11 @@ class BridgeService : Service() {
}
inner class BridgeBinder : BridgeInterface.Stub() {
override fun getApplicationApkPath(): String = applicationInfo.publicSourceDir
override fun broadcastLog(tag: String, level: String, message: String) {
remoteSideContext.log.internalLog(tag, LogLevel.fromShortName(level) ?: LogLevel.INFO, message)
}
override fun fileOperation(action: Int, fileType: Int, content: ByteArray?): ByteArray {
val resolvedFile = BridgeFileType.fromValue(fileType)?.resolve(this@BridgeService)
return when (FileActionType.entries[action]) {
FileActionType.CREATE_AND_READ -> {
resolvedFile?.let {
if (!it.exists()) {
return content?.also { content -> it.writeBytes(content) } ?: ByteArray(
0
)
}
it.readBytes()
} ?: ByteArray(0)
}
FileActionType.READ -> {
resolvedFile?.takeIf { it.exists() }?.readBytes() ?: ByteArray(0)
}
FileActionType.WRITE -> {
content?.also { resolvedFile?.writeBytes(content) } ?: ByteArray(0)
}
FileActionType.DELETE -> {
resolvedFile?.takeIf { it.exists() }?.delete()
ByteArray(0)
}
FileActionType.EXISTS -> {
if (resolvedFile?.exists() == true)
ByteArray(1)
else ByteArray(0)
}
}
}
override fun getApplicationApkPath(): String = applicationInfo.publicSourceDir
override fun fetchLocales(userLocale: String) =
LocaleWrapper.fetchLocales(context = this@BridgeService, userLocale).associate {
it.locale to it.content
}
override fun enqueueDownload(intent: Intent, callback: DownloadCallback) {
DownloadProcessor(
remoteSideContext = remoteSideContext,
@@ -242,6 +196,8 @@ class BridgeService : Service() {
override fun getLogger() = remoteSideContext.messageLogger
override fun getTracker() = remoteSideContext.tracker
override fun getAccountStorage() = remoteSideContext.accountStorage
override fun getFileHandleManager() = remoteSideContext.fileHandleManager
override fun registerMessagingBridge(bridge: MessagingBridge) {
messagingBridge = bridge
}

View File

@@ -222,9 +222,7 @@ class LoggerHistoryRoot : Routes.Route() {
@OptIn(ExperimentalMaterial3Api::class)
override val content: @Composable (NavBackStackEntry) -> Unit = {
LaunchedEffect(Unit) {
loggerWrapper = LoggerWrapper(
context.androidContext.getDatabasePath("message_logger.db")
)
loggerWrapper = LoggerWrapper(context.androidContext)
}
Column {

View File

@@ -17,12 +17,10 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.core.net.toUri
import androidx.navigation.NavBackStackEntry
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.rhunk.snapenhance.common.Constants
import me.rhunk.snapenhance.common.action.EnumAction
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
import me.rhunk.snapenhance.common.ui.rememberAsyncMutableState
import me.rhunk.snapenhance.ui.manager.Routes
import me.rhunk.snapenhance.ui.setup.Requirements
@@ -239,7 +237,7 @@ class HomeSettings : Routes.Route() {
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
var selectedFileType by remember { mutableStateOf(BridgeFileType.entries.first()) }
var selectedFileType by remember { mutableStateOf(InternalFileHandleType.entries.first()) }
Box(
modifier = Modifier
.weight(1f)
@@ -253,19 +251,19 @@ class HomeSettings : Routes.Route() {
modifier = Modifier.fillMaxWidth(0.7f)
) {
TextField(
value = selectedFileType.displayName,
value = selectedFileType.fileName,
onValueChange = {},
readOnly = true,
modifier = Modifier.menuAnchor()
)
ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
BridgeFileType.entries.forEach { fileType ->
InternalFileHandleType.entries.forEach { fileType ->
DropdownMenuItem(onClick = {
expanded = false
selectedFileType = fileType
}, text = {
Text(text = fileType.displayName)
Text(text = fileType.fileName)
})
}
}

View File

@@ -46,14 +46,13 @@ class PickLanguageScreen : SetupScreen(){
}
private fun reloadTranslation(selectedLocale: String) {
context.translation.reloadFromContext(context.androidContext, selectedLocale)
context.translation.reload(selectedLocale)
}
private fun setLocale(locale: String) {
with(context) {
config.locale = locale
config.writeConfig()
translation.reloadFromContext(androidContext, locale)
reloadTranslation(locale)
}
}