v1.6.3
This commit is contained in:
@@ -77,6 +77,7 @@ import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import me.eternal.purrfectsnap.common.ui.TopBarActionButton
|
||||
import me.eternal.purrfectsnap.common.ui.rememberAsyncMutableStateList
|
||||
import me.eternal.purrfectsnap.core.features.impl.experiments.RandomizedDeviceProfile
|
||||
import me.eternal.purrfectsnap.ui.manager.components.AestheticDialog
|
||||
import me.eternal.purrfectsnap.ui.manager.Routes
|
||||
import me.eternal.purrfectsnap.ui.manager.ManagerTheme
|
||||
@@ -188,7 +189,72 @@ class FeaturesRootSection : Routes.Route() {
|
||||
}
|
||||
|
||||
internal fun isRandomizedProfileActionProperty(propertyName: String): Boolean {
|
||||
return propertyName == "generate_fresh_profile_action" || propertyName == "view_current_profile_action"
|
||||
return propertyName == "generate_fresh_profile_action" ||
|
||||
propertyName == "view_current_profile_action" ||
|
||||
propertyName == "backup_profile_action" ||
|
||||
propertyName == "restore_profile_action"
|
||||
}
|
||||
|
||||
internal fun backupRandomizedProfile(onConfigChanged: () -> Unit) {
|
||||
val profileSnapshot = getRandomizedProfileSnapshot()
|
||||
if (profileSnapshot.startsWith("No generated profile")) {
|
||||
context.shortToast(
|
||||
context.translation["manager.dialogs.randomize_device_profile.empty"]
|
||||
?: "No generated profile is available yet. Enable the feature in Snapchat first."
|
||||
)
|
||||
return
|
||||
}
|
||||
activityLauncher {
|
||||
saveFile("randomized-device-profile.json", "application/json") { uri ->
|
||||
runCatching {
|
||||
context.androidContext.contentResolver.openOutputStream(uri.toUri())?.bufferedWriter()?.use {
|
||||
it.write(profileSnapshot)
|
||||
} ?: error("Failed to open backup destination")
|
||||
onConfigChanged()
|
||||
context.shortToast("Randomized profile backup saved")
|
||||
}.onFailure {
|
||||
context.log.error("Failed to back up randomized profile", it)
|
||||
context.shortToast("Failed to back up randomized profile")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreRandomizedProfile(onConfigChanged: () -> Unit) {
|
||||
activityLauncher {
|
||||
openFile("application/json") { uri ->
|
||||
runCatching {
|
||||
val importedJson = context.androidContext.contentResolver.openInputStream(uri.toUri())
|
||||
?.bufferedReader()
|
||||
?.use { it.readText() }
|
||||
?.trim()
|
||||
?: error("Failed to read randomized profile backup")
|
||||
val profile = RandomizedDeviceProfile.fromJson(importedJson)
|
||||
val generationToken = UUID.randomUUID().toString()
|
||||
context.androidContext.getSharedPreferences("purrfectsnap_spoof", 0)
|
||||
.edit()
|
||||
.putString("randomized_device_profile", profile.toJson().toString())
|
||||
.putString("randomized_device_profile_token", generationToken)
|
||||
.putString("android_id", profile.androidId)
|
||||
.putString("advertising_id", profile.advertisingId)
|
||||
.putString("bluetooth_address", profile.bluetoothMacAddress)
|
||||
.putString("gsf_id", profile.gsfId)
|
||||
.putString("random_device", profile.deviceInfo.model)
|
||||
.putString("device_fingerprint", profile.buildFingerprint)
|
||||
.apply()
|
||||
|
||||
val randomizeConfig = context.config.root.experimental.spoof.randomizeDeviceProfile
|
||||
randomizeConfig.profileGenerationToken.set(generationToken)
|
||||
randomizeConfig.currentProfileSnapshot.set(profile.toJson().toString(2))
|
||||
context.config.writeConfig()
|
||||
onConfigChanged()
|
||||
context.shortToast("Randomized profile restored. Restart Snapchat to apply it.")
|
||||
}.onFailure {
|
||||
context.log.error("Failed to restore randomized profile", it)
|
||||
context.shortToast("Failed to restore randomized profile")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun navigateToMainRoot() {
|
||||
@@ -664,6 +730,8 @@ class FeaturesRootSection : Routes.Route() {
|
||||
val actionLabel = when (property.name) {
|
||||
"generate_fresh_profile_action" -> context.translation[property.key.propertyName()] ?: "Generate Fresh Profile"
|
||||
"view_current_profile_action" -> context.translation[property.key.propertyName()] ?: "View Current Profile"
|
||||
"backup_profile_action" -> context.translation[property.key.propertyName()] ?: "Backup Profile"
|
||||
"restore_profile_action" -> context.translation[property.key.propertyName()] ?: "Restore Profile"
|
||||
else -> property.name
|
||||
}
|
||||
Button(
|
||||
@@ -685,8 +753,12 @@ class FeaturesRootSection : Routes.Route() {
|
||||
?: "Fresh randomized profile requested. Restart Snapchat to apply it."
|
||||
)
|
||||
}
|
||||
} else {
|
||||
} else if (property.name == "view_current_profile_action") {
|
||||
showCurrentRandomProfileDialog = true
|
||||
} else if (property.name == "backup_profile_action") {
|
||||
backupRandomizedProfile(onConfigChanged)
|
||||
} else if (property.name == "restore_profile_action") {
|
||||
restoreRandomizedProfile(onConfigChanged)
|
||||
}
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
|
||||
@@ -33,8 +33,8 @@ tasks.register<GetVersionTask>("getVersion") {
|
||||
}
|
||||
|
||||
// You can still set these for legacy use by submodules or scripts:
|
||||
rootProject.ext.set("appVersionName", providers.gradleProperty("APP_VERSION_NAME").orElse("1.6.2").get())
|
||||
rootProject.ext.set("appVersionCode", providers.gradleProperty("APP_VERSION_CODE").orElse("314").get().toInt())
|
||||
rootProject.ext.set("appVersionName", providers.gradleProperty("APP_VERSION_NAME").orElse("1.6.3").get())
|
||||
rootProject.ext.set("appVersionCode", providers.gradleProperty("APP_VERSION_CODE").orElse("316").get().toInt())
|
||||
rootProject.ext.set("applicationId", "me.eternal.purrfectsnap")
|
||||
// buildHash: when PurrfectSnap or Snapchat is updated, mappings become outdated and auto-regenerate.
|
||||
// Include version code so each release has a different hash; use random for uniqueness within same version.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## v1.6.3
|
||||
- Fix: Blank Global Settings page
|
||||
- New: Backup & Restore option for Randomized Device Profile feature!
|
||||
|
||||
## v1.6.2
|
||||
- New: Randomized Device Profile Feature!
|
||||
- Show Activation Overlay
|
||||
|
||||
@@ -2057,6 +2057,14 @@
|
||||
"view_current_profile_action": {
|
||||
"name": "View Current Profile",
|
||||
"description": "Inspect the latest randomized profile snapshot"
|
||||
},
|
||||
"backup_profile_action": {
|
||||
"name": "Backup Profile",
|
||||
"description": "Export the full randomized profile as a backup file"
|
||||
},
|
||||
"restore_profile_action": {
|
||||
"name": "Restore Profile",
|
||||
"description": "Import and restore a previously backed up randomized profile"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2410,7 +2410,7 @@
|
||||
"name": "Settings Details",
|
||||
"description": "Enable settings spoofing and fine-tune its namespaces",
|
||||
"properties": {
|
||||
"secure": {
|
||||
"secure_settings": {
|
||||
"name": "Secure Settings",
|
||||
"description": "Enable secure settings spoofing and fine-tune secure values",
|
||||
"properties": {
|
||||
@@ -2424,7 +2424,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"system_settings": {
|
||||
"name": "System Settings",
|
||||
"description": "Enable system settings spoofing and fine-tune system values",
|
||||
"properties": {
|
||||
@@ -2438,7 +2438,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"global_settings": {
|
||||
"name": "Global Settings",
|
||||
"description": "Enable global settings spoofing and fine-tune global values",
|
||||
"properties": {
|
||||
@@ -2565,6 +2565,14 @@
|
||||
"view_current_profile_action": {
|
||||
"name": "View Current Profile",
|
||||
"description": "Inspect the latest randomized profile snapshot"
|
||||
},
|
||||
"backup_profile_action": {
|
||||
"name": "Backup Profile",
|
||||
"description": "Export the full randomized profile as a backup file"
|
||||
},
|
||||
"restore_profile_action": {
|
||||
"name": "Restore Profile",
|
||||
"description": "Import and restore a previously backed up randomized profile"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -115,9 +115,9 @@ class Spoof : ConfigContainer(hasGlobalState = true) {
|
||||
}
|
||||
|
||||
inner class RandomizedSettingsConfig : ConfigContainer(hasGlobalState = true) {
|
||||
val secure = container("secure", RandomizedSecureSettingsConfig().apply { globalState = true })
|
||||
val system = container("system", RandomizedSystemSettingsConfig().apply { globalState = true })
|
||||
val global = container("global", RandomizedGlobalSettingsConfig().apply { globalState = true })
|
||||
val secureSettings = container("secure_settings", RandomizedSecureSettingsConfig().apply { globalState = true })
|
||||
val systemSettings = container("system_settings", RandomizedSystemSettingsConfig().apply { globalState = true })
|
||||
val globalSettings = container("global_settings", RandomizedGlobalSettingsConfig().apply { globalState = true })
|
||||
}
|
||||
|
||||
inner class RandomizedWifiConfig : ConfigContainer(hasGlobalState = true) {
|
||||
@@ -178,6 +178,8 @@ class Spoof : ConfigContainer(hasGlobalState = true) {
|
||||
}
|
||||
val generateFreshProfileAction = string("generate_fresh_profile_action")
|
||||
val viewCurrentProfileAction = string("view_current_profile_action")
|
||||
val backupProfileAction = string("backup_profile_action")
|
||||
val restoreProfileAction = string("restore_profile_action")
|
||||
val profileGenerationToken = string("profile_generation_token") {
|
||||
addFlags(ConfigFlag.HIDDEN)
|
||||
}
|
||||
|
||||
@@ -296,9 +296,9 @@ class DeviceSpooferHook : Feature("Device Spoofer") {
|
||||
|
||||
private fun getRandomizedSettingsToggleState(): RandomizedSettingsToggleState {
|
||||
val config = context.config.experimental.spoof.randomizeDeviceProfile.settingsOptions
|
||||
val secure = config.secure
|
||||
val system = config.system
|
||||
val global = config.global
|
||||
val secure = config.secureSettings
|
||||
val system = config.systemSettings
|
||||
val global = config.globalSettings
|
||||
return RandomizedSettingsToggleState(
|
||||
secure = secure.globalState == true && (secure.base.get() || secure.tts.get()),
|
||||
system = system.globalState == true && (system.base.get() || system.bluetooth.get()),
|
||||
|
||||
@@ -7,8 +7,8 @@ org.gradle.configuration-cache=true
|
||||
org.gradle.configuration-cache.problems=warn
|
||||
nativeAbis=arm64-v8a
|
||||
|
||||
APP_VERSION_NAME=1.6.2
|
||||
APP_VERSION_CODE=314
|
||||
APP_VERSION_NAME=1.6.3
|
||||
APP_VERSION_CODE=316
|
||||
debug_build_hash=18fe2a814d0e2eb5
|
||||
psIntegrityPinnedSha256=
|
||||
EXPECTED_CERT_SHA256=0f188cb17d8ea4902ee15ce98f4928ba4226a4790fa3c52f62d776b08e46ca1c
|
||||
|
||||
Reference in New Issue
Block a user