refactor: file handle manager
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user