mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-28 04:20:20 +02:00
refactor: location spoofer
This commit is contained in:
parent
1a7755e45c
commit
32a458a690
@ -52,10 +52,6 @@
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:excludeFromRecents="true" />
|
||||
<activity
|
||||
android:name=".ui.MapActivity"
|
||||
android:exported="true"
|
||||
android:excludeFromRecents="true" />
|
||||
<activity android:name=".bridge.ForceStartActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:excludeFromRecents="true"
|
||||
|
@ -1,98 +0,0 @@
|
||||
package me.rhunk.snapenhance.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.MotionEvent
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import me.rhunk.snapenhance.R
|
||||
import org.osmdroid.config.Configuration
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.Projection
|
||||
import org.osmdroid.views.overlay.Marker
|
||||
import org.osmdroid.views.overlay.Overlay
|
||||
|
||||
|
||||
class MapActivity : Activity() {
|
||||
|
||||
private lateinit var mapView: MapView
|
||||
|
||||
@SuppressLint("MissingInflatedId", "ResourceType")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val contextBundle = intent.extras?.getBundle("location") ?: return
|
||||
val locationLatitude = contextBundle.getDouble("latitude")
|
||||
val locationLongitude = contextBundle.getDouble("longitude")
|
||||
|
||||
Configuration.getInstance().load(applicationContext, getSharedPreferences("osmdroid", Context.MODE_PRIVATE))
|
||||
|
||||
setContentView(R.layout.map)
|
||||
|
||||
mapView = findViewById(R.id.mapView)
|
||||
mapView.setMultiTouchControls(true);
|
||||
mapView.setTileSource(TileSourceFactory.MAPNIK)
|
||||
|
||||
val startPoint = GeoPoint(locationLatitude, locationLongitude)
|
||||
mapView.controller.setZoom(10.0)
|
||||
mapView.controller.setCenter(startPoint)
|
||||
|
||||
val marker = Marker(mapView)
|
||||
marker.isDraggable = true
|
||||
marker.position = startPoint
|
||||
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
|
||||
|
||||
mapView.overlays.add(object: Overlay() {
|
||||
override fun onSingleTapConfirmed(e: MotionEvent?, mapView: MapView?): Boolean {
|
||||
val proj: Projection = mapView!!.projection
|
||||
val loc = proj.fromPixels(e!!.x.toInt(), e.y.toInt()) as GeoPoint
|
||||
marker.position = loc
|
||||
mapView.invalidate()
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
mapView.overlays.add(marker)
|
||||
|
||||
val applyButton = findViewById<Button>(R.id.apply_location_button)
|
||||
applyButton.setOnClickListener {
|
||||
val bundle = Bundle()
|
||||
bundle.putFloat("latitude", marker.position.latitude.toFloat())
|
||||
bundle.putFloat("longitude", marker.position.longitude.toFloat())
|
||||
setResult(RESULT_OK, intent.putExtra("location", bundle))
|
||||
finish()
|
||||
}
|
||||
|
||||
val setPreciseLocationButton = findViewById<Button>(R.id.set_precise_location_button)
|
||||
|
||||
setPreciseLocationButton.setOnClickListener {
|
||||
val locationDialog = layoutInflater.inflate(R.layout.precise_location_dialog, null)
|
||||
val dialogLatitude = locationDialog.findViewById<EditText>(R.id.dialog_latitude).also { it.setText(marker.position.latitude.toString()) }
|
||||
val dialogLongitude = locationDialog.findViewById<EditText>(R.id.dialog_longitude).also { it.setText(marker.position.longitude.toString()) }
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setView(locationDialog)
|
||||
.setTitle("Set a precise location")
|
||||
.setPositiveButton("Set") { _, _ ->
|
||||
val latitude = dialogLatitude.text.toString().toDoubleOrNull()
|
||||
val longitude = dialogLongitude.text.toString().toDoubleOrNull()
|
||||
if (latitude != null && longitude != null) {
|
||||
val preciseLocation = GeoPoint(latitude, longitude)
|
||||
mapView.controller.setCenter(preciseLocation)
|
||||
marker.position = preciseLocation
|
||||
mapView.invalidate()
|
||||
}
|
||||
}.setNegativeButton("Cancel") { _, _ -> }.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mapView.onDetach()
|
||||
}
|
||||
}
|
@ -149,7 +149,10 @@ class FeaturesSection : Section() {
|
||||
|
||||
if (showDialog) {
|
||||
Dialog(
|
||||
onDismissRequest = { showDialog = false }
|
||||
properties = DialogProperties(
|
||||
usePlatformDefaultWidth = false
|
||||
),
|
||||
onDismissRequest = { showDialog = false },
|
||||
) {
|
||||
dialogComposable()
|
||||
}
|
||||
@ -182,6 +185,24 @@ class FeaturesSection : Section() {
|
||||
)
|
||||
}
|
||||
|
||||
DataProcessors.Type.MAP_COORDINATES -> {
|
||||
registerDialogOnClickCallback()
|
||||
dialogComposable = {
|
||||
alertDialogs.ChooseLocationDialog(property) {
|
||||
showDialog = false
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.widthIn(0.dp, 120.dp),
|
||||
text = (propertyValue.get() as Pair<*, *>).let {
|
||||
"${it.first.toString().toFloatOrNull() ?: 0F}, ${it.second.toString().toFloatOrNull() ?: 0F}"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
DataProcessors.Type.STRING_UNIQUE_SELECTION -> {
|
||||
registerDialogOnClickCallback()
|
||||
|
||||
|
@ -1,41 +1,52 @@
|
||||
package me.rhunk.snapenhance.ui.util
|
||||
|
||||
import android.content.Context
|
||||
import android.view.MotionEvent
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper
|
||||
import me.rhunk.snapenhance.common.config.DataProcessors
|
||||
import me.rhunk.snapenhance.common.config.PropertyPair
|
||||
import org.osmdroid.config.Configuration
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.CustomZoomButtonsController
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.Marker
|
||||
import org.osmdroid.views.overlay.Overlay
|
||||
|
||||
|
||||
class AlertDialogs(
|
||||
private val translation: LocaleWrapper,
|
||||
){
|
||||
@Composable
|
||||
fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) {
|
||||
fun DefaultDialogCard(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
|
||||
Card(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
modifier = Modifier
|
||||
.padding(10.dp, 5.dp, 10.dp, 10.dp),
|
||||
.padding(10.dp, 5.dp, 10.dp, 10.dp)
|
||||
.then(modifier),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@ -195,7 +206,9 @@ class AlertDialogs(
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.padding(top = 10.dp).fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
Button(onClick = { dismiss() }) {
|
||||
@ -252,7 +265,9 @@ class AlertDialogs(
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.padding(top = 10.dp).fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.padding(top = 10.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
Button(onClick = { onDismiss() }) {
|
||||
@ -305,4 +320,145 @@ class AlertDialogs(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChooseLocationDialog(property: PropertyPair<*>, dismiss: () -> Unit = {}) {
|
||||
val coordinates = remember {
|
||||
(property.value.get() as Pair<*, *>).let {
|
||||
it.first.toString().toDouble() to it.second.toString().toDouble()
|
||||
}
|
||||
}
|
||||
val context = LocalContext.current
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
Configuration.getInstance().load(context, context.getSharedPreferences("osmdroid", Context.MODE_PRIVATE))
|
||||
}
|
||||
|
||||
var marker by remember { mutableStateOf<Marker?>(null) }
|
||||
val mapView = remember {
|
||||
MapView(context).apply {
|
||||
setMultiTouchControls(true)
|
||||
zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER)
|
||||
setTileSource(TileSourceFactory.MAPNIK)
|
||||
|
||||
val startPoint = GeoPoint(coordinates.first, coordinates.second)
|
||||
controller.setZoom(10.0)
|
||||
controller.setCenter(startPoint)
|
||||
|
||||
marker = Marker(this).apply {
|
||||
isDraggable = true
|
||||
position = startPoint
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
|
||||
}
|
||||
|
||||
overlays.add(object: Overlay() {
|
||||
override fun onSingleTapConfirmed(e: MotionEvent, mapView: MapView): Boolean {
|
||||
marker?.position = mapView.projection.fromPixels(e.x.toInt(), e.y.toInt()) as GeoPoint
|
||||
mapView.invalidate()
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
overlays.add(marker)
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
mapView.onDetach()
|
||||
}
|
||||
}
|
||||
|
||||
var customCoordinatesDialog by remember { mutableStateOf(false) }
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().fillMaxHeight(fraction = 0.9f),
|
||||
) {
|
||||
AndroidView(
|
||||
factory = { mapView }
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(10.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
) {
|
||||
FilledIconButton(
|
||||
onClick = {
|
||||
val lat = marker?.position?.latitude ?: coordinates.first
|
||||
val lon = marker?.position?.longitude ?: coordinates.second
|
||||
property.value.setAny(lat to lon)
|
||||
dismiss()
|
||||
}) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(60.dp)
|
||||
.padding(5.dp),
|
||||
imageVector = Icons.Filled.Check,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
FilledIconButton(
|
||||
onClick = {
|
||||
customCoordinatesDialog = true
|
||||
}) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(60.dp)
|
||||
.padding(5.dp),
|
||||
imageVector = Icons.Filled.Edit,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (customCoordinatesDialog) {
|
||||
val lat = remember { mutableStateOf(coordinates.first.toString()) }
|
||||
val lon = remember { mutableStateOf(coordinates.second.toString()) }
|
||||
|
||||
DefaultDialogCard(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
) {
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 10.dp),
|
||||
value = lat.value,
|
||||
onValueChange = { lat.value = it },
|
||||
label = { Text(text = "Latitude") },
|
||||
singleLine = true
|
||||
)
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 10.dp),
|
||||
value = lon.value,
|
||||
onValueChange = { lon.value = it },
|
||||
label = { Text(text = "Longitude") },
|
||||
singleLine = true
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
Button(onClick = {
|
||||
customCoordinatesDialog = false
|
||||
}) {
|
||||
Text(text = translation["button.cancel"])
|
||||
}
|
||||
|
||||
Button(onClick = {
|
||||
marker?.position = GeoPoint(lat.value.toDouble(), lon.value.toDouble())
|
||||
mapView.controller.setCenter(marker?.position)
|
||||
mapView.invalidate()
|
||||
customCoordinatesDialog = false
|
||||
}) {
|
||||
Text(text = translation["button.ok"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<org.osmdroid.views.MapView
|
||||
android:id="@+id/mapView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
</org.osmdroid.views.MapView>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/set_precise_location_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="left"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:padding="10dp"
|
||||
android:background="@android:color/white"
|
||||
android:text="Set Precise Location"
|
||||
android:textSize="20sp"
|
||||
tools:ignore="HardcodedText,RtlHardcoded" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/apply_location_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:background="@android:color/white"
|
||||
android:text="Apply"
|
||||
android:textSize="20sp"
|
||||
tools:ignore="HardcodedText,RtlHardcoded" />
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="20dp"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/dialog_latitude"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints=""
|
||||
android:ems="10"
|
||||
android:hint="Latitude"
|
||||
android:inputType="number|numberDecimal|numberSigned" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/dialog_longitude"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints=""
|
||||
android:ems="10"
|
||||
android:hint="Longitude"
|
||||
android:inputType="number|numberDecimal|numberSigned" />
|
||||
|
||||
</LinearLayout>
|
@ -356,6 +356,16 @@
|
||||
"name": "Global",
|
||||
"description": "Tweak Global Snapchat Settings",
|
||||
"properties": {
|
||||
"spoofLocation": {
|
||||
"name": "Location",
|
||||
"description": "Spoof your location",
|
||||
"properties": {
|
||||
"coordinates": {
|
||||
"name": "Coordinates",
|
||||
"description": "Set the coordinates"
|
||||
}
|
||||
}
|
||||
},
|
||||
"snapchat_plus": {
|
||||
"name": "Snapchat Plus",
|
||||
"description": "Enables Snapchat Plus features\nSome Server-sided features may not work"
|
||||
@ -464,20 +474,6 @@
|
||||
"name": "Spoof",
|
||||
"description": "Spoof various information about you",
|
||||
"properties": {
|
||||
"location": {
|
||||
"name": "Location",
|
||||
"description": "Spoof your location",
|
||||
"properties": {
|
||||
"location_latitude": {
|
||||
"name": "Latitude",
|
||||
"description": "The latitude of the location"
|
||||
},
|
||||
"location_longitude": {
|
||||
"name": "Longitude",
|
||||
"description": "The longitude of the location"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"name": "Device",
|
||||
"description": "Spoof your device information",
|
||||
|
@ -8,8 +8,7 @@ enum class EnumAction(
|
||||
val isCritical: Boolean = false,
|
||||
) {
|
||||
CLEAN_CACHE("clean_snapchat_cache", exitOnFinish = true),
|
||||
EXPORT_CHAT_MESSAGES("export_chat_messages"),
|
||||
OPEN_MAP("open_map");
|
||||
EXPORT_CHAT_MESSAGES("export_chat_messages");
|
||||
|
||||
companion object {
|
||||
const val ACTION_PARAMETER = "se_action"
|
||||
|
@ -60,6 +60,12 @@ open class ConfigContainer(
|
||||
container.parentContainerKey = it
|
||||
}.get()
|
||||
|
||||
protected fun mapCoordinates(
|
||||
key: String,
|
||||
defaultValue: Pair<Double, Double> = 0.0 to 0.0,
|
||||
params: ConfigParamsBuilder = {}
|
||||
) = registerProperty(key, DataProcessors.MAP_COORDINATES, PropertyValue(defaultValue), params)
|
||||
|
||||
fun toJson(): JsonObject {
|
||||
val json = JsonObject()
|
||||
properties.forEach { (propertyKey, propertyValue) ->
|
||||
|
@ -14,6 +14,7 @@ object DataProcessors {
|
||||
FLOAT,
|
||||
STRING_MULTIPLE_SELECTION,
|
||||
STRING_UNIQUE_SELECTION,
|
||||
MAP_COORDINATES,
|
||||
CONTAINER,
|
||||
}
|
||||
|
||||
@ -75,6 +76,20 @@ object DataProcessors {
|
||||
deserialize = { obj -> obj.takeIf { !it.isJsonNull }?.asString }
|
||||
)
|
||||
|
||||
val MAP_COORDINATES = PropertyDataProcessor(
|
||||
type = Type.MAP_COORDINATES,
|
||||
serialize = {
|
||||
JsonObject().apply {
|
||||
addProperty("lat", it.first)
|
||||
addProperty("lng", it.second)
|
||||
}
|
||||
},
|
||||
deserialize = { obj ->
|
||||
val jsonObject = obj.asJsonObject
|
||||
jsonObject["lat"].asDouble to jsonObject["lng"].asDouble
|
||||
},
|
||||
)
|
||||
|
||||
fun <T : ConfigContainer> container(container: T) = PropertyDataProcessor(
|
||||
type = Type.CONTAINER,
|
||||
serialize = {
|
||||
|
@ -4,6 +4,10 @@ import me.rhunk.snapenhance.common.config.ConfigContainer
|
||||
import me.rhunk.snapenhance.common.config.FeatureNotice
|
||||
|
||||
class Global : ConfigContainer() {
|
||||
inner class SpoofLocation : ConfigContainer(hasGlobalState = true) {
|
||||
val coordinates = mapCoordinates("coordinates", 0.0 to 0.0) { requireRestart()} // lat, long
|
||||
}
|
||||
val spoofLocation = container("spoofLocation", SpoofLocation())
|
||||
val snapchatPlus = boolean("snapchat_plus") { addNotices(FeatureNotice.BAN_RISK); requireRestart() }
|
||||
val disableMetrics = boolean("disable_metrics")
|
||||
val blockAds = boolean("block_ads")
|
||||
|
@ -4,12 +4,6 @@ import me.rhunk.snapenhance.common.config.ConfigContainer
|
||||
import me.rhunk.snapenhance.common.config.FeatureNotice
|
||||
|
||||
class Spoof : ConfigContainer() {
|
||||
inner class Location : ConfigContainer(hasGlobalState = true) {
|
||||
val latitude = float("location_latitude")
|
||||
val longitude = float("location_longitude")
|
||||
}
|
||||
val location = container("location", Location())
|
||||
|
||||
inner class Device : ConfigContainer(hasGlobalState = true) {
|
||||
val fingerprint = string("fingerprint")
|
||||
val androidId = string("android_id")
|
||||
|
@ -1,21 +0,0 @@
|
||||
package me.rhunk.snapenhance.core.action.impl
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import me.rhunk.snapenhance.common.BuildConfig
|
||||
import me.rhunk.snapenhance.core.action.AbstractAction
|
||||
|
||||
class OpenMap: AbstractAction() {
|
||||
override fun run() {
|
||||
context.runOnUiThread {
|
||||
val mapActivityIntent = Intent()
|
||||
mapActivityIntent.setClassName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".ui.MapActivity")
|
||||
mapActivityIntent.putExtra("location", Bundle().apply {
|
||||
putDouble("latitude", context.config.experimental.spoof.location.latitude.get().toDouble())
|
||||
putDouble("longitude", context.config.experimental.spoof.location.longitude.get().toDouble())
|
||||
})
|
||||
|
||||
context.mainActivity!!.startActivityForResult(mapActivityIntent, 0x1337)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +1,28 @@
|
||||
package me.rhunk.snapenhance.core.features.impl.global
|
||||
|
||||
import android.content.Intent
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import me.rhunk.snapenhance.core.features.Feature
|
||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||
import me.rhunk.snapenhance.core.util.hook.Hooker
|
||||
import me.rhunk.snapenhance.core.util.hook.hook
|
||||
|
||||
class LocationSpoofer: Feature("LocationSpoof", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) {
|
||||
override fun asyncOnActivityCreate() {
|
||||
Hooker.hook(context.mainActivity!!.javaClass, "onActivityResult", HookStage.BEFORE) { param ->
|
||||
val intent = param.argNullable<Intent>(2) ?: return@hook
|
||||
val bundle = intent.getBundleExtra("location") ?: return@hook
|
||||
param.setResult(null)
|
||||
class LocationSpoofer: Feature("LocationSpoof", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
|
||||
override fun onActivityCreate() {
|
||||
if (context.config.global.spoofLocation.globalState != true) return
|
||||
|
||||
with(context.config.experimental.spoof.location) {
|
||||
latitude.set(bundle.getFloat("latitude"))
|
||||
longitude.set(bundle.getFloat("longitude"))
|
||||
val coordinates by context.config.global.spoofLocation.coordinates
|
||||
|
||||
context.longToast("Location set to $latitude, $longitude")
|
||||
}
|
||||
Location::class.java.apply {
|
||||
hook("getLatitude", HookStage.BEFORE) { it.setResult(coordinates.first) }
|
||||
hook("getLongitude", HookStage.BEFORE) { it.setResult(coordinates.second) }
|
||||
hook("getAccuracy", HookStage.BEFORE) { it.setResult(0.0F) }
|
||||
}
|
||||
|
||||
if (context.config.experimental.spoof.location.globalState != true) return
|
||||
|
||||
val latitude by context.config.experimental.spoof.location.latitude
|
||||
val longitude by context.config.experimental.spoof.location.longitude
|
||||
|
||||
val locationClass = android.location.Location::class.java
|
||||
val locationManagerClass = android.location.LocationManager::class.java
|
||||
|
||||
locationClass.hook("getLatitude", HookStage.BEFORE) { it.setResult(latitude.toDouble()) }
|
||||
locationClass.hook("getLongitude", HookStage.BEFORE) { it.setResult(longitude.toDouble()) }
|
||||
locationClass.hook("getAccuracy", HookStage.BEFORE) { it.setResult(0.0F) }
|
||||
|
||||
//Might be redundant because it calls isProviderEnabledForUser which we also hook, meaning if isProviderEnabledForUser returns true this will also return true
|
||||
locationManagerClass.hook("isProviderEnabled", HookStage.BEFORE) { it.setResult(true) }
|
||||
locationManagerClass.hook("isProviderEnabledForUser", HookStage.BEFORE) { it.setResult(true) }
|
||||
LocationManager::class.java.apply {
|
||||
//Might be redundant because it calls isProviderEnabledForUser which we also hook, meaning if isProviderEnabledForUser returns true this will also return true
|
||||
hook("isProviderEnabled", HookStage.BEFORE) { it.setResult(true) }
|
||||
hook("isProviderEnabledForUser", HookStage.BEFORE) { it.setResult(true) }
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ import me.rhunk.snapenhance.common.action.EnumAction
|
||||
import me.rhunk.snapenhance.core.ModContext
|
||||
import me.rhunk.snapenhance.core.action.impl.CleanCache
|
||||
import me.rhunk.snapenhance.core.action.impl.ExportChatMessages
|
||||
import me.rhunk.snapenhance.core.action.impl.OpenMap
|
||||
import me.rhunk.snapenhance.core.manager.Manager
|
||||
|
||||
class ActionManager(
|
||||
@ -16,7 +15,6 @@ class ActionManager(
|
||||
mapOf(
|
||||
EnumAction.CLEAN_CACHE to CleanCache::class,
|
||||
EnumAction.EXPORT_CHAT_MESSAGES to ExportChatMessages::class,
|
||||
EnumAction.OPEN_MAP to OpenMap::class,
|
||||
).map {
|
||||
it.key to it.value.java.getConstructor().newInstance().apply {
|
||||
this.context = modContext
|
||||
|
Loading…
x
Reference in New Issue
Block a user