refactor: config
- add config to logs - sensitive data filter
This commit is contained in:
@@ -191,7 +191,21 @@ class LogManager(
|
||||
}
|
||||
|
||||
fun exportLogsToZip(outputStream: OutputStream) {
|
||||
val zipOutputStream = ZipOutputStream(outputStream)
|
||||
val zipOutputStream = ZipOutputStream(outputStream).apply {
|
||||
setMethod(ZipOutputStream.DEFLATED)
|
||||
}
|
||||
|
||||
// add device info to zip
|
||||
zipOutputStream.putNextEntry(ZipEntry("device_info.json"))
|
||||
val gson = GsonBuilder().setPrettyPrinting().create()
|
||||
zipOutputStream.write(gson.toJson(remoteSideContext.installationSummary).toByteArray())
|
||||
zipOutputStream.closeEntry()
|
||||
|
||||
// add config
|
||||
zipOutputStream.putNextEntry(ZipEntry("config.json"))
|
||||
zipOutputStream.write(remoteSideContext.config.exportToString(exportSensitiveData = false).toByteArray())
|
||||
zipOutputStream.closeEntry()
|
||||
|
||||
//add logFolder to zip
|
||||
logFolder.walk().forEach {
|
||||
if (it.isFile) {
|
||||
@@ -201,12 +215,6 @@ class LogManager(
|
||||
}
|
||||
}
|
||||
|
||||
//add device info to zip
|
||||
zipOutputStream.putNextEntry(ZipEntry("device_info.json"))
|
||||
val gson = GsonBuilder().setPrettyPrinting().create()
|
||||
zipOutputStream.write(gson.toJson(remoteSideContext.installationSummary).toByteArray())
|
||||
zipOutputStream.closeEntry()
|
||||
|
||||
zipOutputStream.close()
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,12 @@ class Routes(
|
||||
routes.navController.navigate(replaceArguments(routeInfo.id, HashMap<String, String>().apply { args() }))
|
||||
}
|
||||
|
||||
fun navigateReload() {
|
||||
routes.navController.navigate(routeInfo.id) {
|
||||
popUpTo(routeInfo.id) { inclusive = true }
|
||||
}
|
||||
}
|
||||
|
||||
fun navigateReset(args: MutableMap<String, String>.() -> Unit = {}) {
|
||||
routes.navController.navigate(replaceArguments(routeInfo.id, HashMap<String, String>().apply { args() })) {
|
||||
popUpTo(routes.navController.graph.findStartDestination().id) {
|
||||
|
||||
@@ -529,6 +529,7 @@ class FeaturesRoot : Routes.Route() {
|
||||
|
||||
var showExportDropdownMenu by remember { mutableStateOf(false) }
|
||||
var showResetConfirmationDialog by remember { mutableStateOf(false) }
|
||||
var showExportDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showResetConfirmationDialog) {
|
||||
AlertDialog(
|
||||
@@ -558,19 +559,50 @@ class FeaturesRoot : Routes.Route() {
|
||||
)
|
||||
}
|
||||
|
||||
val actions = remember {
|
||||
mapOf(
|
||||
translation["export_option"] to {
|
||||
activityLauncher {
|
||||
saveFile("config.json", "application/json") { uri ->
|
||||
if (showExportDialog) {
|
||||
fun exportConfig(
|
||||
exportSensitiveData: Boolean
|
||||
) {
|
||||
showExportDialog = false
|
||||
activityLauncher {
|
||||
saveFile("config.json", "application/json") { uri ->
|
||||
runCatching {
|
||||
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
|
||||
context.config.writeConfig()
|
||||
context.config.exportToString().byteInputStream().copyTo(it)
|
||||
context.config.exportToString(exportSensitiveData).byteInputStream().copyTo(it)
|
||||
context.shortToast(translation["config_export_success_toast"])
|
||||
}
|
||||
}.onFailure {
|
||||
context.longToast(translation.format("config_export_failure_toast", "error" to it.message.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
title = { Text(text = context.translation["manager.dialogs.export_config.title"]) },
|
||||
text = { Text(text = context.translation["manager.dialogs.export_config.content"]) },
|
||||
onDismissRequest = { showExportDialog = false },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = { exportConfig(true) }
|
||||
) {
|
||||
Text(text = context.translation["button.positive"])
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(
|
||||
onClick = { exportConfig(false) }
|
||||
) {
|
||||
Text(text = context.translation["button.negative"])
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val actions = remember {
|
||||
mapOf(
|
||||
translation["export_option"] to { showExportDialog = true },
|
||||
translation["import_option"] to {
|
||||
activityLauncher {
|
||||
openFile("application/json") { uri ->
|
||||
@@ -582,6 +614,9 @@ class FeaturesRoot : Routes.Route() {
|
||||
return@use
|
||||
}
|
||||
context.shortToast(translation["config_import_success_toast"])
|
||||
context.coroutineScope.launch(Dispatchers.Main) {
|
||||
navigateReload()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +68,7 @@ class HomeLogs : Routes.Route() {
|
||||
context.coroutineScope.launch {
|
||||
context.log.clearLogs()
|
||||
}
|
||||
routes.navController.navigate(routeInfo.id) {
|
||||
popUpTo(routeInfo.id) { inclusive = true }
|
||||
}
|
||||
navigateReload()
|
||||
showDropDown = false
|
||||
}, text = {
|
||||
Text(translation["clear_logs_button"])
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
"config_export_success_toast": "Config exported successfully",
|
||||
"config_import_success_toast": "Config imported successfully",
|
||||
"config_import_failure_toast": "Failed to import config {error}",
|
||||
"config_export_failure_toast": "Failed to export config {error}",
|
||||
"saved_config_snackbar": "Config saved"
|
||||
},
|
||||
"social": {
|
||||
@@ -159,6 +160,10 @@
|
||||
"content": "Are you sure you want to reset the config?",
|
||||
"success_toast": "Config reset successfully"
|
||||
},
|
||||
"export_config": {
|
||||
"title": "Export Sensitive Data?",
|
||||
"content": "Do you want to export the config with sensitive data? (Such as location coordinates, etc.)"
|
||||
},
|
||||
"messaging_action": {
|
||||
"title": "Choose content types to process",
|
||||
"select_all_button": "Select All"
|
||||
|
||||
@@ -74,10 +74,11 @@ open class ConfigContainer(
|
||||
params: ConfigParamsBuilder = {}
|
||||
) = registerProperty(key, DataProcessors.INT_COLOR, PropertyValue(defaultValue), params)
|
||||
|
||||
fun toJson(): JsonObject {
|
||||
fun toJson(exportSensitiveData: Boolean = true): JsonObject {
|
||||
val json = JsonObject()
|
||||
properties.forEach { (propertyKey, propertyValue) ->
|
||||
val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataType.serializeAny(it) }
|
||||
if (!exportSensitiveData && propertyKey.params.flags.contains(ConfigFlag.SENSITIVE)) return@forEach
|
||||
val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataType.serializeAny(it, exportSensitiveData) }
|
||||
json.add(propertyKey.name, serializedValue)
|
||||
}
|
||||
return json
|
||||
|
||||
@@ -29,7 +29,8 @@ enum class ConfigFlag {
|
||||
USER_IMPORT,
|
||||
NO_DISABLE_KEY,
|
||||
REQUIRE_RESTART,
|
||||
REQUIRE_CLEAN_CACHE;
|
||||
REQUIRE_CLEAN_CACHE,
|
||||
SENSITIVE;
|
||||
|
||||
val id = 1 shl ordinal
|
||||
}
|
||||
|
||||
@@ -22,17 +22,17 @@ object DataProcessors {
|
||||
data class PropertyDataProcessor<T>
|
||||
internal constructor(
|
||||
val type: Type,
|
||||
private val serialize: (T) -> JsonElement,
|
||||
private val serialize: (T, exportSensitiveData: Boolean) -> JsonElement,
|
||||
private val deserialize: (JsonElement) -> T
|
||||
) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun serializeAny(value: Any) = serialize(value as T)
|
||||
fun serializeAny(value: Any, exportSensitiveData: Boolean) = serialize(value as T, exportSensitiveData)
|
||||
fun deserializeAny(value: JsonElement) = deserialize(value)
|
||||
}
|
||||
|
||||
val STRING = PropertyDataProcessor(
|
||||
type = Type.STRING,
|
||||
serialize = {
|
||||
serialize = { it, _ ->
|
||||
if (it != null) JsonPrimitive(it)
|
||||
else JsonNull.INSTANCE
|
||||
},
|
||||
@@ -44,7 +44,7 @@ object DataProcessors {
|
||||
|
||||
val BOOLEAN = PropertyDataProcessor(
|
||||
type = Type.BOOLEAN,
|
||||
serialize = {
|
||||
serialize = { it, _ ->
|
||||
if (it) JsonPrimitive(true)
|
||||
else JsonPrimitive(false)
|
||||
},
|
||||
@@ -53,19 +53,19 @@ object DataProcessors {
|
||||
|
||||
val INTEGER = PropertyDataProcessor(
|
||||
type = Type.INTEGER,
|
||||
serialize = { JsonPrimitive(it) },
|
||||
serialize = { it, _ -> JsonPrimitive(it) },
|
||||
deserialize = { it.asInt },
|
||||
)
|
||||
|
||||
val FLOAT = PropertyDataProcessor(
|
||||
type = Type.FLOAT,
|
||||
serialize = { JsonPrimitive(it) },
|
||||
serialize = { it, _ -> JsonPrimitive(it) },
|
||||
deserialize = { it.asFloat },
|
||||
)
|
||||
|
||||
val STRING_MULTIPLE_SELECTION = PropertyDataProcessor(
|
||||
type = Type.STRING_MULTIPLE_SELECTION,
|
||||
serialize = { JsonArray().apply { it.forEach { add(it) } } },
|
||||
serialize = { it, _ -> JsonArray().apply { it.forEach { add(it) } } },
|
||||
deserialize = { obj ->
|
||||
obj.asJsonArray.map { it.asString }.toMutableList()
|
||||
},
|
||||
@@ -73,13 +73,13 @@ object DataProcessors {
|
||||
|
||||
val STRING_UNIQUE_SELECTION = PropertyDataProcessor(
|
||||
type = Type.STRING_UNIQUE_SELECTION,
|
||||
serialize = { JsonPrimitive(it) },
|
||||
serialize = { it, _ -> JsonPrimitive(it) },
|
||||
deserialize = { obj -> obj.takeIf { !it.isJsonNull }?.asString?.takeIf { it != "false" && it != "true" } }
|
||||
)
|
||||
|
||||
val MAP_COORDINATES = PropertyDataProcessor(
|
||||
type = Type.MAP_COORDINATES,
|
||||
serialize = {
|
||||
serialize = { it, _ ->
|
||||
JsonObject().apply {
|
||||
addProperty("lat", it.first.takeIf { it in -90.0..90.0 } ?: 0.0)
|
||||
addProperty("lng", it.second.takeIf { it in -180.0..180.0 } ?: 0.0)
|
||||
@@ -94,7 +94,7 @@ object DataProcessors {
|
||||
|
||||
val INT_COLOR = PropertyDataProcessor(
|
||||
type = Type.INT_COLOR,
|
||||
serialize = {
|
||||
serialize = { it, _ ->
|
||||
it?.let { JsonPrimitive(it) } ?: JsonNull.INSTANCE
|
||||
},
|
||||
deserialize = { if (it.isJsonNull) null else it.asString.toIntOrNull() },
|
||||
@@ -102,17 +102,17 @@ object DataProcessors {
|
||||
|
||||
fun <T : ConfigContainer> container(container: T) = PropertyDataProcessor(
|
||||
type = Type.CONTAINER,
|
||||
serialize = {
|
||||
serialize = { it, exportSensitiveData ->
|
||||
JsonObject().apply {
|
||||
addProperty("state", it.globalState)
|
||||
add("properties", it.toJson())
|
||||
add("properties", it.toJson(exportSensitiveData))
|
||||
}
|
||||
},
|
||||
deserialize = { obj ->
|
||||
val jsonObject = obj.asJsonObject
|
||||
container.apply {
|
||||
globalState = jsonObject["state"].takeIf { !it.isJsonNull }?.asBoolean
|
||||
fromJson(jsonObject["properties"].asJsonObject)
|
||||
globalState = jsonObject["state"]?.takeIf { !it.isJsonNull }?.asBoolean
|
||||
jsonObject["properties"]?.asJsonObject?.let { fromJson(it) }
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -54,10 +54,12 @@ class ModConfig(
|
||||
root.fromJson(configObject)
|
||||
}
|
||||
|
||||
fun exportToString(): String {
|
||||
val configObject = root.toJson()
|
||||
fun exportToString(
|
||||
exportSensitiveData: Boolean = true
|
||||
): String {
|
||||
val configObject = root.toJson(exportSensitiveData)
|
||||
configObject.addProperty("_locale", locale)
|
||||
return configObject.toString()
|
||||
return gson.toJson(configObject)
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
|
||||
@@ -17,7 +17,7 @@ class DownloaderConfig : ConfigContainer() {
|
||||
val customAudioCodec = string("custom_audio_codec") { addFlags(ConfigFlag.NO_TRANSLATE) }
|
||||
}
|
||||
|
||||
val saveFolder = string("save_folder") { addFlags(ConfigFlag.FOLDER); requireRestart() }
|
||||
val saveFolder = string("save_folder") { addFlags(ConfigFlag.FOLDER, ConfigFlag.SENSITIVE); requireRestart() }
|
||||
val autoDownloadSources = multiple("auto_download_sources",
|
||||
"friend_snaps",
|
||||
"friend_stories",
|
||||
|
||||
@@ -19,9 +19,9 @@ class Global : ConfigContainer() {
|
||||
)
|
||||
}
|
||||
|
||||
inner class BetterLocation : ConfigContainer(hasGlobalState = true) {
|
||||
inner class BetterLocationConfig : ConfigContainer(hasGlobalState = true) {
|
||||
val spoofLocation = boolean("spoof_location") { requireRestart() }
|
||||
val coordinates = mapCoordinates("coordinates", 0.0 to 0.0) // lat, long
|
||||
val coordinates = mapCoordinates("coordinates", 0.0 to 0.0) { addFlags(ConfigFlag.SENSITIVE) } // lat, long
|
||||
val walkRadius = string("walk_radius") { requireRestart(); inputCheck = { it.toDoubleOrNull()?.isFinite() == true && it.toDouble() >= 0.0 } }
|
||||
val alwaysUpdateLocation = boolean("always_update_location") { requireRestart() }
|
||||
val suspendLocationUpdates = boolean("suspend_location_updates") { requireRestart() }
|
||||
@@ -35,7 +35,7 @@ class Global : ConfigContainer() {
|
||||
val customUploadImageFormat = unique("custom_image_upload_format", "jpeg", "png", "webp") { requireRestart(); addFlags(ConfigFlag.NO_TRANSLATE) }
|
||||
}
|
||||
|
||||
val betterLocation = container("better_location", BetterLocation())
|
||||
val betterLocation = container("better_location", BetterLocationConfig())
|
||||
val snapchatPlus = boolean("snapchat_plus") { requireRestart() }
|
||||
val mediaUploadQualityConfig = container("media_upload_quality", MediaUploadQualityConfig())
|
||||
val disableConfirmationDialogs = multiple("disable_confirmation_dialogs", "erase_message", "remove_friend", "block_friend", "ignore_friend", "hide_friend", "hide_conversation", "clear_conversation") { requireRestart() }
|
||||
|
||||
@@ -5,7 +5,7 @@ import me.rhunk.snapenhance.common.config.ConfigFlag
|
||||
|
||||
class Scripting : ConfigContainer() {
|
||||
val developerMode = boolean("developer_mode", false) { requireRestart() }
|
||||
val moduleFolder = string("module_folder", "modules") { addFlags(ConfigFlag.FOLDER); requireRestart() }
|
||||
val moduleFolder = string("module_folder", "modules") { addFlags(ConfigFlag.FOLDER, ConfigFlag.SENSITIVE); requireRestart() }
|
||||
val autoReload = unique("auto_reload", "snapchat_only", "all")
|
||||
val integratedUI = boolean("integrated_ui", false) { requireRestart() }
|
||||
val disableLogAnonymization = boolean("disable_log_anonymization", false) { requireRestart() }
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package me.rhunk.snapenhance.common.config.impl
|
||||
|
||||
import me.rhunk.snapenhance.common.config.ConfigContainer
|
||||
import me.rhunk.snapenhance.common.config.ConfigFlag
|
||||
|
||||
class Spoof : ConfigContainer(hasGlobalState = true) {
|
||||
val overridePlayStoreInstallerPackageName = boolean("play_store_installer_package_name")
|
||||
val fingerprint = string("fingerprint")
|
||||
val androidId = string("android_id")
|
||||
val fingerprint = string("fingerprint") { addFlags(ConfigFlag.SENSITIVE) }
|
||||
val androidId = string("android_id") { addFlags(ConfigFlag.SENSITIVE) }
|
||||
val removeVpnTransportFlag = boolean("remove_vpn_transport_flag")
|
||||
val removeMockLocationFlag = boolean("remove_mock_location_flag")
|
||||
val randomizePersistentDeviceToken = boolean("randomize_persistent_device_token")
|
||||
|
||||
@@ -89,7 +89,6 @@ class BetterLocation : Feature("Better Location", loadParams = FeatureLoadParams
|
||||
editor.apply {
|
||||
// SCVSLocationUpdate
|
||||
edit(1) {
|
||||
context.log.verbose("SCVSLocationUpdate ${this@apply}")
|
||||
if (config.spoofLocation.get()) {
|
||||
remove(1)
|
||||
remove(2)
|
||||
|
||||
Reference in New Issue
Block a user