DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for...

41
DHIS 2 Android SDK Developer Guide Applicable to version 1.1.2 DHIS 2 June 2020

Transcript of DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for...

Page 1: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

DHIS 2 Android SDK Developer

Guide

Applicable to version 1.1.2

DHIS 2

June 2020

Page 2: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Copyright © 2006-2020 DHIS 2

June 2020

Revision History

master@

Warranty: THIS DOCUMENT IS PROVIDED BY THE AUTHORS ‘’AS IS’’ AND ANY EXPRESS ORIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OFMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENTSHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ORBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER INCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING INANY WAY OUT OF THE USE OF THIS MANUAL AND PRODUCTS MENTIONED HEREIN, EVEN IFADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

License: Permission is granted to copy, distribute and/or modify this document under the termsof the GNU Free Documentation License, Version 1.3 or any later version published by the FreeSoftware Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.A copy of the license is included in the source of this documentation, and is available here online:http://www.gnu.org/licenses/fdl.html

DHIS 2 Android SDK Developer Guide Applicable to version 1.1.2

2

Page 3: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Table of Contents

1 About this guide 2 Compatibility: SDK / core / Android 3 Overview

3.1 Technology overview 4 Getting started

4.1 Installation4.2 D2 initialization

5 Modules and repositories 5.1 Dealing with return types: RxJava5.2 Query building

5.2.1 Filters5.2.2 Order by5.2.3 Include nested fields

5.3 Helpers5.4 Module list

6 Database 6.1 Database scope6.2 Encryption

6.2.1 Encryption performance7 Workflow

7.1 Login/Logout7.2 Metadata synchronization

7.2.1 Corrupted configurations7.3 Data states7.4 Tracker data

7.4.1 Tracker data download7.4.2 Tracker data search7.4.3 Tracker data write7.4.4 Tracker data upload7.4.5 Tracker data: reserved values

7.5 Aggregated data7.5.1 Aggregated data download7.5.2 Aggregated data write7.5.3 Aggregated data upload7.5.4 DataSet instances

7.6 Dealing with FileResources7.6.1 File resources module7.6.2 File resizer helper7.6.3 File resource directory helper

8 Error management 9 SMS module

9.1 SMS version9.2 ConfigCase9.3 SmsSubmitCase9.4 QrCodeCase

10 Object style 10.1 Icon10.2 Color

11 DHIS2 version compatibility strategy 11.1 Example: minor change11.2 Example: major change

12 Direct database interaction

Table of Contents Applicable to version 1.1.2

3

Page 4: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

13 Program rule engine 14 Program indicator engine 15 Debugging 16 Known issues

16.1 Data set completion

Table of Contents Applicable to version 1.1.2

4

Page 5: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

1 About this guide

The DHIS 2 documentation is a collective effort and has been developed by the developmentteam and users. While the guide strives to be complete, there may be certain functionalitieswhich have been omitted or which have yet to be documented. This section explains some of theconventions which are used throughout the document.

DHIS 2 is a browser-based application. In many cases, screenshots have been included forenhanced clarity. Shortcuts to various functionalities are displayed such as Data element > Data

element group. The “>” symbol indicates that you should click Data element and then click Data

element group in the user interface.

Different styles of text have been used to highlight important parts of the text or particular typesof text, such as source code. Each of the conventions used in the document are explained below.

Note

A note contains additional information which should be considered or areference to more information which may be helpful.

Tip

A tip can be a useful piece of advice, such as how to perform aparticular task more efficiently.

Important

Important information should not be ignored, and usually indicatessomething which is required by the application.

Caution

Information contained in these sections should be carefully considered,and if not heeded, could result in unexpected results in analysis,performance, or functionality.

Warning

Information contained in these sections, if not heeded, could result inpermanent data loss or affect the overall usability of the system.

Complete

Information contained in these sections, will indicate that these areissues that have been fully implemented.

Incomplete

Information contained in these sections, will indicate that these areissues that are not implemented and will be ignored.

Not_applicable

1 About this guide Applicable to version 1.1.2

5

Page 6: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Information contained in these sections, will indicate that these areissues not applicable.

Work_in_progress

Information contained in these sections, will indicate that these areissues or features not completely implemented or with unexpectedbehaviour already reported.

Program listings usually contain some type of computer code.

They will be displayed with a shaded background and a different font.

Commands will be displayed in bold text, and represent a command whichwould need to be executed on the operating system or database.

Links to external web sites or cross references will be displayed in blue text, and underlined like this..

1 About this guide Applicable to version 1.1.2

6

Page 7: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

2 Compatibility: SDK / core / Android

Compatibility table between DHIS2 Android SDK library, DHIS2 core and Android SDK API.

SDK DHIS2 core Android SDK

1.0.0 2.29 -> 2.33 19 - 28

1.0.1 2.29 -> 2.33 19 - 28

1.0.2 2.29 -> 2.33 19 - 28

1.0.3 2.29 -> 2.33 19 - 28

1.1.0 2.29 -> 2.34 19 - 28

1.1.1 2.29 -> 2.34 19 - 28

1.1.2 2.29 -> 2.34 19 - 28

2 Compatibility: SDK / core / Android Applicable to version 1.1.2

7

Page 8: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

3 Overview

DHIS2 Android SDK is a library that abstracts the complexity of interacting with DHIS2 web api. Itaims to be an starting point to build Android apps for DHIS2, covering some tasks that anyAndroid app should implement, like metadata and data synchronization.

Main goals:

Abstract DHIS2 web api. There is no need to perform api queries against the server. TheSDK includes methods to interact with the web api.Work offline. It implements a simplified version of DHIS2 model that is persisted in a localdatabase (SQLite). It ensures that all the metadata required to perform data entry tasks isavailable at any time to build the data entry forms. Data is saved locally and uploaded tothe server when connection is available.Ensure DHIS2 compatibility. It encapsulates the changes between DHIS2 versions so theapp does not have to care about them. In case the SDK introduces some changes toaccommodate a new DHIS2 version, the app can safely detect these changes at compile-time.

3.1 Technology overview

The SDK is written in Java 8 using the reduced subset of features allowed in the minimumAndroid API version. The SDK uses some Android-specific components, such as libraries to createpaged list (LiveData, PagedList) or to access to file system. For this reason, currently the SDK is

only runnable in an Android environment.

It uses RxJava to facilitate the asynchronous treatment of some methods. Although it is optional,we recommend this approach to ensure non-blocking calls.

Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSONparsing, Retrofit and OkHttpClient for API communication or SQLBrite for DB migrations.

3 Overview 3.1 Technology overview

8

Page 9: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

4 Getting started

4.1 Installation

Include dependency in build.gradle.

dependencies {

implementation "org.hisp.dhis:android-core:1.1.2"

...

}

