From d03f64a62a5b3ff3c8b46799175f6ef7b3abf81a Mon Sep 17 00:00:00 2001
From: auth <64337177+authorisation@users.noreply.github.com>
Date: Mon, 29 May 2023 17:36:12 +0200
Subject: [PATCH] feat: location spoof (#9)
* feat: location spoof
---------
Co-authored-by: rhunk <101876869+rhunk@users.noreply.github.com>
---
app/build.gradle | 1 +
app/src/main/AndroidManifest.xml | 5 +-
app/src/main/assets/lang/en_US.json | 9 ++-
.../snapenhance/action/AbstractAction.kt | 4 +-
.../rhunk/snapenhance/action/impl/OpenMap.kt | 23 ++++++
.../snapenhance/config/ConfigCategory.kt | 1 +
.../snapenhance/config/ConfigProperty.kt | 19 +++++
.../features/impl/extras/LocationSpoofer.kt | 64 ++++++++++++++++
.../features/impl/ui/menus/MapActivity.kt | 73 +++++++++++++++++++
.../impl/ui/menus/impl/SettingsMenu.kt | 26 ++++---
.../snapenhance/manager/impl/ActionManager.kt | 2 +
.../manager/impl/FeatureManager.kt | 3 +
app/src/main/res/layout/map.xml | 25 +++++++
13 files changed, 242 insertions(+), 13 deletions(-)
create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt
create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/LocationSpoofer.kt
create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/MapActivity.kt
create mode 100644 app/src/main/res/layout/map.xml
diff --git a/app/build.gradle b/app/build.gradle
index 40b2e7ac..b3f8a75d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -143,4 +143,5 @@ dependencies {
compileOnly files('libs/LSPosed-api-1.0-SNAPSHOT.jar')
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.arthenica:ffmpeg-kit-full-gpl:5.1.LTS'
+ implementation 'org.osmdroid:osmdroid-android:6.1.16'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index bfcfc9a0..ddd607ea 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -41,7 +41,10 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/assets/lang/en_US.json b/app/src/main/assets/lang/en_US.json
index 303e092d..29dd2243 100644
--- a/app/src/main/assets/lang/en_US.json
+++ b/app/src/main/assets/lang/en_US.json
@@ -7,6 +7,7 @@
"ui": "UI",
"extras": "Extras",
"tweaks": "Tweaks",
+ "location_spoof": "Location Spoof",
"experimental": "Experimental",
"debugging": "Debugging"
},
@@ -14,7 +15,8 @@
"action": {
"clean_cache": "Clean Cache",
"clear_message_logger": "Clear Message Logger",
- "refresh_mappings": "Refresh Mappings"
+ "refresh_mappings": "Refresh Mappings",
+ "open_map": "Pick a location on the map"
},
"property": {
@@ -57,7 +59,10 @@
"use_download_manager": "Use Android Download Manager",
"app_passcode": "Set App Passcode",
"app_lock_on_resume": "App Lock On Resume",
- "meo_passcode_bypass": "My Eyes Only Passcode Bypass"
+ "meo_passcode_bypass": "My Eyes Only Passcode Bypass",
+ "location_spoof": "Snapmap Location Spoofer",
+ "latitude_value": "Latitude",
+ "longitude_value": "Longitude"
},
"option": {
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt b/app/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt
index 6026691c..4bc21cc0 100644
--- a/app/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt
@@ -1,10 +1,12 @@
package me.rhunk.snapenhance.action
import me.rhunk.snapenhance.ModContext
+import me.rhunk.snapenhance.config.ConfigProperty
import java.io.File
abstract class AbstractAction(
- val nameKey: String
+ val nameKey: String,
+ val dependsOnProperty: ConfigProperty? = null,
) {
lateinit var context: ModContext
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt b/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt
new file mode 100644
index 00000000..5a89fdaf
--- /dev/null
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt
@@ -0,0 +1,23 @@
+package me.rhunk.snapenhance.action.impl
+
+import android.content.Intent
+import android.os.Bundle
+import me.rhunk.snapenhance.BuildConfig
+import me.rhunk.snapenhance.action.AbstractAction
+import me.rhunk.snapenhance.config.ConfigProperty
+import me.rhunk.snapenhance.features.impl.ui.menus.MapActivity
+
+class OpenMap: AbstractAction("action.open_map", dependsOnProperty = ConfigProperty.LOCATION_SPOOF) {
+ override fun run() {
+ context.runOnUiThread {
+ val mapActivityIntent = Intent()
+ mapActivityIntent.setClassName(BuildConfig.APPLICATION_ID, MapActivity::class.java.name)
+ mapActivityIntent.putExtra("location", Bundle().apply {
+ putDouble("latitude", context.config.string(ConfigProperty.LATITUDE).toDouble())
+ putDouble("longitude", context.config.string(ConfigProperty.LONGITUDE).toDouble())
+ })
+
+ context.mainActivity!!.startActivityForResult(mapActivityIntent, 0x1337)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt b/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt
index be067b15..6b6e2bf4 100644
--- a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt
@@ -10,5 +10,6 @@ enum class ConfigCategory(
UI("category.ui"),
EXTRAS("category.extras"),
TWEAKS("category.tweaks"),
+ LOCATION_SPOOF("category.location_spoof"),
EXPERIMENTAL("category.experimental");
}
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt b/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt
index f9445460..e610b6b6 100644
--- a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt
@@ -213,6 +213,25 @@ enum class ConfigProperty(
),
NEW_MAP_UI("property.new_map_ui", "description.new_map_ui", ConfigCategory.TWEAKS, ConfigStateValue(false)),
+ LOCATION_SPOOF(
+ "property.location_spoof",
+ "description.location_spoof",
+ ConfigCategory.LOCATION_SPOOF,
+ ConfigStateValue(false)
+ ),
+ LATITUDE(
+ "property.latitude_value",
+ "description.latitude_value",
+ ConfigCategory.LOCATION_SPOOF,
+ ConfigStringValue("0.0000")
+ ),
+ LONGITUDE(
+ "property.longitude_value",
+ "description.longitude_value",
+ ConfigCategory.LOCATION_SPOOF,
+ ConfigStringValue("0.0000")
+ ),
+
USE_DOWNLOAD_MANAGER(
"property.use_download_manager",
"description.use_download_manager",
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/LocationSpoofer.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/LocationSpoofer.kt
new file mode 100644
index 00000000..1b2ebeb0
--- /dev/null
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/LocationSpoofer.kt
@@ -0,0 +1,64 @@
+package me.rhunk.snapenhance.features.impl.extras
+
+import android.content.Intent
+import me.rhunk.snapenhance.config.ConfigProperty
+import me.rhunk.snapenhance.features.Feature
+import me.rhunk.snapenhance.features.FeatureLoadParams
+import me.rhunk.snapenhance.hook.HookStage
+import me.rhunk.snapenhance.hook.Hooker
+
+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(2) ?: return@hook
+ val bundle = intent.getBundleExtra("location") ?: return@hook
+ param.setResult(null)
+ val latitude = bundle.getFloat("latitude")
+ val longitude = bundle.getFloat("longitude")
+
+ with(context.config) {
+ get(ConfigProperty.LATITUDE).read(latitude.toString())
+ get(ConfigProperty.LONGITUDE).read(longitude.toString())
+ writeConfig()
+ }
+ context.longToast("Location set to $latitude, $longitude")
+ }
+
+ if (!context.config.bool(ConfigProperty.LOCATION_SPOOF)) return
+ val locationClass = android.location.Location::class.java
+ val locationManagerClass = android.location.LocationManager::class.java
+
+ Hooker.hook(locationClass, "getLatitude", HookStage.BEFORE) { hookAdapter ->
+ hookAdapter.setResult(getLatitude())
+ }
+
+ Hooker.hook(locationClass, "getLongitude", HookStage.BEFORE) { hookAdapter ->
+ hookAdapter.setResult(getLongitude())
+ }
+
+ Hooker.hook(locationClass, "getAccuracy", HookStage.BEFORE) { hookAdapter ->
+ hookAdapter.setResult(getAccuracy())
+ }
+
+ //Might be redundant because it calls isProviderEnabledForUser which we also hook, meaning if isProviderEnabledForUser returns true this will also return true
+ Hooker.hook(locationManagerClass, "isProviderEnabled", HookStage.BEFORE) { hookAdapter ->
+ hookAdapter.setResult(true)
+ }
+
+ Hooker.hook(locationManagerClass, "isProviderEnabledForUser", HookStage.BEFORE) {hookAdapter ->
+ hookAdapter.setResult(true)
+ }
+ }
+
+ private fun getLatitude():Double {
+ return context.config.string(ConfigProperty.LATITUDE).toDouble()
+ }
+
+ private fun getLongitude():Double {
+ return context.config.string(ConfigProperty.LONGITUDE).toDouble()
+ }
+
+ private fun getAccuracy():Float {
+ return 0.0f
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/MapActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/MapActivity.kt
new file mode 100644
index 00000000..d06a7618
--- /dev/null
+++ b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/menus/MapActivity.kt
@@ -0,0 +1,73 @@
+package me.rhunk.snapenhance.features.impl.ui.menus
+
+import android.app.Activity
+import android.content.Context
+import android.os.Bundle
+import android.view.MotionEvent
+import android.widget.Button
+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
+
+
+//TODO: Implement correctly
+class MapActivity : Activity() {
+
+ private lateinit var mapView: MapView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val contextBundle = intent.extras?.getBundle("location") ?: return
+ val latitude = contextBundle.getDouble("latitude")
+ val longitude = 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(latitude, longitude)
+ 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