diff --git a/.github/actions/commonSetup/action.yml b/.github/actions/commonSetup/action.yml index e481396b14..621d1f2081 100644 --- a/.github/actions/commonSetup/action.yml +++ b/.github/actions/commonSetup/action.yml @@ -15,6 +15,8 @@ runs: - name: "Setup Gradle" uses: gradle/gradle-build-action@v2 with: + # See https://github.com/marketplace/actions/gradle-build-action#enable-dependency-graph-generation-for-a-workflow + dependency-graph: generate-and-submit # Limit the size of the cache entry. # These directories contain instrumented/transformed dependency jars which can be reconstructed relatively quickly. gradle-home-cache-excludes: | diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000000..04193c4016 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,20 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + # Gotta Catch 'Em All! (i.e. don't wait and propose them only "trickled") + open-pull-requests-limit: 99 + + - package-ecosystem: gradle + directory: / + schedule: + interval: weekly + day: monday + time: "04:00" + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + time: "04:00" diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 0000000000..6b30b500f4 --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,51 @@ +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '32 13 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-22.04-64core + timeout-minutes: 60 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Switch to Java 17 from Eclipse Temurin distro + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: temurin + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + + # TODO: use Autobuild instead of ./gradlew after https://github.com/github/codeql-action/issues/1417 is fixed + # - name: Autobuild + # uses: github/codeql-action/autobuild@v2 + - name: Build with Gradle + run: ./gradlew --scan --full-stacktrace -Dorg.gradle.dependency.verification=off compileDebugAndroidTestSources + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/README.md b/README.md index e586745802..5f21cf67c0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Android FHIR SDK -[![master](https://github.com/google/android-fhir/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/google/android-fhir/actions/workflows/build.yml) [![master](https://storage.googleapis.com/android-fhir-build-badges/build.svg)](https://storage.googleapis.com/android-fhir-build-badges/build.html) [![codecov](https://codecov.io/gh/google/android-fhir/branch/master/graph/badge.svg?token=PDSC4WRDTQ)](https://codecov.io/gh/google/android-fhir/branch/master) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://chat.fhir.org/#narrow/stream/276344-android) +[![master](https://github.com/google/android-fhir/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/google/android-fhir/actions/workflows/build.yml) [![codecov](https://codecov.io/gh/google/android-fhir/branch/master/graph/badge.svg?token=PDSC4WRDTQ)](https://codecov.io/gh/google/android-fhir/branch/master) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://chat.fhir.org/#narrow/stream/276344-android) The Android FHIR SDK is a set of Kotlin libraries for building offline-capable, mobile-first healthcare applications using the [HL7® FHIR® standard](https://www.hl7.org/fhir/) on Android. It @@ -15,7 +15,7 @@ The SDK contains the following libraries: | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| | Data Capture Library | [![Google Maven](https://badgen.net/maven/v/metadata-url/dl.google.com/dl/android/maven2/com/google/android/fhir/data-capture/maven-metadata.xml)](https://maven.google.com/web/index.html?#com.google.android.fhir:data-capture) | [code](https://github.com/google/android-fhir/tree/master/datacapture)| [wiki](https://github.com/google/android-fhir/wiki/Structured-Data-Capture-Library) | Android 7.0 (API level 24) | Collect, validate, and process healthcare data on Android | | FHIR Engine Library | [![Google Maven](https://badgen.net/maven/v/metadata-url/dl.google.com/dl/android/maven2/com/google/android/fhir/engine/maven-metadata.xml)](https://maven.google.com/web/index.html?#com.google.android.fhir:engine) | [code](https://github.com/google/android-fhir/tree/master/engine) | [wiki](https://github.com/google/android-fhir/wiki/FHIR-Engine-Library) | Android 7.0 (API level 24) | Store and manage FHIR resources locally on Android and synchronize with FHIR server | -| Workflow Library | [![Google Maven](https://badgen.net/maven/v/metadata-url/dl.google.com/dl/android/maven2/com/google/android/fhir/workflow/maven-metadata.xml)](https://maven.google.com/web/index.html?#com.google.android.fhir:workflow) | [code](https://github.com/google/android-fhir/tree/master/workflow) | [wiki](https://github.com/google/android-fhir/wiki/Workflow-Library) | Android 8.0 (API level 26) | Provide decision support and analytics in clinical workflow on Android including implementation of specific FHIR operations ($measure_evaluate and $apply) | +| Workflow Library | [![Google Maven](https://badgen.net/maven/v/metadata-url/dl.google.com/dl/android/maven2/com/google/android/fhir/workflow/maven-metadata.xml)](https://maven.google.com/web/index.html?#com.google.android.fhir:workflow) | [code](https://github.com/google/android-fhir/tree/master/workflow) | [wiki](https://github.com/google/android-fhir/wiki/Workflow-Library) | Android 7.0 (API level 24) | Provide decision support and analytics in clinical workflow on Android including implementation of specific FHIR operations ($measure_evaluate and $apply) | ## Demo apps @@ -38,7 +38,7 @@ To contribute to the project, please see [Contributing](https://github.com/googl You can create a [GitHub issue](https://github.com/google/android-fhir/issues) for bugs and feature requests. -In case you find any security bug, please do NOT create a Github issue. Instead, email us at . +In case you find any security bug, please do NOT create a GitHub issue. Instead, email us at . If you want to provide any feedback or discuss use cases you can: * email us at diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 495fa527bb..01550c144c 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -11,7 +11,7 @@ repositories { dependencies { implementation("com.diffplug.spotless:spotless-plugin-gradle:6.21.0") - implementation("com.android.tools.build:gradle:8.1.1") + implementation("com.android.tools.build:gradle:8.1.2") implementation("app.cash.licensee:licensee-gradle-plugin:1.3.0") implementation("com.osacky.flank.gradle:fladle:0.17.4") diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 8816648053..67fbcd8903 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -17,22 +17,6 @@ import org.gradle.api.artifacts.Configuration import org.gradle.kotlin.dsl.exclude -/* - * Copyright 2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - object Dependencies { object Androidx { @@ -175,6 +159,9 @@ object Dependencies { "$androidFhirGroup:$androidFhirEngineModule:${Versions.androidFhirEngine}" const val androidFhirKnowledge = "$androidFhirGroup:knowledge:${Versions.androidFhirKnowledge}" + const val apacheCommonsCompress = + "org.apache.commons:commons-compress:${Versions.apacheCommonsCompress}" + const val desugarJdkLibs = "com.android.tools:desugar_jdk_libs:${Versions.desugarJdkLibs}" const val fhirUcum = "org.fhir:ucum:${Versions.fhirUcum}" const val gson = "com.google.code.gson:gson:${Versions.gson}" @@ -259,6 +246,7 @@ object Dependencies { const val androidFhirCommon = "0.1.0-alpha04" const val androidFhirEngine = "0.1.0-beta03" const val androidFhirKnowledge = "0.1.0-alpha01" + const val apacheCommonsCompress = "1.21" const val desugarJdkLibs = "2.0.3" const val caffeine = "2.9.1" const val fhirUcum = "1.0.3" diff --git a/catalog/build.gradle.kts b/catalog/build.gradle.kts index a0b2e2b91a..e4bb7a4373 100644 --- a/catalog/build.gradle.kts +++ b/catalog/build.gradle.kts @@ -56,6 +56,7 @@ dependencies { implementation(Dependencies.Navigation.navUiKtx) implementation(project(path = ":datacapture")) + implementation(project(path = ":engine")) implementation(project(path = ":contrib:barcode")) testImplementation(Dependencies.junit) diff --git a/catalog/src/main/AndroidManifest.xml b/catalog/src/main/AndroidManifest.xml index 342b41fba5..17469a9339 100644 --- a/catalog/src/main/AndroidManifest.xml +++ b/catalog/src/main/AndroidManifest.xml @@ -24,6 +24,7 @@ /> bufferedReader.readText() } + .let { stringValue -> + FhirContext.forR4Cached().newJsonParser().parseResource(stringValue) as Bundle + } + .entry + .map { bundleEntryComponent -> bundleEntryComponent.resource } + .let { resources -> fhirEngine.create(*resources.toTypedArray()) } + } + } + + override fun getDataCaptureConfig(): DataCaptureConfig = dataCaptureConfig +} diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/DemoQuestionnaireFragment.kt b/catalog/src/main/java/com/google/android/fhir/catalog/DemoQuestionnaireFragment.kt index fa7bfdecba..cbc0e6f49b 100644 --- a/catalog/src/main/java/com/google/android/fhir/catalog/DemoQuestionnaireFragment.kt +++ b/catalog/src/main/java/com/google/android/fhir/catalog/DemoQuestionnaireFragment.kt @@ -34,12 +34,14 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs +import ca.uhn.fhir.context.FhirContext import com.google.android.fhir.catalog.ModalBottomSheetFragment.Companion.BUNDLE_ERROR_KEY import com.google.android.fhir.catalog.ModalBottomSheetFragment.Companion.REQUEST_ERROR_KEY import com.google.android.fhir.datacapture.QuestionnaireFragment import com.google.android.fhir.datacapture.QuestionnaireFragment.Companion.SUBMIT_REQUEST_KEY import com.google.android.material.card.MaterialCardView import kotlinx.coroutines.launch +import org.hl7.fhir.r4.model.Patient class DemoQuestionnaireFragment : Fragment() { private val viewModel: DemoQuestionnaireViewModel by viewModels() @@ -164,7 +166,15 @@ class DemoQuestionnaireFragment : Fragment() { setReorderingAllowed(true) replace( R.id.container, - QuestionnaireFragment.builder().setQuestionnaire(questionnaireJsonString).build(), + QuestionnaireFragment.builder() + .setQuestionnaire(questionnaireJsonString) + .setQuestionnaireLaunchContexts( + FhirContext.forR4Cached() + .newJsonParser() + .encodeResourceToString(Patient().apply { id = "P1" }) + .let { listOf(it) }, + ) + .build(), QUESTIONNAIRE_FRAGMENT_TAG, ) } diff --git a/catalog/src/main/res/drawable/ic_answers_behavior.xml b/catalog/src/main/res/drawable/ic_answers_behavior.xml new file mode 100644 index 0000000000..b7d4ed1bec --- /dev/null +++ b/catalog/src/main/res/drawable/ic_answers_behavior.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/catalog/src/main/res/values/strings.xml b/catalog/src/main/res/values/strings.xml index 1429f57926..3371f69d71 100644 --- a/catalog/src/main/res/values/strings.xml +++ b/catalog/src/main/res/values/strings.xml @@ -58,6 +58,7 @@ Calculated Expression + Answer Expression Structured data capture \n Catalog + setFragmentResult(REQUEST_KEY, bundleOf(RESULT_KEY to RESULT_YES)) + dialog?.dismiss() + } + .setNegativeButton(android.R.string.cancel) { dialog, _ -> + setFragmentResult(REQUEST_KEY, bundleOf(RESULT_KEY to RESULT_NO)) + dialog?.dismiss() + } + .create() + } + + companion object { + const val TAG = "QuestionnaireCancelDialogFragment" + const val REQUEST_KEY = "request-key" + const val RESULT_KEY = "result-key" + const val RESULT_NO = "no" + const val RESULT_YES = "yes" + } +} diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireFragment.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireFragment.kt index 6af20abaee..19cfaf8609 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireFragment.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireFragment.kt @@ -97,6 +97,11 @@ class QuestionnaireFragment : Fragment() { paginationPreviousButton.setOnClickListener { viewModel.goToPreviousPage() } val paginationNextButton = view.findViewById(R.id.pagination_next_button) paginationNextButton.setOnClickListener { viewModel.goToNextPage() } + view.findViewById