Additionally, you need to include this repository in your root gradle file if it is not already there:

allprojects {

repositories {

...

maven { url 'https://jitpack.io' }

}

}

4.2 D2 initialization

In order to start using the SDK, the first step is to initialize a D2 object. The helper class D2Manager offers static methods to setup and initialize the D2 instance. Also, it ensures that D2is a singleton across the application.

The minimum configuration that needs to be passed to the D2Manager is the following:

Using the configuration you can instantiate D2.

Once the Single is completed, you can access D2 with the following method:

If you are not using RxJava, you can instantiate D2 in a blocking way:

The object D2Configuration has a lot of fields to configure the behavior of the SDK.

Attribute Required Description Default

context true Application context -

D2Configuration configuration = D2Configuration.builder()

.context(context)

.build();

Single<D2> d2Single = D2Manager.instantiateD2(configuration);

D2 d2 = D2Manager.getD2();

D2 d2 = D2Manager.blockingInstantiateD2(configuration);

4 Getting started 4.1 Installation

9

Page 10: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Attribute Required Description Default

appName false Use to create the“user-agent” header

From AndroidManifest

appVersion false Use to create the“user-agent” header

From AndroidManifest

readTimeoutInSeconds false Read timeout forhttp queries

30 seconds

connectTimeoutInSeconds false Connect timeout forhttp queries

30 seconds

writeTimeoutInSeconds false Write timeout forhttp queries

30 seconds

interceptors false Interceptors forOkHttpClient

None

networkInterceptors false NetworkInterceptorsfor OkHttpClient

None

4 Getting started 4.2 D2 initialization

10

Page 11: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

5 Modules and repositories

D2 object is the entry point to interact with the SDK. The SDK forces the D2 object to be asingleton across the application.

Modules are the layer below D2. They act as a wrapper for related functionality. A moduleincludes some related repositories and might expose some services and helpers.

Repositories act as a facade for the DB (or web API in some cases). They offer read capabilities formetadata and read/write for data.

5.1 Dealing with return types: RxJava

The SDK uses RxJava classes (Observable, Single, Completable, Flowable) as the preferred returntype for all the methods. The reasons for choosing RxJava classes are mainly two:

To facilitate the asynchronous treatment of returned objects. Most of the actions in theSDK are time consuming and must be executed in a secondary thread. These return typesforce the app to deal with this asynchronous behavior.To notify about progress. Methods like metadata or data sync might take several minutesto finish. From a user perspective, it is very helpful to have a sense of progress.

This does not mean that applications are forced to use RxJava in their code: they are only forcedto deal with the asynchronous behavior of some methods. The SDK usually exposes blockingversion of every method.

For example, the same query using RxJava and AsyncTask:

Using RxJava

Using AsyncTask

Accessing the database is time consuming and it’s recommended to do it in a separate threadusing any of the recommended methods. However, procedures that involve accessing the webAPI, like log in, metadata or data download or upload must run in a separate thread, otherwiseAndroid will throw an error.

d2.programModule().programs()

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.get() .subscribe(programs ->

{}); //List<Program>

new AsyncTask<Void, Void, List<Program>>() {

protected List<Program> doInBackground() {

return d2.programModule().programs().blockingGet();

}

protected void onPostExecute(List<Program> programs) {

}

}.execute();

5 Modules and repositories 5.1 Dealing with return types: RxJava

11

Page 12: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

5.2 Query building

Repositories offer a builder syntax with compile-time validation to access the resources. A typicalquery is composed of some modifiers (filter, order, nested fields) and ends with an action (get,count, getPaged,…).

5.2.1 Filters

Repositories expose the list of available filters prefixed by the keyword “by”. The list of filteroperators available for each filter is dependant on the filter value type: for example, a value type Date will offer operators like after, before, inPeriods, while a value type Boolean will offer isFalse or isTrue.

Several filters can be appended to the same query in any order. Filters are joined globally usingthe operator “AND”. This means that a query like

will return the events assigned to the orgunit “DiszpKrYNg8” AND whose eventDate is after“2019-05-05”.

5.2.2 Order by

Ordering modifiers are prefixed by the keyword “orderBy”.

Several “orderBy” modifiers can be appended to the same query. The order of the “orderBy”modifiers within the query determines the order priority. This means that a query like

will order by EventDate descendant in first place, and then by LastUpdated descendant.

5.2.3 Include nested fields

Repositories return classes that are not an exact match of database tables: they are morecomplex objects that might include some properties obtained from other tables. For example,

// Generic syntax

d2.<module>.<repository>

.[ filter | orderBy | nested fields ]

.<action>;

// An example for events

d2.eventModule().events()

.byOrganisationUnitUid().eq("DiszpKrYNg8")

.byEventDate().after(Date("2019-05-05"))

.orderByEventDate(DESC)

.withTrackedEntityDataValues()

.get();

d2.eventModule().events()

.byOrganisationUnitUid().eq("DiszpKrYNg8")

.byEventDate().after(Date("2019-05-05"))

...

d2.eventModule().events()

.orderByEventDate(DESC)

.orderByLastUpdated(DESC)

...

5 Modules and repositories 5.2 Query building

12

Page 13: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

the Event class has a property called trackedEntityDataValues that include a list ofTrackedEntityDataValues. The main reason to choose this kind of objects is to absorb thecomplexity of dealing with link tables so the app does not have to care about building linksbetween objects.

Due to performance issues, this kind of properties are not included by default: they must bequeried explicitly. In the repositories, the properties that are not included by default and need tobe queried are prefixed by the keyword “with”.

Several properties can be appended in the same query in any order. For example, a query like

will return a nested TrackedEntityType object.

5.3 Helpers

The SDK include some helpers in the package org.hisp.dhis.android.core.arch.helpers.They can be easily found in Android Studio by searching Helper in class names. They includesome helpful methods to perform common operations:

AccessHelper: related to access (sharing settings) object.CollectionsHelper: common operations to collections.CoordinateHelper, GeometryHelper: geospatial data manipulation.FileResizeHelper, FileResourceDirectoryHelper: file resource manipulation.UidsHelper: common operations to collections of objects with uid.UserHelper: operations related to user authentication.

5.4 Module list

System modules:

importModulemaintenanceModulesystemInfoModulesettingModulewipeModule

Big block modules:

metadataModuleaggregatedDataModule

Concrete modules:

categoryModuleconstantModuledataElementModuledataSetModuleoptionModuledataValueModuleenrollmentModuleeventModulefileResourceModuleindicatorModule

d2.programModule().programs()

.withTrackedEntityType()

...

• • • • • •

• • • • •

• •

• • • • • • • • • •

5 Modules and repositories 5.3 Helpers

13

Page 14: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

