Skip to main content

Get Started

Setting up the React Native Tracking SDK on Android has six parts: the Maven repository and dependency, backend configuration in BuildConfig, the Kotlin bridge, manifest and runtime permissions, the TypeScript wrapper, and a native rebuild.

Android only

These steps cover Android. iOS support for the React Native Tracking SDK is coming soon.


Prerequisites and requirements​

  • React Native project with an editable native android/ folder and a configured Android development environment.
  • Maven repository credentials for the Tracking SDK artifact - see Contact us to request access.
  • Tracking backend configuration (base URLs, push endpoint, API key, client ID, tenant name) and a valid tracking username - see Contact us to request access.
  • For caller-managed authentication only: a valid access token and any required backend registration completed beforehand. See Authentication.

1. Configure the Maven repository​

Merge the following repositories into android/settings.gradle. Preserve your existing React Native plugin configuration and any repositories other dependencies need. Do not create a second dependencyResolutionManagement block if one already exists.

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
maven {
url "https://developer.huawei.com/repo/"
}
maven {
name = "sstrackingAndroid"
url = uri("https://artifact.sovereignsolutions.tech/artifactory/YOUR_ANDROID_REPO")
credentials {
username = providers.gradleProperty("artifactory_user").get()
password = providers.gradleProperty("artifactory_password").get()
}
}
}
}

Put the credentials in your user-level ~/.gradle/gradle.properties, outside the repository:

artifactory_user=YOUR_REPOSITORY_USERNAME
artifactory_password=YOUR_REPOSITORY_PASSWORD

For CI, provide the same Gradle properties through protected environment variables named ORG_GRADLE_PROJECT_artifactory_user and ORG_GRADLE_PROJECT_artifactory_password.

2. Add the dependency​

In android/app/build.gradle:

dependencies {
implementation "com.sovereignsolutions:tracking-sdk:3.0.4"
// Keep your existing React Native and other dependencies.
}

3. Configure the tracking backend​

The Kotlin bridge reads its configuration from the app's generated BuildConfig. Merge these settings into android/app/build.gradle, replacing every placeholder with the values for one environment:

android {
buildFeatures {
buildConfig = true
}
defaultConfig {
buildConfigField "String", "TRACKING_BASE_URL", '"https://auth.example.com/"'
buildConfigField "String", "TRACKING_REGISTRATION_BASE_URL", '"https://registration.example.com/"'
buildConfigField "String", "TRACKING_SERVER_URL", '"https://tracking.example.com/YOUR_PUSH_ENDPOINT"'
buildConfigField "String", "TRACKING_API_KEY", '"YOUR_API_KEY"'
buildConfigField "String", "TRACKING_CLIENT_ID", '"YOUR_CLIENT_ID"'
buildConfigField "String", "TRACKING_TENANT_NAME", '"YOUR_TENANT_NAME"'
}
}

These URLs are illustrative. Use the exact base URLs and push endpoint supplied for your deployment, and keep the trailing slash on base URLs. All six fields must exist so the Kotlin bridge compiles - including fields that may be empty in caller-managed authentication mode.

FieldPurposeNonblank value required
TRACKING_BASE_URLAuthentication and tracking API base URLBoth modes
TRACKING_REGISTRATION_BASE_URLLogin / registration backend base URLSDK-managed authentication
TRACKING_SERVER_URLFull location push URLBoth modes
TRACKING_API_KEYSDK authentication API keySDK-managed authentication
TRACKING_CLIENT_IDClient identifierSDK-managed authentication
TRACKING_TENANT_NAMEBackend tenantBoth modes
BuildConfig is not secret storage

Values compiled into BuildConfig ship inside the APK. Use build variants or CI-supplied configuration to separate environments, and keep production values out of committed examples.

4. Install and register the Kotlin bridge​

Copy these two files from the sample app into your application:

  • android/app/src/main/java/sovereignsolutions/TrackingSdkModule.kt
  • android/app/src/main/java/sovereignsolutions/TrackingSdkPackage.kt

For an app with Android namespace com.example.myapp, place them under android/app/src/main/java/com/example/myapp/ and change the first line of both files to:

package com.example.myapp

Use the app's Android namespace, which determines the generated BuildConfig package. If the bridge lives in another package, explicitly import your app's BuildConfig, and import TrackingSdkPackage in MainApplication.kt.

Register the package in the existing package list in MainApplication.kt:

import com.facebook.react.PackageList
import com.facebook.react.ReactHost
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost

override val reactHost: ReactHost by lazy {
getDefaultReactHost(
context = applicationContext,
packageList = PackageList(this).packages.apply {
add(TrackingSdkPackage())
},
)
}

Keep the rest of your template's application setup, including onCreate(). If your template uses getPackages() instead, add TrackingSdkPackage() to that list. Register it only once. The bridge is registered manually - adding the Maven dependency alone does not expose a JavaScript module.

The bridge exports the module name TrackingSdk. Its initialization maps the BuildConfig values to the SDK like this (excerpt - use the complete file from the sample app, which also handles activity binding, Promise handling, state persistence and device health):

