From 3a0017b9ecfd3d8beafc2812d2f7f7eb1e24a52d Mon Sep 17 00:00:00 2001
From: Shivendu Mishra <77795429+shivenducs1136@users.noreply.github.com>
Date: Wed, 7 Jun 2023 18:38:55 +0530
Subject: [PATCH] Moved openForm(),openSavedForm and prefillAndOpenForm() to
FormsInteractor (#54)
* Moved openForm(), openSavedForm() and prefillAndOpenForm() to FormsInteractor.
---
odk/extension/README.md | 18 ++-
.../extension/handlers/ODKFormsHandler.kt | 123 +++++++++++++++++-
.../collect/extension/handlers/ODKHandler.kt | 118 -----------------
.../extension/interactors/FormsInteractor.kt | 11 ++
.../extension/interactors/ODKInteractor.kt | 8 --
.../modules/FormsInteractorModule.kt | 8 +-
.../oce_sample/ODKFeatureTesterActivity.kt | 7 +-
7 files changed, 158 insertions(+), 135 deletions(-)
diff --git a/odk/extension/README.md b/odk/extension/README.md
index 5e4e70dd391..9ce28649b08 100644
--- a/odk/extension/README.md
+++ b/odk/extension/README.md
@@ -44,6 +44,16 @@ This method opens a saved instance of a form with a given form ID, or creates a
+`fun prefillAndOpenForm(formId: String, tagValueMap: HashMap, context: Context)`
+
+This method pre-fills a form with data from a given map of key-value pairs, and then opens the form in a given Android context. \
+***Parameters:*** \
+`formId` - A string representing the ID of the form to be opened. \
+`tagValueMap` - A HashMap containing the key-value pairs representing the data to pre-fill the form with. \
+`context` - A Context object representing the Android application context.
+
+
+
## FormsDatabaseInteractor Interface
The FormsDatabaseInteractor interface provides a set of methods to interact with the local forms database. This interface enables developers to manage the forms that are stored on the device, including retrieving, adding, and deleting forms.
@@ -260,22 +270,22 @@ Note: This creates a separate form instance of the original form and does not al
-`updateForm(form: Form, tag: String, tagValue: String, listener: FormsProcessListener)`
+`updateForm(formPath: String, tag: String, tagValue: String, listener: FormsProcessListener?)`
Prefills the values of a form given a tag and value or a list of tags and values. \
***Parameters:*** \
-`form`- The form to update. This is an ODK Form object. \
+`formPath`- A string that represents the path of the form. \
`tag`- The tag of the form element to update. This is a string value. \
`tagValue`- The new value to set for the form element. This is a string value. \
`listener`- An optional listener to handle the update process. This is a FormsProcessListener object.
-`updateForm(form: Form, values: HashMap, listener: FormsProcessListener)`
+`updateForm(formPath: String, values: HashMap, listener: FormsProcessListener?)`
Prefills the values of a form based on a list of a tags and values. \
***Parameters:*** \
-`form`- The form to update. This is an ODK Form object. \
+`formPath`- A string that represents the path of the form. \
`values`- A HashMap containing tag-value pairs to update the form with. This is a map from string keys to string values. \
`listener`- An optional listener to handle the update process. This is a FormsProcessListener object.
diff --git a/odk/extension/src/main/java/io/samagra/odk/collect/extension/handlers/ODKFormsHandler.kt b/odk/extension/src/main/java/io/samagra/odk/collect/extension/handlers/ODKFormsHandler.kt
index afff5ed7830..63ffa2b90b2 100644
--- a/odk/extension/src/main/java/io/samagra/odk/collect/extension/handlers/ODKFormsHandler.kt
+++ b/odk/extension/src/main/java/io/samagra/odk/collect/extension/handlers/ODKFormsHandler.kt
@@ -3,8 +3,12 @@ package io.samagra.odk.collect.extension.handlers
import android.content.Context
import android.content.Intent
import android.util.Log
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import io.samagra.odk.collect.extension.interactors.FormInstanceInteractor
import io.samagra.odk.collect.extension.interactors.FormsDatabaseInteractor
import io.samagra.odk.collect.extension.interactors.FormsInteractor
+import io.samagra.odk.collect.extension.interactors.FormsNetworkInteractor
+import io.samagra.odk.collect.extension.listeners.FileDownloadListener
import io.samagra.odk.collect.extension.listeners.FormsProcessListener
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -12,6 +16,7 @@ import kotlinx.coroutines.launch
import org.javarosa.core.model.FormDef
import org.odk.collect.android.activities.FormEntryActivity
import org.odk.collect.android.events.FormEventBus
+import org.odk.collect.android.events.FormStateEvent
import org.odk.collect.android.external.FormsContract
import org.odk.collect.android.formentry.loading.FormInstanceFileCreator
import org.odk.collect.android.formentry.saving.DiskFormSaver
@@ -24,6 +29,8 @@ import org.odk.collect.android.utilities.ApplicationConstants
import org.odk.collect.android.utilities.MediaUtils
import org.odk.collect.entities.EntitiesRepository
import org.odk.collect.forms.Form
+import org.odk.collect.forms.instances.Instance
+import org.odk.collect.forms.instances.InstancesRepository
import org.w3c.dom.Document
import java.io.File
import java.io.FileOutputStream
@@ -39,7 +46,10 @@ class ODKFormsHandler @Inject constructor(
private val formsDatabaseInteractor: FormsDatabaseInteractor,
private val storagePathProvider: StoragePathProvider,
private val mediaUtils: MediaUtils,
- private val entitiesRepository: EntitiesRepository
+ private val entitiesRepository: EntitiesRepository,
+ private val formsNetworkInteractor: FormsNetworkInteractor,
+ private val instancesRepository: InstancesRepository,
+ private val formInstanceInteractor: FormInstanceInteractor
): FormsInteractor {
override fun openFormWithFormId(formId: String, context: Context) {
@@ -172,7 +182,6 @@ class ODKFormsHandler @Inject constructor(
})
}
}
-
private fun updateDocumentBasedOnTag(document: Document, tag: String, tagValue: String): Document {
try {
if (document.getElementsByTagName(tag).item(0).childNodes.length > 0)
@@ -188,4 +197,114 @@ class ODKFormsHandler @Inject constructor(
}
return document
}
+
+ override fun openForm(formId: String, context: Context) {
+ CoroutineScope(Job()).launch {
+ // Delete any saved instances of this form
+ val savedInstances = instancesRepository.getAllByFormId(formId)
+ for (instance in savedInstances) {
+ if (instance.status == Instance.STATUS_INCOMPLETE) {
+ instancesRepository.delete(instance.dbId)
+ }
+ }
+ val requiredForm = formsDatabaseInteractor.getLatestFormById(formId)
+ if (requiredForm == null) {
+ downloadAndOpenForm(formId, context)
+ }
+ else {
+ val xmlFile = File(requiredForm.formFilePath)
+ if (xmlFile.exists() && (requiredForm.formMediaPath == null || mediaExists(requiredForm))) {
+ openFormWithFormId(formId, context)
+ }
+ else {
+ requiredForm.formMediaPath?.let { File(it).deleteRecursively() }
+ xmlFile.delete()
+ formsDatabaseInteractor.deleteByFormId(formId)
+ downloadAndOpenForm(formId, context)
+ }
+ }
+ }
+ }
+
+ override fun openSavedForm(formId: String, context: Context) {
+ CoroutineScope(Job()).launch {
+ val compositeDisposable = CompositeDisposable()
+ compositeDisposable.add(
+ FormEventBus.getState()
+ .subscribe { event ->
+ when (event) {
+ is FormStateEvent.OnFormOpenFailed -> {
+ if (event.formId == formId) {
+ compositeDisposable.clear()
+ openForm(formId, context)
+ }
+ }
+ is FormStateEvent.OnFormOpened -> {
+ if (event.formId == formId) {
+ compositeDisposable.clear()
+ }
+ }
+ else -> {}
+ }
+ }
+ )
+
+ formInstanceInteractor.openLatestSavedInstanceWithFormId(formId, context)
+ }
+ }
+
+ override fun prefillAndOpenForm(formId: String, tagValueMap: HashMap, context: Context) {
+ CoroutineScope(Job()).launch {
+ val compositeDisposable = CompositeDisposable()
+ compositeDisposable.add(FormEventBus.getState().subscribe { event ->
+ when (event) {
+ is FormStateEvent.OnFormSaved -> {
+ if (event.formId == formId) {
+ val prefilledInstance = formInstanceInteractor.getInstanceByPath(event.instancePath)
+ if (prefilledInstance != null) {
+ formInstanceInteractor.openInstance(prefilledInstance, context)
+ }
+ else {
+ FormEventBus.formOpenFailed(formId, "Form instance cannot be found!")
+ }
+ compositeDisposable.clear()
+ }
+ }
+ is FormStateEvent.OnFormOpenFailed -> if (event.formId == formId) compositeDisposable.clear()
+ is FormStateEvent.OnFormSaveError -> if (event.formId == formId) compositeDisposable.clear()
+ else -> {}
+ }
+ })
+ prefillForm(formId, tagValueMap)
+ }
+ }
+
+ private fun downloadAndOpenForm(formId: String, context: Context) {
+ formsNetworkInteractor.downloadFormById(formId, object : FileDownloadListener {
+ override fun onComplete(downloadedFile: File) {
+ openFormWithFormId(formId, context)
+ }
+ })
+ }
+
+ private fun mediaExists(form: Form): Boolean {
+ val document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(File(form.formFilePath))
+ val values = document.getElementsByTagName("value")
+ for (index in 0 until values.length) {
+ val attributes = values.item(index).attributes
+ if (attributes.length > 0) {
+ val nodeValue = attributes.item(0).nodeValue
+ if (nodeValue == "image" || nodeValue == "audio" || nodeValue == "video") {
+ var mediaFileName = values.item(index).firstChild.nodeValue
+ if (mediaFileName.isNotBlank()) {
+ mediaFileName = mediaFileName.substring(mediaFileName.lastIndexOf("/") + 1)
+ val mediaFile = File(form.formMediaPath + "/" + mediaFileName)
+ if (!mediaFile.exists())
+ return false
+ }
+ }
+ }
+ }
+ return true
+ }
}
diff --git a/odk/extension/src/main/java/io/samagra/odk/collect/extension/handlers/ODKHandler.kt b/odk/extension/src/main/java/io/samagra/odk/collect/extension/handlers/ODKHandler.kt
index f3b9a9c31ca..e76fd7f446e 100644
--- a/odk/extension/src/main/java/io/samagra/odk/collect/extension/handlers/ODKHandler.kt
+++ b/odk/extension/src/main/java/io/samagra/odk/collect/extension/handlers/ODKHandler.kt
@@ -33,20 +33,11 @@ class ODKHandler @Inject constructor(
): ODKInteractor {
private lateinit var formsNetworkInteractor: FormsNetworkInteractor
- private lateinit var formsDatabaseInteractor: FormsDatabaseInteractor
- private lateinit var formsInteractor: FormsInteractor
- private lateinit var instancesRepository: InstancesRepository
- private lateinit var formInstanceInteractor: FormInstanceInteractor
override fun setupODK(settingsJson: String, lazyDownload: Boolean, listener: ODKProcessListener) {
try {
ConfigHandler(application).configure(settingsJson)
formsNetworkInteractor = DaggerFormsNetworkInteractorComponent.factory().create(application).getFormsNetworkInteractor()
- formsDatabaseInteractor = DaggerFormsDatabaseInteractorComponent.factory().create(application).getFormsDatabaseInteractor()
- formsInteractor = DaggerFormsInteractorComponent.factory().create(application).getFormsInteractor()
- instancesRepository = DaggerAppDependencyComponent.builder().application(application).build().instancesRepositoryProvider().get()
- formInstanceInteractor = DaggerFormInstanceInteractorComponent.factory().create(application).getFormInstanceInteractor()
-
if (!lazyDownload) {
formsNetworkInteractor.downloadRequiredForms(object: FileDownloadListener {
override fun onProgress(progress: Int) { listener.onProgress(progress) }
@@ -66,113 +57,4 @@ class ODKHandler @Inject constructor(
CoroutineScope(Job()).launch{ ConfigHandler(application).reset(listener) }
}
- override fun openForm(formId: String, context: Context) {
- CoroutineScope(Job()).launch {
- // Delete any saved instances of this form
- val savedInstances = instancesRepository.getAllByFormId(formId)
- for (instance in savedInstances) {
- if (instance.status == Instance.STATUS_INCOMPLETE) {
- instancesRepository.delete(instance.dbId)
- }
- }
- val requiredForm = formsDatabaseInteractor.getLatestFormById(formId)
- if (requiredForm == null) {
- downloadAndOpenForm(formId, context)
- }
- else {
- val xmlFile = File(requiredForm.formFilePath)
- if (xmlFile.exists() && (requiredForm.formMediaPath == null || mediaExists(requiredForm))) {
- formsInteractor.openFormWithFormId(formId, context)
- }
- else {
- requiredForm.formMediaPath?.let { File(it).deleteRecursively() }
- xmlFile.delete()
- formsDatabaseInteractor.deleteByFormId(formId)
- downloadAndOpenForm(formId, context)
- }
- }
- }
- }
-
- override fun openSavedForm(formId: String, context: Context) {
- CoroutineScope(Job()).launch {
- val compositeDisposable = CompositeDisposable()
- compositeDisposable.add(
- FormEventBus.getState()
- .subscribe { event ->
- when (event) {
- is FormStateEvent.OnFormOpenFailed -> {
- if (event.formId == formId) {
- compositeDisposable.clear()
- openForm(formId, context)
- }
- }
- is FormStateEvent.OnFormOpened -> {
- if (event.formId == formId) {
- compositeDisposable.clear()
- }
- }
- else -> {}
- }
- }
- )
-
- formInstanceInteractor.openLatestSavedInstanceWithFormId(formId, context)
- }
- }
-
- override fun prefillAndOpenForm(formId: String, tagValueMap: HashMap, context: Context) {
- CoroutineScope(Job()).launch {
- val compositeDisposable = CompositeDisposable()
- compositeDisposable.add(FormEventBus.getState().subscribe { event ->
- when (event) {
- is FormStateEvent.OnFormSaved -> {
- if (event.formId == formId) {
- val prefilledInstance = formInstanceInteractor.getInstanceByPath(event.instancePath)
- if (prefilledInstance != null) {
- formInstanceInteractor.openInstance(prefilledInstance, context)
- }
- else {
- FormEventBus.formOpenFailed(formId, "Form instance cannot be found!")
- }
- compositeDisposable.clear()
- }
- }
- is FormStateEvent.OnFormOpenFailed -> if (event.formId == formId) compositeDisposable.clear()
- is FormStateEvent.OnFormSaveError -> if (event.formId == formId) compositeDisposable.clear()
- else -> {}
- }
- })
- formsInteractor.prefillForm(formId, tagValueMap)
- }
- }
-
- private fun downloadAndOpenForm(formId: String, context: Context) {
- formsNetworkInteractor.downloadFormById(formId, object : FileDownloadListener {
- override fun onComplete(downloadedFile: File) {
- formsInteractor.openFormWithFormId(formId, context)
- }
- })
- }
-
- private fun mediaExists(form: Form): Boolean {
- val document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(File(form.formFilePath))
- val values = document.getElementsByTagName("value")
- for (index in 0 until values.length) {
- val attributes = values.item(index).attributes
- if (attributes.length > 0) {
- val nodeValue = attributes.item(0).nodeValue
- if (nodeValue == "image" || nodeValue == "audio" || nodeValue == "video") {
- var mediaFileName = values.item(index).firstChild.nodeValue
- if (mediaFileName.isNotBlank()) {
- mediaFileName = mediaFileName.substring(mediaFileName.lastIndexOf("/") + 1)
- val mediaFile = File(form.formMediaPath + "/" + mediaFileName)
- if (!mediaFile.exists())
- return false
- }
- }
- }
- }
- return true
- }
}
diff --git a/odk/extension/src/main/java/io/samagra/odk/collect/extension/interactors/FormsInteractor.kt b/odk/extension/src/main/java/io/samagra/odk/collect/extension/interactors/FormsInteractor.kt
index 7a52ca58ea7..374137d52bf 100644
--- a/odk/extension/src/main/java/io/samagra/odk/collect/extension/interactors/FormsInteractor.kt
+++ b/odk/extension/src/main/java/io/samagra/odk/collect/extension/interactors/FormsInteractor.kt
@@ -48,4 +48,15 @@ interface FormsInteractor {
* Note: This modifies the original form described by the form path.
*/
fun updateForm(formPath: String, values: HashMap,listener: FormsProcessListener?)
+
+ /** Opens the latest version related to the formId. Deletes any
+ * saved instance of a form with this particular formId. */
+ fun openForm(formId: String, context: Context)
+
+ /** Opens a saved form. If no saved instance is found, opens a new form. */
+ fun openSavedForm(formId: String, context: Context)
+
+ /** This method pre-fills a form with data from a given map of key-value pairs,
+ * and then opens the form in a given Android context. */
+ fun prefillAndOpenForm(formId: String, tagValueMap: HashMap, context: Context)
}
diff --git a/odk/extension/src/main/java/io/samagra/odk/collect/extension/interactors/ODKInteractor.kt b/odk/extension/src/main/java/io/samagra/odk/collect/extension/interactors/ODKInteractor.kt
index b05ff1a5b30..3bf950c32b6 100644
--- a/odk/extension/src/main/java/io/samagra/odk/collect/extension/interactors/ODKInteractor.kt
+++ b/odk/extension/src/main/java/io/samagra/odk/collect/extension/interactors/ODKInteractor.kt
@@ -25,12 +25,4 @@ interface ODKInteractor {
/** Resets everything in odk and deletes all data. */
fun resetODK(listener: ODKProcessListener)
- /** Opens the latest version related to the formId. Deletes any
- * saved instance of a form with this particular formId. */
- fun openForm(formId: String, context: Context)
-
- /** Opens a saved form. If no saved instance is found, opens a new form. */
- fun openSavedForm(formId: String, context: Context)
-
- fun prefillAndOpenForm(formId: String, tagValueMap: HashMap, context: Context)
}
diff --git a/odk/extension/src/main/java/io/samagra/odk/collect/extension/modules/FormsInteractorModule.kt b/odk/extension/src/main/java/io/samagra/odk/collect/extension/modules/FormsInteractorModule.kt
index 1e1ba23b779..bcfecac56fa 100644
--- a/odk/extension/src/main/java/io/samagra/odk/collect/extension/modules/FormsInteractorModule.kt
+++ b/odk/extension/src/main/java/io/samagra/odk/collect/extension/modules/FormsInteractorModule.kt
@@ -4,7 +4,9 @@ import android.app.Application
import dagger.Module
import dagger.Provides
import io.samagra.odk.collect.extension.annotations.ODKFormsInteractor
+import io.samagra.odk.collect.extension.components.DaggerFormInstanceInteractorComponent
import io.samagra.odk.collect.extension.components.DaggerFormsDatabaseInteractorComponent
+import io.samagra.odk.collect.extension.components.DaggerFormsNetworkInteractorComponent
import io.samagra.odk.collect.extension.handlers.ODKFormsHandler
import io.samagra.odk.collect.extension.interactors.FormsInteractor
import org.odk.collect.android.injection.config.DaggerAppDependencyComponent
@@ -23,6 +25,10 @@ class FormsInteractorModule {
val storagePathProvider = appDependencyComponent.storagePathProvider()
val entitiesRepository = appDependencyComponent.entitiesRepositoryProvider().get(currentProjectProvider.getCurrentProject().uuid)
val formsDatabaseInteractor = DaggerFormsDatabaseInteractorComponent.factory().create(application).getFormsDatabaseInteractor()
- return ODKFormsHandler(currentProjectProvider, formsDatabaseInteractor, storagePathProvider, mediaUtils, entitiesRepository)
+ val formsNetworkInteractor = DaggerFormsNetworkInteractorComponent.factory().create(application).getFormsNetworkInteractor()
+ val instancesRepository = DaggerAppDependencyComponent.builder().application(application).build().instancesRepositoryProvider().get()
+ val formInstanceInteractor = DaggerFormInstanceInteractorComponent.factory().create(application).getFormInstanceInteractor()
+
+ return ODKFormsHandler(currentProjectProvider, formsDatabaseInteractor, storagePathProvider, mediaUtils, entitiesRepository, formsNetworkInteractor,instancesRepository,formInstanceInteractor)
}
}
diff --git a/sample/src/main/java/io/samagra/oce_sample/ODKFeatureTesterActivity.kt b/sample/src/main/java/io/samagra/oce_sample/ODKFeatureTesterActivity.kt
index 011e494346e..7d607d35def 100644
--- a/sample/src/main/java/io/samagra/oce_sample/ODKFeatureTesterActivity.kt
+++ b/sample/src/main/java/io/samagra/oce_sample/ODKFeatureTesterActivity.kt
@@ -12,6 +12,7 @@ import android.widget.Toast
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.samagra.odk.collect.extension.interactors.FormsDatabaseInteractor
+import io.samagra.odk.collect.extension.interactors.FormsInteractor
import io.samagra.odk.collect.extension.interactors.FormsNetworkInteractor
import io.samagra.odk.collect.extension.interactors.ODKInteractor
import io.samagra.odk.collect.extension.listeners.FileDownloadListener
@@ -40,6 +41,7 @@ class ODKFeatureTesterActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var progressBar: ProgressBar
private lateinit var odkInteractor: ODKInteractor
+ private lateinit var formsInteractor: FormsInteractor
private lateinit var networkInteractor: FormsNetworkInteractor
private lateinit var formsDatabaseInteractor: FormsDatabaseInteractor
@@ -78,6 +80,7 @@ class ODKFeatureTesterActivity : AppCompatActivity(), View.OnClickListener {
currentProjectProvider.getCurrentProject().name
formsDatabaseInteractor = ODKProvider.getFormsDatabaseInteractor()
networkInteractor = ODKProvider.getFormsNetworkInteractor()
+ formsInteractor = ODKProvider.getFormsInteractor()
downloadFormsButton.isEnabled=true
downloadAllFormsButton.isEnabled=true
clearAllFormsButton.isEnabled=true
@@ -124,7 +127,7 @@ class ODKFeatureTesterActivity : AppCompatActivity(), View.OnClickListener {
val formId: String = openFormsInput.text.toString().trim()
if (formId.isNotBlank()) {
progressBar.visibility = View.VISIBLE
- odkInteractor.openForm(formId, context)
+ formsInteractor.openForm(formId, context)
}
}
R.id.download_form_button -> {
@@ -190,7 +193,7 @@ class ODKFeatureTesterActivity : AppCompatActivity(), View.OnClickListener {
val formId: String = openSavedInput.text.toString().trim()
if (formId.isNotBlank()) {
progressBar.visibility = View.VISIBLE
- odkInteractor.openSavedForm(formId, context)
+ formsInteractor.openSavedForm(formId, context)
}
}
}