legendSetModulenoteModuleprogramModuleoptionModuleorganisationUnitModuleperiodModulerelationshipModuletrackedEntityModuleuserModulesmsModule

• • • • • • • • • •

5 Modules and repositories 5.4 Module list

14

Page 15: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

6 Database

6.1 Database scope

The SDK keeps the data of a [server, user] pair in an isolated database.

At the moment, just one [server, user] pair is supported, so logging out and logging in in withanother [server, user] pair will delete the current database and create a new one.

6.2 Encryption

As of SDK version 1.1.0, it is possible to store the data in an encrypted database. The encryptionkey is generated randomly by the SDK and kept secure.

The encryption status (if the database is encrypted or not) can be configured at server level in theandroid-settings-app. The default status is false: If the app is not installed, the database won’t beencrypted.

During the first login for a given server and user, the encryption status will be downloaded fromthe API and a database of the given type will be created.

In later logins or metadata synchronizations, the SDK will download again the encryption statusfrom the server and, if changed, will encrypt or decrypt the current database without data loss.

6.2.1 Encryption performance

Database size: the database size is approximately the same, regardless of being encryptedor not.Speed: reads and writes are on average 5 to 10% slower using an encrypted database.

6 Database 6.1 Database scope

15

Page 16: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

7 Workflow

Currently, the SDK is primarily oriented to build apps that work in an offline mode. In short, theSDK maintains a local database instance that is used to get the work done locally (create forms,manage data, …). When requested by the client, this local database is synchronized with theserver.

A typical workflow would be like this:

Login

Sync metadata: the SDK downloads a subset of the server metadata so it is available to beused at any time. Metadata sync is totally user-dependent (see Synchronization for moredetails)Download data: if you want to have existing data available in the device even when offline,you can download and save existing tracker and aggregated data in the device.Do the work: at this point the app is able to create the data entry forms and show someexisting data. Then the user can edit/delete/update data.Upload data: from time to time, the work done in the local database instance is sent to theserver.Sync metadata: it is recommended to sync metadata quite often to detect changes inmetadata configuration.

7.1 Login/Logout

Before interacting with the server it is required to login into the DHIS 2 instance. Currently, theSDK does only support one pair “user - server” simultaneously. That means that only one usercan be authenticated in only one server at the same time.

After a logout, the SDK keeps track of the last logged user so that it is able to differentiaterecurring and new users. It also keeps a hash of the user credentials in order to authenticate theuser even when there is no connectivity. Given that said, the login method will:

If an authenticated user already exists: throw an error.Else if Online:

Try login online: the SDK will send the username and password to the API, which willdetermine whether they are correct. If successful: - If no database exists: create newdatabase with encryption value from server. - If database for another [serverUrl,user] exists, delete it and create new database with encryption value from server.Not synced data of previously logged user will be permanently lost. - If database forthe current [serverUrl, user] pair exists, open the database and encrypt or decryptdatabase if encryption status has changed in the server.If user account has been disabled in server: delete database and throw an error.

Else if Offline: If the [serverUrl, user] pair was the last authenticated:

Try login offline: the SDK will verify that the credentials are the same as thelast provided, which were previously validated by the API.

If the [serverUrl, user] pair was not the last authenticated: throw an error

Calling module or repository methods before a successful login or after a logout will result in“Database not created” errors.

1. 2.

3.

4.

5.

6.

d2.userModule().logIn(username, password, serverUrl)

d2.userModule().logOut()

• •

◦ •

◦ ▪

7 Workflow 7.1 Login/Logout

16

Page 17: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Logout method removes user credentials, so a new login is required before any interaction withthe server. Metadata and data is preserved so a user is able to logout/login without losing anyinformation.

7.2 Metadata synchronization

Metadata synchronization is usually the first step after login. It fetches and persists the metadataneeded by the current user. To launch metadata synchronization we must execute:

In order to save bandwidth usage and storage space, the SDK does not synchronize all themetadata in the server but a subset. This subset is defined as the metadata required by the userin order to perform data entry tasks: render programs and datasets, execute program rules,evaluate in-line program indicators, etc.

Based on that, metadata sync includes the following elements:

Element Condition or scope

System info All

System settings KeyFlag, KeyStyle

User Only authenticated user

UserRole Roles assigned toauthenticated user

Authority Authorities assigned toauthenticated user

Program Programs that user has (atleast) read data access to andthat are assigned to anyorgunit visible by the user

RelationshipTypes All

OptionGroups Only if server is greater than2.29

DataSet DataSets that user has (atleast) read data access to andthat are assigned to anyorgunit visible by the user

Indicators Indicators assigned todownloaded dataSets

OrganisationUnit OrganisationUnits in CAPTUREor SEARCH scope(descendants included)

OrganisationUnitGroup Groups assigned todownloadedorganisationUnits

OrganisationUnitLevel All

Constant All

SMS Module metadata Only if SMS module enabled

d2.metadataModule().download();

7 Workflow 7.2 Metadata synchronization

17

Page 18: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

In the case of Programs and DataSets, metadata sync includes all the metadata related to them:stages, sections, dataElements, options, categories, etc. Those elements that are not related toany Program or DataSet are not included.

7.2.1 Corrupted configurations

This partial metadata synchronization may expose server-side misconfiguration issues. Forexample, a ProgramRuleVariable pointing to a DataElement that does not belong to the programanymore. Due to the use of database-level constraints, this misconfiguration will appear as aForeign Key error.

The SDK does not fail the synchronization, but it stores the errors in a table for inspection. Theseerrors can be accessed by:

7.3 Data states

Data objects have a read-only state property that indicates the current state of the object interms of synchronization with the server. This state is maintained by the SDK.

The possible states are:

SYNCED. The element is synced with the server. There are no local changes for this value.TO_POST. Data created locally that does not exist in the server yet.TO_UPDATE. Data modified locally that exists in the server.UPLOADING. Data is being uploaded. If it is modified before receiving any server response,its state is back to TO_UPDATE. When the server response arrives, its state does not changeto SYNCED, but it remains in TO_UPDATE to indicate that there are local changes.SENT_BY_SMS. Data is sent by sms and there is no server response yet. Some servers donot have the capability to send a response, so this state means that data has been sent,but we do not know if it has been correctly imported in the server or not.SYNCED_BY_SMS. Data is sent by sms and there is a successful response from the server.ERROR. Data that received an error from the server after the last upload.WARNING. Data that received a warning from the server after the last upload.

Additionally, in TrackedEntityInstance we might have:

RELATIONSHIP. This TrackedEntityInstance has been downloaded with the sole purpose offulfilling a relationship to another TEI. This RELATIONSHIP TEI only has basic information(uid, type, etc) and the list of TrackedEntityAttributes to be able to print meaningfulinformation about the relationship. Other data such as enrollments, events orrelationships are not downloaded for this TEI. Also, this TEI cannot be modified oruploaded to the server.

