Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
1a11fb9
Refactor gradle dependencies to use toml file
ellykits Nov 3, 2025
57fc39b
Migrate gradle plugins to toml file
ellykits Nov 3, 2025
adec2c6
Merge branch 'master' into dependency-management
ellykits Nov 5, 2025
0b98d35
Order dependencies declarations lexographically
ellykits Nov 7, 2025
977a39e
Merge branch 'master' into dependency-management
ellykits Nov 7, 2025
1a5a374
Restore necessary comment
ellykits Nov 7, 2025
5d12ae6
Add datacapture kmp module
ellykits Nov 15, 2025
1e15fb5
Fix MediaItem to suit CMP
LZRS Nov 18, 2025
81468cb
Remove AndroidView used in Header compose
LZRS Nov 19, 2025
a644091
Merge pull request #1 from LZRS/migrate-sdc-kmp
ellykits Nov 19, 2025
e47bd6c
Move more code to commonMain package
ellykits Nov 19, 2025
1890b40
Resolve build issues
ellykits Nov 19, 2025
4b7b2d6
Use platform specific DataCapture
ellykits Nov 21, 2025
78ebf17
Init sdc-kmp-demo module
FikriMilano Nov 23, 2025
938ea9c
Add QuestionnaireScreen
FikriMilano Nov 23, 2025
a051949
Use fhirR4Json
FikriMilano Nov 23, 2025
f34d0a0
Display QR on UI
FikriMilano Nov 23, 2025
2676fd4
Fix answer not showing on submission
FikriMilano Nov 23, 2025
6cf4e07
Add horizontal scroll in QuestionnaireResponseScreen
FikriMilano Nov 23, 2025
6154cb2
Add missing Cancel word
FikriMilano Nov 24, 2025
10b4822
Remove not needed comment
FikriMilano Nov 24, 2025
5e62f3c
Update sample questionnaire
FikriMilano Nov 24, 2025
f1470ba
Merge pull request #3 from opensrp/sdc-kmp-demo
ellykits Nov 24, 2025
548917b
Add wasm target for datacapture kmp
ellykits Nov 24, 2025
f9f59c7
Add missing actuals to expect functions
ellykits Nov 24, 2025
c1eae1f
Add missing expect/actual function
ellykits Nov 24, 2025
8156e64
Resolve runing wasm target
ellykits Nov 24, 2025
86cf4ee
Run spotless and refactor hardcoded string
ellykits Nov 24, 2025
9a91614
Fix launching questionnaire on jvm targets
ellykits Nov 24, 2025
e728d17
Add migrated Compose QuestionnaireTheme
LZRS Nov 19, 2025
f2d75f2
Fix set answer for decimal/string widgets
LZRS Nov 24, 2025
afaae14
Fix integer type widget not picking set value
LZRS Nov 24, 2025
081fe47
Fix decimal widget not picking up correct value
LZRS Nov 24, 2025
bc10369
Fix onSubmit navigation after leaving composition
LZRS Nov 24, 2025
ef870ed
Add iosApp to project
LZRS Nov 27, 2025
8a3e632
Merge pull request #2 from LZRS/migrate-sdc-kmp
ellykits Nov 27, 2025
e92ea84
Merge branch 'master' into kmp-migration
ellykits Nov 27, 2025
d6b6eaa
Refactor function
ellykits Nov 27, 2025
9cb2f6f
Order content lexographically per section
ellykits Nov 27, 2025
2c7ea4f
Update kotlin-fhir v1.0.0-beta02
LZRS Nov 28, 2025
f4935fb
Merge pull request #4 from LZRS/update-kotlin-fhirv1.0.0-beta02
ellykits Nov 28, 2025
4a795bc
Show review page screen
LZRS Nov 28, 2025
81c2011
Merge pull request #5 from LZRS/kmp-show_review
ellykits Nov 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ captures/
.classpath
.project
.settings
.vscode

# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
Expand Down Expand Up @@ -101,4 +102,6 @@ docs/use/api/*/**
# Synthea
synthea

.vscode
### Kotlin/JS
kotlin-js-store/
node_modules/
32 changes: 21 additions & 11 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.plugin.use.PluginDependency
import org.gradle.api.provider.Provider

plugins {
`kotlin-dsl`
}
Expand All @@ -9,15 +13,21 @@ repositories {
}

dependencies {
implementation("com.diffplug.spotless:spotless-plugin-gradle:6.22.0")

implementation("com.android.tools.build:gradle:8.9.2")

implementation("app.cash.licensee:licensee-gradle-plugin:1.8.0")
implementation("com.osacky.flank.gradle:fladle:0.17.4")

implementation("com.spotify.ruler:ruler-gradle-plugin:1.4.0")

implementation("ca.uhn.hapi.fhir:hapi-fhir-structures-r4:6.10.0")
implementation("com.squareup:kotlinpoet:1.17.0")
implementation(libs.spotless.plugin.gradle)
implementation(libs.gradle)
implementation(libs.licensee.gradle.plugin)
implementation(libs.fladle)
implementation(libs.ruler.gradle.plugin)
implementation(libs.hapi.fhir.structures.r4.v6100)
implementation(libs.kotlinpoet)
implementation(plugin(libs.plugins.android.kotlin.multiplatform.library))
implementation(plugin(libs.plugins.compose.compiler))
implementation(plugin(libs.plugins.compose.hotreload))
implementation(plugin(libs.plugins.compose.multiplatform))
implementation(plugin(libs.plugins.kotlin.multiplatform))
implementation(plugin(libs.plugins.android.application.build.src))
implementation(plugin(libs.plugins.kotlin.serialization.build.src))
}

fun DependencyHandler.plugin(plugin: Provider<PluginDependency>) =
plugin.map { "${it.pluginId}:${it.pluginId}.gradle.plugin:${it.version}" }
7 changes: 7 additions & 0 deletions buildSrc/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/Plugins.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object Plugins {
object Versions {
const val androidGradlePlugin = "8.9.2"
const val benchmarkPlugin = "1.4.0-rc01"
const val kspPlugin = "2.1.20-2.0.1"
const val kotlin = "2.1.20"
const val kspPlugin = "2.2.20-2.0.4"
const val kotlin = "2.2.20"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Google LLC
* Copyright 2023-2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -81,7 +81,7 @@ internal object SearchParameterRepositoryGenerator {
SearchParamDefinition(
className = searchParamDefinitionClass,
name = searchParameter.name,
paramTypeCode = searchParameter.type.toCode().toUpperCase(Locale.US),
paramTypeCode = searchParameter.type.toCode().uppercase(Locale.US),
path = path.value,
),
)
Expand All @@ -90,7 +90,7 @@ internal object SearchParameterRepositoryGenerator {
SearchParamDefinition(
className = searchParamDefinitionClass,
name = searchParameter.name,
paramTypeCode = searchParameter.type.toCode().toUpperCase(Locale.US),
paramTypeCode = searchParameter.type.toCode().uppercase(Locale.US),
path = path.value,
),
)
Expand Down
1 change: 1 addition & 0 deletions datacapture-kmp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
187 changes: 187 additions & 0 deletions datacapture-kmp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
@file:OptIn(ExperimentalWasmDsl::class)

import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl

plugins {
id("org.jetbrains.kotlin.multiplatform")
id("com.android.kotlin.multiplatform.library")
id("org.jetbrains.kotlin.plugin.compose")
id("org.jetbrains.compose.hot-reload")
id("org.jetbrains.compose")
alias(libs.plugins.ksp)
}

kotlin {
jvmToolchain(11)

androidLibrary {
namespace = "com.google.android.fhir.datacapture"
compileSdk = 36
minSdk = 24
withJava()
withHostTestBuilder {}
withDeviceTestBuilder { sourceSetTreeName = "test" }
.configure { instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" }

experimentalProperties["android.experimental.kmp.enableAndroidResources"] = true

compilations.configureEach {
compilerOptions.configure {
jvmTarget.set(
org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11,
)
}
}
packaging {
resources.excludes.addAll(
listOf("META-INF/ASL2.0", "META-INF/ASL-2.0.txt", "META-INF/LGPL-3.0.txt"),
)
}
}

// For iOS targets, this is also where you should
// configure native binary output. For more information, see:
// https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#build-xcframeworks

// A step-by-step guide on how to include this library in an XCode
// project can be found here:
// https://developer.android.com/kotlin/multiplatform/migrate
val xcfName = "sharedKit"

iosX64 { binaries.framework { baseName = xcfName } }

iosArm64 { binaries.framework { baseName = xcfName } }

iosSimulatorArm64 { binaries.framework { baseName = xcfName } }

wasmJs {
browser()
binaries.library()
}

jvm("desktop")

js {
browser()
binaries.library()
}

// Source set declarations.
// Declaring a target automatically creates a source set with the same name. By default, the
// Kotlin Gradle Plugin creates additional source sets that depend on each other, since it is
// common to share sources between related targets.
// See: https://kotlinlang.org/docs/multiplatform-hierarchy.html
sourceSets {
all {
languageSettings {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions { freeCompilerArgs.add("-Xexpect-actual-classes") }
}
}

commonMain {
dependencies {
implementation(libs.material.icons.extended)
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.navigation.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.kermit)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlin.fhir)
implementation(libs.kotlinx.io.core)
implementation(libs.kotlinx.serialization.json)
}
}

commonTest { dependencies { implementation(libs.kotlin.test) } }

androidMain {
resources.srcDir("res")
dependencies {

// Add Android-specific dependencies here. Note that this source set depends on
// commonMain by default and will correctly pull the Android artifacts of any KMP
// dependencies declared in commonMain.
// api(libs.hapi.fhir.structures.r4)
implementation(libs.accompanist.themeadapter.material3)
// implementation(libs.android.fhir.common)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.core)
implementation(libs.androidx.fragment)
implementation(libs.material)
// implementation(libs.hapi.fhir.caching.guava)
/* implementation(libs.hapi.fhir.validation) {
exclude(module = "commons-logging")
exclude(module = "httpclient")
}*/
implementation(libs.kotlinx.coroutines.core)
implementation(libs.timber)
implementation(libs.glide)
/*
constraints {
Dependencies.hapiFhirConstraints().forEach { (libName, constraints) ->
api(libName, constraints)
implementation(libName, constraints)
}
}*/
}
}

