Skip to content

Android TripWrapper Migration Guide

AigarsM edited this page Oct 8, 2026 · 3 revisions

Android TripWrapper — Migration Guide

Moving an existing app from direct SCPSmartDriveManager use onto the TripWrapper facade, or onto the consolidated SDK's ScopeSDK, which offers the same calls. Last updated: 12 Oct 2026 for SmartDrive 1.6.9 / Consolidated SDK 1.1.9.

Table of Contents

  1. Why TripWrapper?
  2. Package installation
  3. New files to add
  4. Application-class changes
  5. Code to delete
  6. Step-by-step checklist
  7. Architecture after migration
  8. FAQ

1. Why TripWrapper?

A direct integration makes the app responsible for things only the SDK can get right: the order in which preferences must be written before the service starts, when the service may run at all, which vehicle to record for, which permissions matter, and how to learn that a trip started, ended or uploaded. Every app re-derived these, and every app got a slightly different subset wrong. The facade encodes them once.

Before vs after

Direct manager TripWrapper
Configuration ~15 preference writes and setter calls, in a required order One typed object, validated at start
Starting Enable autostart, run the service, only after login and permissions, which the app tracked initialize at every process start and start with the settings; the facade arms when session, vehicle and permissions are all present, and after a restart it resumes from the last configuration before the app has its settings again
The vehicle The app wrote the unit serial number into the SDK PortalAPI's announcement is heard by the SDK; an app without PortalAPI passes the vehicle to vehicleSelected; deviceID() on the host for a rule of the app's own
Permissions The app kept its own list requiredPermissions(configuration), also the recording gate
Trip events Broadcast receivers, helper LiveData Four callbacks, TripListener, or tripState
Sending sendAllTrips on resume, a processing-trips check after restart The SDK's worker, resendFailedTrips, retryFailedTrips() for a button, and confirmProcessingTrips { } before a fetched trip list; the SDK also checks on every return to the foreground
Diagnosing Read the log isHealthy(), getMissingProtocols(), a snapshot in the log at start and every trip

The demo app's integration helper went from 492 lines to 150 in this migration, and what remains adapts callbacks to LiveData.

2. Package installation

Either the consolidated SDK, which adds PortalAPI, BLE and Wellness at matching versions:

implementation 'com.scopetechnology:sdk:1.1.9'

or SmartDrive alone, if you ship your own Portal and BLE integrations:

implementation 'com.scopetechnology:smartdrive:1.6.9'
implementation 'com.scopetechnology:shared-classes:1.1.7'

Both require compileSdk 36 or later since these versions; the artifacts' AAR metadata says so, and a lower compileSdk fails the build at checkAarMetadata.

The examples below use TripWrapper; with the consolidated SDK the class is ScopeSDK, the host interface ScopeHost, the configuration ScopeConfiguration, and every call is the same.

3. New files to add

3.1 The host

class AppHost : TripWrapperHost {
    override fun isAuthorized(): Boolean = /* your session check */
}

If your old code chose the vehicle by a rule PortalAPI does not know, that rule becomes the second method:

    override fun deviceID(): String? = /* the unit serial number your rule picks, or null */

and you call TripWrapper.vehicleChanged() when the answer changes. If PortalAPI selects your vehicle, leave deviceID() out: the SDK hears PortalAPI's announcement itself. If your app has no PortalAPI, leave it out too and pass the vehicle the user picks, with its unit type, to TripWrapper.vehicleSelected(unitSerialNumber, unitType); the SDK remembers both across restarts, and the unit type decides how the vehicle records.

3.2 The configuration

Translate your configure routine. Each setting has a field; the mapping table lists them. A configure routine that looked like this:

with(SCPSmartDriveManager.getInstance(context)) {
    setAppName(...); setAppNameAbbreviation(...); setTripActivity(MainActivity::class.java)
    recordTripServiceHandledFromAppClass(true)
    isTripAutoSendingEnabled = true
    resendFailedTrips(true)
    showNotificationForValidTripOnly(true)
    setTripRecordingOnlyWhenBeaconInRange(beaconMode)
}
SmartDriveUtils.setBooleanPreference(context, MHubConstants.DISTRACTION_DETECTION_PREF, true)
SmartDriveUtils.setFloatPreference(context, MHubConstants.DISTRACTION_DETECTION_MINIMUM_SPEED_PREF, 5.0f)
// ... a dozen more preference writes

becomes:

val configuration = TripWrapperConfiguration(
    mode = if (beaconMode) Mode.BEACON else Mode.SMART,
    appName = getString(R.string.app_name),
    appNameAbbreviation = "APP",
    notificationActivity = MainActivity::class.java,
    smartDrive = SmartDriveConfiguration(
        resendFailedTrips = true,
        showNotificationForValidTripOnly = true,
        distractedDrivingEnabled = true,
        distractionMinSpeedKmh = 5,
    ),
)

