This page describes how to configure an Android Studio project to use the Structured Data Capture Library, and simple examples of how to use the library.
This guide is intended for developers who are familiar with basic Android development with Kotlin, and proficient in working with FHIR concepts and resources.
The Structured Data Capture Library is available through Google’s Maven repository, which should be already configured for Android projects created in Android Studio 3.0 and higher.
Add the following dependencies to your module’s app-level build.gradle
file,
typically app/build.gradle
:
dependencies {
// ...
implementation 'com.google.android.fhir:data-capture:1.0.0'
implementation 'androidx.fragment:fragment-ktx:1.5.5'
}
The minimum API level supported is 24. The library also requires Android Gradle Plugin version 4.0.0 or later for Java 8+ API desugaring support.
The following examples assume you already have a FHIR questionnaire as a JSON file. If you need one, the FHIR specification has several examples like this one.
QuestionnaireFragment
is the interface for working with
FHIR questionnaires, and also a
fragment for rendering them.
See the QuestionnaireFragment guide for details. To render a questionnaire in
your app, follow these steps:
Add a FragmentContainerView
to your activity’s layout to contain the
Questionnaire.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
... >
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Create a bundle with the JSON questionnaire content for the fragment, in
this case as a String
read from a JSON file stored in the assets
folder.
// Small questionnaires can be read directly as a string, larger ones
// should be passed as a URI instead.
val questionnaireJsonString =
application.assets.open("questionnaire.json")
.bufferedReader().use { it.readText() }
val bundle = bundleOf( QuestionnaireFragment.EXTRA_QUESTIONNAIRE_JSON_STRING to
questionnaireJsonString )
Set the fragment to the FragmentContainerView
.
if (savedInstanceState == null) {
supportFragmentManager.commit {
setReorderingAllowed(true)
add<QuestionnaireFragment>(R.id.fragment_container_view, args = bundle)
}
}
The getQuestionnaireResponse
function of QuestionnaireFragment
returns the
current form answers in a
FHIR QuestionnaireResponse,
specifically as an instance of the
HAPI FHIR Structures data model’s QuestionnaireResponse.
This provides a
convenient interface
to work with the data:
val fragment: QuestionnaireFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container_view) as QuestionnaireFragment
val questionnaireResponse = fragment.getQuestionnaireResponse()
// For example, convert the whole response to a string
val questionnaireResponseString = context.newJsonParser().encodeResourceToString(questionnaireResponse)
Log.d(
"response", questionnaireResponseString
)
This could be in a callback for the user after pressing a Submit button.
ResourceMapper
converts data between a QuestionnaireResponse and other FHIR
resources by implementing data
extraction and
population from the
HL7 Structured Data Capture Implementation Guide.
See the ResourceMapper guide for more information.
The Structured Data Capture Library supports two mechanisms for data extraction: Definition-based extraction and StuctureMap-based extraction.
Both methods require a Questionnaire
and corresponding
QuestionnaireResponse
:
val jsonParser = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser()
val myQuestionnaire =
jsonParser.parseResource(questionnaireJson) as Questionnaire
// also need the QuestionnaireResponse, probably like this
val myQuestionnaireResponse = fragment.getQuestionnaireResponse()
For Definition-based extraction, just pass the Questionnaire
and
QuestionnaireResponse
to ResourceMapper.extract()
.
lifecycle.coroutineScope.launch {
val bundle =
ResourceMapper.extract(questionnaire, questionnaireResponse)
}
StructureMap-based Extraction also requires a StructureMapExtractionContext
that contains the StructureMap. For example, if your StructureMap is written in
the FHIR Mapping Language:
val mappingStr = "map ..."
lifecycle.coroutineScope.launch {
val bundle =
ResourceMapper.extract(
myQuestionnaire,
myQuestionnaireResponse,
StructureMapExtractionContext(context = applicationContext) { _, worker ->
StructureMapUtilities(worker).parse(mappingStr, "")
},
)
}
The extract
function is a suspend function, so these examples must be called
from an appropriate
coroutine.