trackingSdk.initSDK(
baseUrl = BuildConfig.TRACKING_BASE_URL,
registrationBaseUrl = BuildConfig.TRACKING_REGISTRATION_BASE_URL,
serverUrl = BuildConfig.TRACKING_SERVER_URL,
apiKey = BuildConfig.TRACKING_API_KEY,
username = cleanUsername,
clientId = BuildConfig.TRACKING_CLIENT_ID,
tenantName = BuildConfig.TRACKING_TENANT_NAME,
distanceFilter = distanceFilter.toFloat(),
skipLoginAndRegistration = skipLoginAndRegistration,
callerAccessToken = cleanCallerAccessToken
) { success, token, error ->
// The complete bridge handles errors and calls startTracking on success.
}

5. Android manifest and runtime permissions​

Make sure these declarations are present in the merged app manifest, directly inside <manifest> and outside <application>:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Check Android Studio's Merged Manifest view for your build. Preserve the services and receivers the SDK contributes through manifest merging - do not invent or duplicate service declarations. A location foreground service needs the location service type and its permission prerequisites, and tracking should be started while the app's activity is visible. See Android's location foreground service requirements.

  • Location: request foreground location before background location. On Android 11+, background access is granted in system settings - explain why and let the user decline. Approximate foreground access also limits background accuracy. See Android's background location guidance.
  • Notifications: on Android 13+, request notification permission so tracking notifications show normally. Android does not require this grant to launch a foreground service, but the service still supplies a notification. See Android's notification permission guidance.

Runtime permission helper​

Create src/trackingPermissions.ts. This helper requires precise location and background access. It returns false after opening settings - the user returns to the app and taps Start again so permissions are rechecked.

import {Alert, Linking, PermissionsAndroid, Platform} from 'react-native';

function offerSettings(message: string): void {
Alert.alert('Tracking permissions', message, [
{text: 'Cancel', style: 'cancel'},
{
text: 'Open settings',
onPress: () => {
Linking.openSettings().catch(() => {
Alert.alert('Settings unavailable', 'Open app settings manually.');
});
},
},
]);
}

export async function requestTrackingPermissions(): Promise<boolean> {
if (Platform.OS !== 'android') {
return false;
}

const p = PermissionsAndroid.PERMISSIONS;
const granted = PermissionsAndroid.RESULTS.GRANTED;
const apiLevel = Number(Platform.Version);

const location = await PermissionsAndroid.requestMultiple([
p.ACCESS_COARSE_LOCATION,
p.ACCESS_FINE_LOCATION,
]);

if (location[p.ACCESS_FINE_LOCATION] !== granted) {
offerSettings('Enable precise location to use this tracking example.');
return false;
}

if (apiLevel >= 29) {
// Motion recognition is requested separately from location.
await PermissionsAndroid.request(p.ACTIVITY_RECOGNITION);

const backgroundGranted = await PermissionsAndroid.check(
p.ACCESS_BACKGROUND_LOCATION,
);

if (!backgroundGranted) {
if (apiLevel >= 30) {
offerSettings(
'Tracking needs location while the app is in the background. ' +
'In app settings, open Location and select Allow all the time. ' +
'Then return and tap Start again.',
);
return false;
}

const background = await PermissionsAndroid.request(
p.ACCESS_BACKGROUND_LOCATION,
);
if (background !== granted) {
return false;
}
}
}

if (apiLevel >= 33) {
await PermissionsAndroid.request(p.POST_NOTIFICATIONS);
}

return true;
}

The helper tolerates a denied notification or motion permission - use device health to explain any degraded behaviour. Device location services must also be switched on.

6. Add the TypeScript wrapper​

Copy src/TrackingSdk.ts from the sample app into your project at the same path. On Android, configuration comes exclusively from BuildConfig; the iOS configuration object in that file is not used.

The public initialization signature is:

initAndStartTracking(
username: string,
skipLoginAndRegistration: boolean,
callerAccessToken: string | null = null,
distanceFilter: number = 50,
): Promise<any>

The wrapper trims username and rejects a blank username, a negative or non-finite distanceFilter, and a missing token when skipping authentication. distanceFilter is passed to the SDK as a float; it is not an upload interval.

The native argument order differs from the wrapper's order. Always call the wrapper from application code:

// Public wrapper order:
await initAndStartTracking('driver-001', false, null, 50);

// Equivalent native order, for reference only:
// NativeModules.TrackingSdk.initAndStartTracking('driver-001', 50, false, null);

7. Build and run​

Install JavaScript dependencies and start Metro:

yarn install
yarn start

In another terminal at the project root:

yarn android
Rebuild after native changes

Rebuild the Android app after changing Gradle configuration, Kotlin files or package registration. A Metro reload does not install native changes.

To check dependency resolution and build without launching the app:

cd android
./gradlew :app:dependencies --configuration debugRuntimeClasspath
./gradlew :app:assembleDebug

Next steps​

  • Authentication - choose SDK-managed or caller-provided tokens
  • Usage - start, stop and query tracking from React Native
  • Troubleshooting - error codes and a device validation checklist
  • Sample apps - reference source code on GitHub