Anything you wrote that has no typed field yet goes in smartDrive.configureSmartDrive = { manager -> ... }, which receives the manager and runs after every field. Compare your old values with the defaults first: most of what the old routines wrote was the SDK default restated.

4. Application-class changes

override fun onCreate() {
    super.onCreate()
    Timber.plant(/* your tree */)                           // the SDK logs through Timber
    BasePortalApi.init(...)                                  // if you use PortalAPI
    val host = AppHost()
    TripWrapper.initialize(applicationContext, host)        // every process: restores the last accepted configuration
    TripWrapper.start(applicationContext, configuration, host)   // whenever the settings are known or change
}

Then three calls in the places your app already has:

  • TripWrapper.authenticated() after a successful login.
  • TripWrapper.permissionsChanged() once every permission in TripWrapper.requiredPermissions(configuration) is granted.
  • TripWrapper.logout() where you log out.

And, only for an app without PortalAPI, TripWrapper.vehicleSelected(unitSerialNumber, unitType) where the user picks a vehicle.

The order in which launch, login and permission grants happen no longer matters; each call re-checks.

5. Code to delete

The configure routine. Every set... call and preference write that the configuration now covers.

Vehicle seeding. lastSelectedUnitSerialNumber = ... and whatever observed the selected vehicle to keep it in step. PortalAPI announces the vehicle and the SDK listens; an app without PortalAPI passes it to vehicleSelected once, on selection.

Service control. enableTripAutoStart(), runRecordTripService(), disableTripAutoStart(), stopRecordTripService(), and the login and permission checks that guarded them.

Trip event plumbing. Broadcast receivers for trip start and end, the distance broadcast, observers of the manager's live speed, and the helper LiveData that fanned them out. The four callbacks replace all of it, on the main thread.

Send-on-resume. sendAllTrips() in onResume, and the processing-trips check after restarts. Turn on resendFailedTrips and, if you want a button, call retryFailedTrips(). The processing-trips check is the SDK's own since 1.6.8: it runs after each upload and on every return to the foreground, and confirmProcessingTrips { fetchTrips() } runs it before you show a fetched trip list next to the processing count.

The permission list. Your own array of permissions, replaced by requiredPermissions(configuration).

6. Step-by-step checklist

  1. Add the dependency; build.
  2. Write the host. Decide whether deviceID() is yours to answer, or whether vehicleSelected is your call on vehicle selection.
  3. Translate the configure routine into a configuration, comparing each value with the SDK default.
  4. Add initialize and start to Application.onCreate, after Timber and PortalAPI.
  5. Add authenticated(), permissionsChanged() and logout().
  6. Replace the permission list with requiredPermissions(configuration).
  7. Wire the four callbacks where the broadcast receivers were.
  8. Delete everything in section 5. Search for SCPSmartDriveManager and justify every remaining use; the mock activity for testing is a legitimate one.
  9. Run on a device. Read the TRIPWRAPPER HEALTH SNAPSHOT block in the log; it names anything still missing.
  10. Record one trip end to end and confirm onTripSent reports delivery.

7. Architecture after migration

Screens
   │ observe
   ▼
Your helper (optional)   ← the four callbacks turned into LiveData, or nothing at all with Compose
   │
   ▼
TripWrapper / ScopeSDK   ← host, configuration, five calls
   │
   ▼
SmartDrive engine        ← unchanged

Your app no longer imports MHubConstants or SmartDriveUtils, and SCPSmartDriveManager appears at most in the test mock.

8. FAQ

Can I keep calling the manager for something the configuration does not cover? Yes, inside smartDrive.configureSmartDrive. Not from elsewhere: the facade re-applies its configuration and would overwrite a direct write.

My tenant configuration arrives from the backend. Where does it go? Build the TripWrapperConfiguration from it, then start. If it can change while the app runs, build a new configuration and call start again; the facade re-applies what changed, and equal settings change nothing. Call initialize from Application.onCreate as well: until the backend has answered, the SDK records with the configuration it remembered from the last start.

What happens after a reboot, or when the system restarts the recording service? initialize in Application.onCreate restores the configuration the last start accepted and arms recording at once, so nothing waits for your settings fetch; logout forgets it. Before SmartDrive 1.6.6, recording waited for start.

What about TripManagementHelper and its LiveData? Not needed. onTripSent and tripCounts carry what a Pending trips screen showed from it, and since SmartDrive 1.6.7 the counts follow the SDK's own trip deletions and logout as well.

Does stop() replace stopRecordTripService()? Yes. stop() disarms the engine and keeps local trips; authenticated() arms it again. logout() also clears the vehicle and the trips.

Who deletes old trips now? The facade. start() schedules the SDK's daily DeleteOldTripsWorker for smartDrive.tripRetentionDays, 30 by default, under the same unique name apps used when they scheduled it themselves. Delete your own scheduling and pass your retention value in the configuration instead; null cancels the sweep and keeps trips.

The iOS host asks for a username. Where is it on Android? There is none. The Android engine never used it, so the facade does not ask.

Clone this wiki locally