fix(better_location): suspend location updates

Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
rhunk 2024-06-05 18:57:21 +02:00
parent a633f913d3
commit 0688c276bb
7 changed files with 55 additions and 90 deletions

View File

@ -255,6 +255,42 @@ class BetterLocationRoot : Routes.Route() {
.fillMaxSize()
.clipToBounds()
) {
item {
@Composable
fun ConfigToggle(
text: String,
state: MutableState<Boolean>,
onCheckedChange: (Boolean) -> Unit
) {
Row(
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = text)
Spacer(modifier = Modifier.weight(1f))
Switch(
checked = state.value,
onCheckedChange = {
state.value = it
onCheckedChange(it)
}
)
}
}
ConfigToggle(
translation["spoof_location_toggle"],
remember { mutableStateOf(context.config.root.global.betterLocation.spoofLocation.get()) }
) {
context.config.root.global.betterLocation.spoofLocation.set(it)
}
ConfigToggle(
translation["suspend_location_updates"],
remember { mutableStateOf(context.config.root.global.betterLocation.suspendLocationUpdates.get()) }
) {
context.config.root.global.betterLocation.suspendLocationUpdates.set(it)
}
}
item {
Row(
modifier = Modifier
@ -271,23 +307,6 @@ class BetterLocationRoot : Routes.Route() {
}
}
}
item {
Row(
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = translation["spoof_location_toggle"])
Spacer(modifier = Modifier.weight(1f))
var isSpoofing by remember { mutableStateOf(context.config.root.global.betterLocation.spoofLocation.get()) }
Switch(
checked = isSpoofing,
onCheckedChange = {
isSpoofing = it
context.config.root.global.betterLocation.spoofLocation.set(it)
}
)
}
}
item {
Row(
modifier = Modifier

View File

@ -150,7 +150,7 @@
"no_files_hint": "Here you can import files for use in Snapchat. Press the button below to import a file."
},
"better_location": {
"spoofed_coordinates_title": "Spoofed Coordinates\nLat {latitude}, Lng {longitude}",
"spoofed_coordinates_title": "Lat {latitude}, Lng {longitude}",
"save_coordinates_dialog_title": "Save Coordinates",
"saved_name_dialog_hint": "Saved Name",
"latitude_dialog_hint": "Latitude",
@ -159,6 +159,7 @@
"choose_location_button": "Choose Location",
"teleport_to_friend_button": "Teleport to Friend",
"spoof_location_toggle": "Spoof Location",
"suspend_location_updates": "Suspend Location Updates",
"saved_coordinates_title": "Saved Coordinates",
"no_saved_coordinates_hint": "No saved coordinates",
"delete_dialog_title": "Delete Saved Coordinate",
@ -778,7 +779,7 @@
},
"suspend_location_updates": {
"name": "Suspend Location Updates",
"description": "Adds a button in map settings to suspend location updates"
"description": "Prevents your location from being updated"
},
"spoof_battery_level": {
"name": "Spoof Battery Level",
@ -1624,9 +1625,6 @@
"bitmoji_scene_changed": "{username} has changed their Bitmoji scene"
},
"suspend_location_updates": {
"switch_text": "Suspend Location Updates"
},
"material3_strings": {
"date_range_picker_start_headline": "From",
"date_range_picker_end_headline": "To",

View File

@ -30,7 +30,6 @@ enum class InternalFileHandleType(
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");

View File

@ -20,11 +20,11 @@ class Global : ConfigContainer() {
}
inner class BetterLocationConfig : ConfigContainer(hasGlobalState = true) {
val spoofLocation = boolean("spoof_location") { requireRestart() }
val spoofLocation = boolean("spoof_location")
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() }
val suspendLocationUpdates = boolean("suspend_location_updates")
val spoofBatteryLevel = string("spoof_battery_level") { requireRestart(); inputCheck = { it.isEmpty() || it.toIntOrNull() in 0..100 } }
val spoofHeadphones = boolean("spoof_headphones") { requireRestart() }
}

View File

@ -107,7 +107,6 @@ class FeatureManager(
MessageIndicators(),
EditTextOverride(),
PreventForcedLogout(),
SuspendLocationUpdates(),
ConversationToolbox(),
SpotlightCommentsUsername(),
OperaViewerParamsOverride(),

View File

@ -26,7 +26,6 @@ import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent
import me.rhunk.snapenhance.core.event.events.impl.UnaryCallEvent
import me.rhunk.snapenhance.core.features.Feature
import me.rhunk.snapenhance.core.features.FeatureLoadParams
import me.rhunk.snapenhance.core.features.impl.global.SuspendLocationUpdates
import me.rhunk.snapenhance.core.util.RandomWalking
import me.rhunk.snapenhance.core.util.hook.HookStage
import me.rhunk.snapenhance.core.util.hook.hook
@ -35,7 +34,6 @@ import me.rhunk.snapenhance.core.util.ktx.isDarkTheme
import me.rhunk.snapenhance.mapper.impl.CallbackMapper
import java.nio.ByteBuffer
import java.util.UUID
import kotlin.time.Duration.Companion.days
data class FriendLocation(
val userId: String,
@ -91,11 +89,10 @@ class BetterLocation : Feature("Better Location", loadParams = FeatureLoadParams
remove(7)
addVarInt(7, System.currentTimeMillis()) // timestamp
}
}
if (context.feature(SuspendLocationUpdates::class).isSuspended()) {
remove(7)
addVarInt(7, System.currentTimeMillis() - 15.days.inWholeMilliseconds)
}
if (context.config.global.betterLocation.suspendLocationUpdates.get()) {
remove(1)
}
// SCVSDeviceData
@ -172,19 +169,18 @@ class BetterLocation : Feature("Better Location", loadParams = FeatureLoadParams
override fun init() {
if (context.config.global.betterLocation.globalState != true) return
if (context.config.global.betterLocation.spoofLocation.get()) {
LocationManager::class.java.apply {
hook("isProviderEnabled", HookStage.BEFORE) { it.setResult(true) }
hook("isProviderEnabledForUser", HookStage.BEFORE) { it.setResult(true) }
}
Location::class.java.apply {
hook("getLatitude", HookStage.BEFORE) { it.setResult(getLat()) }
hook("getLongitude", HookStage.BEFORE) { it.setResult(getLong()) }
}
val canSpoofLocation = { context.config.global.betterLocation.spoofLocation.get() }
LocationManager::class.java.apply {
hook("isProviderEnabled", HookStage.BEFORE, { canSpoofLocation() }) { it.setResult(true) }
hook("isProviderEnabledForUser", HookStage.BEFORE, { canSpoofLocation() }) { it.setResult(true) }
}
Location::class.java.apply {
hook("getLatitude", HookStage.BEFORE, { canSpoofLocation() }) { it.setResult(getLat()) }
hook("getLongitude", HookStage.BEFORE, { canSpoofLocation() }) { it.setResult(getLong()) }
}
val mapFeaturesRootId = context.resources.getId("map_features_root")
val mapLayerSelectorId = context.resources.getId("map_layer_selector")
context.event.subscribe(AddViewEvent::class) { event ->
if (event.view.id != mapFeaturesRootId) return@subscribe
@ -234,10 +230,10 @@ class BetterLocation : Feature("Better Location", loadParams = FeatureLoadParams
context.mappings.useMapper(CallbackMapper::class) {
callbacks.getClass("ServerStreamingEventHandler")?.hook("onEvent", HookStage.BEFORE) { param ->
val buffer = param.arg<ByteBuffer>(1).let {
val buffer = param.argNullable<ByteBuffer>(1)?.let {
it.position(0)
ByteArray(it.capacity()).also { buffer -> it.get(buffer); it.position(0) }
}
} ?: return@hook
onLocationEvent(ProtoReader(buffer))
}
}

View File

@ -1,46 +0,0 @@
package me.rhunk.snapenhance.core.features.impl.global
import android.view.ViewGroup
import android.widget.Switch
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
import me.rhunk.snapenhance.core.ui.ViewAppearanceHelper
import me.rhunk.snapenhance.core.util.ktx.getId
import me.rhunk.snapenhance.core.util.ktx.getLayoutId
class SuspendLocationUpdates : BridgeFileFeature(
"Suspend Location Updates",
loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC, bridgeFileType = InternalFileHandleType.SUSPEND_LOCATION_STATE) {
fun isSuspended() = exists("true")
private fun setSuspended(suspended: Boolean) = setState("true", suspended)
override fun onActivityCreate() {
if (context.config.global.betterLocation.takeIf { it.globalState == true }?.suspendLocationUpdates?.get() != true) return
reload()
val locationSharingSettingsContainerId = context.resources.getLayoutId("v3_screen_location_sharing_settings")
val recyclerViewContainerId = context.resources.getId("recycler_view_container")
context.event.subscribe(LayoutInflateEvent::class) { event ->
if (event.layoutId != locationSharingSettingsContainerId) return@subscribe
val viewGroup = event.view as? ViewGroup ?: return@subscribe
viewGroup.post {
val container = viewGroup.findViewById<ViewGroup>(recyclerViewContainerId)
container.addView(Switch(event.view.context).apply {
isChecked = isSuspended()
ViewAppearanceHelper.applyTheme(this)
text = this@SuspendLocationUpdates.context.translation["suspend_location_updates.switch_text"]
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
setOnCheckedChangeListener { _, isChecked ->
setSuspended(isChecked)
}
})
}
}
}
}