A complete guide to integrating the Roam Android SDK into your application — from setup to permissions, tracking, services, and real-time location updates.
Git repo link: https://github.com/roam-ai/roam-android
- Download the latest
.aarfrom GitHub Releases or provided by Echo. - Create a
/libsfolder in your app module and place the.aarinside. - Add the dependency in app/build.gradle:
dependencies {
implementation files("libs/roam.aar")
implementation "com.google.android.gms:play-services-location:21.3.0"
implementation "com.squareup.retrofit2:retrofit:2.11.0"
implementation "com.squareup.retrofit2:converter-gson:2.11.0"
implementation "com.google.android.gms:play-services-ads-identifier:18.2.0"
}
Add to settings.gradle:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
Add to app/build.gradle:
dependencies {
implementation "io.github.roam-sdk:roam-android:0.2.0"
}
Initialize Roam inside your Application class.
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// Batch sync only
Roam.initialize(this, "YOUR_SECRET_KEY")
// or - Batch sync + live updates via receiver
val receiver = LocationReceiver()
Roam.initialize(this, "YOUR_SECRET_KEY", receiver)
// Enable batch sync
Roam.setConfig(true, RoamBatchPublish.Builder().enableAll().build())
}
}
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
// Batch sync only
Roam.initialize(this, "YOUR_SECRET_KEY");
// or - Batch sync + live updates via receiver
LocationReceiver receiver = new LocationReceiver();
Roam.initialize(this, "YOUR_SECRET_KEY", receiver);
Roam.setConfig(true, new RoamBatchPublish.Builder().enableAll().build());
}
}Roam supports built-in permission handling or manual runtime permission flows.
Manifest
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Kotlin
if (!Roam.checkPhoneStatePermission()) {
Roam.requestPhoneStatePermission(this)
}
Manifest
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<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" />
Kotlin
if (!Roam.checkLocationPermission()) {
Roam.requestLocationPermission(this)
} else if (!Roam.checkBackgroundLocationPermission()) {
Roam.requestBackgroundLocationPermission(this)
} else if (!Roam.checkLocationServices()) {
Roam.requestLocationServices(this)
} else if (!Roam.checkPostNotificationPermission()) {
Roam.requestPostNotificationPermission(this)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
Roam.REQUEST_CODE_LOCATION_PERMISSION -> if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED)
Log.d("Permissions", "Foreground location granted")
Roam.REQUEST_CODE_BACKGROUND_LOCATION_PERMISSION -> if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED)
Log.d("Permissions", "Background location granted")
Roam.REQUEST_CODE_POST_NOTIFICATION -> if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED)
Log.d("Permissions", "Post Notifications granted")
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Roam.REQUEST_CODE_LOCATION_ENABLED) {
val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Log.d("Permissions", "Location services enabled")
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case Roam.REQUEST_CODE_LOCATION_PERMISSION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Log.d("Permissions", "Foreground location granted");
break;
case Roam.REQUEST_CODE_BACKGROUND_LOCATION_PERMISSION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Log.d("Permissions", "Background location granted");
break;
case Roam.REQUEST_CODE_POST_NOTIFICATION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Log.d("Permissions", "Post Notifications granted");
break;
}
}Kotlin
// Request Fine Location permission
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
2001
)
}
// Request Background Location permission (Android 10+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
2002
)
}
// Request Phone State permission
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_PHONE_STATE),
2005
)
}
// Ensure GPS is enabled
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
startActivityForResult(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS), 2003)
}
// Request Notification permission (Android 13+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
2004
)
}
}
Java
// Request Fine Location permission
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
2001
);
}
// Request Background Location permission (Android 10+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
2002
);
}
// Request Phone State permission
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.READ_PHONE_STATE},
2005
);
}
// Ensure GPS is enabled
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
startActivityForResult(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS), 2003);
}
// Request Notification permission (Android 13+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.POST_NOTIFICATIONS},
2004
);
}
}Kotlin
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
2001 -> if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED)
Log.d("Permissions", "Foreground location granted")
2002 -> if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED)
Log.d("Permissions", "Background location granted")
2004 -> if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED)
Log.d("Permissions", "Post Notifications granted")
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 2003) {
val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Log.d("Permissions", "Location services enabled")
}
}
}Java
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 2001:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d("Permissions", "Foreground location granted");
}
break;
case 2002:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d("Permissions", "Background location granted");
}
break;
case 2004:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d("Permissions", "Post Notifications granted");
}
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 2003) {
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Log.d("Permissions", "Location services enabled");
}
}
}Roam.setForegroundNotification(
true,
"Tracking Active",
"Tap to return to app",
R.drawable.ic_launcher,
"com.example.MainActivity"
)
class RoamForegroundLocationService : Service() {
companion object {
fun start(context: Context) {
context.startForegroundService(Intent(context, RoamForegroundLocationService::class.java))
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(1, createNotification())
return START_STICKY
}
private fun createNotification(): Notification {
return NotificationCompat.Builder(this, "roam_service")
.setContentTitle("Location Tracking")
.setContentText("Running in background")
.setSmallIcon(R.drawable.ic_launcher)
.build()
}
}
Roam.startTracking(object : TrackingCallback {
override fun onSuccess(message: String?) {}
override fun onError(error: RoamError?) {}
})
Roam.stopTracking(object : TrackingCallback {
override fun onSuccess(message: String?) {}
override fun onError(error: RoamError?) {}
})
Roam.startTracking(new TrackingCallback() {
@Override
public void onSuccess(String message) {}
@Override
public void onError(RoamError error) {}
});class RoamLocationReceiver : RoamReceiver() {
override fun onLocationUpdated(context: Context, roamLocation: RoamLocation) {
Log.d("RoamLocationReceiver", "Location: $roamLocation")
}
override fun onError(context: Context?, roamError: RoamError?) {
Log.e("RoamLocationReceiver", "Error: $roamError")
}
}
<receiver android:name=".RoamLocationReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.roam.android.RECEIVED"/>
</intent-filter>
</receiver>