Feature:Routs **⚠️ AI-GENERATED — REVIEW REQUIRED**#45
Open
ro011110ot wants to merge 11 commits into
Open
Conversation
… add stale-delta guard; translate all comments to English
…elta stagnation Location getters (getLatitude, getLongitude, etc.) are hooked individually and called in rapid succession by the target app (microseconds apart). Without a minimum interval, every advance() call sees deltaSeconds ~ 0.000001, making progress per call ~1e-10 of a segment — route appears stuck. Add MIN_ADVANCE_INTERVAL_NANOS = 100ms so only one advance per window fires with a real time delta. Also fix stale-delta guard on stop->restart to reinitialize position properly.
…terval - RoutePlayer.logger was never set in ModuleEntry, so all debug output was silently dropped. Now wired so logs appear in target app logcat. - advance() now skips calls with <100ms delta to prevent micro-second stagnation from rapid Location getter invocations. - Stale-delta guard (>2s) reinitializes position instead of jumping.
…ypoint Markers - Add RouteMapView composable: embedded osmdroid mini-map in RouteDetailScreen showing the route as a blue Polyline connecting waypoints, with numbered Markers at each waypoint - Add HandleActiveRouteOverlay effect: draws active route on the main MapScreen when route playback is active, auto-zooms to fit bounding box - Wire activeRouteWaypoints through MapUiState, MapViewModel (collected from repository when isPlaying changes), MapViewContainer, and MapScreen - Fix all remaining German comments to English across the codebase: - Route.kt: translate KDoc to English - RouteDetailScreen.kt: translate inline comments - MapViewModel.kt: translate KDoc, inline comments, and waypoint name strings - MapDialogs.kt: translate AddToRouteDialog KDoc - Rename 'Wegpunkt' waypoint name strings to 'Waypoint' for consistency
…-time position tracking Flickering fix: - Remove early return in RouteMapView when waypoints empty — always render the map - MapView now stays stable across recompositions, no layout shifts on data load Numbered markers: - Add RouteMapUtils.kt with createNumberedMarkerDrawable() — generates bitmap markers with a blue circle and white number overlay - Update RouteMapView.kt and HandleActiveRouteOverlay to use numbered markers instead of default Marker icons - Add createCurrentPositionDrawable() for the live position dot Real-time position tracking: - Add KEY_CURRENT_ROUTE_LAT/KEY_CURRENT_ROUTE_LON constants in Constants.kt - Add getCurrentRouteLat()/getCurrentRouteLon() to PrefrencesRepository (reads from remote prefs written by RoutePlayer) - RoutePlayer.persistCurrentPosition() now writes computed lat/lon to remote prefs every timer tick (every 500ms); clearCurrentPosition() resets on stop - MapViewModel init now polls every 1500ms for current route position when isPlaying - Add HandleCurrentRoutePosition composable in MapViewEffects.kt — green animated dot at the current route playback position on the main map - Wire currentRoutePosition through MapUiState, MapViewContainer, and MapScreen All code and comments are in English.
…ff main thread - Guard HandleActiveRouteOverlay zoomToBoundingBox with view-size check to avoid infinite loop in Projection.getCloserPixel() on 0x0 layout - Add flowOn(Dispatchers.IO) to getIsPlayingFlow and getLastClickedLocationFlow collections so LSPosed IPC runs off main - Wrap getActiveRouteWaypoints, getCurrentRouteLat/Lon reads in withContext(IO) to prevent main-thread blocking
…sition advancement Bug 1 — AddToRouteDialog: when user types a new route name, selectedRouteName was not cleared, causing confirmAddToRoute() to use the previously selected route instead of the typed name. Fix: onNewRouteNameChange now clears selectedRouteName, onRouteSelectionChange clears newRouteNameInput. Bug 2 — Route playback position never changed in target apps because system-level hooks (SystemServicesHooks in system_server) and the per-app getLastKnownLocation() hook called LocationUtil.createFakeLocation() without first calling updateLocation(). They read LocationUtil.latitude from their own process memory, which RoutePlayer never updates (it runs only in the target app's process). Fix: updateLocation() is now called inside createFakeLocation() so every fake location creation uses the up-to-date position regardless of which process calls it.
…ing German comments - Add lastUsedRouteName field to MapViewModel that tracks the most recently created or selected route name - showAddToRouteDialog() now pre-selects lastUsedRouteName instead of always picking the first route in the list - onRouteSelectionChange() records the user's choice as lastUsedRouteName - confirmAddToRoute() records the target route after a successful save - Remove debug Log statements added during diagnosis - Translate remaining German inline comments in RouteDetailScreen.kt (Geschwindigkeit -> Playback speed, Schleifen-Modus -> Loop mode, Dialog vom MapScreen aus -> trigger dialog from MapScreen)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds a complete Route Management system to XposedFakeLocation — users can
create, edit, delete, and playback routes (sequences of waypoints that the fake location
follows automatically). It also fixes two bugs found during development (dialog selection
conflict and stale-position delivery to target apps) and addresses UI flickering on the
route minimap.
Changes
New Feature — Route Management
RouteandRouteWaypointclasses, stored viaPreferencesRepositoryRoutesScreen.kt+RoutesViewModel.kt): list all savedroutes, swipe-to-delete, tap to open details
RouteDetailScreen.kt+RouteDetailViewModel.kt):RouteMapView.kt)MapDialogs.kt + MapViewModel.kt): add the current spoofmarker location as a waypoint to an existing route or create a new route
MapViewEffects.kt): when a route is playing,the main map shows numbered markers and a polyline for the active route; a
movable position marker follows the current playback progress
RoutePlayer.kt): interpolates position between waypointsusing Haversine distance, runs as a scheduled timer in the Xposed target process,
writes current lat/lon directly into
LocationUtilfieldsLocationApiHooks.kt):getLatitude()andgetLongitude()hooks callLocationUtil.updateLocation(), which reads theroute player's computed position
manager polls every 2s to update the real-time indicator
Bug Fixes
AddToRouteDialog selection conflict (
MapViewModel.kt):onNewRouteNameChangenow clearsselectedRouteNameso the dialog correctlytargets the new-route input
onRouteSelectionChangeclearsnewRouteNameInputwhen an existing route ispicked, preventing a stale blank input from overriding the selection
Stale position delivery to target apps (
LocationUtil.kt):createFakeLocation()now callsupdateLocation()internally before buildingthe
LocationobjectgetLatitude()/getLongitude()inLocationApiHooks.kt) only calledupdateLocation()themselves, butLocationManager.getLastKnownLocation()and other APIs that callcreateFakeLocation()directly received stale0.0 / 0.0coordinatesRoutePlayer.computeAndSetPosition()writes intoLocationUtilfields, but
createFakeLocation()never refreshed themLocationuse the most recentroute position
Route minimap flickering (
RouteMapView.kt):LaunchedEffect(waypoints)waited for theMapViewto be laid out beforedrawing (using
withFrameNanospolling untilwidth > 0 && height > 0)zoomToBoundingBoxanimation disabled (false+postto main thread)overlays.clear()+ animatedzoom ran before the view had its final size
Technical Improvements
MapViewEffects.kt): guard allzoomToBoundingBoxcalls withmapView.width > 0 && mapView.height > 0; movedPreferencesRepositoryIPCoff the main thread with
Dispatchers.IORoutePlayer.kt): added minimum advance interval guard toprevent micro-delta stagnation; removed stale
lastUpdateTimereset that causedposition resets
values-de/strings.xmlwith 219 translated stringsvalues-zh/strings.xmlwith 21 translated stringscomments in
RouteDetailScreen.ktwere translatedTesting
without flicker; playback starts/stops correctly
[RoutePlayer]logcat output showing increasing
elapsed/dist/lat/lon values);createFakeLocation()delivers the correct position to all hook sitesKnown Limitations
Follow-up Work
feat(routes): complete route management system with playback engine
This commit adds a full Route Management feature — users can create, edit,
delete, and playback sequences of waypoints on a minimap and on the main map.
New files (14):
active route waypoints, playback speed/loop/state, current position)
Modified files (6):
LocationManager.getLastKnownLocation()) use the current route position
onRouteSelectionChange clears newRouteNameInput
availableRouteNames, selectedRouteName, newRouteNameInput
i18n:
Bug fixes bundled:
the Location so system-level hooks get the current route position
All inline comments are in English per project convention.
3. Route Feature Overview (for project discussion / documentation)
Route Management Feature — Overview
What it does
Routes let users define a sequence of GPS waypoints. When playback is active,
the Xposed module automatically moves the device's fake location along the
route, interpolating between waypoints at a configurable speed.
Architecture
Data Layer
Route(data/model/Route.kt): holds a name and a list ofRouteWaypointsRouteWaypoint(data/model/RouteWaypoint.kt): lat/lon, name, sort orderPreferencesRepository(data/repository/PrefrencesRepository.kt):Routes are serialized as JSON via Gson and stored in
SharedPreferences.The active-route waypoints, playback speed, loop flag, and is-playing state
are mirrored into a second preferences file accessible from the Xposed
target process (see
PreferencesUtil.kt).Manager App (UI)
All composables live under
manager/ui/routes/:RoutesScreen.ktRoutesViewModel.ktRouteDetailScreen.ktRouteDetailViewModel.ktRouteMapView.ktMapDialogs.kt(AddToRouteDialog)MapViewModel.ktMapViewEffects.ktNavigation:
NavGraph.ktregistersScreen.RouteDetail; the drawer inDrawer.kthas a "Routes" entry. All map interactions (center, zoom, add-to-route)share the existing
MapViewModel.Xposed Module (Target Process)
RoutePlayer.kt(xposed/utils/): singleton object that:loadActiveRoute(): reads waypoints, speed, loop flag from the remotepreferences (the same
SharedPreferencesthe manager writes to)computeAndSetPosition()which interpolates along theroute and writes
LocationUtil.latitude/LocationUtil.longitudedirectlyLocationUtil.kt(xposed/utils/):updateLocation()now callsRoutePlayer.loadActiveRoute()first, so arunning route always takes precedence over the manual spoof location
createFakeLocation()callsupdateLocation()— this is the critical fixthat makes
LocationManager.getLastKnownLocation()(and similar APIs usedby apps like Pokémon GO) return the route position instead of
0.0 / 0.0LocationApiHooks.kt(xposed/hooks/):Location.getLatitude()andLocation.getLongitude()at the class-loader level
updateLocation()and returnsLocationUtil.latitude/LocationUtil.longitudewhenisPlaying == truegetAccuracy(),getAltitude(), and provider-based methodsMain-Map Overlay
MapViewEffects.kthas anHandleActiveRouteOverlayeffect that:uiState.activeRouteWaypointsanduiState.currentRoutePositionMapViewKey Design Decisions
Two-process communication via SharedPreferences (not Binder/AIDL):
package context available to the Xposed hook
RoutePlayer writes directly to LocationUtil fields:
@Volatileon the sharedisActiveflagInterpolation, not waypoint snapping:
on lat/lon (fine for short segments <1 km)
interpolation itself
Known Limitations & Risks
stops. The user must restart playback from the manager.
concurrent edits could theoretically lose data. Rare in practice because the
manager mostly reads and the module mostly writes.
(1000+ waypoints) may cause jank during serialization.
getLastKnownLocation()not hooked: onlygetLatitude()/getLongitude()on an existing
Locationobject are hooked. Apps that callLocationManager.getLastKnownLocation()receive a newly-constructedLocationfrom
createFakeLocation(), which now works after theupdateLocation()fix.could add per-segment speed metadata.
What to Review
RoutePlayer.kt: timer lifecycle, thread safety, edge cases when theroute has 1 or 0 waypoints, loop wrap-around
LocationUtil.createFakeLocation(): theupdateLocation()call changesbehavior for all users, not just route users. Ensure manual spoof still works.
MapViewEffects.kt: the overlay effect runs as aSideEffectthatcaptures
mapViewby reference. Verify it doesn't leak across configurationchanges.
PrefrencesRepository.kt: the Gson serialization is fragile ifRouteor
RouteWaypointfields are renamed. Consider a migration strategy.sensitive data leaks through this channel.
strings.xml: new strings added invalues/strings.xml,values-de/strings.xml,and
values-zh/strings.xml. Other locales will show the English fallback for