7.4 Tracker data

7.4.1 Tracker data download

By default, the SDK only downloads TrackedEntityInstances and Events that are located in usercapture scope, but it is also possible to download TrackedEntityInstances in search scope.

The tracked entity module contains the TrackedEntityInstanceDownloader. The downloaderfollows a builder pattern which allows the download of tracked entity instances filtering by different parameters as well as defining some limits. The same behavior can be found within theevent module for events.

d2.maintenanceModule().foreignKeyViolations()

• • • •

• • •

7 Workflow 7.2.1 Corrupted configurations

18

Page 19: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

The downloader tracks the latest successful download in order to avoid downloading unmodifieddata. It makes use of paging with a best effort strategy: in case a page fails to be downloaded orpersisted, it is skipped and it will continue with the next pages.

This is an example of how it can be used.

Currently, it is possible to specify the next filters:

byProgramUid(). Filters by program uid and downloads the not synced objects inside theprogram.byUid(). Filters by the tracked entity instance uid and downloads a unique object. Thisfilter can be used to download the tracked entity instances found within search scope.(Only for tracked entity instances).

The downloader also allows to limit the number of downloaded objects. These limits can also becombined with each other.

limit(). Limit the maximum number of objects to download.limitByProgram(). Take the established limit and apply it to each program. The numberof objects that will be downloaded will be the one obtained by multiplying the limit set bythe number of user programs.limitByOrgunit(). Take the established limit and apply it for each organisation unit. Thenumber of objects that will be downloaded will be the one obtained by multiplying the limitset by the number of user organisation units.

The next snippet of code shows an example of the TrackedEntityInstanceDownloader usage.

Additionally, if you want the images associated to Image data values available to be downloadedin the device, you must download them. See Dealing with FileResources section for more details.

7.4.2 Tracker data search

DHIS2 has a functionality to filter TrackedEntityInstances by related properties, like attributes,organisation units, programs or enrollment dates. The Sdk provides the TrackedEntityInstanceQueryCollectionRepository with methods that allow thedownload of tracked entity instances within the search scope. It can be found inside the trackedentity instance module.

d2.trackedEntityModule().trackedEntityInstanceDownloader()

.[filters]

.[limits]

.download()

d2.eventModule().eventDownloader()

.[filters]

.[limits]

.download()

• •

