diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d271823555..a474bc2443 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -71,10 +71,10 @@ jobs:
run: ./gradlew spotlessCheck --scan --full-stacktrace
- name: Build (full) with Gradle
- run: ./gradlew build -x :datacapture-kmp:build -x :sdc-kmp-demo:build --scan --full-stacktrace
+ run: ./gradlew build -Pcatalog.wasm.enabled=false -x :datacapture-kmp:build -x :sdc-kmp-demo:build --scan --full-stacktrace
- name: Check with Gradle
- run: ./gradlew check -x :datacapture-kmp:check -x :sdc-kmp-demo:check --scan --full-stacktrace
+ run: ./gradlew check -Pcatalog.wasm.enabled=false -x :datacapture-kmp:check -x :sdc-kmp-demo:check --scan --full-stacktrace
- name: Run tests for datacapture Kotlin Multiplatform desktop environment with X Virtual Framebuffer
run: |
diff --git a/LICENSES-3RD-PARTY/LICENSE-JAI-IMAGEIO b/LICENSES-3RD-PARTY/LICENSE-JAI-IMAGEIO
new file mode 100644
index 0000000000..589e191988
--- /dev/null
+++ b/LICENSES-3RD-PARTY/LICENSE-JAI-IMAGEIO
@@ -0,0 +1,39 @@
+Copyright (c) 2005 Sun Microsystems, Inc.
+Copyright © 2010-2014 University of Manchester
+Copyright © 2010-2015 Stian Soiland-Reyes
+Copyright © 2015 Peter Hull
+All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistribution of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+- Redistribution in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+Neither the name of Sun Microsystems, Inc. or the names of
+contributors may be used to endorse or promote products derived
+from this software without specific prior written permission.
+
+This software is provided "AS IS," without a warranty of any
+kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+You acknowledge that this software is not designed or intended for
+use in the design, construction, operation or maintenance of any
+nuclear facility.
diff --git a/buildSrc/src/main/kotlin/LicenseeConfig.kt b/buildSrc/src/main/kotlin/LicenseeConfig.kt
index 102eb51058..e94bb3ae75 100644
--- a/buildSrc/src/main/kotlin/LicenseeConfig.kt
+++ b/buildSrc/src/main/kotlin/LicenseeConfig.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023-2025 Google LLC
+ * Copyright 2023-2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -224,6 +224,11 @@ private fun Project.configureLicensee() {
// Logback
allowDependency("ch.qos.logback", "logback-core", "1.4.14") { because("LGPL") }
+
+ // jai-imageio-core in zxing
+ allowDependency("com.github.jai-imageio", "jai-imageio-core", "1.4.0") {
+ because("https://github.com/jai-imageio/jai-imageio-core/blob/master/LICENSE.txt")
+ }
}
}
@@ -234,4 +239,9 @@ private val nonStandardLicenseUrls =
"http://www.opensource.org/licenses/bsd-license.php",
"https://asm.ow2.io/license.html",
"https://developer.android.com/studio/terms.html",
+ "https://github.com/vinceglb/FileKit/blob/main/LICENSE",
+ "https://github.com/hypfvieh/dbus-java/blob/master/LICENSE",
+ "https://developers.google.com/ml-kit/terms",
+ "https://github.com/icerockdev/moko-permissions/blob/master/LICENSE.md",
+ "https://github.com/jordond/compass/blob/master/LICENSE",
)
diff --git a/buildSrc/src/main/kotlin/Releases.kt b/buildSrc/src/main/kotlin/Releases.kt
index cccd11fcf8..c7dfd1b397 100644
--- a/buildSrc/src/main/kotlin/Releases.kt
+++ b/buildSrc/src/main/kotlin/Releases.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023-2025 Google LLC
+ * Copyright 2023-2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/catalog-iosApp/.gitignore b/catalog-iosApp/.gitignore
new file mode 100644
index 0000000000..2c8444494f
--- /dev/null
+++ b/catalog-iosApp/.gitignore
@@ -0,0 +1,4 @@
+## User settings
+xcuserdata/
+
+Configuration/LocalConfig.xcconfig
diff --git a/catalog-iosApp/Configuration/Config.xcconfig b/catalog-iosApp/Configuration/Config.xcconfig
new file mode 100644
index 0000000000..bb2e6e9418
--- /dev/null
+++ b/catalog-iosApp/Configuration/Config.xcconfig
@@ -0,0 +1,14 @@
+TEAM_ID=
+
+CURRENT_PROJECT_VERSION=1
+MARKETING_VERSION=1.0
+
+// Optionally include `LocalConfig.xcconfig`
+// that includes local overrides if it exists.
+// `#include?` is supported as of Xcode 8
+
+#include? "LocalConfig.xcconfig"
+
+PRODUCT_NAME=Catalog
+DEVELOPMENT_TEAM=$(TEAM_ID)
+PRODUCT_BUNDLE_IDENTIFIER=com.google.android.fhir.catalog$(TEAM_ID)
diff --git a/catalog-iosApp/README.md b/catalog-iosApp/README.md
new file mode 100644
index 0000000000..ed3d17d970
--- /dev/null
+++ b/catalog-iosApp/README.md
@@ -0,0 +1,64 @@
+#### Running on a real iOS device
+
+To run in a real iOS device, you'll need the following:
+
+* The `TEAM_ID` associated with your [Apple ID](https://support.apple.com/en-us/HT204316)
+* The iOS device registered in Xcode
+
+> **Note**
+> Before you continue, we suggest creating a simple "Hello, world!" project in Xcode to ensure you can successfully run
+> apps on your device.
+> You can follow the instructions below or watch
+> this [Stanford CS193P lecture recording](https://youtu.be/bqu6BquVi2M?start=716&end=1399).
+
+
+How to create and run a simple project in Xcode
+
+1. On the Xcode welcome screen, select **Create a new project in Xcode**.
+2. On the **iOS** tab, choose the **App** template. Click **Next**.
+3. Specify the product name and keep other settings default. Click **Next**.
+4. Select where to store the project on your computer and click **Create**. You'll see an app that displays "Hello,
+ world!" on the device screen.
+5. At the top of your Xcode screen, click on the device name near the **Run** button.
+6. Plug your device into the computer. You'll see this device in the list of run options.
+7. Choose your device and click **Run**.
+
+
+
+##### Finding your Team ID
+
+Install KDoctor with [Homebrew](https://brew.sh/):
+
+ ```text
+ brew install kdoctor
+ ```
+
+Run `kdoctor --team-ids` to find your Team ID.
+KDoctor will list all Team IDs currently configured on your system, for example:
+
+```text
+3ABC246XYZ (Max Sample)
+ZABCW6SXYZ (SampleTech Inc.)
+```
+
+
+Alternative way to find your Team ID
+
+If KDoctor doesn't work for you, try this alternative method:
+
+1. In Android Studio, run the `iosApp` configuration with the selected real device. The build should fail.
+2. Go to Xcode and select **Open a project or file**.
+3. Navigate to the `iosApp/iosApp.xcworkspace` file of your project.
+4. In the left-hand menu, select `iosApp`.
+5. Navigate to **Signing & Capabilities**.
+6. In the **Team** list, select your team.
+
+If you haven't set up your team yet, use the **Add account** option and follow the steps.
+
+
+
+To run the application, set the `TEAM_ID`:
+
+1. Navigate to the `Configuration/LocalConfig.xcconfig` file.
+2. Set your `TEAM_ID`.
+3. Re-open the project in Android Studio. It should show the registered iOS device in the `iosApp` run configuration.
\ No newline at end of file
diff --git a/catalog-iosApp/iosApp.xcodeproj/project.pbxproj b/catalog-iosApp/iosApp.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..39467daf98
--- /dev/null
+++ b/catalog-iosApp/iosApp.xcodeproj/project.pbxproj
@@ -0,0 +1,390 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 77;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ BE6507D32F3CBEB300EE3190 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = BE6507D22F3CBEB300EE3190 /* README.md */; };
+ BE6507D52F3CBECE00EE3190 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = BE6507D42F3CBECE00EE3190 /* .gitignore */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ B9DA97B12DC1472C00A4DA20 /* Catalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Catalog.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ BE6507D22F3CBEB300EE3190 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
+ BE6507D42F3CBECE00EE3190 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
+ B99700CA2DC9B8D800C7335B /* Exceptions for "iosApp" folder in "iosApp" target */ = {
+ isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
+ membershipExceptions = (
+ Info.plist,
+ );
+ target = B9DA97B02DC1472C00A4DA20 /* iosApp */;
+ };
+/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
+
+/* Begin PBXFileSystemSynchronizedRootGroup section */
+ B9DA97B32DC1472C00A4DA20 /* iosApp */ = {
+ isa = PBXFileSystemSynchronizedRootGroup;
+ exceptions = (
+ B99700CA2DC9B8D800C7335B /* Exceptions for "iosApp" folder in "iosApp" target */,
+ );
+ path = iosApp;
+ sourceTree = "";
+ };
+ B9DA98002DC14AA900A4DA20 /* Configuration */ = {
+ isa = PBXFileSystemSynchronizedRootGroup;
+ path = Configuration;
+ sourceTree = "";
+ };
+/* End PBXFileSystemSynchronizedRootGroup section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ B9DA97AE2DC1472C00A4DA20 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ B9DA97A82DC1472C00A4DA20 = {
+ isa = PBXGroup;
+ children = (
+ BE6507D42F3CBECE00EE3190 /* .gitignore */,
+ BE6507D22F3CBEB300EE3190 /* README.md */,
+ B9DA98002DC14AA900A4DA20 /* Configuration */,
+ B9DA97B32DC1472C00A4DA20 /* iosApp */,
+ B9DA97B22DC1472C00A4DA20 /* Products */,
+ );
+ sourceTree = "";
+ };
+ B9DA97B22DC1472C00A4DA20 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ B9DA97B12DC1472C00A4DA20 /* Catalog.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ B9DA97B02DC1472C00A4DA20 /* iosApp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = B9DA97BF2DC1472D00A4DA20 /* Build configuration list for PBXNativeTarget "iosApp" */;
+ buildPhases = (
+ B9DA97F42DC1497100A4DA20 /* Compile Kotlin Framework */,
+ B9DA97AD2DC1472C00A4DA20 /* Sources */,
+ B9DA97AE2DC1472C00A4DA20 /* Frameworks */,
+ B9DA97AF2DC1472C00A4DA20 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ fileSystemSynchronizedGroups = (
+ B9DA97B32DC1472C00A4DA20 /* iosApp */,
+ );
+ name = iosApp;
+ packageProductDependencies = (
+ );
+ productName = iosApp;
+ productReference = B9DA97B12DC1472C00A4DA20 /* Catalog.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ B9DA97A92DC1472C00A4DA20 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1620;
+ LastUpgradeCheck = 1620;
+ TargetAttributes = {
+ B9DA97B02DC1472C00A4DA20 = {
+ CreatedOnToolsVersion = 16.2;
+ };
+ };
+ };
+ buildConfigurationList = B9DA97AC2DC1472C00A4DA20 /* Build configuration list for PBXProject "iosApp" */;
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = B9DA97A82DC1472C00A4DA20;
+ minimizedProjectReferenceProxies = 1;
+ preferredProjectObjectVersion = 77;
+ productRefGroup = B9DA97B22DC1472C00A4DA20 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ B9DA97B02DC1472C00A4DA20 /* iosApp */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ B9DA97AF2DC1472C00A4DA20 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ BE6507D52F3CBECE00EE3190 /* .gitignore in Resources */,
+ BE6507D32F3CBEB300EE3190 /* README.md in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ B9DA97F42DC1497100A4DA20 /* Compile Kotlin Framework */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Compile Kotlin Framework";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :catalog:embedAndSignAppleFrameworkForXcode\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ B9DA97AD2DC1472C00A4DA20 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ B9DA97BD2DC1472D00A4DA20 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 18.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ B9DA97BE2DC1472D00A4DA20 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 18.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ B9DA97C02DC1472D00A4DA20 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReferenceAnchor = B9DA98002DC14AA900A4DA20 /* Configuration */;
+ baseConfigurationReferenceRelativePath = Config.xcconfig;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+ ENABLE_PREVIEWS = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
+ );
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = iosApp/Info.plist;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ B9DA97C12DC1472D00A4DA20 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReferenceAnchor = B9DA98002DC14AA900A4DA20 /* Configuration */;
+ baseConfigurationReferenceRelativePath = Config.xcconfig;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+ ENABLE_PREVIEWS = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
+ );
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = iosApp/Info.plist;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ B9DA97AC2DC1472C00A4DA20 /* Build configuration list for PBXProject "iosApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B9DA97BD2DC1472D00A4DA20 /* Debug */,
+ B9DA97BE2DC1472D00A4DA20 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ B9DA97BF2DC1472D00A4DA20 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B9DA97C02DC1472D00A4DA20 /* Debug */,
+ B9DA97C12DC1472D00A4DA20 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = B9DA97A92DC1472C00A4DA20 /* Project object */;
+}
diff --git a/catalog-iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/catalog-iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..919434a625
--- /dev/null
+++ b/catalog-iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/catalog-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/catalog-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000000..eb87897008
--- /dev/null
+++ b/catalog-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/catalog-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/catalog-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..4e8d485bf0
--- /dev/null
+++ b/catalog-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,36 @@
+{
+ "images" : [
+ {
+ "filename" : "app-icon-1024.png",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "tinted"
+ }
+ ],
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/catalog-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/catalog-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png
new file mode 100644
index 0000000000..53fc536fb9
Binary files /dev/null and b/catalog-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png differ
diff --git a/catalog-iosApp/iosApp/Assets.xcassets/Contents.json b/catalog-iosApp/iosApp/Assets.xcassets/Contents.json
new file mode 100644
index 0000000000..73c00596a7
--- /dev/null
+++ b/catalog-iosApp/iosApp/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/catalog-iosApp/iosApp/ContentView.swift b/catalog-iosApp/iosApp/ContentView.swift
new file mode 100644
index 0000000000..bf3d1f709a
--- /dev/null
+++ b/catalog-iosApp/iosApp/ContentView.swift
@@ -0,0 +1,21 @@
+import UIKit
+import SwiftUI
+import CatalogKit
+
+struct ComposeView: UIViewControllerRepresentable {
+ func makeUIViewController(context: Context) -> UIViewController {
+ MainViewControllerKt.MainViewController()
+ }
+
+ func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
+}
+
+struct ContentView: View {
+ var body: some View {
+ ComposeView()
+ .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
+ }
+}
+
+
+
diff --git a/catalog-iosApp/iosApp/Info.plist b/catalog-iosApp/iosApp/Info.plist
new file mode 100644
index 0000000000..3d898ba8db
--- /dev/null
+++ b/catalog-iosApp/iosApp/Info.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CADisableMinimumFrameDurationOnPhone
+
+
+ NSCameraUsageDescription
+ Camera access needed to take photos
+
+ NSPhotoLibraryUsageDescription
+ Photo library access needed to select images
+
+ NSDocumentsFolderUsageDescription
+ Document access needed to select files for attachment
+
+ NSDownloadsFolderUsageDescription
+ Download access needed to select files for attachment
+
+ NSDesktopFolderUsageDescription
+ Desktop access needed to select files for attachment
+
+ NSLocationWhenInUseUsageDescription
+ Location access needed for the location widget
+
+
diff --git a/catalog-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/catalog-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 0000000000..73c00596a7
--- /dev/null
+++ b/catalog-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/catalog-iosApp/iosApp/iOSApp.swift b/catalog-iosApp/iosApp/iOSApp.swift
new file mode 100644
index 0000000000..d83dca6113
--- /dev/null
+++ b/catalog-iosApp/iosApp/iOSApp.swift
@@ -0,0 +1,10 @@
+import SwiftUI
+
+@main
+struct iOSApp: App {
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ }
+ }
+}
\ No newline at end of file
diff --git a/catalog/build.gradle.kts b/catalog/build.gradle.kts
index 0e6e5dab98..e5d8979948 100644
--- a/catalog/build.gradle.kts
+++ b/catalog/build.gradle.kts
@@ -1,14 +1,22 @@
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
+
plugins {
- alias(libs.plugins.android.application)
- alias(libs.plugins.kotlin.android)
+ id("org.jetbrains.kotlin.multiplatform")
+ id("com.android.application")
+ id("org.jetbrains.kotlin.plugin.compose")
+ id("org.jetbrains.compose")
+ id("org.jetbrains.kotlin.plugin.serialization")
alias(libs.plugins.androidx.navigation.safeargs)
}
-configureRuler()
+// configureRuler()
android {
namespace = "com.google.android.fhir.catalog"
compileSdk = Sdk.COMPILE_SDK
+
defaultConfig {
applicationId = Releases.Catalog.applicationId
minSdk = Sdk.MIN_SDK
@@ -17,9 +25,6 @@ android {
versionName = Releases.Catalog.versionName
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
-
- buildFeatures { viewBinding = true }
-
buildTypes {
release {
isMinifyEnabled = true
@@ -27,37 +32,80 @@ android {
}
}
compileOptions {
- // Flag to enable support for the new language APIs
- // See https://developer.android.com/studio/write/java8-support
- isCoreLibraryDesugaringEnabled = true
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
-
packaging {
resources.excludes.addAll(
listOf("META-INF/ASL2.0", "META-INF/ASL-2.0.txt", "META-INF/LGPL-3.0.txt"),
)
}
- kotlin { jvmToolchain(11) }
}
-dependencies {
- androidTestImplementation(libs.androidx.test.espresso.core)
- androidTestImplementation(libs.androidx.test.ext.junit)
-
- coreLibraryDesugaring(libs.desugar.jdk.libs)
-
- implementation(libs.androidx.appcompat)
- implementation(libs.androidx.constraintlayout)
- implementation(libs.androidx.core)
- implementation(libs.androidx.fragment)
- implementation(libs.androidx.navigation.fragment)
- implementation(libs.androidx.navigation.ui)
- implementation(libs.kotlin.stdlib)
- implementation(libs.material)
- implementation(project(path = ":contrib:barcode"))
- implementation(project(path = ":contrib:locationwidget"))
- implementation(project(path = ":datacapture"))
- implementation(project(path = ":engine"))
-
- testImplementation(libs.junit)
+kotlin {
+ androidTarget { compilerOptions { jvmTarget.set(JvmTarget.JVM_11) } }
+
+ jvm("desktop")
+
+ val isWasmEnabled = project.findProperty("catalog.wasm.enabled") == "true"
+ if (isWasmEnabled) {
+ @OptIn(ExperimentalWasmDsl::class)
+ wasmJs {
+ browser {
+ val rootProjectDir = rootProject.projectDir.path
+ commonWebpackConfig {
+ devServer =
+ (devServer ?: KotlinWebpackConfig.DevServer()).copy(
+ static = (devServer?.static ?: mutableListOf()).apply { add(rootProjectDir) },
+ )
+ }
+ }
+ binaries.executable()
+ }
+ }
+
+ listOf(
+ iosX64(),
+ iosArm64(),
+ iosSimulatorArm64(),
+ )
+ .forEach { iosTarget ->
+ iosTarget.binaries.framework {
+ baseName = "CatalogKit"
+ isStatic = true
+ }
+ }
+
+ sourceSets {
+ androidMain.dependencies {
+ implementation(libs.androidx.appcompat)
+ implementation(libs.androidx.core)
+ implementation(libs.material)
+ // TODO restore after these libraries are migrated to Kotlin Multiplatform
+ // implementation(project(":engine"))
+ // implementation(project(":contrib:barcode"))
+ // implementation(project(":contrib:locationwidget"))
+ }
+ commonMain.dependencies {
+ implementation(compose.runtime)
+ implementation(compose.foundation)
+ implementation(compose.material3)
+ implementation(compose.ui)
+ implementation(compose.components.resources)
+ implementation(compose.components.uiToolingPreview)
+ implementation(libs.androidx.lifecycle.viewmodel.compose)
+ implementation(libs.androidx.lifecycle.runtime.compose)
+ implementation(libs.material.icons.extended)
+ implementation(libs.kermit)
+ implementation(libs.kotlin.fhir)
+ implementation(libs.kotlinx.serialization.json)
+ implementation(libs.kotlinx.coroutines.core)
+ implementation(libs.navigation.compose)
+ implementation(project(":contrib:barcode"))
+ implementation(project(":contrib:locationwidget"))
+ implementation(project(":datacapture-kmp"))
+ }
+
+ val desktopMain by getting { dependencies { implementation(compose.desktop.currentOs) } }
+ }
}
diff --git a/catalog/src/main/AndroidManifest.xml b/catalog/src/androidMain/AndroidManifest.xml
similarity index 93%
rename from catalog/src/main/AndroidManifest.xml
rename to catalog/src/androidMain/AndroidManifest.xml
index 17469a9339..dc1f5303ed 100644
--- a/catalog/src/main/AndroidManifest.xml
+++ b/catalog/src/androidMain/AndroidManifest.xml
@@ -30,7 +30,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_card_file_box_round"
android:supportsRtl="true"
- android:theme="@style/Theme.Androidfhir"
+ android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
>
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/DemoQuestionnaireViewModel.kt b/catalog/src/androidMain/kotlin/com/google/android/fhir/catalog/CatalogApplication.kt
similarity index 53%
rename from catalog/src/main/java/com/google/android/fhir/catalog/DemoQuestionnaireViewModel.kt
rename to catalog/src/androidMain/kotlin/com/google/android/fhir/catalog/CatalogApplication.kt
index 264225325f..bbdc4c83ec 100644
--- a/catalog/src/main/java/com/google/android/fhir/catalog/DemoQuestionnaireViewModel.kt
+++ b/catalog/src/androidMain/kotlin/com/google/android/fhir/catalog/CatalogApplication.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2023 Google LLC
+ * Copyright 2022-2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,15 +17,17 @@
package com.google.android.fhir.catalog
import android.app.Application
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.SavedStateHandle
-import ca.uhn.fhir.context.FhirContext
-import ca.uhn.fhir.context.FhirVersionEnum
-import org.hl7.fhir.r4.model.QuestionnaireResponse
+import com.google.android.fhir.datacapture.DataCapture
+import com.google.android.fhir.datacapture.DataCaptureConfig
-class DemoQuestionnaireViewModel(application: Application, private val state: SavedStateHandle) :
- AndroidViewModel(application) {
+class CatalogApplication : Application(), DataCaptureConfig.Provider {
- fun getQuestionnaireResponseJson(response: QuestionnaireResponse) =
- FhirContext.forCached(FhirVersionEnum.R4).newJsonParser().encodeResourceToString(response)
+ override fun onCreate() {
+ super.onCreate()
+ DataCapture.initialize(this)
+ }
+
+ override fun getDataCaptureConfig(): DataCaptureConfig {
+ return DataCaptureConfig(xFhirQueryResolver = { emptyList() })
+ }
}
diff --git a/datacapture-kmp/src/androidHostTest/kotlin/com/google/android/fhir/datacapture/ExampleUnitTest.kt b/catalog/src/androidMain/kotlin/com/google/android/fhir/catalog/MainActivity.kt
similarity index 60%
rename from datacapture-kmp/src/androidHostTest/kotlin/com/google/android/fhir/datacapture/ExampleUnitTest.kt
rename to catalog/src/androidMain/kotlin/com/google/android/fhir/catalog/MainActivity.kt
index d1f961b2b5..8cf8cedbc1 100644
--- a/datacapture-kmp/src/androidHostTest/kotlin/com/google/android/fhir/datacapture/ExampleUnitTest.kt
+++ b/catalog/src/androidMain/kotlin/com/google/android/fhir/catalog/MainActivity.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Google LLC
+ * Copyright 2021-2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,15 @@
* limitations under the License.
*/
-package com.google.android.fhir.datacapture
+package com.google.android.fhir.catalog
-import kotlin.test.Test
-import kotlin.test.assertEquals
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
-/**
- * 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)
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent { App() }
}
}
diff --git a/catalog/src/main/res/mipmap-anydpi-v26/ic_card_file_box.xml b/catalog/src/androidMain/res/mipmap-anydpi-v26/ic_card_file_box.xml
similarity index 100%
rename from catalog/src/main/res/mipmap-anydpi-v26/ic_card_file_box.xml
rename to catalog/src/androidMain/res/mipmap-anydpi-v26/ic_card_file_box.xml
diff --git a/catalog/src/main/res/mipmap-anydpi-v26/ic_card_file_box_round.xml b/catalog/src/androidMain/res/mipmap-anydpi-v26/ic_card_file_box_round.xml
similarity index 54%
rename from catalog/src/main/res/mipmap-anydpi-v26/ic_card_file_box_round.xml
rename to catalog/src/androidMain/res/mipmap-anydpi-v26/ic_card_file_box_round.xml
index 78b23ed063..c242d1fe57 100644
--- a/catalog/src/main/res/mipmap-anydpi-v26/ic_card_file_box_round.xml
+++ b/catalog/src/androidMain/res/mipmap-anydpi-v26/ic_card_file_box_round.xml
@@ -1,5 +1,9 @@
-
+
diff --git a/catalog/src/main/res/mipmap-hdpi/ic_card_file_box.png b/catalog/src/androidMain/res/mipmap-hdpi/ic_card_file_box.png
similarity index 100%
rename from catalog/src/main/res/mipmap-hdpi/ic_card_file_box.png
rename to catalog/src/androidMain/res/mipmap-hdpi/ic_card_file_box.png
diff --git a/catalog/src/main/res/mipmap-hdpi/ic_card_file_box_foreground.png b/catalog/src/androidMain/res/mipmap-hdpi/ic_card_file_box_foreground.png
similarity index 100%
rename from catalog/src/main/res/mipmap-hdpi/ic_card_file_box_foreground.png
rename to catalog/src/androidMain/res/mipmap-hdpi/ic_card_file_box_foreground.png
diff --git a/catalog/src/main/res/mipmap-hdpi/ic_card_file_box_round.png b/catalog/src/androidMain/res/mipmap-hdpi/ic_card_file_box_round.png
similarity index 100%
rename from catalog/src/main/res/mipmap-hdpi/ic_card_file_box_round.png
rename to catalog/src/androidMain/res/mipmap-hdpi/ic_card_file_box_round.png
diff --git a/catalog/src/main/res/mipmap-mdpi/ic_card_file_box.png b/catalog/src/androidMain/res/mipmap-mdpi/ic_card_file_box.png
similarity index 100%
rename from catalog/src/main/res/mipmap-mdpi/ic_card_file_box.png
rename to catalog/src/androidMain/res/mipmap-mdpi/ic_card_file_box.png
diff --git a/catalog/src/main/res/mipmap-mdpi/ic_card_file_box_foreground.png b/catalog/src/androidMain/res/mipmap-mdpi/ic_card_file_box_foreground.png
similarity index 100%
rename from catalog/src/main/res/mipmap-mdpi/ic_card_file_box_foreground.png
rename to catalog/src/androidMain/res/mipmap-mdpi/ic_card_file_box_foreground.png
diff --git a/catalog/src/main/res/mipmap-mdpi/ic_card_file_box_round.png b/catalog/src/androidMain/res/mipmap-mdpi/ic_card_file_box_round.png
similarity index 100%
rename from catalog/src/main/res/mipmap-mdpi/ic_card_file_box_round.png
rename to catalog/src/androidMain/res/mipmap-mdpi/ic_card_file_box_round.png
diff --git a/catalog/src/main/res/mipmap-xhdpi/ic_card_file_box.png b/catalog/src/androidMain/res/mipmap-xhdpi/ic_card_file_box.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xhdpi/ic_card_file_box.png
rename to catalog/src/androidMain/res/mipmap-xhdpi/ic_card_file_box.png
diff --git a/catalog/src/main/res/mipmap-xhdpi/ic_card_file_box_foreground.png b/catalog/src/androidMain/res/mipmap-xhdpi/ic_card_file_box_foreground.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xhdpi/ic_card_file_box_foreground.png
rename to catalog/src/androidMain/res/mipmap-xhdpi/ic_card_file_box_foreground.png
diff --git a/catalog/src/main/res/mipmap-xhdpi/ic_card_file_box_round.png b/catalog/src/androidMain/res/mipmap-xhdpi/ic_card_file_box_round.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xhdpi/ic_card_file_box_round.png
rename to catalog/src/androidMain/res/mipmap-xhdpi/ic_card_file_box_round.png
diff --git a/catalog/src/main/res/mipmap-xxhdpi/ic_card_file_box.png b/catalog/src/androidMain/res/mipmap-xxhdpi/ic_card_file_box.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xxhdpi/ic_card_file_box.png
rename to catalog/src/androidMain/res/mipmap-xxhdpi/ic_card_file_box.png
diff --git a/catalog/src/main/res/mipmap-xxhdpi/ic_card_file_box_foreground.png b/catalog/src/androidMain/res/mipmap-xxhdpi/ic_card_file_box_foreground.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xxhdpi/ic_card_file_box_foreground.png
rename to catalog/src/androidMain/res/mipmap-xxhdpi/ic_card_file_box_foreground.png
diff --git a/catalog/src/main/res/mipmap-xxhdpi/ic_card_file_box_round.png b/catalog/src/androidMain/res/mipmap-xxhdpi/ic_card_file_box_round.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xxhdpi/ic_card_file_box_round.png
rename to catalog/src/androidMain/res/mipmap-xxhdpi/ic_card_file_box_round.png
diff --git a/catalog/src/main/res/mipmap-xxxhdpi/ic_card_file_box.png b/catalog/src/androidMain/res/mipmap-xxxhdpi/ic_card_file_box.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xxxhdpi/ic_card_file_box.png
rename to catalog/src/androidMain/res/mipmap-xxxhdpi/ic_card_file_box.png
diff --git a/catalog/src/main/res/mipmap-xxxhdpi/ic_card_file_box_foreground.png b/catalog/src/androidMain/res/mipmap-xxxhdpi/ic_card_file_box_foreground.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xxxhdpi/ic_card_file_box_foreground.png
rename to catalog/src/androidMain/res/mipmap-xxxhdpi/ic_card_file_box_foreground.png
diff --git a/catalog/src/main/res/mipmap-xxxhdpi/ic_card_file_box_round.png b/catalog/src/androidMain/res/mipmap-xxxhdpi/ic_card_file_box_round.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xxxhdpi/ic_card_file_box_round.png
rename to catalog/src/androidMain/res/mipmap-xxxhdpi/ic_card_file_box_round.png
diff --git a/catalog/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/catalog/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xxxhdpi/ic_launcher.png
rename to catalog/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png
diff --git a/catalog/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/catalog/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png
similarity index 100%
rename from catalog/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
rename to catalog/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png
diff --git a/catalog/src/main/res/values-night/colors.xml b/catalog/src/androidMain/res/values-night/colors.xml
similarity index 55%
rename from catalog/src/main/res/values-night/colors.xml
rename to catalog/src/androidMain/res/values-night/colors.xml
index d80470737a..f76e31bf10 100644
--- a/catalog/src/main/res/values-night/colors.xml
+++ b/catalog/src/androidMain/res/values-night/colors.xml
@@ -15,6 +15,38 @@
limitations under the License.
-->
+ #A8C7FA
+ #3B5CFF
+ #062E6F
+ #0842A0
+ #D3E3FD
+
+ #7FCFFF
+ #003355
+ #004A77
+ #C2E7FF
+
+ #91D5A3
+ #0A3818
+ #0F5223
+ #C4EED0
+
+ #F2B8B5
+ #601410
+ #8C1D18
+ #F9DEDC
+
+ #1F1F1F
+ #E3E3E3
+
+ #1F1F1F
+ #E3E3E3
+
+ #444746
+ #C4C7C5
+
+ #8E918F
+
#000000
#0C0A20
#201441
diff --git a/catalog/src/main/res/values/colors.xml b/catalog/src/androidMain/res/values/colors.xml
similarity index 55%
rename from catalog/src/main/res/values/colors.xml
rename to catalog/src/androidMain/res/values/colors.xml
index f9b63e732a..80b8681cfc 100644
--- a/catalog/src/main/res/values/colors.xml
+++ b/catalog/src/androidMain/res/values/colors.xml
@@ -1,127 +1,76 @@
-
- #FFBB86FC
- #FF6200EE
- #FF3700B3
- #FF03DAC5
- #FF018786
- #FF000000
- #FFFFFFFF
- #F8F9FA
- #F1F3F4
- #E8EAED
- #F1F3F4
- #3C4043
- #5F6368
- #202124
- #1A73E8
- #1967D2
- #1A73E8
- #8AB4F8
- #618AB4F8
- #D2E3FC
- #E8F0FE
- #D93025
- #F28B82
- #5F6368
- #DADCE0
-
#0B57D0
+ #A8C7FA
+ #FFFFFF
+ #062E6F
#FFFFFF
+ #0842A0
#D3E3FD
#041E49
+ #D3E3FD
#00639B
+ #7FCFFF
+ #003355
#FFFFFF
+ #004A77
#C2E7FF
#001D35
+ #C2E7FF
#146C2E
+ #91D5A3
+ #0A3818
#FFFFFF
+ #0F5223
#C4EED0
#072711
-
- #B3261E
- #FFFFFF
- #F9DEDC
- #410E0B
-
- #FFFFFF
- #1F1F1F
-
- #FFFFFF
- #1F1F1F
-
- #E1E3E1
- #444746
-
- #747775
-
- #A8C7FA
- #062E6F
- #0842A0
- #D3E3FD
-
- #7FCFFF
- #003355
- #004A77
- #C2E7FF
-
- #146C2E
- #0A3818
- #0F5223
#C4EED0
+ #B3261E
#F2B8B5
#601410
+ #FFFFFF
#8C1D18
+ #F9DEDC
+ #410E0B
#F9DEDC
#1F1F1F
+ #FFFFFF
+ #1F1F1F
#E3E3E3
#1F1F1F
+ #FFFFFF
+ #1F1F1F
#E3E3E3
#444746
+ #E1E3E1
+ #444746
#C4C7C5
+ #747775
#8E918F
-
- #7A9FFF
- #668FFF
- #5581FF
- #476FFF
- #3B5CFF
- #3249FF
- #2936FF
- #2024FF
- #1816FF
- #FFFFFF
- #FFFFFF
- #FFFFFF
- #FFFFFF
- #FFFFFF
- #FFFFFF
- #FFFFFF
- #FFFFFF
- #FFFFFF
+ #000000
+ #0C0A20
+ #201441
+ #341F63
+ #482A85
+ #5C35A6
+ #7F5FBA
+ #A289CF
+ #C5B3E3
#FFFFFF
-
+ #FFFFFF
+ #FFFFFF
+ #FFFFFF
+ #FFFFFF
+ #FFFFFF
+ #FFFFFF
+ #000000
+ #000000
diff --git a/catalog/src/main/res/layout/fragment_questionnaire_container.xml b/catalog/src/androidMain/res/values/strings.xml
similarity index 68%
rename from catalog/src/main/res/layout/fragment_questionnaire_container.xml
rename to catalog/src/androidMain/res/values/strings.xml
index fb73b0b80c..ed024c1ead 100644
--- a/catalog/src/main/res/layout/fragment_questionnaire_container.xml
+++ b/catalog/src/androidMain/res/values/strings.xml
@@ -1,4 +1,3 @@
-
-
+
+ Structured Data Capture Catalog
+
diff --git a/catalog/src/main/res/xml/file_paths.xml b/catalog/src/androidMain/res/xml/file_paths.xml
similarity index 100%
rename from catalog/src/main/res/xml/file_paths.xml
rename to catalog/src/androidMain/res/xml/file_paths.xml
diff --git a/catalog/src/main/res/drawable/ic_answers_behavior.xml b/catalog/src/commonMain/composeResources/drawable/ic_answers_behavior.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_answers_behavior.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_answers_behavior.xml
diff --git a/catalog/src/main/res/drawable/ic_attachment.xml b/catalog/src/commonMain/composeResources/drawable/ic_attachment.xml
similarity index 94%
rename from catalog/src/main/res/drawable/ic_attachment.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_attachment.xml
index b168e17c49..0c76cc76c1 100644
--- a/catalog/src/main/res/drawable/ic_attachment.xml
+++ b/catalog/src/commonMain/composeResources/drawable/ic_attachment.xml
@@ -7,7 +7,7 @@
android:tint="#1A73E8"
>
diff --git a/catalog/src/main/res/drawable/ic_autocomplete.xml b/catalog/src/commonMain/composeResources/drawable/ic_autocomplete.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_autocomplete.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_autocomplete.xml
diff --git a/contrib/barcode/src/main/res/drawable/ic_barcode.xml b/catalog/src/commonMain/composeResources/drawable/ic_barcode.xml
similarity index 100%
rename from contrib/barcode/src/main/res/drawable/ic_barcode.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_barcode.xml
diff --git a/catalog/src/main/res/drawable/ic_behaviors.xml b/catalog/src/commonMain/composeResources/drawable/ic_behaviors.xml
similarity index 54%
rename from catalog/src/main/res/drawable/ic_behaviors.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_behaviors.xml
index fa88a7f158..f15a83d1bb 100644
--- a/catalog/src/main/res/drawable/ic_behaviors.xml
+++ b/catalog/src/commonMain/composeResources/drawable/ic_behaviors.xml
@@ -4,30 +4,21 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal"
+ android:tint="#FF000000"
>
+
+
+
-
-
-
diff --git a/catalog/src/main/res/drawable/ic_booleanchoice.xml b/catalog/src/commonMain/composeResources/drawable/ic_booleanchoice.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_booleanchoice.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_booleanchoice.xml
diff --git a/catalog/src/main/res/drawable/ic_calculations_behavior.xml b/catalog/src/commonMain/composeResources/drawable/ic_calculations_behavior.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_calculations_behavior.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_calculations_behavior.xml
diff --git a/catalog/src/main/res/drawable/ic_components.xml b/catalog/src/commonMain/composeResources/drawable/ic_components.xml
similarity index 82%
rename from catalog/src/main/res/drawable/ic_components.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_components.xml
index 1426bb5310..b712ca1d0e 100644
--- a/catalog/src/main/res/drawable/ic_components.xml
+++ b/catalog/src/commonMain/composeResources/drawable/ic_components.xml
@@ -4,10 +4,10 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal"
+ android:tint="#FF000000"
>
diff --git a/catalog/src/main/res/drawable/ic_context.xml b/catalog/src/commonMain/composeResources/drawable/ic_context.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_context.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_context.xml
diff --git a/catalog/src/main/res/drawable/ic_dateofbirth.xml b/catalog/src/commonMain/composeResources/drawable/ic_dateofbirth.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_dateofbirth.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_dateofbirth.xml
diff --git a/catalog/src/main/res/drawable/ic_datepicker.xml b/catalog/src/commonMain/composeResources/drawable/ic_datepicker.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_datepicker.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_datepicker.xml
diff --git a/catalog/src/main/res/drawable/ic_defaultlayout.xml b/catalog/src/commonMain/composeResources/drawable/ic_defaultlayout.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_defaultlayout.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_defaultlayout.xml
diff --git a/catalog/src/main/res/drawable/ic_dynamic_text_behavior.xml b/catalog/src/commonMain/composeResources/drawable/ic_dynamic_text_behavior.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_dynamic_text_behavior.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_dynamic_text_behavior.xml
diff --git a/catalog/src/main/res/drawable/ic_group_1278.xml b/catalog/src/commonMain/composeResources/drawable/ic_group_1278.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_group_1278.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_group_1278.xml
diff --git a/catalog/src/main/res/drawable/ic_help.xml b/catalog/src/commonMain/composeResources/drawable/ic_help.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_help.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_help.xml
diff --git a/catalog/src/main/res/drawable/ic_icon.xml b/catalog/src/commonMain/composeResources/drawable/ic_icon.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_icon.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_icon.xml
diff --git a/catalog/src/main/res/drawable/ic_info_24.xml b/catalog/src/commonMain/composeResources/drawable/ic_info_24.xml
similarity index 78%
rename from catalog/src/main/res/drawable/ic_info_24.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_info_24.xml
index 27241420a4..31dc46860e 100644
--- a/catalog/src/main/res/drawable/ic_info_24.xml
+++ b/catalog/src/commonMain/composeResources/drawable/ic_info_24.xml
@@ -4,10 +4,10 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal"
+ android:tint="#FF000000"
>
diff --git a/catalog/src/main/res/drawable/ic_initial_value_component.xml b/catalog/src/commonMain/composeResources/drawable/ic_initial_value_component.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_initial_value_component.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_initial_value_component.xml
diff --git a/catalog/src/main/res/drawable/ic_item_answer_media.xml b/catalog/src/commonMain/composeResources/drawable/ic_item_answer_media.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_item_answer_media.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_item_answer_media.xml
diff --git a/catalog/src/main/res/drawable/ic_item_media.xml b/catalog/src/commonMain/composeResources/drawable/ic_item_media.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_item_media.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_item_media.xml
diff --git a/catalog/src/main/res/drawable/ic_layouts.xml b/catalog/src/commonMain/composeResources/drawable/ic_layouts.xml
similarity index 84%
rename from catalog/src/main/res/drawable/ic_layouts.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_layouts.xml
index 20aa044337..fe41081449 100644
--- a/catalog/src/main/res/drawable/ic_layouts.xml
+++ b/catalog/src/commonMain/composeResources/drawable/ic_layouts.xml
@@ -4,11 +4,11 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal"
+ android:tint="#FF000000"
android:autoMirrored="true"
>
diff --git a/catalog/src/main/res/drawable/ic_location_on.xml b/catalog/src/commonMain/composeResources/drawable/ic_location_on.xml
similarity index 84%
rename from catalog/src/main/res/drawable/ic_location_on.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_location_on.xml
index 9821fffba8..c16679f482 100644
--- a/catalog/src/main/res/drawable/ic_location_on.xml
+++ b/catalog/src/commonMain/composeResources/drawable/ic_location_on.xml
@@ -2,12 +2,11 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
- android:tint="#1A73E8"
android:viewportWidth="24"
android:viewportHeight="24"
>
diff --git a/catalog/src/main/res/drawable/ic_modal.xml b/catalog/src/commonMain/composeResources/drawable/ic_modal.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_modal.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_modal.xml
diff --git a/catalog/src/main/res/drawable/ic_multiplechoice.xml b/catalog/src/commonMain/composeResources/drawable/ic_multiplechoice.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_multiplechoice.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_multiplechoice.xml
diff --git a/catalog/src/main/res/drawable/ic_openchoice.xml b/catalog/src/commonMain/composeResources/drawable/ic_openchoice.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_openchoice.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_openchoice.xml
diff --git a/catalog/src/main/res/drawable/ic_paginatedlayout.xml b/catalog/src/commonMain/composeResources/drawable/ic_paginatedlayout.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_paginatedlayout.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_paginatedlayout.xml
diff --git a/catalog/src/main/res/drawable/ic_rangepicker.xml b/catalog/src/commonMain/composeResources/drawable/ic_rangepicker.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_rangepicker.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_rangepicker.xml
diff --git a/catalog/src/main/res/drawable/ic_readonlylayout.xml b/catalog/src/commonMain/composeResources/drawable/ic_readonlylayout.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_readonlylayout.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_readonlylayout.xml
diff --git a/catalog/src/main/res/drawable/ic_repeatgroups.xml b/catalog/src/commonMain/composeResources/drawable/ic_repeatgroups.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_repeatgroups.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_repeatgroups.xml
diff --git a/catalog/src/main/res/drawable/ic_reviewlayout.xml b/catalog/src/commonMain/composeResources/drawable/ic_reviewlayout.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_reviewlayout.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_reviewlayout.xml
diff --git a/catalog/src/main/res/drawable/ic_rule.xml b/catalog/src/commonMain/composeResources/drawable/ic_rule.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_rule.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_rule.xml
diff --git a/catalog/src/main/res/drawable/ic_singlechoice.xml b/catalog/src/commonMain/composeResources/drawable/ic_singlechoice.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_singlechoice.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_singlechoice.xml
diff --git a/catalog/src/main/res/drawable/ic_skiplogic_behavior.xml b/catalog/src/commonMain/composeResources/drawable/ic_skiplogic_behavior.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_skiplogic_behavior.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_skiplogic_behavior.xml
diff --git a/catalog/src/main/res/drawable/ic_slider.xml b/catalog/src/commonMain/composeResources/drawable/ic_slider.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_slider.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_slider.xml
diff --git a/catalog/src/main/res/drawable/ic_textfield.xml b/catalog/src/commonMain/composeResources/drawable/ic_textfield.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_textfield.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_textfield.xml
diff --git a/catalog/src/main/res/drawable/ic_timepicker.xml b/catalog/src/commonMain/composeResources/drawable/ic_timepicker.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_timepicker.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_timepicker.xml
diff --git a/catalog/src/main/res/drawable/ic_unitoptions.xml b/catalog/src/commonMain/composeResources/drawable/ic_unitoptions.xml
similarity index 100%
rename from catalog/src/main/res/drawable/ic_unitoptions.xml
rename to catalog/src/commonMain/composeResources/drawable/ic_unitoptions.xml
diff --git a/catalog/src/main/res/drawable/text_format_48dp.xml b/catalog/src/commonMain/composeResources/drawable/text_format_48dp.xml
similarity index 100%
rename from catalog/src/main/res/drawable/text_format_48dp.xml
rename to catalog/src/commonMain/composeResources/drawable/text_format_48dp.xml
diff --git a/catalog/src/main/assets/behavior_answer_expression.json b/catalog/src/commonMain/composeResources/files/behavior_answer_expression.json
similarity index 97%
rename from catalog/src/main/assets/behavior_answer_expression.json
rename to catalog/src/commonMain/composeResources/files/behavior_answer_expression.json
index 5470ff179c..01b07921d6 100644
--- a/catalog/src/main/assets/behavior_answer_expression.json
+++ b/catalog/src/commonMain/composeResources/files/behavior_answer_expression.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"extension": [
diff --git a/catalog/src/main/assets/behavior_calculated_expression.json b/catalog/src/commonMain/composeResources/files/behavior_calculated_expression.json
similarity index 97%
rename from catalog/src/main/assets/behavior_calculated_expression.json
rename to catalog/src/commonMain/composeResources/files/behavior_calculated_expression.json
index c41b6b0f69..2d1980f7f2 100644
--- a/catalog/src/main/assets/behavior_calculated_expression.json
+++ b/catalog/src/commonMain/composeResources/files/behavior_calculated_expression.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "birthdate",
diff --git a/catalog/src/main/assets/behavior_context_variables.json b/catalog/src/commonMain/composeResources/files/behavior_context_variables.json
similarity index 98%
rename from catalog/src/main/assets/behavior_context_variables.json
rename to catalog/src/commonMain/composeResources/files/behavior_context_variables.json
index 9e175dc476..62d8372eeb 100644
--- a/catalog/src/main/assets/behavior_context_variables.json
+++ b/catalog/src/commonMain/composeResources/files/behavior_context_variables.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/behavior_dynamic_question_text.json b/catalog/src/commonMain/composeResources/files/behavior_dynamic_question_text.json
similarity index 98%
rename from catalog/src/main/assets/behavior_dynamic_question_text.json
rename to catalog/src/commonMain/composeResources/files/behavior_dynamic_question_text.json
index 50af5eca49..7c9916167b 100644
--- a/catalog/src/main/assets/behavior_dynamic_question_text.json
+++ b/catalog/src/commonMain/composeResources/files/behavior_dynamic_question_text.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"text": "Choose an option below",
diff --git a/catalog/src/main/assets/behavior_questionnaire_constraint.json b/catalog/src/commonMain/composeResources/files/behavior_questionnaire_constraint.json
similarity index 99%
rename from catalog/src/main/assets/behavior_questionnaire_constraint.json
rename to catalog/src/commonMain/composeResources/files/behavior_questionnaire_constraint.json
index a592ccb466..3ba983fa7c 100644
--- a/catalog/src/main/assets/behavior_questionnaire_constraint.json
+++ b/catalog/src/commonMain/composeResources/files/behavior_questionnaire_constraint.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/behavior_skip_logic.json b/catalog/src/commonMain/composeResources/files/behavior_skip_logic.json
similarity index 99%
rename from catalog/src/main/assets/behavior_skip_logic.json
rename to catalog/src/commonMain/composeResources/files/behavior_skip_logic.json
index d0081550ba..9a2fe4f0cc 100644
--- a/catalog/src/main/assets/behavior_skip_logic.json
+++ b/catalog/src/commonMain/composeResources/files/behavior_skip_logic.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemExtractionContext",
diff --git a/catalog/src/main/assets/behavior_skip_logic_with_expression.json b/catalog/src/commonMain/composeResources/files/behavior_skip_logic_with_expression.json
similarity index 85%
rename from catalog/src/main/assets/behavior_skip_logic_with_expression.json
rename to catalog/src/commonMain/composeResources/files/behavior_skip_logic_with_expression.json
index 8151396ba9..ea455ac045 100644
--- a/catalog/src/main/assets/behavior_skip_logic_with_expression.json
+++ b/catalog/src/commonMain/composeResources/files/behavior_skip_logic_with_expression.json
@@ -1,10 +1,11 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/variable",
"valueExpression": {
- "name": "has-fever",
+ "name": "feverish",
"language": "text/fhirpath",
"expression": "%resource.descendants().where(linkId='1').answer.value"
}
@@ -31,7 +32,7 @@
}
],
"linkId": "1.1",
- "text": "Define the questionnaire variable 'has-fever' based on the answer to the question 'Does the patient have a fever?",
+ "text": "Define the questionnaire variable 'feverish' based on the answer to the question 'Does the patient have a fever?",
"type": "display"
}
]
@@ -45,7 +46,7 @@
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression",
"valueExpression": {
"language": "text/fhirpath",
- "expression": "%has-fever"
+ "expression": "%feverish"
}
}
],
@@ -65,7 +66,7 @@
}
],
"linkId": "2.1",
- "text": "Enabled if variable 'has-fever' evaluates to true",
+ "text": "Enabled if variable 'feverish' evaluates to true",
"type": "display"
}
]
diff --git a/catalog/src/main/assets/component_attachment.json b/catalog/src/commonMain/composeResources/files/component_attachment.json
similarity index 98%
rename from catalog/src/main/assets/component_attachment.json
rename to catalog/src/commonMain/composeResources/files/component_attachment.json
index 4a4ce8bf93..eb61bc2430 100644
--- a/catalog/src/main/assets/component_attachment.json
+++ b/catalog/src/commonMain/composeResources/files/component_attachment.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"extension": [
diff --git a/catalog/src/main/assets/component_attachment_with_validation.json b/catalog/src/commonMain/composeResources/files/component_attachment_with_validation.json
similarity index 98%
rename from catalog/src/main/assets/component_attachment_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_attachment_with_validation.json
index 6b3aee0303..1689de3a21 100644
--- a/catalog/src/main/assets/component_attachment_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_attachment_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"extension": [
diff --git a/catalog/src/main/assets/component_auto_complete.json b/catalog/src/commonMain/composeResources/files/component_auto_complete.json
similarity index 99%
rename from catalog/src/main/assets/component_auto_complete.json
rename to catalog/src/commonMain/composeResources/files/component_auto_complete.json
index e751258cd2..c9b111f8be 100644
--- a/catalog/src/main/assets/component_auto_complete.json
+++ b/catalog/src/commonMain/composeResources/files/component_auto_complete.json
@@ -6,6 +6,7 @@
},
"status": "draft",
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"type": "choice",
diff --git a/catalog/src/main/assets/component_auto_complete_with_validation.json b/catalog/src/commonMain/composeResources/files/component_auto_complete_with_validation.json
similarity index 99%
rename from catalog/src/main/assets/component_auto_complete_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_auto_complete_with_validation.json
index 519b94df4c..2fab92ef61 100644
--- a/catalog/src/main/assets/component_auto_complete_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_auto_complete_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/commonMain/composeResources/files/component_barcode_widget.json b/catalog/src/commonMain/composeResources/files/component_barcode_widget.json
new file mode 100644
index 0000000000..95f3873cd4
--- /dev/null
+++ b/catalog/src/commonMain/composeResources/files/component_barcode_widget.json
@@ -0,0 +1,26 @@
+{
+ "resourceType": "Questionnaire",
+ "status": "active",
+ "language": "en",
+ "date": "2020-11-18T07:24:47.111Z",
+ "item": [
+ {
+ "linkId": "barcode",
+ "type": "string",
+ "text": "Barcode Widget",
+ "extension": [
+ {
+ "url": "https://github.com/google/android-fhir/StructureDefinition/questionnaire-itemControl",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "https://github.com/google/android-fhir/questionnaire-item-control",
+ "code": "barcode"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/catalog/src/main/assets/component_boolean_choice.json b/catalog/src/commonMain/composeResources/files/component_boolean_choice.json
similarity index 97%
rename from catalog/src/main/assets/component_boolean_choice.json
rename to catalog/src/commonMain/composeResources/files/component_boolean_choice.json
index 9e7c8d137d..6d69aba348 100644
--- a/catalog/src/main/assets/component_boolean_choice.json
+++ b/catalog/src/commonMain/composeResources/files/component_boolean_choice.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_boolean_choice_with_validation.json b/catalog/src/commonMain/composeResources/files/component_boolean_choice_with_validation.json
similarity index 97%
rename from catalog/src/main/assets/component_boolean_choice_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_boolean_choice_with_validation.json
index b706ee1b2a..07abb60446 100644
--- a/catalog/src/main/assets/component_boolean_choice_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_boolean_choice_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_date_picker.json b/catalog/src/commonMain/composeResources/files/component_date_picker.json
similarity index 97%
rename from catalog/src/main/assets/component_date_picker.json
rename to catalog/src/commonMain/composeResources/files/component_date_picker.json
index 473bfc2fe3..c9998338c3 100644
--- a/catalog/src/main/assets/component_date_picker.json
+++ b/catalog/src/commonMain/composeResources/files/component_date_picker.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_date_picker_with_validation.json b/catalog/src/commonMain/composeResources/files/component_date_picker_with_validation.json
similarity index 97%
rename from catalog/src/main/assets/component_date_picker_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_date_picker_with_validation.json
index 94a6a075a8..cbcd65f87a 100644
--- a/catalog/src/main/assets/component_date_picker_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_date_picker_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_date_time_picker.json b/catalog/src/commonMain/composeResources/files/component_date_time_picker.json
similarity index 97%
rename from catalog/src/main/assets/component_date_time_picker.json
rename to catalog/src/commonMain/composeResources/files/component_date_time_picker.json
index 1084fe3225..1ccc316350 100644
--- a/catalog/src/main/assets/component_date_time_picker.json
+++ b/catalog/src/commonMain/composeResources/files/component_date_time_picker.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_date_time_picker_with_validation.json b/catalog/src/commonMain/composeResources/files/component_date_time_picker_with_validation.json
similarity index 88%
rename from catalog/src/main/assets/component_date_time_picker_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_date_time_picker_with_validation.json
index d82c6ef49f..9b47269d9e 100644
--- a/catalog/src/main/assets/component_date_time_picker_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_date_time_picker_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_dropdown.json b/catalog/src/commonMain/composeResources/files/component_dropdown.json
similarity index 98%
rename from catalog/src/main/assets/component_dropdown.json
rename to catalog/src/commonMain/composeResources/files/component_dropdown.json
index f5797d05dc..f7407a5626 100644
--- a/catalog/src/main/assets/component_dropdown.json
+++ b/catalog/src/commonMain/composeResources/files/component_dropdown.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_dropdown_with_validation.json b/catalog/src/commonMain/composeResources/files/component_dropdown_with_validation.json
similarity index 98%
rename from catalog/src/main/assets/component_dropdown_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_dropdown_with_validation.json
index d20a24a75e..85d907879b 100644
--- a/catalog/src/main/assets/component_dropdown_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_dropdown_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_help.json b/catalog/src/commonMain/composeResources/files/component_help.json
similarity index 98%
rename from catalog/src/main/assets/component_help.json
rename to catalog/src/commonMain/composeResources/files/component_help.json
index 86f6eac6c7..eb4d935977 100644
--- a/catalog/src/main/assets/component_help.json
+++ b/catalog/src/commonMain/composeResources/files/component_help.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_initial_value.json b/catalog/src/commonMain/composeResources/files/component_initial_value.json
similarity index 99%
rename from catalog/src/main/assets/component_initial_value.json
rename to catalog/src/commonMain/composeResources/files/component_initial_value.json
index 1a294ef2f5..50112ef173 100644
--- a/catalog/src/main/assets/component_initial_value.json
+++ b/catalog/src/commonMain/composeResources/files/component_initial_value.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1.0",
diff --git a/catalog/src/main/assets/component_item_media.json b/catalog/src/commonMain/composeResources/files/component_item_media.json
similarity index 99%
rename from catalog/src/main/assets/component_item_media.json
rename to catalog/src/commonMain/composeResources/files/component_item_media.json
index 902dfb72ca..723c56d07f 100644
--- a/catalog/src/main/assets/component_item_media.json
+++ b/catalog/src/commonMain/composeResources/files/component_item_media.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1.1",
diff --git a/catalog/src/main/assets/component_location_widget.json b/catalog/src/commonMain/composeResources/files/component_location_widget.json
similarity index 100%
rename from catalog/src/main/assets/component_location_widget.json
rename to catalog/src/commonMain/composeResources/files/component_location_widget.json
index 73c26da715..5ac3d947d1 100644
--- a/catalog/src/main/assets/component_location_widget.json
+++ b/catalog/src/commonMain/composeResources/files/component_location_widget.json
@@ -1,7 +1,7 @@
{
"resourceType": "Questionnaire",
- "language": "en",
"status": "active",
+ "language": "en",
"date": "2020-11-18T07:24:47.111Z",
"item": [
{
diff --git a/catalog/src/main/assets/component_modal.json b/catalog/src/commonMain/composeResources/files/component_modal.json
similarity index 98%
rename from catalog/src/main/assets/component_modal.json
rename to catalog/src/commonMain/composeResources/files/component_modal.json
index 54b8dc9222..a9a31f27c2 100644
--- a/catalog/src/main/assets/component_modal.json
+++ b/catalog/src/commonMain/composeResources/files/component_modal.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"extension": [
diff --git a/catalog/src/main/assets/component_modal_with_validation.json b/catalog/src/commonMain/composeResources/files/component_modal_with_validation.json
similarity index 98%
rename from catalog/src/main/assets/component_modal_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_modal_with_validation.json
index 49a08f569c..735db07de8 100644
--- a/catalog/src/main/assets/component_modal_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_modal_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1.1",
diff --git a/catalog/src/main/assets/component_multi_select_choice.json b/catalog/src/commonMain/composeResources/files/component_multi_select_choice.json
similarity index 99%
rename from catalog/src/main/assets/component_multi_select_choice.json
rename to catalog/src/commonMain/composeResources/files/component_multi_select_choice.json
index 8e5a72c3e2..c76871b24d 100644
--- a/catalog/src/main/assets/component_multi_select_choice.json
+++ b/catalog/src/commonMain/composeResources/files/component_multi_select_choice.json
@@ -3,6 +3,7 @@
"status": "active",
"version": "0.0.1",
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1.0.0",
diff --git a/catalog/src/main/assets/component_multi_select_choice_with_validation.json b/catalog/src/commonMain/composeResources/files/component_multi_select_choice_with_validation.json
similarity index 99%
rename from catalog/src/main/assets/component_multi_select_choice_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_multi_select_choice_with_validation.json
index 415f17faa4..fdeff244fa 100644
--- a/catalog/src/main/assets/component_multi_select_choice_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_multi_select_choice_with_validation.json
@@ -3,6 +3,7 @@
"status": "active",
"version": "0.0.1",
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1.0.0",
diff --git a/catalog/src/main/assets/component_open_choice.json b/catalog/src/commonMain/composeResources/files/component_open_choice.json
similarity index 98%
rename from catalog/src/main/assets/component_open_choice.json
rename to catalog/src/commonMain/composeResources/files/component_open_choice.json
index f9a8b36b72..7c848ed98e 100644
--- a/catalog/src/main/assets/component_open_choice.json
+++ b/catalog/src/commonMain/composeResources/files/component_open_choice.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_open_choice_with_validation.json b/catalog/src/commonMain/composeResources/files/component_open_choice_with_validation.json
similarity index 98%
rename from catalog/src/main/assets/component_open_choice_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_open_choice_with_validation.json
index b12fc6f908..59cc0cdd5a 100644
--- a/catalog/src/main/assets/component_open_choice_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_open_choice_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"type": "choice",
diff --git a/catalog/src/main/assets/component_per_question_custom_style.json b/catalog/src/commonMain/composeResources/files/component_per_question_custom_style.json
similarity index 99%
rename from catalog/src/main/assets/component_per_question_custom_style.json
rename to catalog/src/commonMain/composeResources/files/component_per_question_custom_style.json
index 2f2e8f3b86..37b926863c 100644
--- a/catalog/src/main/assets/component_per_question_custom_style.json
+++ b/catalog/src/commonMain/composeResources/files/component_per_question_custom_style.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_quantity.json b/catalog/src/commonMain/composeResources/files/component_quantity.json
similarity index 96%
rename from catalog/src/main/assets/component_quantity.json
rename to catalog/src/commonMain/composeResources/files/component_quantity.json
index 93f5d1b682..54fdfa682b 100644
--- a/catalog/src/main/assets/component_quantity.json
+++ b/catalog/src/commonMain/composeResources/files/component_quantity.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_quantity_with_validation.json b/catalog/src/commonMain/composeResources/files/component_quantity_with_validation.json
similarity index 96%
rename from catalog/src/main/assets/component_quantity_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_quantity_with_validation.json
index 39760c474c..27196f431f 100644
--- a/catalog/src/main/assets/component_quantity_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_quantity_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_repeated_group.json b/catalog/src/commonMain/composeResources/files/component_repeated_group.json
similarity index 99%
rename from catalog/src/main/assets/component_repeated_group.json
rename to catalog/src/commonMain/composeResources/files/component_repeated_group.json
index 683323e799..0a11783d1e 100644
--- a/catalog/src/main/assets/component_repeated_group.json
+++ b/catalog/src/commonMain/composeResources/files/component_repeated_group.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_single_choice.json b/catalog/src/commonMain/composeResources/files/component_single_choice.json
similarity index 98%
rename from catalog/src/main/assets/component_single_choice.json
rename to catalog/src/commonMain/composeResources/files/component_single_choice.json
index 81b2000cf2..9ff1f8c7c7 100644
--- a/catalog/src/main/assets/component_single_choice.json
+++ b/catalog/src/commonMain/composeResources/files/component_single_choice.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"extension": [
diff --git a/catalog/src/main/assets/component_single_choice_with_validation.json b/catalog/src/commonMain/composeResources/files/component_single_choice_with_validation.json
similarity index 98%
rename from catalog/src/main/assets/component_single_choice_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_single_choice_with_validation.json
index 25e4db11d1..63a83a9c40 100644
--- a/catalog/src/main/assets/component_single_choice_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_single_choice_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"extension": [
diff --git a/catalog/src/main/assets/component_slider.json b/catalog/src/commonMain/composeResources/files/component_slider.json
similarity index 96%
rename from catalog/src/main/assets/component_slider.json
rename to catalog/src/commonMain/composeResources/files/component_slider.json
index d2b58c5fd6..db2d401469 100644
--- a/catalog/src/main/assets/component_slider.json
+++ b/catalog/src/commonMain/composeResources/files/component_slider.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_slider_with_validation.json b/catalog/src/commonMain/composeResources/files/component_slider_with_validation.json
similarity index 98%
rename from catalog/src/main/assets/component_slider_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_slider_with_validation.json
index 16abca1843..91b6e77c2e 100644
--- a/catalog/src/main/assets/component_slider_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_slider_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"extension": [
diff --git a/catalog/src/main/assets/component_text_fields.json b/catalog/src/commonMain/composeResources/files/component_text_fields.json
similarity index 99%
rename from catalog/src/main/assets/component_text_fields.json
rename to catalog/src/commonMain/composeResources/files/component_text_fields.json
index 51c35a7b05..0449126cc9 100644
--- a/catalog/src/main/assets/component_text_fields.json
+++ b/catalog/src/commonMain/composeResources/files/component_text_fields.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_text_fields_with_validation.json b/catalog/src/commonMain/composeResources/files/component_text_fields_with_validation.json
similarity index 99%
rename from catalog/src/main/assets/component_text_fields_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_text_fields_with_validation.json
index 3bacc243b5..d0e3df8304 100644
--- a/catalog/src/main/assets/component_text_fields_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_text_fields_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_time_picker.json b/catalog/src/commonMain/composeResources/files/component_time_picker.json
similarity index 97%
rename from catalog/src/main/assets/component_time_picker.json
rename to catalog/src/commonMain/composeResources/files/component_time_picker.json
index ac904d99c8..5e9c64233a 100644
--- a/catalog/src/main/assets/component_time_picker.json
+++ b/catalog/src/commonMain/composeResources/files/component_time_picker.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/component_time_picker_with_validation.json b/catalog/src/commonMain/composeResources/files/component_time_picker_with_validation.json
similarity index 97%
rename from catalog/src/main/assets/component_time_picker_with_validation.json
rename to catalog/src/commonMain/composeResources/files/component_time_picker_with_validation.json
index 198a8f5bea..041bbcaedf 100644
--- a/catalog/src/main/assets/component_time_picker_with_validation.json
+++ b/catalog/src/commonMain/composeResources/files/component_time_picker_with_validation.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/layout_default.json b/catalog/src/commonMain/composeResources/files/layout_default.json
similarity index 99%
rename from catalog/src/main/assets/layout_default.json
rename to catalog/src/commonMain/composeResources/files/layout_default.json
index 29b841381e..54ec1a92b5 100644
--- a/catalog/src/main/assets/layout_default.json
+++ b/catalog/src/commonMain/composeResources/files/layout_default.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/layout_paginated.json b/catalog/src/commonMain/composeResources/files/layout_paginated.json
similarity index 99%
rename from catalog/src/main/assets/layout_paginated.json
rename to catalog/src/commonMain/composeResources/files/layout_paginated.json
index bf2e339892..a7a3e12132 100644
--- a/catalog/src/main/assets/layout_paginated.json
+++ b/catalog/src/commonMain/composeResources/files/layout_paginated.json
@@ -1,5 +1,6 @@
{
"resourceType": "Questionnaire",
+ "status": "active",
"item": [
{
"linkId": "1",
diff --git a/catalog/src/main/assets/layout_review.json b/catalog/src/commonMain/composeResources/files/layout_review.json
similarity index 99%
rename from catalog/src/main/assets/layout_review.json
rename to catalog/src/commonMain/composeResources/files/layout_review.json
index 1e0a68975b..5427280660 100644
--- a/catalog/src/main/assets/layout_review.json
+++ b/catalog/src/commonMain/composeResources/files/layout_review.json
@@ -1,6 +1,7 @@
{
"title": "COVID-19 Screening",
"resourceType": "Questionnaire",
+ "status": "active",
"meta": {
"profile": [
"http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire|2.7"
diff --git a/catalog/src/main/assets/resource_data_bundle.json b/catalog/src/commonMain/composeResources/files/resource_data_bundle.json
similarity index 100%
rename from catalog/src/main/assets/resource_data_bundle.json
rename to catalog/src/commonMain/composeResources/files/resource_data_bundle.json
diff --git a/catalog/src/main/res/values/strings.xml b/catalog/src/commonMain/composeResources/values/strings.xml
similarity index 90%
rename from catalog/src/main/res/values/strings.xml
rename to catalog/src/commonMain/composeResources/values/strings.xml
index 69b5f26fa8..7874351f0e 100644
--- a/catalog/src/main/res/values/strings.xml
+++ b/catalog/src/commonMain/composeResources/values/strings.xml
@@ -38,6 +38,7 @@
Repeated Group
Attachment
Location Widget
+ Barcode Widget
Per question custom style
@@ -83,4 +84,12 @@
Widgets
Miscellaneous components
Open questionnaire
+ Errors found
+ Fix the following questions:
+ • %s
+ Fix questions
+ Submit anyway
+ Loading...
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/App.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/App.kt
new file mode 100644
index 0000000000..8516550a4d
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/App.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2025-2026 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.catalog
+
+import android_fhir.catalog.generated.resources.Res
+import android_fhir.catalog.generated.resources.ic_behaviors
+import android_fhir.catalog.generated.resources.ic_components
+import android_fhir.catalog.generated.resources.ic_layouts
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Icon
+import androidx.compose.material3.NavigationBar
+import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavDestination.Companion.hierarchy
+import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavHostController
+import androidx.navigation.NavType
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
+import androidx.savedstate.read
+import com.google.android.fhir.catalog.ui.behaviors.BehaviorListScreen
+import com.google.android.fhir.catalog.ui.behaviors.BehaviorListViewModel
+import com.google.android.fhir.catalog.ui.components.ComponentListScreen
+import com.google.android.fhir.catalog.ui.components.ComponentListViewModel
+import com.google.android.fhir.catalog.ui.layouts.LayoutListScreen
+import com.google.android.fhir.catalog.ui.layouts.LayoutListViewModel
+import com.google.android.fhir.catalog.ui.questionnaire.QuestionnaireResponseScreen
+import com.google.android.fhir.catalog.ui.questionnaire.QuestionnaireScreen
+import com.google.android.fhir.catalog.ui.questionnaire.QuestionnaireViewModel
+import com.google.android.fhir.catalog.ui.theme.AppTheme
+import kotlinx.coroutines.CoroutineScope
+import org.jetbrains.compose.resources.painterResource
+
+sealed class Screen(val route: String, val label: String, val icon: @Composable () -> Unit) {
+ object Components :
+ Screen(
+ "component_list",
+ "Components",
+ { Icon(painterResource(Res.drawable.ic_components), contentDescription = null) },
+ )
+
+ object Layouts :
+ Screen(
+ "layout_list",
+ "Layouts",
+ { Icon(painterResource(Res.drawable.ic_layouts), contentDescription = null) },
+ )
+
+ object Behaviors :
+ Screen(
+ "behavior_list",
+ "Behaviors",
+ { Icon(painterResource(Res.drawable.ic_behaviors), contentDescription = null) },
+ )
+}
+
+@Composable
+fun App() {
+ AppTheme {
+ Surface {
+ val navController: NavHostController = rememberNavController()
+ val scope: CoroutineScope = rememberCoroutineScope()
+ val componentViewModel: ComponentListViewModel = viewModel { ComponentListViewModel() }
+ val layoutViewModel: LayoutListViewModel = viewModel { LayoutListViewModel() }
+ val behaviorViewModel: BehaviorListViewModel = viewModel { BehaviorListViewModel() }
+
+ var submittedResponseJson by remember { mutableStateOf(null) }
+
+ val navBackStackEntry by navController.currentBackStackEntryAsState()
+ val currentDestination = navBackStackEntry?.destination
+
+ val items = listOf(Screen.Components, Screen.Layouts, Screen.Behaviors)
+
+ Scaffold(
+ bottomBar = {
+ if (items.any { it.route == currentDestination?.route }) {
+ NavigationBar {
+ items.forEach { screen ->
+ NavigationBarItem(
+ icon = screen.icon,
+ label = { Text(screen.label) },
+ selected =
+ currentDestination?.hierarchy?.any { it.route == screen.route } == true,
+ onClick = {
+ navController.navigate(screen.route) {
+ popUpTo(navController.graph.findStartDestination().id) { saveState = true }
+ launchSingleTop = true
+ restoreState = true
+ }
+ },
+ )
+ }
+ }
+ }
+ },
+ ) { innerPadding ->
+ NavHost(
+ navController = navController,
+ startDestination = Screen.Components.route,
+ modifier = Modifier.padding(innerPadding),
+ ) {
+ composable(Screen.Components.route) {
+ ComponentListScreen(
+ viewModel = componentViewModel,
+ onComponentClick = { component, title ->
+ val validationPart =
+ component.questionnaireFileWithValidation?.let { "?validationFile=$it" } ?: ""
+ navController.navigate(
+ "questionnaire/${component.questionnaireFile}/$title/false/false/false$validationPart",
+ )
+ },
+ )
+ }
+ composable(Screen.Layouts.route) {
+ LayoutListScreen(
+ viewModel = layoutViewModel,
+ onLayoutClick = { layout, title ->
+ navController.navigate(
+ "questionnaire/${layout.questionnaireFileName}/$title/${layout.showReviewPage}/${layout.showReviewPageFirst}/${layout.isReadOnly}",
+ )
+ },
+ )
+ }
+ composable(Screen.Behaviors.route) {
+ BehaviorListScreen(
+ viewModel = behaviorViewModel,
+ onBehaviorClick = { behavior, title ->
+ navController.navigate(
+ "questionnaire/${behavior.questionnaireFileName}/$title/false/false/false",
+ )
+ },
+ )
+ }
+ composable(
+ route =
+ "questionnaire/{fileName}/{title}/{review}/{reviewFirst}/{readOnly}?validationFile={validationFile}",
+ arguments =
+ listOf(
+ navArgument("fileName") { type = NavType.StringType },
+ navArgument("title") { type = NavType.StringType },
+ navArgument("review") { type = NavType.BoolType },
+ navArgument("reviewFirst") { type = NavType.BoolType },
+ navArgument("readOnly") { type = NavType.BoolType },
+ navArgument("validationFile") {
+ type = NavType.StringType
+ nullable = true
+ },
+ ),
+ ) { backStackEntry ->
+ val arguments = backStackEntry.arguments
+ val fileName =
+ arguments?.read { if (contains("fileName")) getString("fileName") else null } ?: ""
+ val title =
+ arguments?.read { if (contains("title")) getString("title") else null } ?: ""
+ val review =
+ arguments?.read { if (contains("review")) getBoolean("review") else null } ?: false
+ val reviewFirst =
+ arguments?.read { if (contains("reviewFirst")) getBoolean("reviewFirst") else null }
+ ?: false
+ val readOnly =
+ arguments?.read { if (contains("readOnly")) getBoolean("readOnly") else null }
+ ?: false
+ val validationFile =
+ arguments?.read {
+ if (contains("validationFile")) getString("validationFile") else null
+ }
+
+ QuestionnaireScreen(
+ viewModel = viewModel { QuestionnaireViewModel() },
+ title = title,
+ fileName = fileName,
+ validationFileName = validationFile,
+ showReviewPage = review,
+ showReviewPageFirst = reviewFirst,
+ isReadOnly = readOnly,
+ coroutineScope = scope,
+ onBackClick = { navController.popBackStack() },
+ navigateToResponse = { responseJson ->
+ submittedResponseJson = responseJson
+ navController.navigate("questionnaire_response/$title")
+ },
+ )
+ }
+ composable(
+ route = "questionnaire_response/{title}",
+ arguments = listOf(navArgument("title") { type = NavType.StringType }),
+ ) { backStackEntry ->
+ val arguments = backStackEntry.arguments
+ val title =
+ arguments?.read { if (contains("title")) getString("title") else null } ?: ""
+ QuestionnaireResponseScreen(
+ responseJson = submittedResponseJson ?: "",
+ onBackClick = { navController.popBackStack() },
+ )
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/behaviors/BehaviorListScreen.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/behaviors/BehaviorListScreen.kt
new file mode 100644
index 0000000000..a7dd057d09
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/behaviors/BehaviorListScreen.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2025-2026 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.catalog.ui.behaviors
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.items
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.google.android.fhir.catalog.ui.shared.CatalogItemCard
+import com.google.android.fhir.catalog.ui.shared.CatalogTopAppBar
+import org.jetbrains.compose.resources.stringResource
+
+@Composable
+fun BehaviorListScreen(
+ viewModel: BehaviorListViewModel,
+ onBehaviorClick: (BehaviorListViewModel.Behavior, String) -> Unit,
+) {
+ Scaffold(
+ topBar = { CatalogTopAppBar() },
+ ) { padding ->
+ LazyVerticalGrid(
+ columns = GridCells.Fixed(2),
+ modifier = Modifier.fillMaxSize().padding(padding),
+ contentPadding = PaddingValues(8.dp),
+ ) {
+ items(viewModel.getBehaviorList()) { behavior ->
+ val title = stringResource(behavior.text)
+ CatalogItemCard(
+ icon = behavior.icon,
+ text = title,
+ onClick = { onBehaviorClick(behavior, title) },
+ )
+ }
+ }
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/behaviors/BehaviorListViewModel.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/behaviors/BehaviorListViewModel.kt
new file mode 100644
index 0000000000..6023bb3d5d
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/behaviors/BehaviorListViewModel.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2022-2026 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.catalog.ui.behaviors
+
+import android_fhir.catalog.generated.resources.Res
+import android_fhir.catalog.generated.resources.behavior_name_answer_expression
+import android_fhir.catalog.generated.resources.behavior_name_calculated_expression
+import android_fhir.catalog.generated.resources.behavior_name_context_variables
+import android_fhir.catalog.generated.resources.behavior_name_dynamic_question_text
+import android_fhir.catalog.generated.resources.behavior_name_questionnaire_constraint
+import android_fhir.catalog.generated.resources.behavior_name_skip_logic
+import android_fhir.catalog.generated.resources.behavior_name_skip_logic_with_expression
+import android_fhir.catalog.generated.resources.ic_answers_behavior
+import android_fhir.catalog.generated.resources.ic_calculations_behavior
+import android_fhir.catalog.generated.resources.ic_context
+import android_fhir.catalog.generated.resources.ic_dynamic_text_behavior
+import android_fhir.catalog.generated.resources.ic_rule
+import android_fhir.catalog.generated.resources.ic_skiplogic_behavior
+import androidx.lifecycle.ViewModel
+import org.jetbrains.compose.resources.DrawableResource
+import org.jetbrains.compose.resources.StringResource
+
+class BehaviorListViewModel : ViewModel() {
+
+ fun getBehaviorList(): List {
+ return Behavior.entries
+ }
+
+ enum class Behavior(
+ val questionnaireFileName: String,
+ val icon: DrawableResource,
+ val text: StringResource,
+ ) {
+ CALCULATED_EXPRESSION(
+ "behavior_calculated_expression.json",
+ Res.drawable.ic_calculations_behavior,
+ Res.string.behavior_name_calculated_expression,
+ ),
+ ANSWER_EXPRESSION(
+ "behavior_answer_expression.json",
+ Res.drawable.ic_answers_behavior,
+ Res.string.behavior_name_answer_expression,
+ ),
+ CONTEXT_VARIABLES(
+ "behavior_context_variables.json",
+ Res.drawable.ic_context,
+ Res.string.behavior_name_context_variables,
+ ),
+ SKIP_LOGIC(
+ "behavior_skip_logic.json",
+ Res.drawable.ic_skiplogic_behavior,
+ Res.string.behavior_name_skip_logic,
+ ),
+ SKIP_LOGIC_WITH_EXPRESSION(
+ "behavior_skip_logic_with_expression.json",
+ Res.drawable.ic_skiplogic_behavior,
+ Res.string.behavior_name_skip_logic_with_expression,
+ ),
+ DYNAMIC_QUESTION_TEXT(
+ "behavior_dynamic_question_text.json",
+ Res.drawable.ic_dynamic_text_behavior,
+ Res.string.behavior_name_dynamic_question_text,
+ ),
+ QUESTIONNAIRE_CONSTRAINT(
+ "behavior_questionnaire_constraint.json",
+ Res.drawable.ic_rule,
+ Res.string.behavior_name_questionnaire_constraint,
+ ),
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/components/ComponentListScreen.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/components/ComponentListScreen.kt
new file mode 100644
index 0000000000..1654ba94c9
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/components/ComponentListScreen.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2025-2026 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.catalog.ui.components
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import com.google.android.fhir.catalog.ui.shared.CatalogItemCard
+import com.google.android.fhir.catalog.ui.shared.CatalogTopAppBar
+import org.jetbrains.compose.resources.stringResource
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ComponentListScreen(
+ viewModel: ComponentListViewModel,
+ onComponentClick: (ComponentListViewModel.Component, String) -> Unit,
+) {
+ Scaffold(
+ topBar = { CatalogTopAppBar() },
+ ) { padding ->
+ LazyVerticalGrid(
+ columns = GridCells.Fixed(2),
+ modifier = Modifier.fillMaxSize().padding(padding),
+ contentPadding = PaddingValues(8.dp),
+ ) {
+ viewModel.viewItemList.forEach { item ->
+ when (item) {
+ is ComponentListViewModel.ViewItem.HeaderItem -> {
+ item(span = { GridItemSpan(2) }) {
+ Text(
+ text = stringResource(item.header.title),
+ style = MaterialTheme.typography.bodyMedium,
+ fontWeight = FontWeight.Normal,
+ modifier = Modifier.padding(16.dp).fillMaxWidth(),
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
+ is ComponentListViewModel.ViewItem.ComponentItem -> {
+ item {
+ val title = stringResource(item.component.text)
+ CatalogItemCard(
+ icon = item.component.icon,
+ text = title,
+ onClick = { onComponentClick(item.component, title) },
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/components/ComponentListViewModel.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/components/ComponentListViewModel.kt
new file mode 100644
index 0000000000..26249adf1d
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/components/ComponentListViewModel.kt
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2023-2026 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.catalog.ui.components
+
+import android_fhir.catalog.generated.resources.Res
+import android_fhir.catalog.generated.resources.component_name_attachment
+import android_fhir.catalog.generated.resources.component_name_auto_complete
+import android_fhir.catalog.generated.resources.component_name_barcode_widget
+import android_fhir.catalog.generated.resources.component_name_boolean_choice
+import android_fhir.catalog.generated.resources.component_name_date_picker
+import android_fhir.catalog.generated.resources.component_name_date_time_picker
+import android_fhir.catalog.generated.resources.component_name_dropdown
+import android_fhir.catalog.generated.resources.component_name_help
+import android_fhir.catalog.generated.resources.component_name_initial_value
+import android_fhir.catalog.generated.resources.component_name_item_answer_media
+import android_fhir.catalog.generated.resources.component_name_item_media
+import android_fhir.catalog.generated.resources.component_name_location_widget
+import android_fhir.catalog.generated.resources.component_name_modal
+import android_fhir.catalog.generated.resources.component_name_multiple_choice
+import android_fhir.catalog.generated.resources.component_name_open_choice
+import android_fhir.catalog.generated.resources.component_name_per_question_custom_style
+import android_fhir.catalog.generated.resources.component_name_quantity
+import android_fhir.catalog.generated.resources.component_name_repeated_group
+import android_fhir.catalog.generated.resources.component_name_single_choice
+import android_fhir.catalog.generated.resources.component_name_slider
+import android_fhir.catalog.generated.resources.component_name_text_field
+import android_fhir.catalog.generated.resources.component_name_time_picker
+import android_fhir.catalog.generated.resources.ic_attachment
+import android_fhir.catalog.generated.resources.ic_autocomplete
+import android_fhir.catalog.generated.resources.ic_barcode
+import android_fhir.catalog.generated.resources.ic_booleanchoice
+import android_fhir.catalog.generated.resources.ic_datepicker
+import android_fhir.catalog.generated.resources.ic_group_1278
+import android_fhir.catalog.generated.resources.ic_help
+import android_fhir.catalog.generated.resources.ic_initial_value_component
+import android_fhir.catalog.generated.resources.ic_item_answer_media
+import android_fhir.catalog.generated.resources.ic_item_media
+import android_fhir.catalog.generated.resources.ic_location_on
+import android_fhir.catalog.generated.resources.ic_modal
+import android_fhir.catalog.generated.resources.ic_multiplechoice
+import android_fhir.catalog.generated.resources.ic_openchoice
+import android_fhir.catalog.generated.resources.ic_repeatgroups
+import android_fhir.catalog.generated.resources.ic_singlechoice
+import android_fhir.catalog.generated.resources.ic_slider
+import android_fhir.catalog.generated.resources.ic_textfield
+import android_fhir.catalog.generated.resources.ic_timepicker
+import android_fhir.catalog.generated.resources.ic_unitoptions
+import android_fhir.catalog.generated.resources.misc_components
+import android_fhir.catalog.generated.resources.text_format_48dp
+import android_fhir.catalog.generated.resources.widgets
+import androidx.lifecycle.ViewModel
+import org.jetbrains.compose.resources.DrawableResource
+import org.jetbrains.compose.resources.StringResource
+
+class ComponentListViewModel : ViewModel() {
+
+ sealed class ViewItem {
+ data class HeaderItem(val header: Header) : ViewItem()
+
+ data class ComponentItem(val component: Component) : ViewItem()
+ }
+
+ enum class Header(val title: StringResource) {
+ WIDGETS(Res.string.widgets),
+ MISC_COMPONENTS(Res.string.misc_components),
+ }
+
+ enum class Component(
+ val questionnaireFile: String,
+ val icon: DrawableResource,
+ val text: StringResource,
+ val questionnaireFileWithValidation: String? = null,
+ ) {
+ BOOLEAN_CHOICE(
+ "component_boolean_choice.json",
+ Res.drawable.ic_booleanchoice,
+ Res.string.component_name_boolean_choice,
+ "component_boolean_choice_with_validation.json",
+ ),
+ SINGLE_CHOICE(
+ "component_single_choice.json",
+ Res.drawable.ic_singlechoice,
+ Res.string.component_name_single_choice,
+ "component_single_choice_with_validation.json",
+ ),
+ MULTIPLE_CHOICE(
+ "component_multi_select_choice.json",
+ Res.drawable.ic_multiplechoice,
+ Res.string.component_name_multiple_choice,
+ "component_multi_select_choice_with_validation.json",
+ ),
+ DROPDOWN(
+ "component_dropdown.json",
+ Res.drawable.ic_group_1278,
+ Res.string.component_name_dropdown,
+ "component_dropdown_with_validation.json",
+ ),
+ MODAL(
+ "component_modal.json",
+ Res.drawable.ic_modal,
+ Res.string.component_name_modal,
+ "component_modal_with_validation.json",
+ ),
+ OPEN_CHOICE(
+ "component_open_choice.json",
+ Res.drawable.ic_openchoice,
+ Res.string.component_name_open_choice,
+ "component_open_choice_with_validation.json",
+ ),
+ TEXT_FIELD(
+ "component_text_fields.json",
+ Res.drawable.ic_textfield,
+ Res.string.component_name_text_field,
+ "component_text_fields_with_validation.json",
+ ),
+ AUTO_COMPLETE(
+ "component_auto_complete.json",
+ Res.drawable.ic_autocomplete,
+ Res.string.component_name_auto_complete,
+ "component_auto_complete_with_validation.json",
+ ),
+ DATE_PICKER(
+ "component_date_picker.json",
+ Res.drawable.ic_datepicker,
+ Res.string.component_name_date_picker,
+ "component_date_picker_with_validation.json",
+ ),
+ TIME_PICKER(
+ "component_time_picker.json",
+ Res.drawable.ic_timepicker,
+ Res.string.component_name_time_picker,
+ "component_time_picker_with_validation.json",
+ ),
+ DATE_TIME_PICKER(
+ "component_date_time_picker.json",
+ Res.drawable.ic_timepicker,
+ Res.string.component_name_date_time_picker,
+ "component_date_time_picker_with_validation.json",
+ ),
+ SLIDER(
+ "component_slider.json",
+ Res.drawable.ic_slider,
+ Res.string.component_name_slider,
+ "component_slider_with_validation.json",
+ ),
+ QUANTITY(
+ "component_quantity.json",
+ Res.drawable.ic_unitoptions,
+ Res.string.component_name_quantity,
+ "component_quantity_with_validation.json",
+ ),
+ ATTACHMENT(
+ "component_attachment.json",
+ Res.drawable.ic_attachment,
+ Res.string.component_name_attachment,
+ "component_attachment_with_validation.json",
+ ),
+ REPEATED_GROUP(
+ "component_repeated_group.json",
+ Res.drawable.ic_repeatgroups,
+ Res.string.component_name_repeated_group,
+ ),
+ HELP(
+ "component_help.json",
+ Res.drawable.ic_help,
+ Res.string.component_name_help,
+ ),
+ ITEM_MEDIA(
+ "component_item_media.json",
+ Res.drawable.ic_item_media,
+ Res.string.component_name_item_media,
+ ),
+ ITEM_ANSWER_MEDIA(
+ "",
+ Res.drawable.ic_item_answer_media,
+ Res.string.component_name_item_answer_media,
+ ),
+ INITIAL_VALUE(
+ "component_initial_value.json",
+ Res.drawable.ic_initial_value_component,
+ Res.string.component_name_initial_value,
+ ),
+ LOCATION_WIDGET(
+ "component_location_widget.json",
+ Res.drawable.ic_location_on,
+ Res.string.component_name_location_widget,
+ ),
+ BARCODE_WIDGET(
+ "component_barcode_widget.json",
+ Res.drawable.ic_barcode,
+ Res.string.component_name_barcode_widget,
+ ),
+ QUESTION_ITEM_CUSTOM_STYLE(
+ "component_per_question_custom_style.json",
+ Res.drawable.text_format_48dp,
+ Res.string.component_name_per_question_custom_style,
+ ),
+ }
+
+ val viewItemList =
+ listOf(
+ ViewItem.HeaderItem(Header.WIDGETS),
+ ViewItem.ComponentItem(Component.BOOLEAN_CHOICE),
+ ViewItem.ComponentItem(Component.SINGLE_CHOICE),
+ ViewItem.ComponentItem(Component.MULTIPLE_CHOICE),
+ ViewItem.ComponentItem(Component.DROPDOWN),
+ ViewItem.ComponentItem(Component.MODAL),
+ ViewItem.ComponentItem(Component.OPEN_CHOICE),
+ ViewItem.ComponentItem(Component.TEXT_FIELD),
+ ViewItem.ComponentItem(Component.AUTO_COMPLETE),
+ ViewItem.ComponentItem(Component.DATE_PICKER),
+ ViewItem.ComponentItem(Component.TIME_PICKER),
+ ViewItem.ComponentItem(Component.DATE_TIME_PICKER),
+ ViewItem.ComponentItem(Component.SLIDER),
+ ViewItem.ComponentItem(Component.QUANTITY),
+ ViewItem.ComponentItem(Component.ATTACHMENT),
+ ViewItem.ComponentItem(Component.REPEATED_GROUP),
+ ViewItem.HeaderItem(Header.MISC_COMPONENTS),
+ ViewItem.ComponentItem(Component.HELP),
+ ViewItem.ComponentItem(Component.ITEM_MEDIA),
+ ViewItem.ComponentItem(Component.ITEM_ANSWER_MEDIA),
+ ViewItem.ComponentItem(Component.INITIAL_VALUE),
+ ViewItem.ComponentItem(Component.LOCATION_WIDGET),
+ ViewItem.ComponentItem(Component.BARCODE_WIDGET),
+ ViewItem.ComponentItem(Component.QUESTION_ITEM_CUSTOM_STYLE),
+ )
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/layouts/LayoutListScreen.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/layouts/LayoutListScreen.kt
new file mode 100644
index 0000000000..143eadee9f
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/layouts/LayoutListScreen.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2025-2026 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.catalog.ui.layouts
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.items
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.google.android.fhir.catalog.ui.shared.CatalogItemCard
+import com.google.android.fhir.catalog.ui.shared.CatalogTopAppBar
+import org.jetbrains.compose.resources.stringResource
+
+@Composable
+fun LayoutListScreen(
+ viewModel: LayoutListViewModel,
+ onLayoutClick: (LayoutListViewModel.Layout, String) -> Unit,
+) {
+ Scaffold(
+ topBar = { CatalogTopAppBar() },
+ ) { padding ->
+ LazyVerticalGrid(
+ columns = GridCells.Fixed(2),
+ modifier = Modifier.fillMaxSize().padding(padding),
+ contentPadding = PaddingValues(8.dp),
+ ) {
+ items(viewModel.getLayoutList()) { layout ->
+ val title = stringResource(layout.text)
+ CatalogItemCard(
+ icon = layout.icon,
+ text = title,
+ onClick = { onLayoutClick(layout, title) },
+ )
+ }
+ }
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/layouts/LayoutListViewModel.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/layouts/LayoutListViewModel.kt
new file mode 100644
index 0000000000..df04ff4c95
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/layouts/LayoutListViewModel.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2022-2026 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.catalog.ui.layouts
+
+import android_fhir.catalog.generated.resources.Res
+import android_fhir.catalog.generated.resources.ic_defaultlayout
+import android_fhir.catalog.generated.resources.ic_paginatedlayout
+import android_fhir.catalog.generated.resources.ic_readonlylayout
+import android_fhir.catalog.generated.resources.ic_reviewlayout
+import android_fhir.catalog.generated.resources.layout_name_default_text
+import android_fhir.catalog.generated.resources.layout_name_paginated
+import android_fhir.catalog.generated.resources.layout_name_read_only
+import android_fhir.catalog.generated.resources.layout_name_review
+import androidx.lifecycle.ViewModel
+import org.jetbrains.compose.resources.DrawableResource
+import org.jetbrains.compose.resources.StringResource
+
+class LayoutListViewModel : ViewModel() {
+
+ fun getLayoutList(): List {
+ return Layout.entries
+ }
+
+ enum class Layout(
+ val questionnaireFileName: String,
+ val icon: DrawableResource,
+ val text: StringResource,
+ val showReviewPage: Boolean = false,
+ val showReviewPageFirst: Boolean = false,
+ val isReadOnly: Boolean = false,
+ ) {
+ DEFAULT(
+ "layout_default.json",
+ Res.drawable.ic_defaultlayout,
+ Res.string.layout_name_default_text,
+ ),
+ PAGINATED(
+ "layout_paginated.json",
+ Res.drawable.ic_paginatedlayout,
+ Res.string.layout_name_paginated,
+ ),
+ REVIEW(
+ "layout_review.json",
+ Res.drawable.ic_reviewlayout,
+ Res.string.layout_name_review,
+ showReviewPage = true,
+ showReviewPageFirst = true,
+ ),
+ READ_ONLY(
+ "layout_review.json",
+ Res.drawable.ic_readonlylayout,
+ Res.string.layout_name_read_only,
+ isReadOnly = true,
+ ),
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/QuestionnaireResponseScreen.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/QuestionnaireResponseScreen.kt
new file mode 100644
index 0000000000..47a9c844f7
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/QuestionnaireResponseScreen.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2025-2026 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.catalog.ui.questionnaire
+
+import android_fhir.catalog.generated.resources.Res
+import android_fhir.catalog.generated.resources.close
+import android_fhir.catalog.generated.resources.questionnaire_response_subtitle
+import android_fhir.catalog.generated.resources.questionnaire_response_title
+import android_fhir.catalog.generated.resources.questionnaire_submitted
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.Button
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.resources.stringResource
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun QuestionnaireResponseScreen(
+ responseJson: String,
+ onBackClick: () -> Unit,
+) {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = {
+ Text(
+ text = stringResource(Res.string.questionnaire_response_title),
+ modifier = Modifier.fillMaxWidth(),
+ )
+ },
+ navigationIcon = {
+ IconButton(onClick = onBackClick) {
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
+ }
+ },
+ )
+ },
+ ) { padding ->
+ Column(
+ modifier =
+ Modifier.fillMaxSize()
+ .padding(padding)
+ .padding(horizontal = 24.dp)
+ .verticalScroll(rememberScrollState()),
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.Top,
+ ) {
+ Text(
+ text = stringResource(Res.string.questionnaire_submitted),
+ style = MaterialTheme.typography.headlineMedium,
+ modifier = Modifier.padding(bottom = 8.dp),
+ )
+ Text(
+ text = stringResource(Res.string.questionnaire_response_subtitle),
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(bottom = 16.dp),
+ )
+ HorizontalDivider(modifier = Modifier.padding(bottom = 24.dp))
+ Text(
+ text = responseJson,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ Spacer(modifier = Modifier.height(24.dp))
+ Button(
+ onClick = onBackClick,
+ modifier = Modifier.width(132.dp).align(Alignment.CenterHorizontally),
+ ) {
+ Text(stringResource(Res.string.close))
+ }
+ Spacer(modifier = Modifier.height(24.dp))
+ }
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/QuestionnaireScreen.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/QuestionnaireScreen.kt
new file mode 100644
index 0000000000..d9eef2cca1
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/QuestionnaireScreen.kt
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2025-2026 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.catalog.ui.questionnaire
+
+import android_fhir.catalog.generated.resources.Res
+import android_fhir.catalog.generated.resources.behavior_name_calculated_expression
+import android_fhir.catalog.generated.resources.behavior_name_calculated_expression_info
+import android_fhir.catalog.generated.resources.behavior_name_skip_logic
+import android_fhir.catalog.generated.resources.behavior_name_skip_logic_info
+import android_fhir.catalog.generated.resources.ic_info_24
+import android_fhir.catalog.generated.resources.loading
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.BiasAlignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import com.google.android.fhir.catalog.ui.questionnaire.components.ErrorStateToggleAction
+import com.google.android.fhir.datacapture.Questionnaire
+import com.google.android.fhir.datacapture.QuestionnaireItemViewFactoryMatcher
+import com.google.android.fhir.datacapture.QuestionnaireItemViewFactoryMatchersProvider
+import com.google.android.fhir.datacapture.contrib.views.barcode.BarcodeItemViewFactoryMatcher
+import com.google.android.fhir.datacapture.contrib.views.locationwidget.LocationDataItemViewFactoryMatcher
+import com.google.android.fhir.datacapture.contrib.views.locationwidget.LocationItemViewFactoryMatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.jetbrains.compose.resources.painterResource
+import org.jetbrains.compose.resources.stringResource
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun QuestionnaireScreen(
+ viewModel: QuestionnaireViewModel,
+ title: String,
+ fileName: String,
+ coroutineScope: CoroutineScope,
+ validationFileName: String? = null,
+ showReviewPage: Boolean = false,
+ showReviewPageFirst: Boolean = false,
+ isReadOnly: Boolean = false,
+ onBackClick: () -> Unit,
+ navigateToResponse: (String) -> Unit,
+) {
+ val viewItemMatchersProvider = remember {
+ object : QuestionnaireItemViewFactoryMatchersProvider {
+ override fun get(): List {
+ return listOf(
+ BarcodeItemViewFactoryMatcher,
+ LocationItemViewFactoryMatcher,
+ LocationDataItemViewFactoryMatcher,
+ )
+ }
+ }
+ }
+
+ var isErrorState by remember { mutableStateOf(false) }
+ var questionnaireJson by remember { mutableStateOf(null) }
+
+ val skipLogicTitle = stringResource(Res.string.behavior_name_skip_logic)
+ val calculatedExpressionTitle = stringResource(Res.string.behavior_name_calculated_expression)
+
+ val launchContextMap = remember {
+ mapOf("patient" to "{\"resourceType\":\"Patient\",\"id\":\"P1\"}")
+ }
+
+ LaunchedEffect(fileName, validationFileName, isErrorState) {
+ val fileToLoad =
+ if (isErrorState && validationFileName != null) validationFileName else fileName
+ if (fileToLoad.isNotEmpty()) {
+ questionnaireJson = viewModel.getQuestionnaire(fileToLoad)
+ }
+ }
+
+ Scaffold(
+ topBar = {
+ Column {
+ TopAppBar(
+ title = { Text(title) },
+ navigationIcon = {
+ IconButton(onClick = onBackClick) {
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
+ }
+ },
+ actions = {
+ if (validationFileName != null) {
+ ErrorStateToggleAction(
+ isErrorState = isErrorState,
+ onToggle = { isErrorState = it },
+ )
+ }
+ },
+ )
+ }
+ },
+ ) { paddingValues ->
+ Surface(
+ modifier = Modifier.fillMaxSize().padding(paddingValues),
+ color = Color(0xFFF5F5F5),
+ ) {
+ Box(modifier = Modifier.fillMaxSize()) {
+ Box(modifier = Modifier.fillMaxWidth()) {
+ questionnaireJson?.let { json ->
+ Questionnaire(
+ questionnaireJson = json,
+ questionnaireLaunchContextMap = launchContextMap,
+ showSubmitButton = true,
+ showReviewPage = showReviewPage,
+ showReviewPageFirst = showReviewPageFirst,
+ isReadOnly = isReadOnly,
+ showCancelButton = false,
+ onSubmit = { getResponse ->
+ coroutineScope.launch {
+ val response = getResponse()
+ val responseJson = viewModel.getQuestionnaireResponseJson(response)
+ navigateToResponse(responseJson)
+ }
+ },
+ matchersProvider = viewItemMatchersProvider,
+ onCancel = {},
+ )
+ }
+ ?: run { Text(stringResource(Res.string.loading), modifier = Modifier.padding(16.dp)) }
+ }
+
+ if (title == skipLogicTitle || title == calculatedExpressionTitle) {
+ InfoCard(
+ modifier = Modifier.fillMaxWidth().align(BiasAlignment(0f, 0.8f)).padding(16.dp),
+ title = title,
+ info =
+ if (title == skipLogicTitle) {
+ stringResource(Res.string.behavior_name_skip_logic_info)
+ } else {
+ stringResource(Res.string.behavior_name_calculated_expression_info)
+ },
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun InfoCard(title: String, info: String, modifier: Modifier = Modifier) {
+ Card(
+ modifier = modifier,
+ colors =
+ CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.secondaryContainer,
+ ),
+ ) {
+ Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_info_24),
+ contentDescription = null,
+ modifier = Modifier.size(20.dp),
+ tint = MaterialTheme.colorScheme.onSecondaryContainer,
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = title,
+ style = MaterialTheme.typography.bodyMedium,
+ fontWeight = FontWeight.Bold,
+ color = MaterialTheme.colorScheme.onSecondaryContainer,
+ )
+ }
+ Text(
+ text = info,
+ style = MaterialTheme.typography.bodySmall,
+ modifier = Modifier.padding(top = 8.dp),
+ color = MaterialTheme.colorScheme.onSecondaryContainer,
+ )
+ }
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/QuestionnaireViewModel.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/QuestionnaireViewModel.kt
new file mode 100644
index 0000000000..8399eb9d90
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/QuestionnaireViewModel.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2022-2026 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.catalog.ui.questionnaire
+
+import android_fhir.catalog.generated.resources.Res
+import androidx.lifecycle.ViewModel
+import com.google.android.fhir.datacapture.validation.Invalid
+import com.google.android.fhir.datacapture.validation.QuestionnaireResponseValidator
+import com.google.fhir.model.r4.FhirR4Json
+import com.google.fhir.model.r4.Questionnaire
+import com.google.fhir.model.r4.QuestionnaireResponse
+
+class QuestionnaireViewModel : ViewModel() {
+ private val fhirJson = FhirR4Json()
+
+ fun getQuestionnaireResponseJson(response: QuestionnaireResponse): String {
+ return fhirJson.encodeToString(response)
+ }
+
+ suspend fun getQuestionnaire(fileName: String) = Res.readBytes("files/$fileName").decodeToString()
+
+ /** Validates questionnaire response and returns list of invalid field names. */
+ suspend fun validateAndGetErrors(
+ questionnaireJson: String,
+ response: QuestionnaireResponse,
+ launchContextMap: Map? = null,
+ ): List {
+ val questionnaire = fhirJson.decodeFromString(questionnaireJson) as Questionnaire
+ val parentMap = buildQuestionnaireItemParentMap(questionnaire)
+ val launchContextResources = launchContextMap?.mapValues { fhirJson.decodeFromString(it.value) }
+
+ val validationResults =
+ QuestionnaireResponseValidator.validateQuestionnaireResponse(
+ questionnaire,
+ response,
+ parentMap,
+ launchContextResources,
+ )
+
+ return buildList {
+ validationResults.forEach { (linkId, results) ->
+ val hasError = results.any { it is Invalid }
+ if (hasError) {
+ // Find the questionnaire item with this linkId to get its text
+ findItemText(questionnaire.item, linkId)?.let { add(it) }
+ }
+ }
+ }
+ }
+
+ private fun buildQuestionnaireItemParentMap(
+ questionnaire: Questionnaire,
+ ): Map {
+ val map = mutableMapOf()
+ fun traverse(item: Questionnaire.Item) {
+ for (child in item.item) {
+ map[child] = item
+ traverse(child)
+ }
+ }
+ for (item in questionnaire.item) {
+ traverse(item)
+ }
+ return map
+ }
+
+ private fun findItemText(items: List, linkId: String): String? {
+ for (item in items) {
+ if (item.linkId.value == linkId) {
+ return item.text?.value
+ }
+ val nested = findItemText(item.item, linkId)
+ if (nested != null) return nested
+ }
+ return null
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/components/ErrorStateComponents.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/components/ErrorStateComponents.kt
new file mode 100644
index 0000000000..1352a8f805
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/questionnaire/components/ErrorStateComponents.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2026 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.catalog.ui.questionnaire.components
+
+import android_fhir.catalog.generated.resources.Res
+import android_fhir.catalog.generated.resources.options
+import android_fhir.catalog.generated.resources.show_error_state
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Settings
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.resources.stringResource
+
+/** Action button that toggles error state via a bottom sheet. */
+@Composable
+fun ErrorStateToggleAction(
+ isErrorState: Boolean,
+ onToggle: (Boolean) -> Unit,
+) {
+ var showBottomSheet by remember { mutableStateOf(false) }
+
+ IconButton(onClick = { showBottomSheet = true }) {
+ Icon(Icons.Outlined.Settings, contentDescription = "Options")
+ }
+
+ if (showBottomSheet) {
+ ErrorStateBottomSheet(
+ isErrorState = isErrorState,
+ onDismiss = { showBottomSheet = false },
+ onToggle = { onToggle(!isErrorState) },
+ )
+ }
+}
+
+/** Bottom sheet for toggling error state display. */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun ErrorStateBottomSheet(
+ isErrorState: Boolean,
+ onDismiss: () -> Unit,
+ onToggle: () -> Unit,
+) {
+ ModalBottomSheet(onDismissRequest = onDismiss) {
+ Column(
+ modifier = Modifier.fillMaxWidth().padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(
+ text = stringResource(Res.string.options),
+ style = MaterialTheme.typography.headlineSmall,
+ modifier = Modifier.padding(bottom = 24.dp),
+ )
+ Card(
+ modifier =
+ Modifier.fillMaxWidth()
+ .then(
+ if (!isErrorState) {
+ Modifier.border(1.dp, Color.LightGray, MaterialTheme.shapes.small)
+ } else {
+ Modifier
+ },
+ ),
+ colors =
+ CardDefaults.cardColors(
+ containerColor =
+ if (isErrorState) {
+ MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.5f)
+ } else {
+ Color.Transparent
+ },
+ ),
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth().clickable(onClick = onToggle).padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Checkbox(
+ checked = isErrorState,
+ onCheckedChange = { onToggle() },
+ )
+ Spacer(modifier = Modifier.width(16.dp))
+ Text(
+ text = stringResource(Res.string.show_error_state),
+ style = MaterialTheme.typography.bodyLarge,
+ color = if (isErrorState) MaterialTheme.colorScheme.primary else Color.Unspecified,
+ )
+ }
+ }
+ Spacer(modifier = Modifier.padding(bottom = 32.dp))
+ }
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/shared/CatalogItemCard.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/shared/CatalogItemCard.kt
new file mode 100644
index 0000000000..6eca044f50
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/shared/CatalogItemCard.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2025-2026 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.catalog.ui.shared
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.resources.DrawableResource
+import org.jetbrains.compose.resources.painterResource
+
+@Composable
+fun CatalogItemCard(
+ icon: DrawableResource,
+ text: String,
+ onClick: () -> Unit,
+) {
+ Surface(
+ onClick = onClick,
+ modifier = Modifier.padding(4.dp).fillMaxWidth().height(180.dp),
+ shape = RoundedCornerShape(8.dp),
+ tonalElevation = 1.dp,
+ shadowElevation = 2.dp,
+ ) {
+ Column {
+ Box(
+ modifier = Modifier.fillMaxWidth().height(128.dp).background(Color(0xFFF8F8F8)),
+ contentAlignment = Alignment.Center,
+ ) {
+ Image(
+ painter = painterResource(icon),
+ contentDescription = null,
+ contentScale = ContentScale.None,
+ )
+ }
+ Box(
+ modifier = Modifier.fillMaxSize().padding(8.dp),
+ contentAlignment = Alignment.Center,
+ ) {
+ Text(
+ text = text,
+ style = MaterialTheme.typography.bodyMedium,
+ textAlign = TextAlign.Center,
+ maxLines = 2,
+ )
+ }
+ }
+ }
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/shared/CatalogTopAppBar.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/shared/CatalogTopAppBar.kt
new file mode 100644
index 0000000000..f205dd3530
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/shared/CatalogTopAppBar.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2025-2026 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.catalog.ui.shared
+
+import android_fhir.catalog.generated.resources.Res
+import android_fhir.catalog.generated.resources.open_questionnaire
+import android_fhir.catalog.generated.resources.toolbar_text
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.MoreVert
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import org.jetbrains.compose.resources.stringResource
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun CatalogTopAppBar(
+ onOpenQuestionnaireAction: () -> Unit = {},
+) {
+ var showMenu by remember { mutableStateOf(false) }
+
+ CenterAlignedTopAppBar(
+ title = {
+ val text = stringResource(Res.string.toolbar_text)
+ val lines = text.split("\n")
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ if (lines.size > 1) {
+ Text(
+ text = lines.first().trim(),
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ Text(
+ text = lines.last().trim(),
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ } else {
+ Text(text = text, style = MaterialTheme.typography.titleMedium)
+ }
+ }
+ },
+ actions = {
+ Box {
+ IconButton(onClick = { showMenu = !showMenu }) {
+ Icon(Icons.Default.MoreVert, contentDescription = null)
+ }
+ DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
+ DropdownMenuItem(
+ text = { Text(stringResource(Res.string.open_questionnaire)) },
+ onClick = {
+ showMenu = false
+ onOpenQuestionnaireAction()
+ },
+ )
+ }
+ }
+ },
+ )
+}
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/theme/Color.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/theme/Color.kt
new file mode 100644
index 0000000000..3f540759ab
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/theme/Color.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2025-2026 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.catalog.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+// Light Colors (40 Tone)
+val PrimaryBlue40 = Color(0xFF0B57D0)
+val OnPrimaryBlue100 = Color(0xFFFFFFFF)
+val PrimaryContainerBlue90 = Color(0xFFD3E3FD)
+val OnPrimaryContainerBlue10 = Color(0xFF041E49)
+
+val SecondaryBlue40 = Color(0xFF00639B)
+val OnSecondaryBlue100 = Color(0xFFFFFFFF)
+val SecondaryContainerBlue90 = Color(0xFFC2E7FF)
+val OnSecondaryContainerBlue10 = Color(0xFF001D35)
+
+val TertiaryGreen40 = Color(0xFF146C2E)
+val OnTertiaryGreen100 = Color(0xFFFFFFFF)
+val TertiaryContainerGreen90 = Color(0xFFC4EED0)
+val OnTertiaryContainerGreen10 = Color(0xFF072711)
+
+val ErrorRed40 = Color(0xFFB3261E)
+val ErrorContainerRed100 = Color(0xFFFFFFFF)
+val OnErrorRed90 = Color(0xFFF9DEDC)
+val OnErrorContainerRed10 = Color(0xFF410E0B)
+
+val BackgroundNeutral100 = Color(0xFFFFFFFF)
+val OnBackgroundNeutral10 = Color(0xFF1F1F1F)
+
+val SurfaceNeutral100 = Color(0xFFFFFFFF)
+val OnSurfaceNeutral10 = Color(0xFF1F1F1F)
+
+val SurfaceVariantNeutralVariant90 = Color(0xFFE1E3E1)
+val OnSurfaceVariantNeutralVariant30 = Color(0xFF444746)
+
+val OutlineNeutralVariant50 = Color(0xFF747775)
+
+// Dark Colors (80 Tone)
+val PrimaryBlue80 = Color(0xFFA8C7FA)
+val OnPrimaryBlue20 = Color(0xFF062E6F)
+val PrimaryContainerBlue30 = Color(0xFF0842A0)
+val OnPrimaryContainerBlue90 = Color(0xFFD3E3FD)
+
+val SecondaryBlue80 = Color(0xFF7FCFFF)
+val OnSecondaryBlue20 = Color(0xFF003355)
+val SecondaryContainerBlue30 = Color(0xFF004A77)
+val OnSecondaryContainerBlue90 = Color(0xFFC2E7FF)
+
+val TertiaryGreen80 = Color(0xFF91D5A3) // Corrected tone
+val OnTertiaryGreen20 = Color(0xFF0A3818)
+val TertiaryContainerGreen30 = Color(0xFF0F5223)
+val OnTertiaryContainerGreen90 = Color(0xFFC4EED0)
+
+val ErrorRed80 = Color(0xFFF2B8B5)
+val ErrorContainerRed20 = Color(0xFF601410)
+val OnErrorRed30 = Color(0xFF8C1D18)
+val OnErrorContainerRed90 = Color(0xFFF9DEDC)
+
+val BackgroundNeutral10 = Color(0xFF1F1F1F)
+val OnBackgroundNeutral90 = Color(0xFFE3E3E3)
+
+val SurfaceNeutral10 = Color(0xFF1F1F1F)
+val OnSurfaceNeutral90 = Color(0xFFE3E3E3)
+
+val SurfaceVariantNeutralVariant30 = Color(0xFF444746)
+val OnSurfaceVariantNeutralVariant80 = Color(0xFFC4C7C5)
+
+val OutlineNeutralVariant60 = Color(0xFF8E918F)
diff --git a/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/theme/Theme.kt b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/theme/Theme.kt
new file mode 100644
index 0000000000..fda1eb8dd4
--- /dev/null
+++ b/catalog/src/commonMain/kotlin/com/google/android/fhir/catalog/ui/theme/Theme.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2025-2026 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.catalog.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import com.google.android.fhir.datacapture.theme.QuestionnaireTheme
+
+private val DarkColorScheme =
+ darkColorScheme(
+ primary = PrimaryBlue80,
+ onPrimary = OnPrimaryBlue20,
+ primaryContainer = PrimaryContainerBlue30,
+ onPrimaryContainer = OnPrimaryContainerBlue90,
+ secondary = SecondaryBlue80,
+ onSecondary = OnSecondaryBlue20,
+ secondaryContainer = SecondaryContainerBlue30,
+ onSecondaryContainer = OnSecondaryContainerBlue90,
+ tertiary = TertiaryGreen80,
+ onTertiary = OnTertiaryGreen20,
+ tertiaryContainer = TertiaryContainerGreen30,
+ onTertiaryContainer = OnTertiaryContainerGreen90,
+ error = ErrorRed80,
+ errorContainer = ErrorContainerRed20,
+ onError = OnErrorRed30,
+ onErrorContainer = OnErrorContainerRed90,
+ background = BackgroundNeutral10,
+ onBackground = OnBackgroundNeutral90,
+ surface = SurfaceNeutral10,
+ onSurface = OnSurfaceNeutral90,
+ surfaceVariant = SurfaceVariantNeutralVariant30,
+ onSurfaceVariant = OnSurfaceVariantNeutralVariant80,
+ outline = OutlineNeutralVariant60,
+ )
+
+private val LightColorScheme =
+ lightColorScheme(
+ primary = PrimaryBlue40,
+ onPrimary = OnPrimaryBlue100,
+ primaryContainer = PrimaryContainerBlue90,
+ onPrimaryContainer = OnPrimaryContainerBlue10,
+ secondary = SecondaryBlue40,
+ onSecondary = OnSecondaryBlue100,
+ secondaryContainer = SecondaryContainerBlue90,
+ onSecondaryContainer = OnSecondaryContainerBlue10,
+ tertiary = TertiaryGreen40,
+ onTertiary = OnTertiaryGreen100,
+ tertiaryContainer = TertiaryContainerGreen90,
+ onTertiaryContainer = OnTertiaryContainerGreen10,
+ error = ErrorRed40,
+ errorContainer = ErrorContainerRed100,
+ onError = OnErrorRed90,
+ onErrorContainer = OnErrorContainerRed10,
+ background = BackgroundNeutral100,
+ onBackground = OnBackgroundNeutral10,
+ surface = SurfaceNeutral100,
+ onSurface = OnSurfaceNeutral10,
+ surfaceVariant = SurfaceVariantNeutralVariant90,
+ onSurfaceVariant = OnSurfaceVariantNeutralVariant30,
+ outline = OutlineNeutralVariant50,
+ )
+
+@Composable
+fun AppTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ content: @Composable () -> Unit,
+) {
+ val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
+
+ QuestionnaireTheme(
+ colorScheme = colorScheme,
+ content = content,
+ )
+}
diff --git a/catalog/src/desktopMain/kotlin/com/google/android/fhir/catalog/Main.kt b/catalog/src/desktopMain/kotlin/com/google/android/fhir/catalog/Main.kt
new file mode 100644
index 0000000000..ae2bee9bbe
--- /dev/null
+++ b/catalog/src/desktopMain/kotlin/com/google/android/fhir/catalog/Main.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2025-2026 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.catalog
+
+import androidx.compose.ui.window.Window
+import androidx.compose.ui.window.WindowPlacement
+import androidx.compose.ui.window.WindowState
+import androidx.compose.ui.window.application
+
+fun main() = application {
+ Window(
+ onCloseRequest = ::exitApplication,
+ title = "Structured data capture catalog",
+ state = WindowState(placement = WindowPlacement.Maximized),
+ ) {
+ App()
+ }
+}
diff --git a/contrib/barcode/src/main/java/com/google/android/fhir/datacapture/contrib/views/barcode/mlkit/md/camera/FrameMetadata.kt b/catalog/src/iosMain/kotlin/com/google/android/fhir/catalog/MainViewController.kt
similarity index 71%
rename from contrib/barcode/src/main/java/com/google/android/fhir/datacapture/contrib/views/barcode/mlkit/md/camera/FrameMetadata.kt
rename to catalog/src/iosMain/kotlin/com/google/android/fhir/catalog/MainViewController.kt
index ecfec801ae..5c7126f7a6 100644
--- a/contrib/barcode/src/main/java/com/google/android/fhir/datacapture/contrib/views/barcode/mlkit/md/camera/FrameMetadata.kt
+++ b/catalog/src/iosMain/kotlin/com/google/android/fhir/catalog/MainViewController.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Google LLC
+ * Copyright 2025-2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package com.google.android.fhir.datacapture.contrib.views.barcode.mlkit.md.camera
+package com.google.android.fhir.catalog
-/** Metadata info of a camera frame. */
-class FrameMetadata(val width: Int, val height: Int, val rotation: Int)
+import androidx.compose.ui.window.ComposeUIViewController
+
+fun MainViewController() = ComposeUIViewController { App() }
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/BehaviorListFragment.kt b/catalog/src/main/java/com/google/android/fhir/catalog/BehaviorListFragment.kt
deleted file mode 100644
index 4cf0cd5059..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/BehaviorListFragment.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2022-2024 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.catalog
-
-import android.os.Bundle
-import android.view.Gravity
-import android.view.View
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import kotlinx.coroutines.launch
-
-class BehaviorListFragment : Fragment(R.layout.behavior_list_fragment) {
- private val viewModel: BehaviorListViewModel by viewModels()
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- setUpBehaviorsRecyclerView()
- (activity as? MainActivity)?.showOpenQuestionnaireMenu(true)
- }
-
- override fun onResume() {
- super.onResume()
- setUpActionBar()
- (requireActivity() as MainActivity).showBottomNavigationView(View.VISIBLE)
- }
-
- private fun setUpActionBar() {
- (activity as MainActivity).setActionBar(
- getString(R.string.toolbar_text),
- Gravity.CENTER_HORIZONTAL,
- )
- setHasOptionsMenu(true)
- }
-
- private fun setUpBehaviorsRecyclerView() {
- val behaviorRecyclerView =
- requireView().findViewById(R.id.sdcBehaviorRecyclerView)
- val adapter =
- BehaviorsRecyclerViewAdapter(::onItemClick).apply { submitList(viewModel.getBehaviorList()) }
- behaviorRecyclerView.adapter = adapter
- behaviorRecyclerView.layoutManager = GridLayoutManager(context, 2)
- }
-
- private fun onItemClick(behavior: BehaviorListViewModel.Behavior) {
- if (behavior.questionnaireFileName.isEmpty()) {
- return
- }
- launchQuestionnaireFragment(behavior)
- }
-
- private fun launchQuestionnaireFragment(behavior: BehaviorListViewModel.Behavior) {
- viewLifecycleOwner.lifecycleScope.launch {
- findNavController()
- .navigate(
- MainNavGraphDirections.actionGlobalGalleryQuestionnaireFragment(
- questionnaireTitleKey = context?.getString(behavior.textId) ?: "",
- questionnaireJsonStringKey =
- getQuestionnaireJsonStringFromAssets(
- context = requireContext(),
- backgroundContext = coroutineContext,
- fileName = behavior.questionnaireFileName,
- ),
- ),
- )
- }
- }
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/BehaviorListViewModel.kt b/catalog/src/main/java/com/google/android/fhir/catalog/BehaviorListViewModel.kt
deleted file mode 100644
index e5cbe1618d..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/BehaviorListViewModel.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2022-2024 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.catalog
-
-import android.app.Application
-import android.content.Context
-import androidx.annotation.DrawableRes
-import androidx.annotation.StringRes
-import androidx.lifecycle.AndroidViewModel
-
-class BehaviorListViewModel(application: Application) : AndroidViewModel(application) {
-
- fun getBehaviorList(): List {
- return Behavior.values().toList()
- }
-
- enum class Behavior(
- @DrawableRes val iconId: Int,
- @StringRes val textId: Int,
- val questionnaireFileName: String,
- ) {
- CALCULATED_EXPRESSION(
- R.drawable.ic_calculations_behavior,
- R.string.behavior_name_calculated_expression,
- "behavior_calculated_expression.json",
- ),
- ANSWER_EXPRESSION(
- R.drawable.ic_answers_behavior,
- R.string.behavior_name_answer_expression,
- "behavior_answer_expression.json",
- ),
- CONTEXT_VARIABLES(
- R.drawable.ic_context,
- R.string.behavior_name_context_variables,
- "behavior_context_variables.json",
- ),
- SKIP_LOGIC(
- R.drawable.ic_skiplogic_behavior,
- R.string.behavior_name_skip_logic,
- "behavior_skip_logic.json",
- ),
- SKIP_LOGIC_WITH_EXPRESSION(
- R.drawable.ic_skiplogic_behavior,
- R.string.behavior_name_skip_logic_with_expression,
- "behavior_skip_logic_with_expression.json",
- ),
- DYNAMIC_QUESTION_TEXT(
- R.drawable.ic_dynamic_text_behavior,
- R.string.behavior_name_dynamic_question_text,
- "behavior_dynamic_question_text.json",
- ),
- QUESTIONNAIRE_CONSTRAINT(
- R.drawable.ic_rule,
- R.string.behavior_name_questionnaire_constraint,
- "behavior_questionnaire_constraint.json",
- ),
- }
-
- fun isBehavior(context: Context, title: String) =
- getBehaviorList().map { context.getString(it.textId) }.contains(title)
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/BehaviorsRecyclerViewAdapter.kt b/catalog/src/main/java/com/google/android/fhir/catalog/BehaviorsRecyclerViewAdapter.kt
deleted file mode 100644
index 82fe0578fa..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/BehaviorsRecyclerViewAdapter.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2022-2023 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.catalog
-
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
-import com.google.android.fhir.catalog.databinding.LandingPageItemBinding
-
-class BehaviorsRecyclerViewAdapter(
- private val onItemClick: (BehaviorListViewModel.Behavior) -> Unit,
-) : ListAdapter(BehaviorDiffUtil()) {
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BehaviorViewHolder {
- return BehaviorViewHolder(
- LandingPageItemBinding.inflate(LayoutInflater.from(parent.context), parent, false),
- onItemClick,
- )
- }
-
- override fun onBindViewHolder(holder: BehaviorViewHolder, position: Int) {
- holder.bind(getItem(position))
- }
-}
-
-class BehaviorViewHolder(
- val binding: LandingPageItemBinding,
- private val onItemClick: (BehaviorListViewModel.Behavior) -> Unit,
-) : RecyclerView.ViewHolder(binding.root) {
- fun bind(behavior: BehaviorListViewModel.Behavior) {
- binding.componentLayoutIconImageview.setImageResource(behavior.iconId)
- binding.componentLayoutTextView.text =
- binding.componentLayoutTextView.context.getString(behavior.textId)
- binding.root.setOnClickListener { onItemClick(behavior) }
- }
-}
-
-class BehaviorDiffUtil : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(
- oldBehavior: BehaviorListViewModel.Behavior,
- newBehavior: BehaviorListViewModel.Behavior,
- ) = oldBehavior === newBehavior
-
- override fun areContentsTheSame(
- oldBehavior: BehaviorListViewModel.Behavior,
- newBehavior: BehaviorListViewModel.Behavior,
- ) = oldBehavior == newBehavior
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/CatalogApplication.kt b/catalog/src/main/java/com/google/android/fhir/catalog/CatalogApplication.kt
deleted file mode 100644
index 204651fd02..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/CatalogApplication.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2022-2024 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.catalog
-
-import android.app.Application
-import ca.uhn.fhir.context.FhirContext
-import com.google.android.fhir.FhirEngine
-import com.google.android.fhir.FhirEngineConfiguration
-import com.google.android.fhir.FhirEngineProvider
-import com.google.android.fhir.datacapture.DataCaptureConfig
-import com.google.android.fhir.search.search
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import org.hl7.fhir.r4.model.Bundle
-
-class CatalogApplication : Application(), DataCaptureConfig.Provider {
- // Only initiate the FhirEngine when used for the first time, not when the app is created.
- private val fhirEngine: FhirEngine by lazy { FhirEngineProvider.getInstance(this) }
-
- private lateinit var dataCaptureConfig: DataCaptureConfig
-
- override fun onCreate() {
- super.onCreate()
-
- FhirEngineProvider.init(FhirEngineConfiguration())
-
- dataCaptureConfig =
- DataCaptureConfig(
- xFhirQueryResolver = { fhirEngine.search(it).map { it.resource } },
- questionnaireItemViewHolderFactoryMatchersProviderFactory =
- ContribQuestionnaireItemViewHolderFactoryMatchersProviderFactory,
- )
-
- CoroutineScope(Dispatchers.IO).launch {
- assets
- .open("resource_data_bundle.json")
- .bufferedReader()
- .use { bufferedReader -> 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/ComponentListFragment.kt b/catalog/src/main/java/com/google/android/fhir/catalog/ComponentListFragment.kt
deleted file mode 100644
index f5d9864396..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/ComponentListFragment.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2022-2024 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.catalog
-
-import android.os.Bundle
-import android.view.Gravity
-import android.view.View
-import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import kotlinx.coroutines.launch
-
-/** Fragment for the component list. */
-class ComponentListFragment : Fragment(R.layout.component_list_fragment) {
- private val viewModel: ComponentListViewModel by viewModels()
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- setUpComponentsRecyclerView()
- (activity as? MainActivity)?.showOpenQuestionnaireMenu(true)
- }
-
- override fun onResume() {
- super.onResume()
- setUpActionBar()
- (activity as MainActivity).showBottomNavigationView(View.VISIBLE)
- }
-
- private fun setUpActionBar() {
- (requireActivity() as AppCompatActivity).supportActionBar?.apply {
- setDisplayHomeAsUpEnabled(false)
- }
- (activity as MainActivity).setActionBar(
- getString(R.string.toolbar_text),
- Gravity.CENTER_HORIZONTAL,
- )
- setHasOptionsMenu(true)
- }
-
- private fun setUpComponentsRecyclerView() {
- val adapter =
- ComponentsRecyclerViewAdapter(::onItemClick).apply { submitList(viewModel.viewItemList) }
- with(requireView().findViewById(R.id.componentsRecyclerView)) {
- this.adapter = adapter
- layoutManager =
- GridLayoutManager(requireContext(), ComponentsRecyclerViewAdapter.ViewType.HEADER.spanCount)
- .apply {
- spanSizeLookup =
- object : GridLayoutManager.SpanSizeLookup() {
- override fun getSpanSize(position: Int): Int {
- return if (
- adapter.getItemViewType(position) ==
- ComponentsRecyclerViewAdapter.ViewType.HEADER.ordinal
- ) {
- ComponentsRecyclerViewAdapter.ViewType.HEADER.spanCount
- } else {
- ComponentsRecyclerViewAdapter.ViewType.ITEM.spanCount
- }
- }
- }
- }
- }
- }
-
- private fun onItemClick(component: ComponentListViewModel.Component) {
- // TODO Remove check when all components questionnaire json are updated.
- // https://github.com/google/android-fhir/issues/1076
- if (component.questionnaireFile.isEmpty()) {
- return
- }
- launchQuestionnaireFragment(component)
- }
-
- private fun launchQuestionnaireFragment(component: ComponentListViewModel.Component) {
- viewLifecycleOwner.lifecycleScope.launch {
- findNavController()
- .navigate(
- MainNavGraphDirections.actionGlobalGalleryQuestionnaireFragment(
- questionnaireTitleKey = context?.getString(component.textId) ?: "",
- questionnaireJsonStringKey =
- getQuestionnaireJsonStringFromAssets(
- context = requireContext(),
- backgroundContext = coroutineContext,
- fileName = component.questionnaireFile,
- ),
- questionnaireWithValidationJsonStringKey =
- component.questionnaireFileWithValidation?.let {
- getQuestionnaireJsonStringFromAssets(
- context = requireContext(),
- backgroundContext = coroutineContext,
- fileName = it,
- )
- },
- ),
- )
- }
- }
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/ComponentListViewModel.kt b/catalog/src/main/java/com/google/android/fhir/catalog/ComponentListViewModel.kt
deleted file mode 100644
index 00f541e5c6..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/ComponentListViewModel.kt
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright 2023-2024 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.catalog
-
-import android.app.Application
-import android.content.Context
-import androidx.annotation.DrawableRes
-import androidx.annotation.StringRes
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.SavedStateHandle
-
-class ComponentListViewModel(application: Application, private val state: SavedStateHandle) :
- AndroidViewModel(application) {
-
- sealed class ViewItem {
- data class HeaderItem(val header: Header) : ViewItem()
-
- data class ComponentItem(val component: Component) : ViewItem()
- }
-
- enum class Header(@StringRes val textId: Int) {
- WIDGETS(R.string.widgets),
- MISC_COMPONENTS(R.string.misc_components),
- }
-
- enum class Component(
- @DrawableRes val iconId: Int,
- @StringRes val textId: Int,
- /** Path to the questionnaire json file with no required fields. */
- val questionnaireFile: String,
- /**
- * Path to the questionnaire json file with some or all required fields. If the user doesn't
- * answer the required questions, an error may be displayed on the particular question.
- */
- val questionnaireFileWithValidation: String? = null,
- ) {
- BOOLEAN_CHOICE(
- R.drawable.ic_booleanchoice,
- R.string.component_name_boolean_choice,
- "component_boolean_choice.json",
- "component_boolean_choice_with_validation.json",
- ),
- SINGLE_CHOICE(
- R.drawable.ic_singlechoice,
- R.string.component_name_single_choice,
- "component_single_choice.json",
- "component_single_choice_with_validation.json",
- ),
- MULTIPLE_CHOICE(
- R.drawable.ic_multiplechoice,
- R.string.component_name_multiple_choice,
- "component_multi_select_choice.json",
- "component_multi_select_choice_with_validation.json",
- ),
- DROPDOWN(
- R.drawable.ic_group_1278,
- R.string.component_name_dropdown,
- "component_dropdown.json",
- "component_dropdown_with_validation.json",
- ),
- MODAL(
- R.drawable.ic_modal,
- R.string.component_name_modal,
- "component_modal.json",
- "component_modal_with_validation.json",
- ),
- OPEN_CHOICE(
- R.drawable.ic_openchoice,
- R.string.component_name_open_choice,
- "component_open_choice.json",
- "component_open_choice_with_validation.json",
- ),
- TEXT_FIELD(
- R.drawable.ic_textfield,
- R.string.component_name_text_field,
- "component_text_fields.json",
- "component_text_fields_with_validation.json",
- ),
- AUTO_COMPLETE(
- R.drawable.ic_autocomplete,
- R.string.component_name_auto_complete,
- "component_auto_complete.json",
- "component_auto_complete_with_validation.json",
- ),
- DATE_PICKER(
- R.drawable.ic_datepicker,
- R.string.component_name_date_picker,
- "component_date_picker.json",
- "component_date_picker_with_validation.json",
- ),
- TIME_PICKER(
- R.drawable.ic_timepicker,
- R.string.component_name_time_picker,
- "component_time_picker.json",
- "component_time_picker_with_validation.json",
- ),
- DATE_TIME_PICKER(
- R.drawable.ic_timepicker,
- R.string.component_name_date_time_picker,
- "component_date_time_picker.json",
- "component_date_time_picker_with_validation.json",
- ),
- SLIDER(
- R.drawable.ic_slider,
- R.string.component_name_slider,
- "component_slider.json",
- "component_slider_with_validation.json",
- ),
- QUANTITY(
- R.drawable.ic_unitoptions,
- R.string.component_name_quantity,
- "component_quantity.json",
- "component_quantity_with_validation.json",
- ),
- ATTACHMENT(
- R.drawable.ic_attachment,
- R.string.component_name_attachment,
- "component_attachment.json",
- "component_attachment_with_validation.json",
- ),
- REPEATED_GROUP(
- R.drawable.ic_repeatgroups,
- R.string.component_name_repeated_group,
- "component_repeated_group.json",
- ),
- HELP(R.drawable.ic_help, R.string.component_name_help, "component_help.json"),
- ITEM_MEDIA(
- R.drawable.ic_item_media,
- R.string.component_name_item_media,
- "component_item_media.json",
- ),
- ITEM_ANSWER_MEDIA(
- R.drawable.ic_item_answer_media,
- R.string.component_name_item_answer_media,
- "",
- ),
- INITIAL_VALUE(
- R.drawable.ic_initial_value_component,
- R.string.component_name_initial_value,
- "component_initial_value.json",
- ),
- LOCATION_WIDGET(
- R.drawable.ic_location_on,
- R.string.component_name_location_widget,
- "component_location_widget.json",
- ),
- QUESTION_ITEM_CUSTOM_STYLE(
- R.drawable.text_format_48dp,
- R.string.component_name_per_question_custom_style,
- "component_per_question_custom_style.json",
- ),
- }
-
- val viewItemList =
- listOf(
- ViewItem.HeaderItem(Header.WIDGETS),
- ViewItem.ComponentItem(Component.BOOLEAN_CHOICE),
- ViewItem.ComponentItem(Component.SINGLE_CHOICE),
- ViewItem.ComponentItem(Component.MULTIPLE_CHOICE),
- ViewItem.ComponentItem(Component.DROPDOWN),
- ViewItem.ComponentItem(Component.MODAL),
- ViewItem.ComponentItem(Component.OPEN_CHOICE),
- ViewItem.ComponentItem(Component.TEXT_FIELD),
- ViewItem.ComponentItem(Component.AUTO_COMPLETE),
- ViewItem.ComponentItem(Component.DATE_PICKER),
- ViewItem.ComponentItem(Component.TIME_PICKER),
- ViewItem.ComponentItem(Component.DATE_TIME_PICKER),
- ViewItem.ComponentItem(Component.SLIDER),
- ViewItem.ComponentItem(Component.QUANTITY),
- ViewItem.ComponentItem(Component.ATTACHMENT),
- ViewItem.ComponentItem(Component.REPEATED_GROUP),
- ViewItem.HeaderItem(Header.MISC_COMPONENTS),
- ViewItem.ComponentItem(Component.HELP),
- ViewItem.ComponentItem(Component.ITEM_MEDIA),
- ViewItem.ComponentItem(Component.ITEM_ANSWER_MEDIA),
- ViewItem.ComponentItem(Component.INITIAL_VALUE),
- ViewItem.ComponentItem(Component.LOCATION_WIDGET),
- ViewItem.ComponentItem(Component.QUESTION_ITEM_CUSTOM_STYLE),
- )
-
- fun isComponent(context: Context, title: String) =
- viewItemList
- .filterIsInstance()
- .map { context.getString(it.component.textId) }
- .contains(title)
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/ComponentsRecyclerViewadapter.kt b/catalog/src/main/java/com/google/android/fhir/catalog/ComponentsRecyclerViewadapter.kt
deleted file mode 100644
index cf0ae81809..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/ComponentsRecyclerViewadapter.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2022-2023 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.catalog
-
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
-import com.google.android.fhir.catalog.databinding.ComponentHeaderLayoutBinding
-import com.google.android.fhir.catalog.databinding.LandingPageItemBinding
-
-class ComponentsRecyclerViewAdapter(
- private val onItemClick: (ComponentListViewModel.Component) -> Unit,
-) : ListAdapter(ComponentDiffUtil()) {
-
- enum class ViewType(val spanCount: Int) {
- HEADER(2),
- ITEM(1),
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
- return when (viewType) {
- ViewType.HEADER.ordinal ->
- ComponentHeaderViewHolder(
- ComponentHeaderLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false),
- )
- ViewType.ITEM.ordinal ->
- ComponentListViewHolder(
- LandingPageItemBinding.inflate(LayoutInflater.from(parent.context), parent, false),
- onItemClick,
- )
- else -> throw IllegalArgumentException("$viewType must be ViewType.")
- }
- }
-
- override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
- if (holder is ComponentViewHolder) {
- holder.bind(getItem(position))
- }
- }
-
- override fun getItemViewType(position: Int): Int {
- return when (getItem(position)) {
- is ComponentListViewModel.ViewItem.HeaderItem -> ViewType.HEADER.ordinal
- is ComponentListViewModel.ViewItem.ComponentItem -> ViewType.ITEM.ordinal
- }
- }
-}
-
-interface ComponentViewHolder {
- fun bind(component: ComponentListViewModel.ViewItem)
-}
-
-class ComponentListViewHolder(
- private val binding: LandingPageItemBinding,
- private val onItemClick: (ComponentListViewModel.Component) -> Unit,
-) : RecyclerView.ViewHolder(binding.root), ComponentViewHolder {
- override fun bind(component: ComponentListViewModel.ViewItem) {
- val componentItem = component as ComponentListViewModel.ViewItem.ComponentItem
- binding.componentLayoutIconImageview.setImageResource(componentItem.component.iconId)
- binding.componentLayoutTextView.text =
- binding.componentLayoutTextView.context.getString(componentItem.component.textId)
- binding.root.setOnClickListener { onItemClick(component.component) }
- }
-}
-
-class ComponentHeaderViewHolder(private val binding: ComponentHeaderLayoutBinding) :
- RecyclerView.ViewHolder(binding.root), ComponentViewHolder {
- override fun bind(component: ComponentListViewModel.ViewItem) {
- val headerItem = component as ComponentListViewModel.ViewItem.HeaderItem
- binding.tvComponentHeader.text =
- binding.tvComponentHeader.context.getString(headerItem.header.textId)
- }
-}
-
-class ComponentDiffUtil : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(
- oldComponent: ComponentListViewModel.ViewItem,
- newComponent: ComponentListViewModel.ViewItem,
- ) = oldComponent === newComponent
-
- override fun areContentsTheSame(
- oldComponent: ComponentListViewModel.ViewItem,
- newComponent: ComponentListViewModel.ViewItem,
- ) = oldComponent == newComponent
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/ContribQuestionnaireItemViewHolderFactoryMatchersProviderFactory.kt b/catalog/src/main/java/com/google/android/fhir/catalog/ContribQuestionnaireItemViewHolderFactoryMatchersProviderFactory.kt
deleted file mode 100644
index 4541a27f50..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/ContribQuestionnaireItemViewHolderFactoryMatchersProviderFactory.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2024 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.catalog
-
-import com.google.android.fhir.datacapture.QuestionnaireFragment
-import com.google.android.fhir.datacapture.QuestionnaireItemViewHolderFactoryMatchersProviderFactory
-import com.google.android.fhir.datacapture.contrib.views.locationwidget.LocationGpsCoordinateViewHolderFactory
-import com.google.android.fhir.datacapture.contrib.views.locationwidget.LocationWidgetViewHolderFactory
-
-object ContribQuestionnaireItemViewHolderFactoryMatchersProviderFactory :
- QuestionnaireItemViewHolderFactoryMatchersProviderFactory {
-
- const val LOCATION_WIDGET_PROVIDER = "location-widget-provider"
-
- override fun get(
- provider: String,
- ): QuestionnaireFragment.QuestionnaireItemViewHolderFactoryMatchersProvider =
- when (provider) {
- LOCATION_WIDGET_PROVIDER ->
- object : QuestionnaireFragment.QuestionnaireItemViewHolderFactoryMatchersProvider() {
- override fun get():
- List {
- return listOf(
- QuestionnaireFragment.QuestionnaireItemViewHolderFactoryMatcher(
- factory = LocationGpsCoordinateViewHolderFactory,
- matches = LocationGpsCoordinateViewHolderFactory::matcher,
- ),
- QuestionnaireFragment.QuestionnaireItemViewHolderFactoryMatcher(
- factory = LocationWidgetViewHolderFactory,
- matches = LocationWidgetViewHolderFactory::matcher,
- ),
- )
- }
- }
- else -> throw NotImplementedError()
- }
-}
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
deleted file mode 100644
index b9a869a6e0..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/DemoQuestionnaireFragment.kt
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright 2023-2026 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.catalog
-
-import android.os.Bundle
-import android.view.Gravity
-import android.view.LayoutInflater
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.commit
-import androidx.fragment.app.setFragmentResultListener
-import androidx.fragment.app.viewModels
-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()
- private val componentListViewModel: ComponentListViewModel by viewModels()
- private val behaviorListViewModel: BehaviorListViewModel by viewModels()
- private val layoutListViewModel: LayoutListViewModel by viewModels()
- private val args: DemoQuestionnaireFragmentArgs by navArgs()
- private var isErrorState = false
- private lateinit var infoCard: MaterialCardView
- private lateinit var infoCardHeader: TextView
- private lateinit var infoCardText: TextView
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ): View {
- requireContext().setTheme(getThemeId(args.questionnaireTitleKey))
- return inflater.inflate(R.layout.fragment_demo_questionnaire, container, false)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- infoCard = view.findViewById(R.id.infoCard)
- when (args.questionnaireTitleKey) {
- getString(R.string.behavior_name_skip_logic) -> {
- infoCardHeader = view.findViewById(R.id.infoCardHeader)
- infoCardHeader.text = args.questionnaireTitleKey
- infoCardText = view.findViewById(R.id.infoCardText)
- infoCardText.text = getString(R.string.behavior_name_skip_logic_info)
- infoCard.visibility = View.VISIBLE
- }
- getString(R.string.behavior_name_calculated_expression) -> {
- infoCardHeader = view.findViewById(R.id.infoCardHeader)
- infoCardHeader.text = args.questionnaireTitleKey
- infoCardText = view.findViewById(R.id.infoCardText)
- infoCardText.text = getString(R.string.behavior_name_calculated_expression_info)
- infoCard.visibility = View.VISIBLE
- }
- else -> infoCard.visibility = View.GONE
- }
- setFragmentResultListener(REQUEST_ERROR_KEY) { _, bundle ->
- isErrorState = bundle.getBoolean(BUNDLE_ERROR_KEY)
- replaceQuestionnaireFragmentWithQuestionnaireJson()
- }
- childFragmentManager.setFragmentResultListener(SUBMIT_REQUEST_KEY, viewLifecycleOwner) { _, _ ->
- onSubmitQuestionnaireClick()
- }
- if (savedInstanceState == null) {
- addQuestionnaireFragment()
- }
- (activity as? MainActivity)?.showOpenQuestionnaireMenu(false)
- }
-
- override fun onResume() {
- super.onResume()
- (requireActivity() as MainActivity).showBottomNavigationView(View.GONE)
- setUpActionBar()
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- android.R.id.home -> {
- NavHostFragment.findNavController(this).navigateUp()
- true
- }
- R.id.error_menu -> {
- launchModalBottomSheetFragment()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- super.onCreateOptionsMenu(menu, inflater)
- getMenu()?.let { inflater.inflate(it, menu) }
- }
-
- private fun setUpActionBar() {
- (requireActivity() as AppCompatActivity).supportActionBar?.apply {
- setDisplayHomeAsUpEnabled(true)
- }
- (requireActivity() as MainActivity).setActionBar(args.questionnaireTitleKey, Gravity.TOP)
- setHasOptionsMenu(true)
- }
-
- private fun addQuestionnaireFragment() {
- viewLifecycleOwner.lifecycleScope.launch {
- if (childFragmentManager.findFragmentByTag(QUESTIONNAIRE_FRAGMENT_TAG) == null) {
- childFragmentManager.commit {
- setReorderingAllowed(true)
- val questionnaireFragmentBuilder =
- QuestionnaireFragment.builder().apply {
- setCustomQuestionnaireItemViewHolderFactoryMatchersProvider(
- ContribQuestionnaireItemViewHolderFactoryMatchersProviderFactory
- .LOCATION_WIDGET_PROVIDER,
- )
- setQuestionnaire(args.questionnaireJsonStringKey!!)
- }
- LayoutListViewModel.questionnaireLambdaMap[args.questionnaireLambdaKey ?: ""]!!.invoke(
- questionnaireFragmentBuilder,
- )
- add(R.id.container, questionnaireFragmentBuilder.build(), QUESTIONNAIRE_FRAGMENT_TAG)
- }
- }
- }
- }
-
- /**
- * Replaces existing [QuestionnaireFragment] with questionnaire json as per [isErrorState] value.
- * If isErrorState is true then existing fragment get replaced with questionnaire json which shows
- * error.
- */
- private fun replaceQuestionnaireFragmentWithQuestionnaireJson() {
- // TODO: remove check once all files are added
- if (args.questionnaireWithValidationJsonStringKey.isNullOrEmpty()) {
- return
- }
- viewLifecycleOwner.lifecycleScope.launch {
- val questionnaireJsonString =
- if (isErrorState) {
- args.questionnaireWithValidationJsonStringKey!!
- } else {
- args.questionnaireJsonStringKey!!
- }
- childFragmentManager.commit {
- setReorderingAllowed(true)
- replace(
- R.id.container,
- QuestionnaireFragment.builder()
- .setQuestionnaire(questionnaireJsonString)
- .setQuestionnaireLaunchContextMap(
- FhirContext.forR4Cached()
- .newJsonParser()
- .encodeResourceToString(Patient().apply { id = "P1" })
- .let { mapOf("patient" to it) },
- )
- .setSubmitButtonText(
- getString(com.google.android.fhir.datacapture.R.string.submit_questionnaire),
- )
- .build(),
- QUESTIONNAIRE_FRAGMENT_TAG,
- )
- }
- }
- }
-
- private fun getThemeId(title: String) =
- if (
- layoutListViewModel.isPaginatedLayout(requireContext(), title) ||
- componentListViewModel.isComponent(requireContext(), title) ||
- behaviorListViewModel.isBehavior(requireContext(), title)
- ) {
- R.style.Theme_Androidfhir_PaginatedLayout
- } else {
- R.style.Theme_Androidfhir_DefaultLayout
- }
-
- private fun getMenu(): Int? =
- if (
- componentListViewModel.isComponent(
- requireContext(),
- args.questionnaireTitleKey!!,
- )
- ) {
- R.menu.component_menu
- } else {
- null
- }
-
- private fun onSubmitQuestionnaireClick() {
- lifecycleScope.launch {
- val questionnaireFragment =
- childFragmentManager.findFragmentByTag(QUESTIONNAIRE_FRAGMENT_TAG) as QuestionnaireFragment
- launchQuestionnaireResponseFragment(
- viewModel.getQuestionnaireResponseJson(questionnaireFragment.getQuestionnaireResponse()),
- )
- }
- }
-
- private fun launchQuestionnaireResponseFragment(response: String) {
- findNavController()
- .navigate(
- DemoQuestionnaireFragmentDirections
- .actionGalleryQuestionnaireFragmentToQuestionnaireResponseFragment(response),
- )
- }
-
- private fun launchModalBottomSheetFragment() {
- findNavController()
- .navigate(
- DemoQuestionnaireFragmentDirections.actionGalleryQuestionnaireFragmentToModalBottomSheet(
- isErrorState,
- ),
- )
- }
-
- companion object {
- const val QUESTIONNAIRE_FRAGMENT_TAG = "questionnaire-fragment-tag"
- }
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/LayoutListFragment.kt b/catalog/src/main/java/com/google/android/fhir/catalog/LayoutListFragment.kt
deleted file mode 100644
index dd91fde727..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/LayoutListFragment.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2022-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.catalog
-
-import android.os.Bundle
-import android.view.Gravity
-import android.view.View
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import kotlinx.coroutines.launch
-
-/** Fragment for the layout list. */
-class LayoutListFragment : Fragment(R.layout.layout_list_fragment) {
- private val viewModel: LayoutListViewModel by viewModels()
-
- override fun onResume() {
- super.onResume()
- setUpActionBar()
- (requireActivity() as MainActivity).showBottomNavigationView(View.VISIBLE)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- setUpLayoutsRecyclerView()
- (activity as? MainActivity)?.showOpenQuestionnaireMenu(true)
- }
-
- private fun setUpLayoutsRecyclerView() {
- val adapter =
- LayoutsRecyclerViewAdapter(::onItemClick).apply { submitList(viewModel.getLayoutList()) }
- val recyclerView = requireView().findViewById(R.id.sdcLayoutsRecyclerView)
- recyclerView.adapter = adapter
- recyclerView.layoutManager = GridLayoutManager(context, 2)
- }
-
- private fun setUpActionBar() {
- (requireActivity() as MainActivity).setNavigationUp(false)
- (activity as MainActivity).setActionBar(
- getString(R.string.toolbar_text),
- Gravity.CENTER_HORIZONTAL,
- )
- setHasOptionsMenu(true)
- }
-
- private fun onItemClick(layout: LayoutListViewModel.Layout) {
- // TODO Remove check when all layout questionnaire json are updated.
- // https://github.com/google/android-fhir/issues/1079
- if (layout.questionnaireFileName.isEmpty()) {
- return
- }
- launchQuestionnaireFragment(layout)
- }
-
- private fun launchQuestionnaireFragment(layout: LayoutListViewModel.Layout) {
- viewLifecycleOwner.lifecycleScope.launch {
- findNavController()
- .navigate(
- MainNavGraphDirections.actionGlobalGalleryQuestionnaireFragment(
- questionnaireTitleKey = context?.getString(layout.textId) ?: "",
- questionnaireJsonStringKey =
- getQuestionnaireJsonStringFromAssets(
- context = requireContext(),
- backgroundContext = coroutineContext,
- fileName = layout.questionnaireFileName,
- ),
- questionnaireLambdaKey = layout.questionnaireLambdaKey,
- ),
- )
- }
- }
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/LayoutListViewModel.kt b/catalog/src/main/java/com/google/android/fhir/catalog/LayoutListViewModel.kt
deleted file mode 100644
index 5a6356985a..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/LayoutListViewModel.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2022-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.catalog
-
-import android.app.Application
-import android.content.Context
-import androidx.annotation.DrawableRes
-import androidx.annotation.StringRes
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.SavedStateHandle
-import com.google.android.fhir.datacapture.QuestionnaireFragment
-
-class LayoutListViewModel(application: Application, private val state: SavedStateHandle) :
- AndroidViewModel(application) {
-
- fun getLayoutList(): List {
- return Layout.values().toList()
- }
-
- enum class Layout(
- @DrawableRes val iconId: Int,
- @StringRes val textId: Int,
- val questionnaireFileName: String,
- val questionnaireLambdaKey: String,
- ) {
- DEFAULT(
- R.drawable.ic_defaultlayout,
- R.string.layout_name_default_text,
- "layout_default.json",
- "",
- ),
- PAGINATED(
- R.drawable.ic_paginatedlayout,
- R.string.layout_name_paginated,
- "layout_paginated.json",
- "",
- ),
- REVIEW(
- R.drawable.ic_reviewlayout,
- R.string.layout_name_review,
- "layout_review.json",
- "showreviewpagefirstandbeforesubmit",
- ),
- READ_ONLY(R.drawable.ic_readonlylayout, R.string.layout_name_read_only, "", ""),
- }
-
- fun isDefaultLayout(context: Context, title: String) =
- context.getString(Layout.DEFAULT.textId) == title
-
- fun isPaginatedLayout(context: Context, title: String) =
- context.getString(Layout.PAGINATED.textId) == title
-
- companion object {
- val questionnaireLambdaMap: Map Unit> =
- mapOf(
- "" to
- {
- showReviewPageFirst(false)
- showReviewPageBeforeSubmit(false)
- },
- "showreviewpagefirstandbeforesubmit" to
- {
- showReviewPageFirst(true)
- showReviewPageBeforeSubmit(true)
- },
- )
- }
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/LayoutsRecyclerViewAdapter.kt b/catalog/src/main/java/com/google/android/fhir/catalog/LayoutsRecyclerViewAdapter.kt
deleted file mode 100644
index 88874c623d..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/LayoutsRecyclerViewAdapter.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2021-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.catalog
-
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
-import com.google.android.fhir.catalog.databinding.LandingPageItemBinding
-
-class LayoutsRecyclerViewAdapter(private val onItemClick: (LayoutListViewModel.Layout) -> Unit) :
- ListAdapter(LayoutDiffUtil()) {
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LayoutViewHolder {
- return LayoutViewHolder(
- LandingPageItemBinding.inflate(LayoutInflater.from(parent.context), parent, false),
- onItemClick,
- )
- }
-
- override fun onBindViewHolder(holder: LayoutViewHolder, position: Int) {
- holder.bind(getItem(position))
- }
-}
-
-class LayoutViewHolder(
- val binding: LandingPageItemBinding,
- private val onItemClick: (LayoutListViewModel.Layout) -> Unit,
-) : RecyclerView.ViewHolder(binding.root) {
- fun bind(layout: LayoutListViewModel.Layout) {
- binding.componentLayoutIconImageview.setImageResource(layout.iconId)
- binding.componentLayoutTextView.text =
- binding.componentLayoutTextView.context.getString(layout.textId)
- binding.root.setOnClickListener { onItemClick(layout) }
- }
-}
-
-class LayoutDiffUtil : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(
- oldLayout: LayoutListViewModel.Layout,
- newLayout: LayoutListViewModel.Layout,
- ) = oldLayout === newLayout
-
- override fun areContentsTheSame(
- oldLayout: LayoutListViewModel.Layout,
- newLayout: LayoutListViewModel.Layout,
- ) = oldLayout == newLayout
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/MainActivity.kt b/catalog/src/main/java/com/google/android/fhir/catalog/MainActivity.kt
deleted file mode 100644
index 09cc2f03eb..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/MainActivity.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2021-2024 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.catalog
-
-import android.net.Uri
-import android.os.Bundle
-import android.view.Menu
-import android.view.MenuItem
-import android.widget.TextView
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.Toolbar
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.Navigation
-import androidx.navigation.findNavController
-import androidx.navigation.ui.NavigationUI
-import com.google.android.material.bottomnavigation.BottomNavigationView
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity(R.layout.activity_main) {
- private var showOpenQuestionnaireMenu = true
- val getContentLauncher =
- registerForActivityResult(ActivityResultContracts.GetContent()) {
- it?.let { launchQuestionnaireFragment(it) }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setSupportActionBar(findViewById(R.id.toolbar))
- setUpBottomNavigationView()
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.open_questionnaire_menu, menu)
- return true
- }
-
- override fun onPrepareOptionsMenu(menu: Menu): Boolean {
- menu.findItem(R.id.select_questionnaire_menu).isVisible = showOpenQuestionnaireMenu
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.select_questionnaire_menu -> {
- getContentLauncher.launch("application/json")
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- fun showOpenQuestionnaireMenu(showMenu: Boolean) {
- showOpenQuestionnaireMenu = showMenu
- invalidateOptionsMenu()
- }
-
- fun showBottomNavigationView(value: Int) {
- findViewById(R.id.bottom_navigation_view).visibility = value
- }
-
- fun setNavigationUp(value: Boolean) {
- supportActionBar?.apply { setDisplayHomeAsUpEnabled(value) }
- }
-
- fun setActionBar(title: String, gravity: Int) {
- val toolbar = findViewById(R.id.toolbar)
- setSupportActionBar(toolbar)
- val titleTextView = toolbar.findViewById(R.id.toolbarTitle)
- titleTextView.text = title
- titleTextView.gravity = gravity
- }
-
- private fun setUpBottomNavigationView() {
- val navController = Navigation.findNavController(this, R.id.nav_host_fragment)
- val bottomNavigationView = findViewById(R.id.bottom_navigation_view)
- NavigationUI.setupWithNavController(bottomNavigationView, navController)
- }
-
- private fun launchQuestionnaireFragment(uri: Uri) {
- lifecycleScope.launch {
- findNavController(R.id.nav_host_fragment)
- .navigate(
- MainNavGraphDirections.actionGlobalGalleryQuestionnaireFragment(
- questionnaireTitleKey = "",
- questionnaireJsonStringKey =
- getQuestionnaireJsonStringFromFileUri(
- context = applicationContext,
- backgroundContext = coroutineContext,
- uri = uri,
- ),
- ),
- )
- }
- }
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/ModalBottomSheetFragment.kt b/catalog/src/main/java/com/google/android/fhir/catalog/ModalBottomSheetFragment.kt
deleted file mode 100644
index 5c2ca4d0fb..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/ModalBottomSheetFragment.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2021-2024 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.catalog
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.CheckBox
-import androidx.core.os.bundleOf
-import androidx.fragment.app.setFragmentResult
-import androidx.navigation.fragment.navArgs
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-
-class ModalBottomSheetFragment : BottomSheetDialogFragment() {
- private val args: ModalBottomSheetFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ): View? {
- return inflater.inflate(R.layout.fragment_modal_bottom_sheet, container, false)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val showHideErrorCheckBox = view.findViewById(R.id.errorToggleCheckBox)
- showHideErrorCheckBox.isChecked = args.errorState
- showHideErrorCheckBox.setOnCheckedChangeListener { _, isChecked ->
- setFragmentResult(
- REQUEST_ERROR_KEY,
- bundleOf(
- BUNDLE_ERROR_KEY to isChecked,
- ),
- )
- }
- (activity as? MainActivity)?.showOpenQuestionnaireMenu(false)
- }
-
- companion object {
- const val REQUEST_ERROR_KEY = "errorRequestKey"
- const val BUNDLE_ERROR_KEY = "errorBundleKey"
- }
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/QuestionnaireFileOperationUtil.kt b/catalog/src/main/java/com/google/android/fhir/catalog/QuestionnaireFileOperationUtil.kt
deleted file mode 100644
index 18dc4061c8..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/QuestionnaireFileOperationUtil.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2023 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.catalog
-
-import android.content.Context
-import android.net.Uri
-import java.io.BufferedReader
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.withContext
-
-suspend fun getQuestionnaireJsonStringFromAssets(
- context: Context,
- backgroundContext: CoroutineContext,
- fileName: String,
-): String? {
- return withContext(backgroundContext) {
- if (fileName.isNotEmpty()) {
- context.assets.open(fileName).bufferedReader().use { it.readText() }
- } else {
- null
- }
- }
-}
-
-suspend fun getQuestionnaireJsonStringFromFileUri(
- context: Context,
- backgroundContext: CoroutineContext,
- uri: Uri,
-): String {
- return withContext(backgroundContext) {
- val reader = BufferedReader(context.contentResolver.openInputStream(uri)?.reader())
- reader.use { reader -> reader.readText() }
- }
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/QuestionnaireResponseDialogFragment.kt b/catalog/src/main/java/com/google/android/fhir/catalog/QuestionnaireResponseDialogFragment.kt
deleted file mode 100644
index 3e4100f1b1..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/QuestionnaireResponseDialogFragment.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2021-2023 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.catalog
-
-import android.app.AlertDialog
-import android.app.Dialog
-import android.os.Bundle
-import androidx.fragment.app.DialogFragment
-import com.google.android.fhir.catalog.databinding.QuestionnaireResponseDialogContentsBinding
-
-class QuestionnaireResponseDialogFragment() : DialogFragment() {
- private var _binding: QuestionnaireResponseDialogContentsBinding? = null
- private val binding
- get() = _binding!!
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val contents = requireArguments().getString(BUNDLE_KEY_CONTENTS)
- return activity?.let {
- _binding = QuestionnaireResponseDialogContentsBinding.inflate(layoutInflater)
- binding.contents.text = contents
-
- AlertDialog.Builder(it).setView(binding.root).create()
- }
- ?: throw IllegalStateException("Activity cannot be null")
- }
-
- companion object {
- const val TAG = "questionnaire-response-dialog-fragment"
- const val BUNDLE_KEY_CONTENTS = "contents"
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-}
diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/QuestionnaireResponseFragment.kt b/catalog/src/main/java/com/google/android/fhir/catalog/QuestionnaireResponseFragment.kt
deleted file mode 100644
index a596dd8820..0000000000
--- a/catalog/src/main/java/com/google/android/fhir/catalog/QuestionnaireResponseFragment.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2021-2023 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.catalog
-
-import android.os.Bundle
-import android.view.Gravity
-import android.view.LayoutInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Button
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
-import androidx.navigation.fragment.NavHostFragment
-import androidx.navigation.fragment.navArgs
-import org.json.JSONObject
-
-class QuestionnaireResponseFragment : Fragment() {
- private val args: QuestionnaireResponseFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ): View? {
- return inflater.inflate(R.layout.fragment_questionnaire_response, container, false)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- setCloseOnClickListener()
- view.findViewById(R.id.questionnaire_response_tv).text =
- JSONObject(args.questionnaireResponse).toString(2)
- (activity as? MainActivity)?.showOpenQuestionnaireMenu(false)
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- android.R.id.home -> {
- NavHostFragment.findNavController(this).navigateUp()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- override fun onResume() {
- super.onResume()
- setUpActionBar()
- }
-
- private fun setUpActionBar() {
- (requireActivity() as AppCompatActivity).supportActionBar?.apply {
- setDisplayHomeAsUpEnabled(true)
- }
- (requireActivity() as MainActivity).setActionBar(
- getString(R.string.questionnaire_response_title),
- Gravity.START,
- )
- setHasOptionsMenu(true)
- }
-
- private fun setCloseOnClickListener() {
- view?.findViewById