mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-16 14:17:09 +02:00
refactor: file handle manager
This commit is contained in:
parent
84fc7c6ee3
commit
c31e6fd0c2
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -10,30 +10,18 @@ import me.rhunk.snapenhance.bridge.logger.TrackerInterface;
|
||||
import me.rhunk.snapenhance.bridge.ConfigStateListener;
|
||||
import me.rhunk.snapenhance.bridge.snapclient.MessagingBridge;
|
||||
import me.rhunk.snapenhance.bridge.AccountStorage;
|
||||
import me.rhunk.snapenhance.bridge.storage.FileHandleManager;
|
||||
|
||||
interface BridgeInterface {
|
||||
/**
|
||||
* broadcast a log message
|
||||
*/
|
||||
oneway void broadcastLog(String tag, String level, String message);
|
||||
|
||||
/**
|
||||
* Execute a file operation
|
||||
* @param fileType the corresponding file type (see BridgeFileType)
|
||||
*/
|
||||
byte[] fileOperation(int action, int fileType, in @nullable byte[] content);
|
||||
|
||||
/**
|
||||
* Get the application APK path (assets for the conversation exporter)
|
||||
*/
|
||||
String getApplicationApkPath();
|
||||
|
||||
/**
|
||||
* Fetch the locales
|
||||
*
|
||||
* @return the map of locales (key: locale short name, value: locale data as json)
|
||||
*/
|
||||
Map<String, ParcelFileDescriptor> fetchLocales(String userLocale);
|
||||
* broadcast a log message
|
||||
*/
|
||||
oneway void broadcastLog(String tag, String level, String message);
|
||||
|
||||
/**
|
||||
* Enqueue a download
|
||||
@ -92,6 +80,8 @@ interface BridgeInterface {
|
||||
|
||||
AccountStorage getAccountStorage();
|
||||
|
||||
FileHandleManager getFileHandleManager();
|
||||
|
||||
oneway void registerMessagingBridge(MessagingBridge bridge);
|
||||
|
||||
oneway void openSettingsOverlay();
|
||||
|
@ -0,0 +1,9 @@
|
||||
package me.rhunk.snapenhance.bridge.storage;
|
||||
|
||||
interface FileHandle {
|
||||
boolean exists();
|
||||
boolean create();
|
||||
boolean delete();
|
||||
|
||||
@nullable ParcelFileDescriptor open(int mode);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package me.rhunk.snapenhance.bridge.storage;
|
||||
|
||||
import me.rhunk.snapenhance.bridge.storage.FileHandle;
|
||||
|
||||
interface FileHandleManager {
|
||||
@nullable FileHandle getFileHandle(String scope, String name);
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package me.rhunk.snapenhance.common.bridge
|
||||
|
||||
import android.content.Context
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.os.ParcelFileDescriptor.AutoCloseInputStream
|
||||
import android.os.ParcelFileDescriptor.AutoCloseOutputStream
|
||||
import me.rhunk.snapenhance.bridge.storage.FileHandle
|
||||
import java.io.File
|
||||
|
||||
|
||||
enum class FileHandleScope(
|
||||
val key: String
|
||||
) {
|
||||
INTERNAL("internal"),
|
||||
LOCALE("locale");
|
||||
|
||||
companion object {
|
||||
fun fromValue(name: String): FileHandleScope? = entries.find { it.key == name }
|
||||
}
|
||||
}
|
||||
|
||||
enum class InternalFileHandleType(
|
||||
val key: String,
|
||||
val fileName: String,
|
||||
val isDatabase: Boolean = false
|
||||
) {
|
||||
CONFIG("config", "config.json"),
|
||||
MAPPINGS("mappings", "mappings.json"),
|
||||
MESSAGE_LOGGER("message_logger", "message_logger.db", isDatabase = true),
|
||||
SUSPEND_LOCATION_STATE("suspend_location_state", "suspend_location_state.txt"),
|
||||
PINNED_BEST_FRIEND("pinned_best_friend", "pinned_best_friend.txt");
|
||||
|
||||
|
||||
fun resolve(context: Context): File = if (isDatabase) {
|
||||
context.getDatabasePath(fileName)
|
||||
} else {
|
||||
File(context.filesDir, fileName)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromValue(name: String): InternalFileHandleType? = entries.find { it.key == name }
|
||||
}
|
||||
}
|
||||
|
||||
fun FileHandle.toWrapper() = FileHandleWrapper(lazy { this })
|
||||
|
||||
open class FileHandleWrapper(
|
||||
private val fileHandle: Lazy<FileHandle>
|
||||
) {
|
||||
fun exists() = fileHandle.value.exists()
|
||||
fun create() = fileHandle.value.create()
|
||||
fun delete() = fileHandle.value.delete()
|
||||
|
||||
fun writeBytes(data: ByteArray) = fileHandle.value.open(
|
||||
ParcelFileDescriptor.MODE_WRITE_ONLY or
|
||||
ParcelFileDescriptor.MODE_CREATE or
|
||||
ParcelFileDescriptor.MODE_TRUNCATE
|
||||
).use { pfd ->
|
||||
AutoCloseOutputStream(pfd).use {
|
||||
it.write(data)
|
||||
}
|
||||
}
|
||||
|
||||
open fun readBytes(): ByteArray = fileHandle.value.open(
|
||||
ParcelFileDescriptor.MODE_READ_ONLY or
|
||||
ParcelFileDescriptor.MODE_CREATE
|
||||
).use { pfd ->
|
||||
AutoCloseInputStream(pfd).use {
|
||||
it.readBytes()
|
||||
}
|
||||
}
|
||||
|
||||
fun inputStream(block: (AutoCloseInputStream) -> Unit) = fileHandle.value.open(
|
||||
ParcelFileDescriptor.MODE_READ_ONLY or
|
||||
ParcelFileDescriptor.MODE_CREATE
|
||||
).use { pfd ->
|
||||
AutoCloseInputStream(pfd).use {
|
||||
block(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun outputStream(block: (AutoCloseOutputStream) -> Unit) = fileHandle.value.open(
|
||||
ParcelFileDescriptor.MODE_WRITE_ONLY or
|
||||
ParcelFileDescriptor.MODE_CREATE or
|
||||
ParcelFileDescriptor.MODE_TRUNCATE
|
||||
).use { pfd ->
|
||||
AutoCloseOutputStream(pfd).use {
|
||||
block(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
package me.rhunk.snapenhance.common.bridge
|
||||
|
||||
import android.content.Context
|
||||
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
|
||||
|
||||
open class FileLoaderWrapper(
|
||||
val fileType: BridgeFileType,
|
||||
val defaultContent: ByteArray
|
||||
) {
|
||||
lateinit var isFileExists: () -> Boolean
|
||||
lateinit var write: (ByteArray) -> Unit
|
||||
lateinit var read: () -> ByteArray
|
||||
lateinit var delete: () -> Unit
|
||||
|
||||
fun loadFromContext(context: Context) {
|
||||
val file = fileType.resolve(context)
|
||||
isFileExists = { file.exists() }
|
||||
read = {
|
||||
if (!file.exists()) {
|
||||
file.createNewFile()
|
||||
file.writeBytes("{}".toByteArray(Charsets.UTF_8))
|
||||
}
|
||||
file.readBytes()
|
||||
}
|
||||
write = { file.writeBytes(it) }
|
||||
delete = { file.delete() }
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package me.rhunk.snapenhance.common.bridge
|
||||
|
||||
import me.rhunk.snapenhance.bridge.storage.FileHandleManager
|
||||
|
||||
open class InternalFileWrapper(
|
||||
fileHandleManager: FileHandleManager,
|
||||
private val fileType: InternalFileHandleType,
|
||||
val defaultValue: String? = null
|
||||
): FileHandleWrapper(lazy { fileHandleManager.getFileHandle(FileHandleScope.INTERNAL.key, fileType.key)!! }) {
|
||||
override fun readBytes(): ByteArray {
|
||||
val bytes = super.readBytes()
|
||||
if (bytes.isEmpty()) {
|
||||
defaultValue?.let { writeBytes(it.toByteArray()) }
|
||||
}
|
||||
return super.readBytes()
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package me.rhunk.snapenhance.common.bridge.types
|
||||
|
||||
import android.content.Context
|
||||
import java.io.File
|
||||
|
||||
|
||||
enum class BridgeFileType(val value: Int, val fileName: String, val displayName: String, private val isDatabase: Boolean = false) {
|
||||
CONFIG(0, "config.json", "Config"),
|
||||
MAPPINGS(1, "mappings.json", "Mappings"),
|
||||
MESSAGE_LOGGER_DATABASE(2, "message_logger.db", "Message Logger",true),
|
||||
PINNED_CONVERSATIONS(3, "pinned_conversations.txt", "Pinned Conversations"),
|
||||
SUSPEND_LOCATION_STATE(4, "suspend_location_state.txt", "Suspend Location State"),
|
||||
PINNED_BEST_FRIEND(5, "pinned_best_friend.txt", "Pinned Best Friend");
|
||||
|
||||
fun resolve(context: Context): File = if (isDatabase) {
|
||||
context.getDatabasePath(fileName)
|
||||
} else {
|
||||
File(context.filesDir, fileName)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromValue(value: Int): BridgeFileType? {
|
||||
return entries.firstOrNull { it.value == value }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package me.rhunk.snapenhance.common.bridge.types
|
||||
|
||||
enum class FileActionType {
|
||||
CREATE_AND_READ, READ, WRITE, DELETE, EXISTS
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package me.rhunk.snapenhance.common.bridge.types
|
||||
|
||||
import android.os.ParcelFileDescriptor
|
||||
import java.util.Locale
|
||||
|
||||
data class LocalePair(
|
||||
val locale: String,
|
||||
val content: ParcelFileDescriptor
|
||||
) {
|
||||
fun getLocale(): Locale {
|
||||
if (locale.contains("_")) {
|
||||
val split = locale.split("_")
|
||||
return Locale(split[0], split[1])
|
||||
}
|
||||
return Locale(locale)
|
||||
}
|
||||
}
|
@ -1,41 +1,22 @@
|
||||
package me.rhunk.snapenhance.common.bridge.wrapper
|
||||
|
||||
import android.content.Context
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.os.ParcelFileDescriptor.AutoCloseInputStream
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import me.rhunk.snapenhance.common.bridge.types.LocalePair
|
||||
import me.rhunk.snapenhance.bridge.storage.FileHandleManager
|
||||
import me.rhunk.snapenhance.common.bridge.FileHandleScope
|
||||
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
||||
import me.rhunk.snapenhance.common.util.ktx.toParcelFileDescriptor
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
class LocaleWrapper {
|
||||
class LocaleWrapper(
|
||||
private val fileHandleManager: FileHandleManager
|
||||
) {
|
||||
companion object {
|
||||
const val DEFAULT_LOCALE = "en_US"
|
||||
|
||||
fun fetchLocales(context: Context, locale: String = DEFAULT_LOCALE): List<LocalePair> {
|
||||
val coroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
val locales = mutableListOf<LocalePair>().apply {
|
||||
add(LocalePair(DEFAULT_LOCALE, context.resources.assets.open("lang/$DEFAULT_LOCALE.json").toParcelFileDescriptor(coroutineScope)))
|
||||
}
|
||||
|
||||
if (locale == DEFAULT_LOCALE) return locales
|
||||
|
||||
val compatibleLocale = context.resources.assets.list("lang")?.firstOrNull { it.startsWith(locale) }?.substringBefore(".") ?: return locales
|
||||
|
||||
locales.add(
|
||||
LocalePair(
|
||||
compatibleLocale,
|
||||
context.resources.assets.open("lang/$compatibleLocale.json").toParcelFileDescriptor(coroutineScope)
|
||||
)
|
||||
)
|
||||
|
||||
return locales
|
||||
}
|
||||
|
||||
fun fetchAvailableLocales(context: Context): List<String> {
|
||||
return context.resources.assets.list("lang")?.map { it.substringBefore(".") }?.sorted() ?: listOf(DEFAULT_LOCALE)
|
||||
}
|
||||
@ -47,14 +28,23 @@ class LocaleWrapper {
|
||||
|
||||
lateinit var loadedLocale: Locale
|
||||
|
||||
private fun load(localePair: LocalePair) {
|
||||
loadedLocale = localePair.getLocale()
|
||||
private fun load(locale: String, pfd: ParcelFileDescriptor) {
|
||||
loadedLocale = if (locale.contains("_")) {
|
||||
val split = locale.split("_")
|
||||
Locale(split[0], split[1])
|
||||
} else {
|
||||
Locale(locale)
|
||||
}
|
||||
|
||||
val translations = AutoCloseInputStream(localePair.content).use {
|
||||
JsonParser.parseReader(it.reader()).asJsonObject
|
||||
val translations = AutoCloseInputStream(pfd).use {
|
||||
runCatching {
|
||||
JsonParser.parseReader(it.reader()).asJsonObject
|
||||
}.onFailure {
|
||||
AbstractLogger.directError("Failed to parse locale file: ${it.message}", it)
|
||||
}.getOrNull()
|
||||
}
|
||||
if (translations == null || translations.isJsonNull) {
|
||||
return
|
||||
throw IllegalStateException("Failed to parse $locale.json")
|
||||
}
|
||||
|
||||
fun scanObject(jsonObject: JsonObject, prefix: String = "") {
|
||||
@ -71,22 +61,25 @@ class LocaleWrapper {
|
||||
scanObject(translations)
|
||||
}
|
||||
|
||||
fun loadFromCallback(callback: (String) -> List<LocalePair>) {
|
||||
callback(userLocale).forEach {
|
||||
load(it)
|
||||
fun load() {
|
||||
load(
|
||||
DEFAULT_LOCALE,
|
||||
fileHandleManager.getFileHandle(FileHandleScope.LOCALE.key, "$DEFAULT_LOCALE.json")?.open(ParcelFileDescriptor.MODE_READ_ONLY) ?: run {
|
||||
throw IllegalStateException("Failed to load default locale")
|
||||
}
|
||||
)
|
||||
|
||||
if (userLocale != DEFAULT_LOCALE) {
|
||||
fileHandleManager.getFileHandle(FileHandleScope.LOCALE.key, "$userLocale.json")?.open(ParcelFileDescriptor.MODE_READ_ONLY)?.let {
|
||||
load(userLocale, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadFromContext(context: Context) {
|
||||
fetchLocales(context, userLocale).forEach {
|
||||
load(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun reloadFromContext(context: Context, locale: String) {
|
||||
fun reload(locale: String) {
|
||||
userLocale = locale
|
||||
translationMap.clear()
|
||||
loadFromContext(context)
|
||||
load()
|
||||
}
|
||||
|
||||
operator fun get(key: String) = translationMap[key] ?: key.also { AbstractLogger.directDebug("Missing translation for $key") }
|
||||
@ -99,7 +92,7 @@ class LocaleWrapper {
|
||||
}
|
||||
|
||||
fun getCategory(key: String): LocaleWrapper {
|
||||
return LocaleWrapper().apply {
|
||||
return LocaleWrapper(fileHandleManager).apply {
|
||||
translationMap.putAll(
|
||||
this@LocaleWrapper.translationMap
|
||||
.filterKeys { it.startsWith("$key.") }
|
||||
|
@ -1,11 +1,13 @@
|
||||
package me.rhunk.snapenhance.common.bridge.wrapper
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonObject
|
||||
import kotlinx.coroutines.*
|
||||
import me.rhunk.snapenhance.bridge.logger.LoggerInterface
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.common.data.StoryData
|
||||
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
||||
import me.rhunk.snapenhance.common.util.SQLiteDatabaseHelper
|
||||
@ -61,6 +63,8 @@ class TrackerLog(
|
||||
class LoggerWrapper(
|
||||
val databaseFile: File
|
||||
): LoggerInterface.Stub() {
|
||||
constructor(context: Context): this(File(context.getDatabasePath(InternalFileHandleType.MESSAGE_LOGGER.fileName).absolutePath))
|
||||
|
||||
private var _database: SQLiteDatabase? = null
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.IO.limitedParallelism(1))
|
||||
|
@ -3,16 +3,19 @@ package me.rhunk.snapenhance.common.bridge.wrapper
|
||||
import android.content.Context
|
||||
import com.google.gson.JsonParser
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import me.rhunk.snapenhance.bridge.storage.FileHandleManager
|
||||
import me.rhunk.snapenhance.common.BuildConfig
|
||||
import me.rhunk.snapenhance.common.Constants
|
||||
import me.rhunk.snapenhance.common.bridge.FileLoaderWrapper
|
||||
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileWrapper
|
||||
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
||||
import me.rhunk.snapenhance.mapper.AbstractClassMapper
|
||||
import me.rhunk.snapenhance.mapper.ClassMapper
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class MappingsWrapper : FileLoaderWrapper(BridgeFileType.MAPPINGS, "{}".toByteArray(Charsets.UTF_8)) {
|
||||
class MappingsWrapper(
|
||||
fileHandleManager: FileHandleManager
|
||||
): InternalFileWrapper(fileHandleManager, InternalFileHandleType.MAPPINGS, defaultValue = "{}") {
|
||||
private lateinit var context: Context
|
||||
private var mappingUniqueHash: Long = 0
|
||||
var isMappingsLoaded = false
|
||||
@ -26,7 +29,7 @@ class MappingsWrapper : FileLoaderWrapper(BridgeFileType.MAPPINGS, "{}".toByteAr
|
||||
this.context = context
|
||||
mappingUniqueHash = getUniqueBuildId()
|
||||
|
||||
if (isFileExists()) {
|
||||
if (exists()) {
|
||||
runCatching {
|
||||
loadCached()
|
||||
}.onFailure {
|
||||
@ -46,10 +49,10 @@ class MappingsWrapper : FileLoaderWrapper(BridgeFileType.MAPPINGS, "{}".toByteAr
|
||||
fun isMappingsOutdated() = mappingUniqueHash != getUniqueBuildId() || isMappingsLoaded.not()
|
||||
|
||||
private fun loadCached() {
|
||||
if (!isFileExists()) {
|
||||
if (!exists()) {
|
||||
throw Exception("Mappings file does not exist")
|
||||
}
|
||||
val mappingsObject = JsonParser.parseString(read().toString(Charsets.UTF_8)).asJsonObject.also {
|
||||
val mappingsObject = JsonParser.parseString(readBytes().toString(Charsets.UTF_8)).asJsonObject.also {
|
||||
mappingUniqueHash = it["unique_hash"].asLong
|
||||
}
|
||||
|
||||
@ -76,7 +79,7 @@ class MappingsWrapper : FileLoaderWrapper(BridgeFileType.MAPPINGS, "{}".toByteAr
|
||||
val result = classMapper.run().apply {
|
||||
addProperty("unique_hash", mappingUniqueHash)
|
||||
}
|
||||
write(result.toString().toByteArray())
|
||||
writeBytes(result.toString().toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,20 +5,22 @@ import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonObject
|
||||
import me.rhunk.snapenhance.bridge.ConfigStateListener
|
||||
import me.rhunk.snapenhance.common.bridge.FileLoaderWrapper
|
||||
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.bridge.storage.FileHandleManager
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileWrapper
|
||||
import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper
|
||||
import me.rhunk.snapenhance.common.config.impl.RootConfig
|
||||
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class ModConfig(
|
||||
private val context: Context
|
||||
private val context: Context,
|
||||
fileHandleManager: FileHandleManager
|
||||
) {
|
||||
private val fileWrapper = InternalFileWrapper(fileHandleManager, InternalFileHandleType.CONFIG, "{}")
|
||||
var locale: String = LocaleWrapper.DEFAULT_LOCALE
|
||||
|
||||
private val gson: Gson = GsonBuilder().setPrettyPrinting().create()
|
||||
private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8))
|
||||
var wasPresent by Delegates.notNull<Boolean>()
|
||||
|
||||
/* Used to notify the bridge client about config changes */
|
||||
@ -30,9 +32,9 @@ class ModConfig(
|
||||
|
||||
private fun createRootConfig() = RootConfig().apply { lateInit(context) }
|
||||
|
||||
private fun load() {
|
||||
fun load() {
|
||||
root = createRootConfig()
|
||||
wasPresent = file.isFileExists()
|
||||
wasPresent = fileWrapper.exists()
|
||||
if (!wasPresent) {
|
||||
writeConfig()
|
||||
return
|
||||
@ -46,7 +48,7 @@ class ModConfig(
|
||||
}
|
||||
|
||||
private fun loadConfig() {
|
||||
val configFileContent = file.read()
|
||||
val configFileContent = fileWrapper.readBytes()
|
||||
val configObject = gson.fromJson(configFileContent.toString(Charsets.UTF_8), JsonObject::class.java)
|
||||
locale = configObject.get("_locale")?.asString ?: LocaleWrapper.DEFAULT_LOCALE
|
||||
root.fromJson(configObject)
|
||||
@ -99,8 +101,8 @@ class ModConfig(
|
||||
}
|
||||
}
|
||||
|
||||
val oldConfig = runCatching { file.read().toString(Charsets.UTF_8) }.getOrNull()
|
||||
file.write(exportToString().toByteArray(Charsets.UTF_8))
|
||||
val oldConfig = runCatching { fileWrapper.readBytes().toString(Charsets.UTF_8) }.getOrNull()
|
||||
fileWrapper.writeBytes(exportToString().toByteArray(Charsets.UTF_8))
|
||||
|
||||
configStateListener?.also {
|
||||
runCatching {
|
||||
@ -125,14 +127,4 @@ class ModConfig(
|
||||
root.fromJson(configObject)
|
||||
writeConfig()
|
||||
}
|
||||
|
||||
fun loadFromContext(context: Context) {
|
||||
file.loadFromContext(context)
|
||||
load()
|
||||
}
|
||||
|
||||
fun loadFromCallback(callback: (FileLoaderWrapper) -> Unit) {
|
||||
callback(file)
|
||||
load()
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@ import me.rhunk.snapenhance.common.bridge.wrapper.MappingsWrapper
|
||||
import me.rhunk.snapenhance.common.config.ModConfig
|
||||
import me.rhunk.snapenhance.core.action.ActionManager
|
||||
import me.rhunk.snapenhance.core.bridge.BridgeClient
|
||||
import me.rhunk.snapenhance.core.bridge.loadFromBridge
|
||||
import me.rhunk.snapenhance.core.database.DatabaseAccess
|
||||
import me.rhunk.snapenhance.core.event.EventBus
|
||||
import me.rhunk.snapenhance.core.event.EventDispatcher
|
||||
@ -48,15 +47,15 @@ class ModContext(
|
||||
val resources: Resources get() = androidContext.resources
|
||||
val gson: Gson = GsonBuilder().create()
|
||||
|
||||
private val _config = ModConfig(androidContext)
|
||||
val config by _config::root
|
||||
private val _config by lazy { ModConfig(androidContext, bridgeClient.getFileHandlerManager()) }
|
||||
val config get() = _config.root
|
||||
val log by lazy { CoreLogger(this.bridgeClient) }
|
||||
val translation = LocaleWrapper()
|
||||
val translation by lazy { LocaleWrapper(bridgeClient.getFileHandlerManager()) }
|
||||
val httpServer = HttpServer()
|
||||
val messageSender = MessageSender(this)
|
||||
|
||||
val features = FeatureManager(this)
|
||||
val mappings = MappingsWrapper()
|
||||
val mappings by lazy { MappingsWrapper(bridgeClient.getFileHandlerManager()) }
|
||||
val actionManager = ActionManager(this)
|
||||
val database = DatabaseAccess(this)
|
||||
val event = EventBus(this)
|
||||
@ -144,9 +143,7 @@ class ModContext(
|
||||
|
||||
fun reloadConfig() {
|
||||
log.verbose("reloading config")
|
||||
_config.loadFromCallback { file ->
|
||||
file.loadFromBridge(bridgeClient)
|
||||
}
|
||||
_config.load()
|
||||
reloadNativeConfig()
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@ import me.rhunk.snapenhance.common.data.MessagingFriendInfo
|
||||
import me.rhunk.snapenhance.common.data.MessagingGroupInfo
|
||||
import me.rhunk.snapenhance.common.util.toSerialized
|
||||
import me.rhunk.snapenhance.core.bridge.BridgeClient
|
||||
import me.rhunk.snapenhance.core.bridge.loadFromBridge
|
||||
import me.rhunk.snapenhance.core.data.SnapClassCache
|
||||
import me.rhunk.snapenhance.core.event.events.impl.NativeUnaryCallEvent
|
||||
import me.rhunk.snapenhance.core.event.events.impl.SnapWidgetBroadcastReceiveEvent
|
||||
@ -148,12 +147,9 @@ class SnapEnhance {
|
||||
log.error("Failed to sync remote", it)
|
||||
}
|
||||
translation.userLocale = getConfigLocale()
|
||||
translation.loadFromCallback { locale ->
|
||||
bridgeClient.fetchLocales(locale)
|
||||
}
|
||||
translation.load()
|
||||
}
|
||||
|
||||
mappings.loadFromBridge(bridgeClient)
|
||||
mappings.init(androidContext)
|
||||
database.init()
|
||||
eventDispatcher.init()
|
||||
|
@ -18,11 +18,8 @@ import me.rhunk.snapenhance.bridge.logger.LoggerInterface
|
||||
import me.rhunk.snapenhance.bridge.logger.TrackerInterface
|
||||
import me.rhunk.snapenhance.bridge.scripting.IScripting
|
||||
import me.rhunk.snapenhance.bridge.snapclient.MessagingBridge
|
||||
import me.rhunk.snapenhance.bridge.storage.FileHandleManager
|
||||
import me.rhunk.snapenhance.common.Constants
|
||||
import me.rhunk.snapenhance.common.bridge.FileLoaderWrapper
|
||||
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.common.bridge.types.FileActionType
|
||||
import me.rhunk.snapenhance.common.bridge.types.LocalePair
|
||||
import me.rhunk.snapenhance.common.data.MessagingFriendInfo
|
||||
import me.rhunk.snapenhance.common.data.MessagingGroupInfo
|
||||
import me.rhunk.snapenhance.common.data.MessagingRuleType
|
||||
@ -34,14 +31,6 @@ import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
fun FileLoaderWrapper.loadFromBridge(bridgeClient: BridgeClient) {
|
||||
isFileExists = { bridgeClient.isFileExists(fileType) }
|
||||
read = { bridgeClient.createAndReadFile(fileType, defaultContent) }
|
||||
write = { bridgeClient.writeFile(fileType, it) }
|
||||
delete = { bridgeClient.deleteFile(fileType) }
|
||||
}
|
||||
|
||||
|
||||
class BridgeClient(
|
||||
private val context: ModContext
|
||||
): ServiceConnection {
|
||||
@ -125,39 +114,6 @@ class BridgeClient(
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: use interfaces instead of direct file access
|
||||
fun createAndReadFile(
|
||||
fileType: BridgeFileType,
|
||||
defaultContent: ByteArray
|
||||
): ByteArray = safeServiceCall {
|
||||
service.fileOperation(FileActionType.CREATE_AND_READ.ordinal, fileType.value, defaultContent)
|
||||
}
|
||||
|
||||
fun readFile(fileType: BridgeFileType): ByteArray = safeServiceCall { service.fileOperation(FileActionType.READ.ordinal, fileType.value, null) }
|
||||
|
||||
fun writeFile(
|
||||
fileType: BridgeFileType,
|
||||
content: ByteArray?
|
||||
): ByteArray = safeServiceCall {
|
||||
service.fileOperation(FileActionType.WRITE.ordinal, fileType.value, content)
|
||||
}
|
||||
|
||||
fun deleteFile(fileType: BridgeFileType) {
|
||||
safeServiceCall {
|
||||
service.fileOperation(FileActionType.DELETE.ordinal, fileType.value, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun isFileExists(fileType: BridgeFileType) = safeServiceCall {
|
||||
service.fileOperation(FileActionType.EXISTS.ordinal, fileType.value, null).isNotEmpty()
|
||||
}
|
||||
|
||||
fun fetchLocales(userLocale: String) = safeServiceCall {
|
||||
service.fetchLocales(userLocale).map {
|
||||
LocalePair(it.key, it.value)
|
||||
}
|
||||
}
|
||||
|
||||
fun getApplicationApkPath(): String = safeServiceCall { service.applicationApkPath }
|
||||
|
||||
fun enqueueDownload(intent: Intent, callback: DownloadCallback) = safeServiceCall {
|
||||
@ -215,6 +171,8 @@ class BridgeClient(
|
||||
|
||||
fun getAccountStorage(): AccountStorage = safeServiceCall { service.accountStorage }
|
||||
|
||||
fun getFileHandlerManager(): FileHandleManager = safeServiceCall { service.fileHandleManager }
|
||||
|
||||
fun registerMessagingBridge(bridge: MessagingBridge) = safeServiceCall { service.registerMessagingBridge(bridge) }
|
||||
|
||||
fun openSettingsOverlay() = safeServiceCall { service.openSettingsOverlay() }
|
||||
|
@ -1,32 +1,38 @@
|
||||
package me.rhunk.snapenhance.core.features
|
||||
|
||||
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.common.bridge.FileHandleScope
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.common.bridge.toWrapper
|
||||
import java.io.BufferedReader
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
abstract class BridgeFileFeature(name: String, private val bridgeFileType: BridgeFileType, loadParams: Int) : Feature(name, loadParams) {
|
||||
abstract class BridgeFileFeature(name: String, private val bridgeFileType: InternalFileHandleType, loadParams: Int) : Feature(name, loadParams) {
|
||||
private val fileLines = mutableListOf<String>()
|
||||
private val fileWrapper by lazy { context.bridgeClient.getFileHandlerManager().getFileHandle(FileHandleScope.INTERNAL.key, bridgeFileType.key)!!.toWrapper() }
|
||||
|
||||
protected fun readFile() {
|
||||
val temporaryLines = mutableListOf<String>()
|
||||
val fileData: ByteArray = context.bridgeClient.createAndReadFile(bridgeFileType, ByteArray(0))
|
||||
with(BufferedReader(InputStreamReader(ByteArrayInputStream(fileData), StandardCharsets.UTF_8))) {
|
||||
var line = ""
|
||||
while (readLine()?.also { line = it } != null) temporaryLines.add(line)
|
||||
close()
|
||||
fileWrapper.inputStream { stream ->
|
||||
with(BufferedReader(InputStreamReader(stream, StandardCharsets.UTF_8))) {
|
||||
var line = ""
|
||||
while (readLine()?.also { line = it } != null) temporaryLines.add(line)
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
fileLines.clear()
|
||||
fileLines.addAll(temporaryLines)
|
||||
}
|
||||
|
||||
private fun updateFile() {
|
||||
val sb = StringBuilder()
|
||||
fileLines.forEach {
|
||||
sb.append(it).append("\n")
|
||||
fileWrapper.outputStream { stream ->
|
||||
fileLines.forEach {
|
||||
stream.write(it.toByteArray())
|
||||
stream.write("\n".toByteArray())
|
||||
stream.flush()
|
||||
}
|
||||
}
|
||||
context.bridgeClient.writeFile(bridgeFileType, sb.toString().toByteArray(Charsets.UTF_8))
|
||||
}
|
||||
|
||||
protected fun exists(line: String) = fileLines.contains(line)
|
||||
|
@ -7,7 +7,7 @@ import com.google.gson.JsonObject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
|
||||
import me.rhunk.snapenhance.core.event.events.impl.NetworkApiRequestEvent
|
||||
import me.rhunk.snapenhance.core.event.events.impl.UnaryCallEvent
|
||||
@ -18,7 +18,7 @@ import java.io.InputStreamReader
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.UUID
|
||||
|
||||
class BestFriendPinning: BridgeFileFeature("Best Friend Pinning", BridgeFileType.PINNED_BEST_FRIEND, loadParams = FeatureLoadParams.INIT_SYNC) {
|
||||
class BestFriendPinning: BridgeFileFeature("Best Friend Pinning", InternalFileHandleType.PINNED_BEST_FRIEND, loadParams = FeatureLoadParams.INIT_SYNC) {
|
||||
private fun updatePinnedBestFriendStatus() {
|
||||
lines().firstOrNull()?.trim()?.let {
|
||||
context.database.updatePinnedBestFriendStatus(it.substring(0, 36), "number_one_bf_for_two_months")
|
||||
|
@ -2,7 +2,7 @@ package me.rhunk.snapenhance.core.features.impl.global
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Switch
|
||||
import me.rhunk.snapenhance.common.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.core.event.events.impl.LayoutInflateEvent
|
||||
import me.rhunk.snapenhance.core.features.BridgeFileFeature
|
||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||
@ -12,7 +12,7 @@ import me.rhunk.snapenhance.core.util.ktx.getLayoutId
|
||||
|
||||
class SuspendLocationUpdates : BridgeFileFeature(
|
||||
"Suspend Location Updates",
|
||||
loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC, bridgeFileType = BridgeFileType.SUSPEND_LOCATION_STATE) {
|
||||
loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC, bridgeFileType = InternalFileHandleType.SUSPEND_LOCATION_STATE) {
|
||||
fun isSuspended() = exists("true")
|
||||
private fun setSuspended(suspended: Boolean) = setState("true", suspended)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user