LOD - Android SDK
v 2.0.0 (released 02-04-2026)
Support
- The LandmarksID SDK LOD supports Android 8.0 Oreo (API 26) and above.
- Compile SDK: 36
- Target SDK: 36
- Java 17
User Permissions
Normal permissions (such as INTERNET, FOREGROUND_SERVICE, and FOREGROUND_SERVICE_LOCATION) are declared in the SDK manifest and will be merged automatically.
Your app must declare the following dangerous permissions in its AndroidManifest.xml and request them at runtime:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION— Required for location services. Must be requested at runtime.ACCESS_BACKGROUND_LOCATION— Required for location updates while the app is in the background. On Android 10+ this must be requested separately from foreground location permissions.
The SDK does not request permissions itself. The host application is responsible for requesting location permissions and forwarding the results to the SDK.
Setup Instructions
Installation
- Add the credentials provided by LANDMARKSID to
~/.gradle/gradle.properties:
gpr.key=YOUR_ACCESS_TOKEN
The access token will be provided by LANDMARKSID.
- Open your root
build.gradlefile and add the GitHub Packages repository:
allprojects {
repositories {
...
maven {
name = "GitHubPackages-LOD"
url = uri("https://maven.pkg.github.com/LANDMARKSID/sdk-android-lod")
credentials {
username = "LANDMARKS-ID"
password = project.findProperty("gpr.key")
}
}
}
}
- Add the dependency in your app-level
build.gradle:
dependencies {
implementation 'com.landmarksid:landmarks-android-sdk-lod:2.0.0'
}
Initialisation
Application Class
Add the following line inside the Application class's onCreate() method:
LandmarksID.getInstance().initMetaData(getApplicationContext());
Starting the SDK
In your main activity's onCreate() method, initialise and start the LandmarksID SDK:
landmarksId = LandmarksID.getInstance().start(this, options);
.start() accepts two arguments — a context and an Options object (described in detail below) — and returns an instance of the LandmarksID SDK.
Configuration
A typical Options builder would look something like this:
Options options = new Options()
.setApiKey(API_KEY)
.setAppMetadata(APP_ID, APP_SECRET)
.setCustomerId("sample-id")
.setCustomData(customData);
All constants in upper case are to be obtained/clarified with LandmarksID, and are not part of the SDK itself.
Permission Handling (Mandatory)
The host app must request location permissions and forward the results to the SDK. The SDK automatically detects when location permissions are granted and starts location services. No additional calls are needed — just forward the result.
The SDK processes any permission result that includes ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION, regardless of the request code used by the application.
Option A: AndroidX Activity Result API (Recommended)
private final ActivityResultLauncher<String[]> locationPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), results -> {
landmarksId.onRequestPermissionsResult(this, results);
});
// To request permissions:
locationPermissionLauncher.launch(new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
});
Option B: Legacy onRequestPermissionsResult
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
landmarksId.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
Background Location Permission
The LOD SDK requires ACCESS_BACKGROUND_LOCATION for geofencing and location services to work while the app is in the background. On Android 10+, background location must be requested separately from foreground location permissions.
Recommended flow:
- Request foreground location permissions first (
ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION). - After foreground permission is granted, prompt the user and request
ACCESS_BACKGROUND_LOCATION. - On Android 10 (Q), the background permission can be requested via the system dialog.
- On Android 11+, the user must be directed to the app settings page to grant "Allow all the time".
private final ActivityResultLauncher<String> backgroundPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestPermission(), granted -> {
// Background permission result handled
});
private void requestBackgroundPermission() {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
// Android 10: can request via system dialog
backgroundPermissionLauncher.launch(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// Android 11+: must redirect to app settings
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", getPackageName(), null));
startActivity(intent);
}
}
Additional Controls
Custom Data (Optional)
.setCustomData(CustomData)
User data that is collected by, or made available to, the application can be passed into the LandmarksID SDK, as custom values. Use this method to pass along a set of key-value pairs constructed using the provided CustomData class. These will be recorded by the LandmarksID SDK with each location event. Multiple custom values can be passed into the method.
CustomData customData = new CustomData();
customData.addString("country", "Germany");
customData.addInt("code", 49);
customData.addFloat("score", 23.58f);
At the moment, the supported types are String, int, and float.
CustomData is an extension of HashSet, and exhibits the same behavior with one notable difference: in case an entry with a duplicate key is added, only the one added last would be kept.
Putting It All Together
public class LocationAwareApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
LandmarksID.getInstance().initMetaData(getApplicationContext());
}
}
public class MainActivity extends AppCompatActivity {
private static final String API_KEY = "<YOUR_API_KEY>";
private static final String APP_ID = "<YOUR_APP_ID>";
private static final String APP_SECRET = "<YOUR_APP_SECRET>";
private LandmarksID landmarksId;
private final ActivityResultLauncher<String[]> locationPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), results -> {
landmarksId.onRequestPermissionsResult(this, results);
boolean hasFine = ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
boolean hasBackground = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ||
ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED;
if (hasFine && !hasBackground) {
requestBackgroundPermission();
}
});
private final ActivityResultLauncher<String> backgroundPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestPermission(), granted -> {
// Background permission result handled
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CustomData customData = new CustomData();
customData.addString("country", "Germany");
customData.addInt("code", 49);
customData.addFloat("score", 23.58f);
Options options = new Options()
.setApiKey(API_KEY)
.setAppMetadata(APP_ID, APP_SECRET)
.setCustomerId("sample-id")
.setCustomData(customData);
landmarksId = LandmarksID.getInstance().start(this, options);
// Request foreground location permissions
locationPermissionLauncher.launch(new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
});
}
private void requestBackgroundPermission() {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
backgroundPermissionLauncher.launch(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", getPackageName(), null));
startActivity(intent);
}
}
}
Upgrading from 1.x
Breaking Changes
SDK distribution moved from JitPack to GitHub Packages — Remove JitPack from your repository configuration and replace with GitHub Packages as described in the Installation section above.
// Before (remove this) maven { url "https://jitpack.io" credentials { username "<accessToken>" } } // Before (remove this) implementation 'com.gitlab.landmarksid:sdk-android-lod:1.6.x' // After implementation 'com.landmarksid:landmarks-android-sdk-lod:2.0.0'LandmarksID.Optionsis now a standaloneOptionsclass — PreviouslyLandmarksID.Options, nowcom.landmarksid.android.Options.// Before LandmarksID.Options options = new LandmarksID.Options() .setApiKey(API_KEY); // After Options options = new Options() .setApiKey(API_KEY);setLocationUsable(Activity)is deprecated — The SDK now handles permission state automatically when you callonRequestPermissionsResult(). Remove all calls tosetLocationUsable().// Before @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { landmarksId.onRequestPermissionsResult(this, requestCode, permissions, grantResults); if (permissionGranted) { landmarksId.setLocationUsable(this); // REMOVE THIS } } // After — Option A: AndroidX Activity Result API (recommended) private final ActivityResultLauncher<String[]> locationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), results -> { landmarksId.onRequestPermissionsResult(this, results); }); // After — Option B: Legacy callback @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { landmarksId.onRequestPermissionsResult(this, requestCode, permissions, grantResults); // That's it — the SDK handles the rest }setLocationUsable()should not be called at startup — Previously some apps calledsetLocationUsable()immediately afterstart()to bootstrap location services. This is no longer needed.// Before landmarksId = LandmarksID.getInstance().start(this, options); landmarksId.setLocationUsable(this); // REMOVE THIS // After landmarksId = LandmarksID.getInstance().start(this, options);CustomDataimport changed — Import changed tocom.landmarksid.lo.types.CustomData.EventListenerinterface updated — If your app implementsEventListener, it now extendscom.landmarksid.lo.logging.EventLogListener. You may need to implement additional methods from the parent interface.Background location requires separate handling —
ACCESS_BACKGROUND_LOCATIONmust be requested separately from foreground permissions on Android 10+. See the Background Location Permission section above for the recommended flow.
Contact Details
If you have any further questions please do not hesitate to contact our friendly team at;
