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 and a licence key,
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.1.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. Swap the licence key
Both SDKs read the licence from the manifest at launch, so the shape is familiar — only the key name changes, and BGeo verifies offline, with no call home:
<application> <!-- Before (Transistorsoft) --> <meta-data android:name="com.transistorsoft.locationmanager.license" android:value="YOUR_TRANSISTOR_LICENSE" />
<!-- After (BGeo) --> <meta-data android:name="com.bgeo.license" android:value="BGEO1....YOUR_KEY" /></application>See License keys for evaluation-build rules and error codes — note in particular that BGeo binds a production key to your Play App Signing certificate.
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. - Replace the licence
<meta-data>inAndroidManifest.xml— see License keys. - 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.