From d8248cc915745f618ffb1f3a2d56d9a6f62a189d Mon Sep 17 00:00:00 2001 From: Ax333l Date: Wed, 3 Jul 2024 12:27:39 +0200 Subject: [PATCH 1/6] fix: import bundles on another thread --- .../manager/domain/repository/PatchBundleRepository.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/domain/repository/PatchBundleRepository.kt b/app/src/main/java/app/revanced/manager/domain/repository/PatchBundleRepository.kt index 4f3ec2f5..f40d6c0b 100644 --- a/app/src/main/java/app/revanced/manager/domain/repository/PatchBundleRepository.kt +++ b/app/src/main/java/app/revanced/manager/domain/repository/PatchBundleRepository.kt @@ -137,7 +137,7 @@ class PatchBundleRepository( private fun addBundle(patchBundle: PatchBundleSource) = _sources.update { it.toMutableMap().apply { put(patchBundle.uid, patchBundle) } } - suspend fun createLocal(patches: InputStream, integrations: InputStream?) { + suspend fun createLocal(patches: InputStream, integrations: InputStream?) = withContext(Dispatchers.Default) { val uid = persistenceRepo.create("", SourceInfo.Local).uid val bundle = LocalPatchBundle("", uid, directoryOf(uid)) @@ -145,7 +145,7 @@ class PatchBundleRepository( addBundle(bundle) } - suspend fun createRemote(url: String, autoUpdate: Boolean) { + suspend fun createRemote(url: String, autoUpdate: Boolean) = withContext(Dispatchers.Default) { val entity = persistenceRepo.create("", SourceInfo.from(url), autoUpdate) addBundle(entity.load()) } From 8d3d500b7b2dee95fbb02aab49404a8d41eb2dd3 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Wed, 3 Jul 2024 13:54:37 +0200 Subject: [PATCH 2/6] feat: add ability to share debug logs --- .../screen/settings/AdvancedSettingsScreen.kt | 11 +++++ .../ui/viewmodel/AdvancedSettingsViewModel.kt | 49 ++++++++++++++++++- app/src/main/res/values/strings.xml | 4 ++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt index f46849c8..3817bd12 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt @@ -2,6 +2,8 @@ package app.revanced.manager.ui.screen.settings import android.app.ActivityManager import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -86,6 +88,15 @@ fun AdvancedSettingsScreen( } ) + val exportDebugLogsLauncher = + rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { + it?.let(vm::exportDebugLogs) + } + SettingsListItem( + headlineContent = stringResource(R.string.debug_logs_export), + modifier = Modifier.clickable { exportDebugLogsLauncher.launch(vm.debugLogFileName) } + ) + GroupHeader(stringResource(R.string.patcher)) BooleanItem( preference = vm.prefs.useProcessRuntime, diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/AdvancedSettingsViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/AdvancedSettingsViewModel.kt index 9efed1dd..5db843c0 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/AdvancedSettingsViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/AdvancedSettingsViewModel.kt @@ -1,21 +1,41 @@ package app.revanced.manager.ui.viewmodel import android.app.Application +import android.net.Uri +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.revanced.manager.R +import app.revanced.manager.domain.bundles.RemotePatchBundle import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.PatchBundleRepository -import app.revanced.manager.domain.bundles.RemotePatchBundle +import app.revanced.manager.util.tag +import app.revanced.manager.util.toast import app.revanced.manager.util.uiSafe +import com.github.pgreze.process.Redirect +import com.github.pgreze.process.process +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter class AdvancedSettingsViewModel( val prefs: PreferencesManager, private val app: Application, private val patchBundleRepository: PatchBundleRepository ) : ViewModel() { + val debugLogFileName: String + get() { + val time = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now()) + + return "revanced-manager_logcat_$time" + } + fun setApiUrl(value: String) = viewModelScope.launch(Dispatchers.Default) { if (value == prefs.api.get()) return@launch @@ -32,4 +52,31 @@ class AdvancedSettingsViewModel( fun resetBundles() = viewModelScope.launch { patchBundleRepository.reset() } + + fun exportDebugLogs(target: Uri) = viewModelScope.launch { + val exitCode = try { + withContext(Dispatchers.IO) { + app.contentResolver.openOutputStream(target)!!.bufferedWriter().use { writer -> + val consumer = Redirect.Consume { flow -> + flow.onEach { + writer.write(it) + }.flowOn(Dispatchers.IO).collect() + } + + process("logcat", "-d", stdout = consumer).resultCode + } + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + Log.e(tag, "Got exception while exporting logs", e) + app.toast(app.getString(R.string.debug_logs_export_failed)) + return@launch + } + + if (exitCode == 0) + app.toast(app.getString(R.string.debug_logs_export_success)) + else + app.toast(app.getString(R.string.debug_logs_export_read_failed, exitCode)) + } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6d3dc16f..5b833f6a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -137,6 +137,10 @@ This is faster and allows Patcher to use more memory. Patcher process memory limit The max amount of memory that the Patcher process can use (in megabytes) + Export debug logs + Failed to read logs (exit code %d) + Failed to export logs + Exported logs API URL Set custom API URL You may have issues with features when using a custom API URL. From 48fe3a707eed24f1634ff009b787254976ea0220 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Wed, 3 Jul 2024 14:46:00 +0200 Subject: [PATCH 3/6] fix: import export screen UX --- .../manager/domain/manager/KeystoreManager.kt | 8 +++++--- .../manager/ui/viewmodel/ImportExportViewModel.kt | 12 ++++++++++-- app/src/main/res/values/strings.xml | 7 ++++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/domain/manager/KeystoreManager.kt b/app/src/main/java/app/revanced/manager/domain/manager/KeystoreManager.kt index bba8035f..c1c4700d 100644 --- a/app/src/main/java/app/revanced/manager/domain/manager/KeystoreManager.kt +++ b/app/src/main/java/app/revanced/manager/domain/manager/KeystoreManager.kt @@ -49,15 +49,17 @@ class KeystoreManager(app: Application, private val prefs: PreferencesManager) { ) ) ) - keystorePath.outputStream().use { - ks.store(it, null) + withContext(Dispatchers.IO) { + keystorePath.outputStream().use { + ks.store(it, null) + } } updatePrefs(DEFAULT, DEFAULT) } suspend fun import(cn: String, pass: String, keystore: InputStream): Boolean { - val keystoreData = keystore.readBytes() + val keystoreData = withContext(Dispatchers.IO) { keystore.readBytes() } try { val ks = ApkSigner.readKeyStore(ByteArrayInputStream(keystoreData), null) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/ImportExportViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/ImportExportViewModel.kt index d568317d..9bf08562 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/ImportExportViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/ImportExportViewModel.kt @@ -55,14 +55,17 @@ class ImportExportViewModel( fun resetOptionsForPackage(packageName: String) = viewModelScope.launch { optionsRepository.clearOptionsForPackage(packageName) + app.toast(app.getString(R.string.patch_options_reset_toast)) } fun clearOptionsForBundle(patchBundle: PatchBundleSource) = viewModelScope.launch { optionsRepository.clearOptionsForPatchBundle(patchBundle.uid) + app.toast(app.getString(R.string.patch_options_reset_toast)) } fun resetOptions() = viewModelScope.launch { optionsRepository.reset() + app.toast(app.getString(R.string.patch_options_reset_toast)) } fun startKeystoreImport(content: Uri) = viewModelScope.launch { @@ -98,6 +101,7 @@ class ImportExportViewModel( private suspend fun tryKeystoreImport(cn: String, pass: String, path: Path): Boolean { path.inputStream().use { stream -> if (keystoreManager.import(cn, pass, stream)) { + app.toast(app.getString(R.string.import_keystore_success)) cancelKeystoreImport() return true } @@ -116,6 +120,7 @@ class ImportExportViewModel( fun exportKeystore(target: Uri) = viewModelScope.launch { keystoreManager.export(contentResolver.openOutputStream(target)!!) + app.toast(app.getString(R.string.export_keystore_success)) } fun regenerateKeystore() = viewModelScope.launch { @@ -123,8 +128,9 @@ class ImportExportViewModel( app.toast(app.getString(R.string.regenerate_keystore_success)) } - fun resetSelection() = viewModelScope.launch(Dispatchers.Default) { - selectionRepository.reset() + fun resetSelection() = viewModelScope.launch { + withContext(Dispatchers.Default) { selectionRepository.reset() } + app.toast(app.getString(R.string.reset_patch_selection_success)) } fun executeSelectionAction(target: Uri) = viewModelScope.launch { @@ -173,6 +179,7 @@ class ImportExportViewModel( } selectionRepository.import(bundleUid, selection) + app.toast(app.getString(R.string.import_patch_selection_success)) } } @@ -191,6 +198,7 @@ class ImportExportViewModel( Json.Default.encodeToStream(selection, it) } } + app.toast(app.getString(R.string.export_patch_selection_success)) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5b833f6a..4fd92ba9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -81,20 +81,25 @@ Password Import Wrong keystore credentials + Imported keystore Export keystore Export the current keystore No keystore to export + Exported keystore Regenerate keystore Generate a new keystore The keystore has been successfully replaced Import patch selection Import patch selection from a JSON file Could not import patch selection: %s + Imported patch selection Export patch selection - Export patch selection from a JSON file + Export patch selection to a JSON file Could not export patch selection: %s + Exported patch selection Reset patch selection Reset the stored patch selection + Patch selection has been reset Reset patch options for app Resets patch options for a single app Resets patch options for bundle From a22158d070a987b88f375b3808925de84c470897 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Thu, 4 Jul 2024 13:58:55 +0200 Subject: [PATCH 4/6] fix: crash when removing used bundles --- .../app.revanced.manager.data.room.AppDatabase/1.json | 8 ++++---- .../manager/data/room/apps/installed/AppliedPatch.kt | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/schemas/app.revanced.manager.data.room.AppDatabase/1.json b/app/schemas/app.revanced.manager.data.room.AppDatabase/1.json index 0fb6425d..da13c490 100644 --- a/app/schemas/app.revanced.manager.data.room.AppDatabase/1.json +++ b/app/schemas/app.revanced.manager.data.room.AppDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "802fa2fda94b930bf0ebb85d195f1022", + "identityHash": "c0c780e55e10c9b095c004733c846b67", "entities": [ { "tableName": "patch_bundles", @@ -231,7 +231,7 @@ }, { "tableName": "applied_patch", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `bundle` INTEGER NOT NULL, `patch_name` TEXT NOT NULL, PRIMARY KEY(`package_name`, `bundle`, `patch_name`), FOREIGN KEY(`package_name`) REFERENCES `installed_app`(`current_package_name`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`bundle`) REFERENCES `patch_bundles`(`uid`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `bundle` INTEGER NOT NULL, `patch_name` TEXT NOT NULL, PRIMARY KEY(`package_name`, `bundle`, `patch_name`), FOREIGN KEY(`package_name`) REFERENCES `installed_app`(`current_package_name`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`bundle`) REFERENCES `patch_bundles`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "packageName", @@ -285,7 +285,7 @@ }, { "table": "patch_bundles", - "onDelete": "NO ACTION", + "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ "bundle" @@ -407,7 +407,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '802fa2fda94b930bf0ebb85d195f1022')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c0c780e55e10c9b095c004733c846b67')" ] } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/data/room/apps/installed/AppliedPatch.kt b/app/src/main/java/app/revanced/manager/data/room/apps/installed/AppliedPatch.kt index 6feb04ed..d2a498a3 100644 --- a/app/src/main/java/app/revanced/manager/data/room/apps/installed/AppliedPatch.kt +++ b/app/src/main/java/app/revanced/manager/data/room/apps/installed/AppliedPatch.kt @@ -22,7 +22,8 @@ import kotlinx.parcelize.Parcelize ForeignKey( PatchBundleEntity::class, parentColumns = ["uid"], - childColumns = ["bundle"] + childColumns = ["bundle"], + onDelete = ForeignKey.CASCADE ) ], indices = [Index(value = ["bundle"], unique = false)] From ec0a077539b3f2799573bea02626bf698c2daf6e Mon Sep 17 00:00:00 2001 From: Ax333l Date: Thu, 4 Jul 2024 19:34:55 +0200 Subject: [PATCH 5/6] feat: implement more patch option types (#2015) --- app/build.gradle.kts | 3 + .../revanced/manager/data/room/Converters.kt | 8 +- .../manager/data/room/options/Option.kt | 90 ++- .../repository/PatchOptionsRepository.kt | 73 +- .../manager/patcher/patch/PatchInfo.kt | 12 +- .../manager/ui/component/NumberInputDialog.kt | 99 +++ .../ui/component/patches/OptionFields.kt | 763 ++++++++++++++---- .../ui/component/settings/IntegerItem.kt | 49 +- .../ui/screen/PatchesSelectorScreen.kt | 4 +- .../ui/viewmodel/SelectedAppInfoViewModel.kt | 15 +- app/src/main/res/values/plurals.xml | 3 + app/src/main/res/values/strings.xml | 5 + gradle/libs.versions.toml | 4 + 13 files changed, 875 insertions(+), 253 deletions(-) create mode 100644 app/src/main/java/app/revanced/manager/ui/component/NumberInputDialog.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 279a681d..c44dd2da 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -188,6 +188,9 @@ dependencies { // Scrollbars implementation(libs.scrollbars) + // Reorderable lists + implementation(libs.reorderable) + // Compose Icons implementation(libs.compose.icons.fontawesome) } diff --git a/app/src/main/java/app/revanced/manager/data/room/Converters.kt b/app/src/main/java/app/revanced/manager/data/room/Converters.kt index 7de50382..a9437f86 100644 --- a/app/src/main/java/app/revanced/manager/data/room/Converters.kt +++ b/app/src/main/java/app/revanced/manager/data/room/Converters.kt @@ -2,7 +2,7 @@ package app.revanced.manager.data.room import androidx.room.TypeConverter import app.revanced.manager.data.room.bundles.Source -import io.ktor.http.* +import app.revanced.manager.data.room.options.Option.SerializedValue import java.io.File class Converters { @@ -17,4 +17,10 @@ class Converters { @TypeConverter fun fileToString(file: File): String = file.path + + @TypeConverter + fun serializedOptionFromString(value: String) = SerializedValue.fromJsonString(value) + + @TypeConverter + fun serializedOptionToString(value: SerializedValue) = value.toJsonString() } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/data/room/options/Option.kt b/app/src/main/java/app/revanced/manager/data/room/options/Option.kt index 3a70a9a5..b59dbd16 100644 --- a/app/src/main/java/app/revanced/manager/data/room/options/Option.kt +++ b/app/src/main/java/app/revanced/manager/data/room/options/Option.kt @@ -3,6 +3,23 @@ package app.revanced.manager.data.room.options import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey +import app.revanced.manager.patcher.patch.Option +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationException +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.add +import kotlinx.serialization.json.boolean +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.float +import kotlinx.serialization.json.int +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonPrimitive +import kotlinx.serialization.json.long +import kotlin.reflect.KClass @Entity( tableName = "options", @@ -19,5 +36,74 @@ data class Option( @ColumnInfo(name = "patch_name") val patchName: String, @ColumnInfo(name = "key") val key: String, // Encoded as Json. - @ColumnInfo(name = "value") val value: String, -) \ No newline at end of file + @ColumnInfo(name = "value") val value: SerializedValue, +) { + @Serializable + data class SerializedValue(val raw: JsonElement) { + fun toJsonString() = json.encodeToString(raw) + fun deserializeFor(option: Option<*>): Any? { + if (raw is JsonNull) return null + + val errorMessage = "Cannot deserialize value as ${option.type}" + try { + if (option.type.endsWith("Array")) { + val elementType = option.type.removeSuffix("Array") + return raw.jsonArray.map { deserializeBasicType(elementType, it.jsonPrimitive) } + } + + return deserializeBasicType(option.type, raw.jsonPrimitive) + } catch (e: IllegalArgumentException) { + throw SerializationException(errorMessage, e) + } catch (e: IllegalStateException) { + throw SerializationException(errorMessage, e) + } catch (e: kotlinx.serialization.SerializationException) { + throw SerializationException(errorMessage, e) + } + } + + companion object { + private val json = Json { + // Patcher does not forbid the use of these values, so we should support them. + allowSpecialFloatingPointValues = true + } + + private fun deserializeBasicType(type: String, value: JsonPrimitive) = when (type) { + "Boolean" -> value.boolean + "Int" -> value.int + "Long" -> value.long + "Float" -> value.float + "String" -> value.content.also { if (!value.isString) throw SerializationException("Expected value to be a string: $value") } + else -> throw SerializationException("Unknown type: $type") + } + + fun fromJsonString(value: String) = SerializedValue(json.decodeFromString(value)) + fun fromValue(value: Any?) = SerializedValue(when (value) { + null -> JsonNull + is Number -> JsonPrimitive(value) + is Boolean -> JsonPrimitive(value) + is String -> JsonPrimitive(value) + is List<*> -> buildJsonArray { + var elementClass: KClass? = null + + value.forEach { + when (it) { + null -> throw SerializationException("List elements must not be null") + is Number -> add(it) + is Boolean -> add(it) + is String -> add(it) + else -> throw SerializationException("Unknown element type: ${it::class.simpleName}") + } + + if (elementClass == null) elementClass = it::class + else if (elementClass != it::class) throw SerializationException("List elements must have the same type") + } + } + + else -> throw SerializationException("Unknown type: ${value::class.simpleName}") + }) + } + } + + class SerializationException(message: String, cause: Throwable? = null) : + Exception(message, cause) +} diff --git a/app/src/main/java/app/revanced/manager/domain/repository/PatchOptionsRepository.kt b/app/src/main/java/app/revanced/manager/domain/repository/PatchOptionsRepository.kt index 43ca3273..9fe5fdc2 100644 --- a/app/src/main/java/app/revanced/manager/domain/repository/PatchOptionsRepository.kt +++ b/app/src/main/java/app/revanced/manager/domain/repository/PatchOptionsRepository.kt @@ -1,18 +1,14 @@ package app.revanced.manager.domain.repository +import android.util.Log import app.revanced.manager.data.room.AppDatabase import app.revanced.manager.data.room.options.Option import app.revanced.manager.data.room.options.OptionGroup +import app.revanced.manager.patcher.patch.PatchInfo import app.revanced.manager.util.Options +import app.revanced.manager.util.tag import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonNull -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.booleanOrNull -import kotlinx.serialization.json.floatOrNull -import kotlinx.serialization.json.intOrNull class PatchOptionsRepository(db: AppDatabase) { private val dao = db.optionDao() @@ -24,19 +20,37 @@ class PatchOptionsRepository(db: AppDatabase) { packageName = packageName ).also { dao.createOptionGroup(it) }.uid - suspend fun getOptions(packageName: String): Options { + suspend fun getOptions( + packageName: String, + bundlePatches: Map> + ): Options { val options = dao.getOptions(packageName) // Bundle -> Patches return buildMap>>(options.size) { options.forEach { (sourceUid, bundlePatchOptionsList) -> // Patches -> Patch options - this[sourceUid] = bundlePatchOptionsList.fold(mutableMapOf()) { bundlePatchOptions, option -> - val patchOptions = bundlePatchOptions.getOrPut(option.patchName, ::mutableMapOf) + this[sourceUid] = + bundlePatchOptionsList.fold(mutableMapOf()) { bundlePatchOptions, dbOption -> + val deserializedPatchOptions = + bundlePatchOptions.getOrPut(dbOption.patchName, ::mutableMapOf) - patchOptions[option.key] = deserialize(option.value) + val option = + bundlePatches[sourceUid]?.get(dbOption.patchName)?.options?.find { it.key == dbOption.key } + if (option != null) { + try { + deserializedPatchOptions[option.key] = + dbOption.value.deserializeFor(option) + } catch (e: Option.SerializationException) { + Log.w( + tag, + "Option ${dbOption.patchName}:${option.key} could not be deserialized", + e + ) + } + } - bundlePatchOptions - } + bundlePatchOptions + } } } } @@ -47,8 +61,12 @@ class PatchOptionsRepository(db: AppDatabase) { groupId to bundlePatchOptions.flatMap { (patchName, patchOptions) -> patchOptions.mapNotNull { (key, value) -> - val serialized = serialize(value) - ?: return@mapNotNull null // Don't save options that we can't serialize. + val serialized = try { + Option.SerializedValue.fromValue(value) + } catch (e: Option.SerializationException) { + Log.e(tag, "Option $patchName:$key could not be serialized", e) + return@mapNotNull null + } Option(groupId, patchName, key, serialized) } @@ -61,29 +79,4 @@ class PatchOptionsRepository(db: AppDatabase) { suspend fun clearOptionsForPackage(packageName: String) = dao.clearForPackage(packageName) suspend fun clearOptionsForPatchBundle(uid: Int) = dao.clearForPatchBundle(uid) suspend fun reset() = dao.reset() - - private companion object { - fun deserialize(value: String): Any? { - val primitive = Json.decodeFromString(value) - - return when { - primitive.isString -> primitive.content - primitive is JsonNull -> null - else -> primitive.booleanOrNull ?: primitive.intOrNull ?: primitive.floatOrNull - } - } - - fun serialize(value: Any?): String? { - val primitive = when (value) { - null -> JsonNull - is String -> JsonPrimitive(value) - is Int -> JsonPrimitive(value) - is Float -> JsonPrimitive(value) - is Boolean -> JsonPrimitive(value) - else -> return null - } - - return Json.encodeToString(primitive) - } - } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/patcher/patch/PatchInfo.kt b/app/src/main/java/app/revanced/manager/patcher/patch/PatchInfo.kt index 0754756b..31e707ba 100644 --- a/app/src/main/java/app/revanced/manager/patcher/patch/PatchInfo.kt +++ b/app/src/main/java/app/revanced/manager/patcher/patch/PatchInfo.kt @@ -15,7 +15,7 @@ data class PatchInfo( val description: String?, val include: Boolean, val compatiblePackages: ImmutableList?, - val options: ImmutableList