Skip to content

BlueCodeSystems/kujaku

 
 

Repository files navigation

JitPack Latest master-SNAPSHOT

kujaku

Kujaku is an Android mapping toolkit for OpenSRP deployments, wrapping MapLibre Native with opinionated helpers for offline basemaps, field data collection, and tracking utilities.

Project Status

  • 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)

Features

  • KujakuMapView surface 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

Requirements

  • 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

Install

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
}

Initialize

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");
    }
}

Usage examples

Add KujakuMapView to an Activity

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(); }
}

Download an offline region

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);
    }
});

Persist field movement with TrackingService

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();

Sample app

A demo client lives under sample/. Install it on a connected device or emulator with:

./gradlew :sample:installDebug

You can also open the project in Android Studio and run the sample configuration directly.

Build & test

./gradlew clean assemble
./gradlew test

Releases

See the GitHub Releases page for published versions, changelogs, and migration notes.

Contributing

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.

License

Licensed under the Apache License 2.0. See LICENSE for details.

Acknowledgements

We’d like to acknowledge The Bill and Melinda Gates Foundation and Qualcomm for supporting us in this work.

About

Mapping and check-in library for Android using MapBox SDK

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Java 100.0%