Migrating from Transistorsoft
BGeo’s API is intentionally shaped like Transistorsoft’s commercial
tslocationmanager: the same method names, the same config keys, the same
event vocabulary. A migration is mostly a dependency swap, not a redesign.
This guide describes the shape of the move rather than a specific version’s exact imports — substitute whatever your current dependency and package names are.
1. Swap the dependency
dependencies { // Before // implementation("com.transistorsoft:tslocationmanager:+")
// After implementation("dev.bgeo:background-geolocation:0.3.0")}Transistorsoft ships from its own Maven repository, so you can usually delete
that maven { url = ... } block from settings.gradle.kts as well — BGeo is
on Maven Central.
2. Move initialisation into Application.onCreate
This is the one structural change worth doing carefully:
class MyApplication : Application() { override fun onCreate() { super.onCreate() BackgroundGeolocation.attach(this) }}attach() wires the engine to
the process for the process’s whole lifetime, which is what lets a
system-restarted process (boot, geofence, service) deliver events to your
listeners without any headless registration.
3. Drop the licence key
BGeo is free and needs no license key. Delete the
com.transistorsoft.locationmanager.license <meta-data> from
AndroidManifest.xml and add nothing in its place.
4. Adapt to coroutines
Transistorsoft’s Android API is callback- and Future-based; BGeo’s is
suspend functions and Flows:
// Before, roughlyBackgroundGeolocation.ready(config) { state -> if (!state.enabled) BackgroundGeolocation.start() }
// AfterlifecycleScope.launch { val state = BackgroundGeolocation.ready(config) if (!state.enabled) BackgroundGeolocation.start()}Every event also has a callback form (onLocation { } returning a
Subscription) if you would rather not restructure call sites at once — see
Events.
Config and method compatibility
Most Transistor config keys and methods behave identically — see the Config reference and Methods reference for exact Kotlin types and defaults. What maps directly (unchanged names and shapes, adapted only to the coroutine idiom):
- Lifecycle:
ready,setConfig,start,stop,getState,changePace - Positioning:
getCurrentPosition,watchPosition,stopWatchPosition - Events:
onLocation,onMotionChange,onHeartbeat,onProviderChange,onHttp,onConnectivityChange,onGeofence,onGeofencesChange— each returns aSubscription, and each also exists as aFlow - Permissions:
requestPermission,getProviderState,isPowerSaveMode/onPowerSaveChange - Odometer:
getOdometer,setOdometer,resetOdometer - Upload queue:
sync,getLocations,destroyLocations,getCount,destroyLocation,insertLocation - Geofences:
addGeofence,addGeofences,removeGeofence,removeGeofences,getGeofences,geofenceExists— see the Geofencing guide - Logging:
logger.error/warn/info/debug/verbose,getLog,destroyLog,uploadLog— see the Logging & debugging guide - HTTP config:
url,method,headers,params,extras,httpRootProperty,autoSync,autoSyncThreshold,disableAutoSyncOnCellular,batchSync,maxBatchSize,maxRecordsToPersist - Motion/filter config:
distanceFilter,stopTimeout,stationaryRadius,heartbeatInterval,desiredAccuracy,locationFilterPolicy,kalmanProfile authorization(JWT refresh, now a typedAuthorizationConfig) — see the HTTP guide’s authorization section- The
notificationsub-keys (title,text,channelId,channelName,smallIcon,color,priority), now a typedNotificationConfig
The location object shape is unchanged (coords, timestamp, isMoving,
activity, battery, odometer, uuid, extras) — see
Data types. Field names are camelCase
Kotlin properties over the same snake_case wire payload (is_moving on the
wire, isMoving on Location), a decoding convenience the raw payload doesn’t
need to make since JS already uses whichever casing the wire sends.
A handful of keys are accepted for API compatibility but currently do
nothing. Don’t rely on foregroundService or backgroundPermissionRationale,
and note that debug only plays sound cues — see
Limitations — accepted-but-no-op config keys
before you build around any of them.
API parity: what’s not here
BGeo deliberately does not implement the following part of Transistorsoft’s surface — an SDK-level scoping decision independent of which language binding sits on top, so it applies here exactly as it does on any other BGeo binding:
| Transistor API / config | Alternative in BGeo |
|---|---|
schedule / startSchedule / scheduleUseAlarmManager | Run your own scheduler (WorkManager, or an AlarmManager alarm) that calls start()/stop(). |
locationTemplate / geofenceTemplate | Shape the upload body with httpRootProperty/params/extras instead of a template string — see Shaping the body. |
transistorAuthorizationToken | N/A (Transistorsoft-account specific) — use authorization for your own backend. |
Custom notification layout / actions / strings | The supported Notification fields (title/text/channelId/channelName/smallIcon/color/priority) cover a fixed layout, not custom actions or arbitrary strings. |
emailLog | uploadLog() to your own logUrl endpoint instead of emailing a log file. |
useSignificantChangesOnly | No equivalent mode — the engine manages its own wake sources; see Tracking lifecycle — wake sources. |
stopOnStationary / stopAfterElapsedMinutes | Call stop() yourself from onMotionChange/onHeartbeat. |
persistMode | N/A — all tracked locations persist by default; a one-shot getCurrentPosition() fix opts in via its persist option instead. |
timestampFormat | N/A — timestamps are always ISO-8601 UTC strings, see Location.timestamp. |
locationsOrderDirection | N/A — the persisted queue is always oldest-first, see Data pipeline — persistence. |
Burst averaging (rollingWindow/burstWindow/maxBurstDistance), an onLocationFilter-style callback, kalmanDebug/filterDebug | Unbuilt — these are unknown keys, silently stored but ignored by the native config dict. The implemented filter surface is locationFilterPolicy, kalmanProfile, and odometerAccuracyThreshold — use those instead of per-fix burst averaging or a filter-decision callback. |
reset() | Call setConfig() with the values you want restored — see the Config reference for the documented default of each key. |
startGeofences() (geofence-only tracking mode) | start() already runs geofences alongside location tracking — there’s no separate mode. |
getLocations() pagination / SQLQuery | getLocations() returns the full queue, oldest-first, as a List<Location>; filter or paginate it yourself. A large offline queue decodes in one go, so read it off the main thread. |
startBackgroundTask() / stopBackgroundTask() | N/A. |
getDeviceInfo() / getSensors() | N/A. |
Behavioral differences worth knowing
- No legacy failure-callback quirk. Some Transistorsoft bindings carry a
quirk where supplying a
failurecallback makesready()/start()resolve instead of failing. This API has no callback parameters at all: a failed call throws aBGeoException, full stop. There is nothing to migrate beyond deleting old failure handlers. sync()semantics are unchanged. It returns aList<Location>snapshot of the queue taken before the drain starts, not what remains afterwards. Seesync().- No headless registration. Transistorsoft’s Android binding for React
Native and Flutter needs a headless task because their runtimes die with the
app. A native app does not: Android restarts your process and calls
Application.onCreate, soattach()there is the whole mechanism. Delete the headless plumbing rather than porting it — see Boot & killed-app behaviour. - The engine is not the same engine. BGeo’s tracking, filtering and upload
are its own implementation with its own defaults; a config that was tuned
against Transistorsoft’s behaviour is a starting point, not a guarantee of
identical output. Re-check
distanceFilter,stopTimeoutandstationaryRadiusagainst a real drive before shipping.
Steps
- Swap the dependency to
dev.bgeo:background-geolocation; drop the Transistorsoft Maven repository fromsettings.gradle.kts. - Move initialisation into
Application.onCreateand callattach()there. - Delete the Transistorsoft licence
<meta-data>fromAndroidManifest.xml— BGeo needs no key. - Re-point imports at
com.bgeo.sdk; add imports for the extension functions (geofences, queue, logger). - Wrap
ready()/start()/stop()call sites in a coroutine, or keep the callback forms for events while you migrate. - Delete headless-task registration.
- Drive the route you care about and compare point density before deleting the old integration.