d2.trackedEntityModule().trackedEntityInstanceDownloader() .byProgramUid("program-

uid")

.limitByOrgunit(true)

.limitByProgram(true)

.limit(50)

.download()

7 Workflow 7.4.2 Tracker data search

19

Page 20: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

The tracked entity instance query is a powerful tool that follows a builder pattern and allows thedownload of tracked entity instances filtering by different parameters.

The source where the TEIs are retrieved from is defined by the repository mode. These are thedifferent repository modes available:

onlineOnly(). Only TrackedEntityInstances coming from the server are returned in thelist. Internet connection is required to use this mode.offlineOnly(). Only TrackedEntityInstances coming from local database are returned inthe list.onlineFirst(). TrackedEntityInstances coming from the server are returned in firstplace. Once there are no more results online, it continues with TrackedEntityInstances inthe local database. Internet connection is required to use this mode.offlineFirst(). TrackedEntityInstances coming from local database are returned in firstplace. Once there are no more results, it continues with TrackedEntityInstances comingfrom the server. This method may speed up the initial load. Internet connection is requiredto use this mode.

This repository follows the same syntax as other repositories. Additionally, the repository offersdifferent strategies to fetch data:

byAttribute(). This method adds an attribute filter to the query. If this method is calledseveral times, conditions are appended with an AND connector. For example:

That means that the instance must have attribute uid1 with value value1 AND attribute uid2 with value value2.

byFilter(). This method adds a filter to the query. If this method is called several times,conditions are appended with an AND connector. For example:

That means that the instance must have attribute uid1 with value value1 AND attribute uid2 with value value2.

byQuery(). Search tracked entity instances with any attribute matching the query.

byProgram(). Filter by enrollment program. Only one program can be specified.

byOrgUnits(). Filter by tracked entity instance organisation units. More than oneorganisation unit can be specified.

d2.trackedEntityModule().trackedEntityInstanceQuery()

.[repository mode]

.[filters]

.get()

d2.trackedEntityModule().trackedEntityInstanceQuery()

.byAttribute("uid1").eq("value1")

.byAttribute("uid2").eq("value2")

.get()

d2.trackedEntityModule().trackedEntityInstanceQuery()

.byFilter("uid1").eq("value1")

.byFilter("uid2").eq("value2")

.get()

7 Workflow 7.4.2 Tracker data search

20

Page 21: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

byOrgUnitMode(). Define the organisation unit mode. The possible modes are the next:

SELECTED. Specified units only.CHILDREN. Immediate children of specified units, including specified units.DESCENDANTS. All units in sub-hierarchy of specified units, including specified units.ACCESSIBLE. All organisation units accessible by the user (search scope).ALL. All units in system. Requires authority.

byProgramStartDate(). Define an enrollment start date. It only applies if a program hasbeen specified.

byProgramEndDate(). Define an enrollment end date. It only applies if a program hasbeen specified.

byTrackedEntityType(). Filter by TrackedEntityType. Only one type can be specified.

byIncludeDeleted(). Whether to include or not deleted tracked entity instances.Currently, this filter only applies to offline instances.

byStates(). Filter by sync status. Using this filter forces offline only mode.

Example:

Important: trackedEntityInstances retrieved using this repository arenot persisted in the database. It is possible to fully download themusing the byUid() filter of the TrackedEntityInstanceDownloaderwithin the tracked entity instance module.

7.4.3 Tracker data write

In general, there are two different cases to manage data creation/edition/deletion: the casewhere the object is identifiable (that is, it has an uid property) and the case where the object isnot identifiable.

Identifiable objects (TrackedEntityInstance, Enrollment, Event). These repositories have a uid()method that gives you access to edition methods for a single object. In case the object does notexist yet, it is required to create it first. A typical workflow to create/edit an object would be:

Use the CreateProjection class to add a new instance in the repository.Save the uid returned by this method.Use the uid() method with the previous uid to get access to edition methods.

And in code this would look like:

• ◦ ◦ ◦ ◦ ◦

d2.trackedEntityModule().trackedEntityInstanceQuery()

.byOrgUnits().eq("orgunitUid")

.byOrgUnitMode().eq(OrganisationUnitMode.DESCENDANTS)

.byProgram().eq("programUid")

.byAttribute("attributeUid").like("value")

.offlineFirst()

• • •

String eventUid = d2.eventModule().events().add(

EventCreateProjection.create("enrollment", "program", "programStage", "orgUnit", "attCombo"));

7 Workflow 7.4.3 Tracker data write

21

Page 22: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Non-identifiable objects (TrackedEntityAttributeValue, TrackedEntityDataValue). Theserepositories have a value() method that gives you access to edition methods for a single object.The parameters accepted by this method are the parameters that unambiguously identify avalue.

For example, writing a TrackedEntityDataValue would be like:

Data values of type Image involve an additional step to create/update/read the associated fileresource. More details in the Dealing with FileResources section below.

7.4.4 Tracker data upload

TrackedEntityInstance and Event repositories have an upload() method to upload Tracker dataand Event data (without registration) respectively. If the repository scope has been reduced byfilter methods, only filtered objects will be uploaded.

Data whose state is ERROR or WARNING cannot be uploaded. It is required to solve the conflictsbefore attempting a new upload: this means to do a modification in the problematic data, whichforces their state back to TO_UPDATE.

7.4.4.1 Tracker conflicts

Server response is parsed to ensure that data has been correctly uploaded to the server. In casethe server response includes import conflicts, these conflicts are stored in the database, so theapp can check them and take an action to solve them.

Conflicts linked to a TrackedEntityInstance, Enrollment or Event are automatically removed aftera successful upload of the object.

7.4.5 Tracker data: reserved values

Tracked Entity Attributes configured as unique and automatically generated are generated by theserver following a pattern defined by the user. These values can only be generated by the server,which means that we need to reserve them in advance so we can make use of them whenoperating offline.

The app is responsible for reserving generated values before going offline. This can be triggeredby:

d2.eventModule().events().uid(eventUid).setStatus(COMPLETED);

d2.trackedEntityModule().trackedEntityDataValues().value(eventUid, dataElementid).set(“5”);

d2.( trackedEntityModule() | eventModule() )

.[ filters ]

.upload();

d2.importModule().trackerImportConflicts()

// Reserve values for all the unique and automatically generated trackedEntityAttributes.

d2.trackedEntityModule().reservedValueManager().downloadAllReservedValues(numValuesToFillUp)

7 Workflow 7.4.4 Tracker data upload

22

Page 23: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Depending on how long the app expects to be offline, it can decide the quantity of values toreserve. In case the attribute pattern is dependant on the orgunit code, the SDK will reservevalues for all the relevant orgunits. More details about the logic in Javadoc.

Reserved values can be obtained by:

7.5 Aggregated data

7.5.1 Aggregated data download

By default, the SDK downloads aggregated data values, dataset complete registration values and approvals corresponding to:

DataSets: all available dataSets (those the user has at least read data access to).OrganisationUnits: capture scope.Periods: all available periods, which means at least:

Days: last 60 days.Weeks: last 13 weeks (including starting day variants).Biweekly: last 13 bi-weeks.Monthly: last 12 months.Bimonthly: last 6 bi-months.Quarters: last 5 quarters.Sixmonthly: last 5 six-months (starting in January and April).Yearly: last 5 years (including financial year variants).

In addition, if any dataset allows data entry for future periods, the Sdk will download thedata for those open periods and store them.

The Sdk also keeps track of the latest successful download in order to avoid downloadingunmodified server data.

In the download of data approvals, workflow and attribute option combination identifiers will beconsidered in addition to the organisation units and periods. The different possible states fordata approval are:

UNAPPROVABLE. Data approval does not apply to this selection. (Data is neither approvednor unapproved).UNAPPROVED_WAITING. Data could be approved for this selection, but is waiting for somelower-level approval before it is ready to be approved.UNAPPROVED_ELSEWHERE. Data is unapproved and is waiting for approval somewhere else(can not be approved here).UNAPPROVED_READY. Data is unapproved, and is ready to be approved for this selection.UNAPPROVED_ABOVE. Data is unapproved above.APPROVED_HERE. Data is approved, and was approved here (so could be unapprovedhere).APPROVED_ELSEWHERE. Data is approved, but was not approved here (so cannot beunapproved here).APPROVED_ABOVE. Data is approved above.

// Reserve values for a particular trackedEntityAttribute.

d2.trackedEntityModule().reservedValueManager().downloadReservedValues("attributeUid", numValuesToFillUp)

d2.trackedEntityModule().reservedValueManager().getValue("attributeUid", "orgunitUid")

d2.aggregatedModule().data().download()

• • •

◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦

• • •

7 Workflow 7.5 Aggregated data

23

Page 24: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

ACCEPTED_HERE. Data is approved and accepted here (so could be unapproved here).ACCEPTED_ELSEWHERE. Data is approved and accepted, but elsewhere.

Data approvals are downloaded only for versions greater than 2.29.

7.5.2 Aggregated data write

7.5.2.1 Periods

In order to write data values or data set complete registrations, it’s mandatory to provide aperiod id. Periods are stored in a table in the database and the provided period ids must bealready present in that table, otherwise, a Foreign Key error will be thrown. To prevent thatsituation, the PeriodHelper is exposed inside the PeriodModule. Before adding aggregateddata related to a dataSet, the following method must be called:

This will ensure that: 1. The app will pick one of the given periods, preventing malformed orwrong periods. 2. The app will only be able to pick the future periods defined by the field DataSet.openFuturePeriods. 3. The app will only be able to pick the past periods definedbased on the limits declared on the section Aggregated Data Download.

7.5.2.2 Data value

DataValueCollectionRepository has a value() method that gives access to edition methods. Theparameters accepted by this method are the parameters that unambiguously identify a value.

7.5.2.3 Data set complete registration

The Sdk provides within the data set module a collection repository for data set completeregistrations. This repository contains methods to add new completions and delete them.

To add a new data set complete registration is available an add() method:

In order to remove them from the database, the repository has a value() method that givesaccess to deletion methods (delete() and deleteIfExist()). The parameters accepted bythis method are the parameters that unambiguously identify the data set complete registration.

• •

Single<List<Period>> periods = d2.periodModule().periodHelper().getPeriodsForDataSet("dataSetUid");

DataValueObjectRepository valueRepository = d2.dataValueModule().dataValues()

.value("periodId", "orgunitId", "dataElementId", "categoryOptionComboId", "attributeOptionComboId");

valueRepository.set("value")

d2.dataSetModule().dataSetCompleteRegistrations()

.add(dataSetCompleteRegistration);

d2.dataSetModule().dataSetCompleteRegistrations() .value("periodId",

"orgunitId", "dataSetUid","attributeOptionCombo")

.delete()

7 Workflow 7.5.2 Aggregated data write

24

Page 25: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

7.5.3 Aggregated data upload

DataValueCollectionRepository has an upload() method to upload aggregated data values.

7.5.4 DataSet instances

A DataSetInstance in the SDK is a handy representation of the existing aggregated data. ADataSetInstance represents a unique combination of DataSet - Period - Orgunit -AttributeOptionCombo and includes extra information like sync state, value count ordisplayName for some properties.

7.6 Dealing with FileResources

The SDK offers a module (the FileResourceModule) and two helpers (the FileResourceDirectoryHelper and FileResizerHelper) that allow to work with files.

7.6.1 File resources module

This module contains methods to download the file resources associated with the downloadeddata and the file resources collection repository of the database.

File resources download. The download() method will search for the tracked entityattribute values and tracked entity data values whose tracked entity attribute type anddata element type are of the image type and whose file resource has not been previouslydownloaded and the method will download the file resources associated.

After downloading the files, you can obtain the different file resources downloadedthrough the repository.

File resource collection repository. Through this repository it is possible to request files,save new ones and upload them to the server.

Get. It behaves in a similar fashion to any other Sdk repository. It allows to getcollections by applying different filters if desired.

d2.dataValueModule().dataValues().upload();

d2.dataSetModule().dataSetInstances()

.[ filters ]

.get()

// For example

d2.dataSetModule().dataSetInstances()

.byDataSetUid().eq("datasetUid")

.byOrganisationUnitUid().eq("orgunitUid") .byPeriod().in("201901",

"201902")

.get();

d2.fileResourceModule().download();

d2.fileResourceModule().fileResources()

.[ filters ]

.get()

7 Workflow 7.5.3 Aggregated data upload

25

Page 26: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Add. To save a file you have to add it using the add() method of the repository byproviding an object of type File. The add() method will return the uid that wasgenerated when adding the file. This uid should be used to update the tracked entityattribute value or the tracked entity data value associated with the file resource.

Upload. Calling the upload() method will trigger a series of successive calls inwhich all non-synchronized files will be sent to the server. After each upload, theserver response will be processed. The server will provide a new uid to the fileresource and the Sdk will automatically rename the file and update the FileResource object and the tracked entity attribute values or tracked entity datavalues associated with it.

7.6.2 File resizer helper

The Sdk provides a helper to resize image files (FileResizerHelper). This helper contains a resizeFile() method that accepts the file you want to reduce and the dimension to which youwant to reduce it.

The possible dimensions are in the following table.

Small Medium Large

256px 512px 1024px

The helper takes the file, measures the height and width of the image, determines which of thetwo sides is larger and reduces the largest of the sides to the given dimension and the other sideis scaled to its proportional size. Image scaling will always keep the proportions.

In the event that the last image is smaller than the dimension to which you want to resize it, thesame file will be returned without being modified.

The resizeFile() method will return a new file located in the same parent directory of the fileto be resized under the name resized-DIMENSION- + the name of the file without resizing.

7.6.3 File resource directory helper

The FileResourceDirectoryHelper helper class provides two methods.

getFileResourceDirectory(). This method returns a File object whose path points tothe sdk_resources directory where the Sdk will save the files associated with the fileresources.

getFileCacheResourceDirectory(). This method returns a File object whose pathpoints to the sdk_cache_resources directory. This should be the place where volatilefiles are stored, such as camera photos or images to be resized. Since the directory iscontained in the cache directory, Android may auto-delete the files in the cache directoryonce the system is about to run out of memory. Third party applications can also deletefiles from the cache directory. Even the user can manually clear the cache from Settings.However, the fact that the cache can be cleared in the methods explained above should

d2.fileResourceModule().fileResources() .add(file); // Single<String> The

fileResource uid

d2.fileResourceModule().fileResources()

.upload()

7 Workflow 7.6.2 File resizer helper

26

Page 27: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

not mean that the cache will automatically get cleared; therefore, the cache will need to betidied up from time to time proactively.

7 Workflow 7.6.3 File resource directory helper

27

Page 28: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

8 Error management

Errors that happen in the context of the SDK are wrapped in a type of exception: D2Error, withthe following fields:

Attribute Type Optional Description

errorComponent D2ErrorComponent true Source of theerror:Database, SDKor Server.

errorCode D2ErrorCode true SDK-definedunique errorcode.

errorDescription String true Description ofthe error inenglish(technicaldetails, just forlogs anddebugging).

httpErrorCode Integer false If caused byHTTP request,HTTP errorcode.

originalException Exception false Original JavaExceptioncausing theerror, if any.

Any operation requested to the SDK can throw an error.

For operations returning RxJava objects, the errors can be extracted in the following way:

For blocking operations, it is also possible to retrieve a D2Error. The errors can beextracted by caching them as shown in the following code snippet:

d2.userModule().logIn(username, password, url)

.subscribe(

user -> { },

error -> { if (error instanceof

D2Error) {

D2Error d2Error = (D2Error) error; Log.e("LOGIN", d2Error.errorComponent() + " " +

d2Error.httpErrorCode() + " " + d2Error.errorCode());

}

}

);

try {

d2.userModule().blockingLogIn(username, password, url);

8 Error management 7.6.3 File resource directory helper

28

Page 29: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

D2Errors are persisted in the Database when they occur, so they can be analyzed afterwardsand diagnose possible problems. They can be accessed through it’s own repository:

The SDK team is now working together with the core team in order to provide a full list ofcommon error codes, but it’s still a work in progress.

} catch (Exception e) {

if (e.getCause() instanceof D2Error) {

D2Error d2Error = (D2Error) e.getCause();

Log.e("LOGIN", d2Error.errorComponent() + " " + d2Error.httpErrorCode() + " " + d2Error.errorCode());

}

}

d2.maintenanceModule().d2Errors()

.byD2ErrorComponent().eq(D2ErrorComponent.Server)

.get();

8 Error management 7.6.3 File resource directory helper

29

Page 30: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

9 SMS module

SMS Module can be used as a fallback method to upload data when an Internet connection is notavailable. It requires an additional setup in the server: an SMS gateway must be configured in theserver to be able to receive SMS; optionally, the server might have the capability to send SMSback to the clients with the response.

Depending on the mobile provider, sending SMS might imply an extra cost. For this reason, SMSModule is only intended for granular data upload. It is not used for metadata download or bulkdata download/upload. Additionally, the data is compressed by using the SMS compressionlibrary so the content can fit in a lower number of messages. This library is shared with thebackend.

For testing purposes you can use the DHIS2 Android SMS Gateway.

In the SDK, the SMS module can be accessed from D2.

This module is disabled by default and it must be explicitly enabled and configured. It includesthree components that give access to module features.

ConfigCase: it is used to set initial data that is common for all sms sending tasks likegateway numbers, timeout, execute downloading of metadata ids object.SmsSubmitCase. it is used to convert the DHIS2 data that will be sent by the Sdk, to send itby SMS and to check the progress of the submission and his result.QrCodeCase: it is used to convert DHIS2 data to String. This String is a compressedrepresentation of the DHIS2 data. This is useful to avoid send large content on SMSes.

A typical workflow to use the SMS Module would be like:

Enable the SMS module.Sync metadata. The SMS Module downloads additional metadata from the server, so thisstep must be done while Internet connection is available and after the module is enabled.Send data using SMS Module.

This a code example of a typical workflow (it used blocking methods for code simplicity):

d2.smsModule()

• •

// Enable SMS Module

d2.smsModule().configCase().setModuleEnabled(true).blockingAwait();

// Sync SMS Module metadata using SMS Module

d2.smsModule().configCase().refreshMetadataIds().blockingAwait();// or using

metadata module

d2.metadataModule().blockingDownload();

// Configure, at least, the gateway number. See ConfigCase for more parameters

d2.smsModule().configCase().setGatewayNumber("gateway-number").blockingAwait();

// Send data. For example a tracker event:

SmsSubmitCase case = d2.smsModule().smsSubmitCase();

9 SMS module 7.6.3 File resource directory helper

30

Page 31: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Important: the app is responsible for asking the user for permissions(READ_PHONE_STATE, SEND_SMS, READ_SMS, RECEIVE_SMS).Otherwise, SMS module will fail.

9.1 SMS version

SMSs are sent in a compressed format from/to the server. This task is done by the SMSCompression library, which is responsible for doing the conversion between the plain text andthe compressed format.

The SDK includes the latest available version of the compression library, but there is noguarantee that the server is using it as well. For this reason, it is required to check the serverversion in order to enable/disable some functionalities. The SMS version in the server can bechecked by:

Overview of versions - features:

Version 1: - Aggregated data. - Tracker / event data, but there are some known bugs. Werecommend not to enable tracker SMS sync in version 1.

Version 2: - Add support for empty lists. - Add support for geometry in events (POINT). - Addmissing properties in events (event data, due date) and enrollments (execution date, incidentdate). - Add support for sending enrollment + events in the same SMS submit case.

For more information, please check SMS compression repository.

9.2 ConfigCase

Use this case to configure the SMS Module before using it. It is required, at least, to:

Enable the module.Set a gateway number.Download the metadata ids.

There are other optional parameters to control if the SDK should wait a response from the serveror not and the response timeout. Also, it is possible to specify the sender number so messagesreceived from other senders are ignored.

9.3 SmsSubmitCase

Use this case to create a new submission and send it. Submission cases are not reusable and canonly be sent once. To create a new submission case call the method:

Integer numSMSs = case.convertTrackerEvent("event-uid").blockingGet();

case.send().blockingSubscribe();

d2.systemInfoModule().versionManager().getSmsVersion()

d2.smsModule().configCase()

• • •

SmsSubmitCase case = d2.smsModule().smsSubmitCase();

9 SMS module 9.1 SMS version

31

Page 32: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

A submission involve the following steps:

Specify the data to submit. This means to call a method like convert*().Send the message.Optionally check for confirmation SMS.

As an example, sending a tracker event will be like:

The next methods can be used to set the DHIS2 data to send:

convertSimpleEvent(). To set a simple event.convertTrackerEvent(). To set a tracker event.convertEnrollment(). To set an enrollment.convertDataSet(). To set a data set.convertRelationship(). To set a relationship.convertDeletion(). To delete an event.

The methods above returns a single with the number of messages that the items takes up. Anexample of the use of these methods is shown in the next snippet.

To send the data converted earlier the Sdk provides a send() method that returns a stream ofthe current states. Also it is possible to get the submission id by calling the method getSubmissionId().

It is also possible to wait for the SMS result by calling the checkConfirmationSms() method. Itreturns a Completable object where completion means that the SMS was received successfully.In case that the result cannot be found, it returns an error. The date accepted is the minimumdate for which confirmation is going to be checked, this is used to skip old messages that mayhave the same submission id.

These methods can fail and return a PreconditionFailed object if some conditions are notsatisfied. The preconditions errors are:

NO_NETWORK.NO_CHECK_NETWORK_PERMISSION.NO_RECEIVE_SMS_PERMISSION.NO_SEND_SMS_PERMISSION.NO_GATEWAY_NUMBER_SET.NO_USER_LOGGED_IN.

• • •

SmsSubmitCase case = d2.smsModule().smsSubmitCase();

Integer numSMSs = case.convertTrackerEvent("event-uid").blockingGet();

case.send().blockingSubscribe();

• • • • • •

Single<Integer> convertTask = d2.smsModule().smsSubmitCase()

.convertEnrollment("enrollment_uid")

d2.smsModule().smsSubmitCase().send()

d2.smsModule().smsSubmitCase().checkConfirmationSms(new Date());

• • • • • •

9 SMS module 9.3 SmsSubmitCase

32

Page 33: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

NO_METADATA_DOWNLOADED.SMS_MODULE_DISABLED.

9.4 QrCodeCase

Use this method to obtain a compressed representation of the data.

QrCodeCase can convert the next type of DHIS2 objects:

Simple events. Using the generateSimpleEventCode() method and passing an eventuid.Tracker events. Using the generateTrackerEventCode() method and passing an eventuid.Enrollments. Using the generateEnrollmentCode() method and passing an enrollmentuid.Relationships. Using the generateRelationshipCode() method and passing arelationship uid.Data sets. Using the generateDataSetCode() method and passing a data set uid, anorganisation unit uid, an attribute option combo and a period id.

Also it is possible to get compressed strings that can be used to delete events:

Deletions. Using the generateDeletionCode() method and passing the uid of the event.

These methods returns a Single with the compressed data. The next code snippet shows anexample of how it can be used.

• •

d2.smsModule().qrCodeCase()

Single<String> convertTask = d2.smsModule().qrCodeCase().generateEnrollmentCode(enrollmentUid);

9 SMS module 9.4 QrCodeCase

33

Page 34: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

10 Object style

Some elements include a property called “style” that defines an icon and a color. This visualinformation is very useful to quickly navigate through the application. Some typical use cases:

Distinguish different programs and trackedEntityTypes in a program list.In data entry forms with an optionSet, show the options with icons and colors instead ofnames.Different programStages within a program.

This property is optional and it is not defined in most of the cases. An object style might have anicon, or a color, or both.

10.1 Icon

The set of icons used in DHIS2 are included in the SDK. They are located within the resources, inthe “drawable” folder. Currently they are predefined and cannot be customized.

10.2 Color

It contains the Hex value for the color. It can be used to customize the background, text color,line headings, etc.

• •

// Illustrative code to get the resource id

if (program.style().icon() != null) {

String iconName = program.style().icon();

int resourceId = getResources().getIdentifier(iconName, "drawable", getPackageName());

}

program.style().color(); // For example #9C33FF

10 Object style 10.1 Icon

34

Page 35: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

11 DHIS2 version compatibility strategy

The SDK guarantees compatibility with the latest three DHIS 2 releases (see Compatibility). Incase the SDK is still compatible with previous DHIS2 versions and no major issues have beendetected, compatibility could be extended to previous versions.

In order to avoid accidental login into unsupported DHIS 2 instances, the SDK blocks connectionsto version that are not supported yet or that have been deprecated.

Regarding data model and compatibility, the main approach is to extend the data model to beable to support all the DHIS 2 versions. It usually happens that new DHIS 2 versions introduceextra functionality and do not remove existing one, so supporting a new DHIS 2 version usuallymeans to use the latest data model.

As a general rule the SDK tries to avoid breaking changes in its API and new features are optionalto the user. This rule is followed as much as possible, but there are cases where supporting oldand new APIs to avoid breaking has a very high cost. In this scenario the SDK might introduce

breaking changes to be compatible with the new DHIS 2 version.

Here you can find a few examples of changes in the SDK and the effect in the app.

11.1 Example: minor change

Until version 2.30, Program model had a boolean attribute called “captureCoordinates”. Thisattribute indicates if coordinates (point) must be stored in that program. As of 2.30, this attributewas replaced by “featureType” with 4 possible values: NONE, POINT, POLYGON,MULTI_POLYGON.

Changes in the SDK:

As of 2.30, the SDK uses the attribute “featureType”. If the server version is lower than 2.30, theSDK maps the “captureCoordinates” value to:

false - NONEtrue - POINT

Changes in the app:

The app is now force to use “featureType”. Modifications in the code are quite straightforward.

11.2 Example: major change

As of 2.30, Relationship model suffered from a deep refactor in order to allow relationshipsbetween event, enrollment and trackedEntityInstances. The SDK adopted the model for 2.30 andexposes this model to the app. When interacting with the API, the SDK translates between bothmodels internally.

Changes in the app:

This change implies that the app must adopt a different model and changes are not sostraightforward.

• •

11 DHIS2 version compatibility strategy 11.1 Example: minor change

35

Page 36: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

12 Direct database interaction

Repository methods cover most of the needs of the application. But in some cases theapplication might want to interact directly with the database.

The SDK exposes a DatabaseAdapter object to execute raw statements in the database. Also, SDKmodel classes include helper methods to create instances from a Cursor.

For example, read the list of constants using repositories and interacting directly with thedatabase.

TableInfo classes include some useful information about table structure, like table and columnnames.

// Using repositories

d2.constantModule().constants().blockingGet() // List<Constant>

// Direct database interaction

String query = "SELECT * FROM " + ConstantTableInfo.TABLE_INFO.name();

try (Cursor cursor = Sdk.d2().databaseAdapter().rawQuery(query)) {

List<Constant> constantList = new ArrayList<>();

if (cursor.getCount() > 0) {

cursor.moveToFirst();

do {

collection.add(Constant.create(cursor));

}while(cursor.moveToNext());

} return

constantList; // List<Constant>

}

12 Direct database interaction 11.2 Example: major change

36

Page 37: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

13 Program rule engine

The program rule engine is not provided within the SDK. It is implemented in a separate library,so the same code is used by backend and android apps.

More info dhis2-rule-engine.

13 Program rule engine 11.2 Example: major change

37

Page 38: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

14 Program indicator engine

The SDK includes its own Program Indicator engine for the evaluation of in-line Program

Indicators. These kind of indicators are evaluated within the context of an enrollment and theyare usually placed in the data entry form offering additional information to the data encoder.This means that, even though they are regular Program Indicators and can be calculated acrossenrollments, they have provide useful information within a single enrollment.

A good example, “Average time between visits”.

A bad example, “Number of active TEIs”: it would always be 1.

In order to trigger the Program Indicator Engine, just execute:

Compatibility table:

Function (d2:)(doc) Supported

ceil Yes

floor Yes

round Yes

modulus Yes

zing Yes

oizp Yes

concatenate Yes

condition Yes

minutesBetween No

daysBetween Yes

monthsBetween Yes

yearsBetween Yes

relationshipCount No

count No

countIfValue No

countIfZeroPos No doc

hasValue No

zpvc Yes

validatePatten Yes

left Yes

right Yes

substring Yes

split Yes

length Yes

d2.programModule()

.programIndicatorEngine() .getProgramIndicatorValue(<enrollment-uid>, <event-uid>, <program-

indicator-uid>);

14 Program indicator engine 11.2 Example: major change

38

Page 39: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

Function (d2:)(doc) Supported

inOrgUnitGroup No doc

hasUserRole No doc

Variables (doc) Supported

current_date Yes

event_date Yes

due_date Yes

event_count Yes

enrollment_date Yes

incident_date Yes

program_stage_id No

program_stage_name No

enrollment_status Yes

value_count Yes

zero_pos_value_count Yes

reporting_period_start N/A

reporting_period_end N/A

tei_count N/A

enrollment_count N/A

organisationunit_count N/A

14 Program indicator engine 11.2 Example: major change

39

Page 40: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

15 Debugging

Besides the regular debugging tools in AndroidStudio, the library Stetho allows the use ofChrome Developer Tools for debugging network traffic and explore the database.

Setup up Stetho by adding the following dependencies in your gradle file:

dependencies {

implementation 'com.facebook.stetho:stetho:1.5.0'

implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'

}

Then add a network interceptor in D2Configuration object:

Finally enable initialize Stetho in the Application class:

At this point you should be able to debug the app/sdk by using Chrome Inspector Tools:

Run a test in debug mode and set a breakpoint.In Chrome Browser open the device inspector.Select the remote target and click on Inspect. A new windows will appear showing theChrome developer tools.Explore database in “Resources > Web SQL”.Explore network traffic in “Network”.

D2Configuration.builder()

... .networkInterceptors(Collections.singletonList(new

StethoInterceptor()))

...

.build();

if (DEBUG) {

Stetho.initializeWithDefaults(this);

}

• • •

• •

15 Debugging 11.2 Example: major change

40

Page 41: DHIS 2 Android SDK Developer Guide · Other libraries internally used by the SDK are: Dagger for dependency injection, Jackson for JSON parsing, Retrofi and t OkHttpClient for API

16 Known issues

16.1 Data set completion

In DHIS2 version 2.33.0 and 2.33.1, if a dataset is mark as uncompleted in the server, thisvalue is not updated in the SDK. In those versions the API did not expose enoughinformation to know if the status was complete or uncomplete.

16 Known issues 16.1 Data set completion

41