Kujaku is an Android mapping toolkit for OpenSRP deployments, wrapping MapLibre Native with opinionated helpers for offline basemaps, field data collection, and tracking utilities.
- Toolchain: Gradle 8.7, Android Gradle Plugin 8.6.0, JDK 17 (Java-only modules)
- CI: GitHub Actions workflow
ci.yml - Branch:
master; no tagged releases yet (see Git history for milestones)
KujakuMapViewsurface with location widgets, layer switching, and drawing helpers- Offline style and region management backed by MapLibre and MBTiles helpers
- Foreground tracking service with persistent storage utilities
- Integration helpers for MapLibre initialization and style caching
- JDK: 17
- Gradle: 8.7 (Gradle wrapper provided)
- Android Gradle Plugin: 8.6.0
- Kotlin: not required (project is Java-based)
- Android compile/target SDK: 35
- Android min SDK: 28
Add Maven Central and depend on the published artifact (replace <version> with the latest release from GitHub Releases):
Groovy DSL
repositories {
mavenCentral()
}
dependencies {
implementation 'io.ona.kujaku:kujaku:<version>' // see Releases for versions
}Kotlin DSL
repositories {
mavenCentral()
}
dependencies {
implementation("io.ona.kujaku:kujaku:<version>") // see Releases for versions
}Call KujakuLibrary.init(context) once, ideally from your Application class, and optionally seed MapLibre with an access token if required for your tile sources:
public final class FieldApp extends Application {
@Override
public void onCreate() {
super.onCreate();
KujakuLibrary.init(this);
MapLibreUtils.initialize(this, "YOUR-MAPLIBRE-OR-MAPBOX-TOKEN");
}
}public final class SurveyMapActivity extends AppCompatActivity {
private KujakuMapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
mapView = findViewById(R.id.kujaku_map_view);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(mapboxMap -> {
mapboxMap.setStyle(Style.MAPBOX_STREETS, style -> {
mapView.focusOnUserLocation(true);
});
});
}
@Override
protected void onStart() { super.onStart(); mapView.onStart(); }
@Override
protected void onResume() { super.onResume(); mapView.onResume(); }
@Override
protected void onPause() { mapView.onPause(); super.onPause(); }
@Override
protected void onStop() { mapView.onStop(); super.onStop(); }
@Override
protected void onDestroy() { mapView.onDestroy(); super.onDestroy(); }
}MapBoxOfflineResourcesDownloader downloader =
MapBoxOfflineResourcesDownloader.getInstance(context, MapLibreUtils.getApiKey());
MapBoxDownloadTask task = new MapBoxDownloadTask();
task.setMapName("field-area-west");
task.setLatitudeLongitudeBoundingBox(latLngBounds);
task.setMinZoom(10);
task.setMaxZoom(16);
downloader.downloadMap(task, new OnDownloadMapListener() {
@Override
public void onMapDownloadSuccessful(MapBoxOfflineQueueTask queueTask) {
Timber.d("Offline region ready: %s", queueTask.getMapName());
}
@Override
public void onError(MapBoxOfflineQueueTask queueTask, String errorMessage) {
Timber.e("Offline download failed: %s", errorMessage);
}
});Context context = getApplicationContext();
Intent intent = new Intent(context, TrackingService.class);
intent.putExtra(TrackingService.KEY_USERNAME, "field-worker-1");
ContextCompat.startForegroundService(context, intent);
TrackingStorage storage = new TrackingStorage(context);
List<KujakuLocation> previous = storage.getPreviousRecordedKujakuLocations();A demo client lives under sample/. Install it on a connected device or emulator with:
./gradlew :sample:installDebugYou can also open the project in Android Studio and run the sample configuration directly.
./gradlew clean assemble
./gradlew testSee the GitHub Releases page for published versions, changelogs, and migration notes.
Issues and pull requests are welcome. Please:
- Align with the toolchain noted above (JDK 17, AGP 8.6.0, Gradle 8.7)
- Run the build and unit tests before opening a PR
- Review the SPECIFICATION for implementation details
If a CONTRIBUTING.md file is added in the future, follow the guidance there as well.
Licensed under the Apache License 2.0. See LICENSE for details.
We’d like to acknowledge The Bill and Melinda Gates Foundation and Qualcomm for supporting us in this work.