diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreHeaderViews.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreHeaderViews.kt index fd4690ea26..84ffef9247 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreHeaderViews.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreHeaderViews.kt @@ -19,6 +19,7 @@ package com.google.android.fhir.datacapture.extensions import android.content.Context import android.text.SpannableStringBuilder import android.text.Spanned +import android.text.method.LinkMovementMethod import android.view.View.GONE import android.view.View.VISIBLE import android.widget.Button @@ -82,7 +83,10 @@ fun initHelpViews( } } } - helpTextView.updateTextAndVisibility(questionnaireItem.localizedHelpSpanned) + helpTextView.apply { + updateTextAndVisibility(questionnaireItem.localizedHelpSpanned) + movementMethod = LinkMovementMethod.getInstance() + } } /** diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/GroupHeaderView.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/GroupHeaderView.kt index da85cbac4e..e2d1872bc4 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/GroupHeaderView.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/GroupHeaderView.kt @@ -17,6 +17,7 @@ package com.google.android.fhir.datacapture.views import android.content.Context +import android.text.method.LinkMovementMethod import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout @@ -55,11 +56,17 @@ class GroupHeaderView(context: Context, attrs: AttributeSet?) : LinearLayout(con helpCardStateChangedCallback = questionnaireViewItem.helpCardStateChangedCallback, ) prefix.updateTextAndVisibility(questionnaireViewItem.questionnaireItem.localizedPrefixSpanned) - // CQF expression takes precedence over static question text - question.updateTextAndVisibility(questionnaireViewItem.questionText) - hint.updateTextAndVisibility( - questionnaireViewItem.enabledDisplayItems.getLocalizedInstructionsSpanned(), - ) + question.apply { + // CQF expression takes precedence over static question text + updateTextAndVisibility(questionnaireViewItem.questionText) + movementMethod = LinkMovementMethod.getInstance() + } + hint.apply { + updateTextAndVisibility( + questionnaireViewItem.enabledDisplayItems.getLocalizedInstructionsSpanned(), + ) + movementMethod = LinkMovementMethod.getInstance() + } visibility = getHeaderViewVisibility(prefix, question, hint) applyCustomOrDefaultStyle( questionnaireViewItem.questionnaireItem, diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/HeaderView.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/HeaderView.kt index 7e5e77231d..b723190448 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/HeaderView.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/HeaderView.kt @@ -17,6 +17,7 @@ package com.google.android.fhir.datacapture.views import android.content.Context +import android.text.method.LinkMovementMethod import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout @@ -55,13 +56,17 @@ class HeaderView(context: Context, attrs: AttributeSet?) : LinearLayout(context, helpCardStateChangedCallback = questionnaireViewItem.helpCardStateChangedCallback, ) prefix.updateTextAndVisibility(questionnaireViewItem.questionnaireItem.localizedPrefixSpanned) - // CQF expression takes precedence over static question text - question.updateTextAndVisibility( - appendAsteriskToQuestionText(question.context, questionnaireViewItem), - ) - hint.updateTextAndVisibility( - questionnaireViewItem.enabledDisplayItems.getLocalizedInstructionsSpanned(), - ) + question.apply { + // CQF expression takes precedence over static question text + updateTextAndVisibility(appendAsteriskToQuestionText(question.context, questionnaireViewItem)) + movementMethod = LinkMovementMethod.getInstance() + } + hint.apply { + updateTextAndVisibility( + questionnaireViewItem.enabledDisplayItems.getLocalizedInstructionsSpanned(), + ) + movementMethod = LinkMovementMethod.getInstance() + } // Make the entire view GONE if there is nothing to show. This is to avoid an empty row in the // questionnaire. visibility = getHeaderViewVisibility(prefix, question, hint) diff --git a/docs/use/WFL/Demo-app.md b/docs/use/WFL/Demo-app.md new file mode 100644 index 0000000000..f0deacdd85 --- /dev/null +++ b/docs/use/WFL/Demo-app.md @@ -0,0 +1,20 @@ +# Demo App + +The *Workflow Demo* app demonstrates the capabilities of CarePlan Generation API and the Activity Flow API. The app has a Patient card in the top showing the details and a carousel in the bottom with each card representing a particular phase of the activity flow. + +To run this app in Android Studio, [create a run/debug configuration](https://developer.android.com/studio/run/rundebugconfig) for the `workflow_demo` module using the [Android App](https://developer.android.com/studio/run/rundebugconfig#android-application) template and run the app using the configuration. + +Alternatively, run the following command to build and install the debug APK on your device/emulator: + +```shell +./gradlew :workflow_demo:installDebug +``` + +## Instructions +1. Click on the **Initialize** button to install the required dependencies for an activity flow. The dependencies are already bundled in the assets folder of the workflow demo app. After the dependencies are successfully installed, **Start** Button becomes enabled in the _Proposal_ card. +2. Now, click on the **Start** to generate a CarePlan which intern has a _Proposal_ Resource. This resource is then used by the app to create a new Activity Flow and the _Proposal_ card now shows the details of the resource with the **Start** button disabled now. The carousel auto moves to the next Phase Card i.e. _Plan_. +3. Repeat step 2 to move forward through the phases. +4. To restart the Activity click **Restart** Flow that removes all the resources related to the flow and moves the app back to step 2. +5. The overflow menu on the action bar may be used to switch between various Activities supported in the demo app. + +![Workflow Demo](workflow_demo_app.gif) diff --git a/docs/use/WFL/Run-an-Activity-Flow.md b/docs/use/WFL/Run-an-Activity-Flow.md new file mode 100644 index 0000000000..ff2731af40 --- /dev/null +++ b/docs/use/WFL/Run-an-Activity-Flow.md @@ -0,0 +1,128 @@ +# ActivityFlow + +![Activity Flow](activity_flow.svg) + +The `ActivityFlow` class manages the workflow of clinical recommendations according to the [FHIR Clinical Practice Guidelines (CPG) specification](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#activity-lifecycle---request-phases-proposal-plan-order). It implements an activity flow as defined in the FHIR CPG IG, allowing you to guide clinical recommendations through various phases (proposal, plan, order, perform). + +You can start new workflows with an appropriate request resource from the generated [CarePlan](Generate-A-Care-Plan.md) or resume existing ones from any phase. + +**Important Considerations:** + +* **Thread Safety:** The `ActivityFlow` is not thread-safe. Concurrent changes from multiple threads may lead to unexpected behavior. +* **Blocking Operations:** Some methods of `ActivityFlow` and its associated `Phase` interface may block the caller thread. Ensure these are called from a worker thread to avoid UI freezes. + +## Creating an ActivityFlow + +Use the appropriate `ActivityFlow.of()` factory function to create an instance. You can start anew flow with a `CPGRequestResource` or resume an existing flow from a `CPGRequestResource` or `CPGEventResource` based on the current state. + +**Example:** +```kotlin +val repository = FhirEngineRepository(FhirContext.forR4Cached(), fhirEngine) +val request = CPGMedicationRequest( medicationRequestGeneratedByCarePlan) +val flow = ActivityFlow.of(repository, request) +``` + +## Navigating phases + +An `ActivityFlow` progresses through a series of phases, represented by the `Phase` class. Access the current phase using `getCurrentPhase()`. + +**Example:** +```kotlin + when (val phase = flow.getCurrentPhase( )) { + is Phase.ProposalPhase -> { /* Handle proposal phase */ } + is Phase.PlanPhase -> { /* Handle plan phase */ } + // ... other phases +} +``` + +## Transitioning between the phases + +`ActivityFlow` provides functions to prepare and initiate the next phase: + +* **`prepare...()`:** Creates a new request or event for the next phase without persisting changes. +* **`initiate...()`:** Creates a new phase based on the provided request/event and persists changes to the repository. + +**Example:** +```kotlin +val preparedPlan = flow.preparePlan().getOrThrow( ) +// ... modify preparedPlan +val planPhase = flow.initiatePlan(preparedPlan).getOrThrow( ) +``` + +## Transitioning to Perform Phase + +The `preparePerform()` function requires the event type as a parameter since the perform phase can create different event resources. + +**Example:** +```kotlin +val preparedPerformEvent = flow.preparePerform( CPGMedicationDispenseEvent::class.java).getOrThrow() + // ... update preparedPerformEvent + val performPhase = flow.initiatePerform( preparedPerformEvent). getOrThrow( ) +``` + +## Updating states in a phase + +* **`RequestPhase`:** (`ProposalPhase`, `PlanPhase`, `OrderPhase`) allows updating the request state using `update()`. +```kotlin +proposalPhase.update( + proposalPhase.getRequestResource().apply { setStatus(Status.ACTIVE) } +) +``` +* **`EventPhase`:** (`PerformPhase`) allows updating the event state using `update()` and completing the phase using `complete()`. +```kotlin +performPhase.update( + performPhase.getEventResource().apply { setStatus(EventStatus.COMPLETED) } +) +``` +## API List +### Factory functions + +* `ActivityFlow.of(...)`: Various overloads for creating `ActivityFlow` instances with different resource types. Refer to the code for specific usage. + +### Phase transition API + +* `preparePlan()`: Prepares a plan resource. +* `initiatePlan(...)`: Initiates the plan phase. +* `prepareOrder()`: Prepares an order resource. +* `initiateOrder(...)`: Initiates the order phase. +* `preparePerform(...)`: Prepares an event resource for the perform phase. +* `initiatePerform(...)`: Initiates the perform phase. + +### Other API +* `getCurrentPhase()`: Returns the current `Phase` of the workflow. + +### Request phase API + +* `getRequestResource()`: Returns a copy of resource. +* `update(..)`: Updates the resource. +* `suspend(..)`: Suspends the phase. +* `resume(..)`: Resumes the phase. +* `enteredInError(..)`: Marks the request entered-in-error. +* `reject(..)`: Rejects the phase. + +### Event phase API + +* `getEventResource()`: Returns a copy of resource. +* `update(..)`: Updates the resource. +* `suspend(..)`: Suspends the phase. +* `resume(..)`: Resumes the phase. +* `enteredInError(..)`: Marks the event entered-in-error. +* `start(..)`: Starts the event. +* `notDone(..)`: Marks the event not-done. +* `stop(..)`: Stop the event. +* `complete(..)`: Marks the event as complete. + + +## Supported activities +The library currently doesn't implement all of the activities outlined in the [activity profiles](https://build.fhir.org/ig/HL7/cqf-recommendations/profiles.html#activity-profiles). New activities may be added as per the requirement from the application developers. + +| Activity | Request | Event | +|--------------------|-------------------------|-----------------------| +| Send a message | CPGCommunicationRequest | CPGCommunication | +| Order a medication | CPGMedicationRequest | CPGMedicationDispense | + +## Additional resources + +* [FHIR Clinical Practice Guidelines IG](https://build.fhir.org/ig/HL7/cqf-recommendations/) +* [Activity Flow](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#activity-flow) +* [Activity Profiles](https://build.fhir.org/ig/HL7/cqf-recommendations/profiles.html#activity-profiles) \ No newline at end of file diff --git a/docs/use/WFL/activity_flow.svg b/docs/use/WFL/activity_flow.svg new file mode 100644 index 0000000000..dac4eda98f --- /dev/null +++ b/docs/use/WFL/activity_flow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/use/WFL/index.md b/docs/use/WFL/index.md index 3681c450ed..da883ad9c3 100644 --- a/docs/use/WFL/index.md +++ b/docs/use/WFL/index.md @@ -6,6 +6,8 @@ The [Workflow](https://build.fhir.org/workflow.html) Library provides decision s 1. The [PlanDefinition](https://build.fhir.org/plandefinition.html) resource describes a plan or protocol for the care of a given patient. This could include a treatment plan for a specific condition, a discharge plan for a hospitalized patient, or a care plan for managing chronic illness. The output of this operation will be a CarePlan resource, which represents the plan that has been tailored and applied to the specific patient or group of patients. The CarePlan resource will include details about the actions that are part of the plan, the timing and frequency of those actions, and any relevant supporting information. It may also include references to other resources that are relevant to the plan, such as observations or procedures that are part of the plan. The Apply operator can be used to determine the specific actions or interventions that should be taken as part of a care plan, based on the patient's current status and other relevant factors. For example, it could be used to determine which medications a patient should be prescribed or to identify any necessary referrals to other healthcare providers. +1. The [Activity Flow](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#activity-flow) is based on the workflow module in FHIR. It is used to create activities for the request resources generated in a CarePlan and take them through the various phases(proposal, plan, order, and perform) of the activity lifecycle. + 1. The [Library](https://build.fhir.org/library.html) resource describes a container for clinical knowledge assets. One of these assets is a shareable library of clinical logic, written in Clinical Quality Language (CQL). Users of the Workflow library can call an evaluation operator directly from the Library resource and run individual expressions at will. The output will be Parameters resource with the results of each expression evaluated. This operator should be used when the use case does not fit into a PlanDefinition or a Measure Evaluate. It's recommended that these 3 types of resources are authored within the scope of a [FHIR IG](https://www.hl7.org/fhir/implementationguide.html). The IG can then be published online and imported by the Android SDK. To import an IG, Android SDK users must simply copy the required files from the IG package into the `assets` folder and parse those files using the regular FHIR Parser. @@ -14,14 +16,17 @@ The workflow library is dependent on the [Engine library](../FEL/index.md). Oper Future features of the library will provide support for Tasking and other Workflow related requirements -## Next Steps +## Next steps * [Getting Started](Getting-Started.md) -* Guides +* Workflow Guides * [Generate a Care Plan](Generate-A-Care-Plan.md) + * [Run an Activity Flow](Run-an-Activity-Flow.md) * [Evaluate a Measure](Evaluate-a-Measure.md) +* Other Operations * [Evaluate a Library](Evaluate-a-Library.md) * [Compile CQL](Compile-and-Execute-CQL.md) + ## Data safety diff --git a/docs/use/WFL/workflow_demo_app.gif b/docs/use/WFL/workflow_demo_app.gif new file mode 100644 index 0000000000..f4f88fea67 Binary files /dev/null and b/docs/use/WFL/workflow_demo_app.gif differ diff --git a/mkdocs.yaml b/mkdocs.yaml index c7b8afc9e7..cad80a0b2b 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -39,6 +39,8 @@ nav: - use/WFL/Evaluate-a-Measure.md - use/WFL/Evaluate-a-Library.md - use/WFL/Compile-and-Execute-CQL.md + - use/WFL/Run-an-Activity-Flow.md + - Demo App: use/WFL/Demo-app.md - use/extensions.md - API Doc: use/api.md - Use Snapshots: use/snapshots.md