getByName("androidDeviceTest") {
dependencies {
implementation(libs.androidx.test.runner)
implementation(libs.androidx.test.core)
implementation(libs.androidx.test.ext.junit)
implementation(libs.androidx.compose.ui.test.junit4)
implementation(libs.androidx.test.core)
/* implementation(libs.androidx.test.espresso.contrib) {
// build fails with error "Duplicate class found" (org.checkerframework.checker.*)
exclude(group = "org.checkerframework", module = "checker")
}*/
implementation(libs.androidx.test.espresso.core)
implementation(libs.androidx.test.ext.junit)
implementation(libs.androidx.test.ext.junit.ktx)
implementation(libs.androidx.test.rules)
implementation(libs.androidx.test.runner)
implementation(libs.junit)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.truth)
}
}

getByName("androidHostTest") {
dependencies {
implementation(libs.androidx.fragment.testing)
implementation(libs.androidx.test.core)
implementation(libs.junit)
implementation(libs.kotlin.test.junit)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.mockito.inline)
implementation(libs.mockito.kotlin)
implementation(libs.robolectric)
implementation(libs.truth)
/* implementation(project(":knowledge")) {
exclude(group = "com.google.android.fhir", module = "engine")
}*/
}
}

val desktopMain by getting {
dependencies {
implementation(compose.desktop.currentOs)
// Provide Main Dispatcher for JVM target
implementation(libs.kotlinx.coroutines.swing)
}
}

iosMain { dependencies {} }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2025 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.
*/

package com.google.android.fhir.datacapture

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
Assert.assertEquals("com.google.android.fhir.datacapture.test", appContext.packageName)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2025 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.
*/

package com.google.android.fhir.datacapture

import kotlin.test.Test
import kotlin.test.assertEquals

/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
Loading
Loading