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.
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.
| Field | Purpose | Nonblank value required |
|---|---|---|
TRACKING_BASE_URL | Authentication and tracking API base URL | Both modes |
TRACKING_REGISTRATION_BASE_URL | Login / registration backend base URL | SDK-managed authentication |
TRACKING_SERVER_URL | Full location push URL | Both modes |
TRACKING_API_KEY | SDK authentication API key | SDK-managed authentication |
TRACKING_CLIENT_ID | Client identifier | SDK-managed authentication |
TRACKING_TENANT_NAME | Backend tenant | Both modes |
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.ktandroid/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 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