The Android FHIR SDK is a set of Kotlin libraries for building offline-capable, mobile-first healthcare applications using the HL7® FHIR® standard on Android.
This tasks is not top priority and would probably be the best if those who are not super familiar with Kotlin can contribute. Simply state what you're working on and send a PR and link it to this issue. Thanks all!
Each Resource in FHIR can have a lastUpdated timestamp to track when it last changed. We should add a column in ResourceEntity to support it. Ref Resource.
When syncing from the hapi test server, we use the id field from the patient object, it is of the form "baseurl/patient/id/history/historyid". What this means is that when there's a new version of patient we will treat it as a new (duplicated) patient in the db.
The fix is to use the actual patient id (which is a number on hapi test server) rather than the URL. In the hapi structures lib it's patient.getIdElement().getIdPart() (java).
Currently testing how the datacapture lib behaves when items in the sample questionnaire are duplicated. When the questionnaire view goes over one screen the submit but does not appear.
The timezone format currently supported by FHIR Search, FHIRPath etc. currently is a subset of ISO8601:
yyyy-mm-ddThh:mm:ss[Z|(+|-)hh:mm]
In Android SimpleDateFormat shows 'X' pattern for ISO 8601 only be supported in API Level 24+ (Nougat+) only.
This is required for Sync implementation to work correctly. Otherwise in API 21 it throws exception java.lang.IllegalArgumentException: Unknown pattern character 'X' while using SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.US)
Following discussions between @yigit@florina-muntenescu@jingtang10, we decided to make fhir engine a singleton per process + db name to avoid racing conditions when updating the db
Modify FhirEngine's API so that it's impossible to create another instance if there is already one (i.e. caching). At the moment in the referende app this is ensured by the initialization code in FhirApplication.kt using by lazy. But we want to be in the engine itself
Mark the inMemory() API as internal since we're only using it in tests and definitly don't want users to actually create in-memory db
Simplify the initialization code and remove the db name option (it's not used anywhere anyway)
The timezone format currently supported by FHIR Search, FHIRPath etc. currently is a subset of ISO8601:
yyyy-mm-ddThh:mm:ss[Z|(+|-)hh:mm]
In Android SimpleDateFormat shows 'X' pattern for ISO 8601 only be supported in API Level 24+ (Nougat+) only.
This is required for Sync implementation to work correctly. Otherwise in API 21 it throws exception java.lang.IllegalArgumentException: Unknown pattern character 'X' while using SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.US)
robolectric runs tests on main thread which becomes a problem w/ room.
The official recommendation for Room is to actually test on a device since different SQLite versions on devices have different issues and we probably need to support really old devices.
Until then, we'll need to allow main thread queries in room. If we decide to keep it, we should at least make it a test only option.
We need to implement mapping of Search Parameters to the underlying data type of the property. Currently, we are assuming the accessors are named getXXElement for date and getXX for all other SearchParamDefinition types. For example Patient.birthDate has getBirthDateElement(). However, this is not universally true in HAPI FHIR. E.g. Observation.effective does not have a getEffectiveElement method.
When you try to launch MainActivity on master branch.
2020-07-28 12:30:33.190 8863-8863/com.google.fhirengine.example E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.google.fhirengine.example, PID: 8863
java.lang.NoSuchMethodError: No static method create(Lcom/google/fhirengine/db/Database;)Lcom/google/fhirengine/cql/FhirEngineDataProvider; in class Lcom/google/fhirengine/cql/FhirEngineDataProvider$Factory; or its super classes (declaration of 'com.google.fhirengine.cql.FhirEngineDataProvider$Factory' appears in /data/app/com.google.fhirengine.example-uoheiqp6VCcDKfyR9_GnEg==/base.apk!classes6.dex)
at com.google.fhirengine.FhirServices$Builder.build(FhirServices.kt:63)
at com.google.fhirengine.FhirEngineBuilder.build(FhirEngineBuilder.kt:50)
at com.google.fhirengine.example.FhirApplication.constructFhirEngine(FhirApplication.kt:46)
at com.google.fhirengine.example.FhirApplication.access$constructFhirEngine(FhirApplication.kt:33)
at com.google.fhirengine.example.FhirApplication$fhirEngine$2.invoke(FhirApplication.kt:36)
at com.google.fhirengine.example.FhirApplication$fhirEngine$2.invoke(FhirApplication.kt:33)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at com.google.fhirengine.example.FhirApplication.getFhirEngine(Unknown Source:2)
at com.google.fhirengine.example.FhirApplication.access$getFhirEngine$p(FhirApplication.kt:33)
at com.google.fhirengine.example.FhirApplication$Companion.fhirEngine(FhirApplication.kt:52)
at com.google.fhirengine.example.FhirApplication.fhirEngine(Unknown Source:2)
at com.google.fhirengine.example.MainActivity.onCreate(MainActivity.java:62)
at android.app.Activity.performCreate(Activity.java:7802)
at android.app.Activity.performCreate(Activity.java:7791)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
In recent github action runs for PRs, tests for API 29 consistently failed due to out of memory issue. e.g:
com.google.fhirengine.db.impl.DatabaseImplTest > select_invalidResourceType_shouldThrowIllegalArgumentException[test(AVD) - 10] FAILED
java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw an exception; no stack trace available
we have some entities (StringIndex, ReferenceIndex) that seem to have the same structure that is driven from a generic structure in FHIR engine.
it might make sense to merge them into one so that wen can add any type of index easily.
I'm not fully sure why it is this way right now so just keeping this issue as a note. we may or may not want to do this.
One use case is:
To populate local db with sample Fhir objects for the Sample App to work in case hardcoded sync doesn't get back any objects from the external Hapi server.
While running master on API level 19, there's this error:
05-15 12:09:05.044 3357-3357/com.google.fhirengine.example E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.google.fhirengine.example, PID: 3357
java.lang.VerifyError: org/apache/log4j/config/PropertySetter
at org.apache.log4j.PropertyConfigurator.parseAppender(PropertyConfigurator.java:805)
at org.apache.log4j.PropertyConfigurator.parseCategory(PropertyConfigurator.java:768)
at org.apache.log4j.PropertyConfigurator.configureRootCategory(PropertyConfigurator.java:648)
at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:514)
at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:580)
at org.apache.log4j.helpers.OptionConverter.selectAndConfigure(OptionConverter.java:526)
at org.apache.log4j.LogManager.(LogManager.java:127)
at org.slf4j.impl.Log4jLoggerFactory.(Log4jLoggerFactory.java:66)
at org.slf4j.impl.StaticLoggerBinder.(StaticLoggerBinder.java:72)
at org.slf4j.impl.StaticLoggerBinder.(StaticLoggerBinder.java:45)
at org.slf4j.LoggerFactory.bind(LoggerFactory.java:150)
at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:124)
at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:412)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:357)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:383)
at ca.uhn.fhir.context.FhirContext.(FhirContext.java:80)
at com.google.fhirengine.resource.ResourceModule.getFhirContext(ResourceModule.java:29)
at com.google.fhirengine.resource.ResourceModule_GetFhirContextFactory.proxyGetFhirContext(ResourceModule_GetFhirContextFactory.java:27)
at com.google.fhirengine.DaggerFhirEngineComponent.getIParser(DaggerFhirEngineComponent.java:34)
at com.google.fhirengine.DaggerFhirEngineComponent.getDatabaseImpl(DaggerFhirEngineComponent.java:38)
at com.google.fhirengine.DaggerFhirEngineComponent.getFhirEngineImpl(DaggerFhirEngineComponent.java:62)
at com.google.fhirengine.DaggerFhirEngineComponent.getFhirEngine(DaggerFhirEngineComponent.java:75)
at com.google.fhirengine.example.MainActivity.onCreate(MainActivity.java:95)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
which is an issue due to missing java libraries on Android which are used by HAPI FHIR.
We index search parameters in FhirIndexerImpl.kt by type. We extract the object instances by splitting the path in the SearchParamDefinition and walking the Resource members depth-first. With splitting the path by dot notation, e.g. Observation.code.value we are missing out on Search params which do not conform to this pattern. E.g. SP_PHONE in Patient is Patient.telecom.where(system='phone').