diff --git a/.kotlin/sessions/kotlin-compiler-9309063494563612695.salive b/.kotlin/sessions/kotlin-compiler-9309063494563612695.salive new file mode 100644 index 00000000..e69de29b diff --git a/common/src/main/assets/lang/ar_AE.json b/common/src/main/assets/lang/ar_AE.json index 4ae79ff1..07aa3905 100644 --- a/common/src/main/assets/lang/ar_AE.json +++ b/common/src/main/assets/lang/ar_AE.json @@ -1155,6 +1155,16 @@ "settings_menu": { "name": "قائمة الإعدادات", "description": "اختر بين تخطيطات قائمة الإعدادات الجديدة والقديمة" + }, + "spoof_snap_score": { + "name": "تزييف نقاط سناب شات", + "description": "تزييف نقاط السناب شات الخاصة بك (محلياً فقط)", + "properties": { + "custom_snap_score": { + "name": "نقاط مخصصة", + "description": "يتم عرض هذا الرقم كنقاط السناب شات" + } + } } } }, diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json index acc95142..3a0964db 100644 --- a/common/src/main/assets/lang/en_US.json +++ b/common/src/main/assets/lang/en_US.json @@ -1196,6 +1196,16 @@ "settings_menu": { "name": "Settings Menu", "description": "Choose between the new and legacy settings menu layouts" + }, + "spoof_snap_score": { + "name": "Spoof Snap Score", + "description": "Spoof your Snap Score (locally)", + "properties": { + "custom_snap_score": { + "name": "Custom Snap Score", + "description": "The custom Snap Score to fake" + } + } } } }, diff --git a/common/src/main/kotlin/me/eternal/purrfectsnap/common/config/impl/UserInterfaceTweaks.kt b/common/src/main/kotlin/me/eternal/purrfectsnap/common/config/impl/UserInterfaceTweaks.kt index 263fb8b8..f04362e6 100644 --- a/common/src/main/kotlin/me/eternal/purrfectsnap/common/config/impl/UserInterfaceTweaks.kt +++ b/common/src/main/kotlin/me/eternal/purrfectsnap/common/config/impl/UserInterfaceTweaks.kt @@ -62,4 +62,19 @@ class UserInterfaceTweaks : ConfigContainer() { } val preventForcedKeyboard = boolean("prevent_forced_keyboard") { requireRestart() } val settingsMenu = unique("settings_menu", "default", "legacy") { requireRestart() }.apply { set("default") } + + inner class SpoofSnapScore : ConfigContainer(hasGlobalState = true) { + val customSnapScore = string("custom_snap_score") { + requireRestart() + inputCheck = { input -> + if (input.isEmpty()) true + else { + val digits = input.replace(Regex("[^0-9]"), "") + digits.isNotEmpty() && digits.length <= 7 && digits.toLong() <= 9999999L + } + } + } + } + + val spoofSnapScore = container("spoof_snap_score", SpoofSnapScore()) { requireRestart() } } diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/FeatureManager.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/FeatureManager.kt index 5093b81a..09022b0c 100644 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/FeatureManager.kt +++ b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/FeatureManager.kt @@ -159,6 +159,7 @@ class FeatureManager( PreventForcedKeyboard(), CustomTheming(), HideTypingIndicator(), + FakeSnapScore(), ) features.values.toList().forEach { feature -> diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/ui/FakeSnapScore.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/ui/FakeSnapScore.kt new file mode 100644 index 00000000..83400897 --- /dev/null +++ b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/ui/FakeSnapScore.kt @@ -0,0 +1,54 @@ +package me.eternal.purrfectsnap.core.features.impl.ui + +import me.eternal.purrfectsnap.core.features.Feature +import me.eternal.purrfectsnap.core.util.hook.HookStage +import me.eternal.purrfectsnap.core.util.hook.hook + +class FakeSnapScore : Feature("Fake Snap Score") { + override fun init() { + if (context.config.userInterface.spoofSnapScore.globalState != true) return + + val customScoreRaw = context.config.userInterface.spoofSnapScore.customSnapScore.getNullable()?.trim()?.takeIf { it.isNotBlank() } + ?: return + + val customScore = try { + val digitsOnly = customScoreRaw.replace(Regex("[^0-9]"), "") + if (digitsOnly.isNotEmpty()) { + val clampedVal = digitsOnly.toLong().coerceAtMost(9999999L) + java.text.NumberFormat.getNumberInstance(java.util.Locale.US).format(clampedVal) + } else { + null + } + } catch (e: Exception) { + null + } ?: return + + onNextActivityCreate { + android.widget.TextView::class.java.hook("setText", HookStage.BEFORE) { param -> + val text = param.argNullable(0)?.toString() ?: return@hook + + if (text.matches(Regex("^[0-9\\s,.]+$"))) { + val digits = text.replace(Regex("[^0-9]"), "") + + if (digits.length >= 4 || text.contains(",")) { + var parent = (param.thisObject() as android.widget.TextView).parent + var isProfile = false + + while (parent != null) { + val className = parent.javaClass.simpleName.lowercase() + if (className.contains("profile") || className.contains("identity")) { + isProfile = true + break + } + parent = parent.parent + } + + if (isProfile) { + param.setArg(0, customScore) + } + } + } + } + } + } +}