Skip to content

Commit

Permalink
Added ODKEventExporter and FormEventExporter.
Browse files Browse the repository at this point in the history
Supported events:
  - OnFormOpened
  - OnFormOpenFailed
  - OnFormSaved
  - OnFormSaveError
  - OnFormUploaded
  - OnFormUploadFailed
  - OnFormDownloaded
  - OnFormDownloadFailed
  • Loading branch information
chinmoy12c committed Mar 17, 2023
1 parent ebbd63d commit ad3397f
Show file tree
Hide file tree
Showing 15 changed files with 170 additions and 54 deletions.
4 changes: 4 additions & 0 deletions notes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Points to note before release
google-services.json
odk server
additional steps required
4 changes: 2 additions & 2 deletions odk/collect/collect_app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ apply from: '../../config/quality.gradle'
apply from: '../../config/jacoco.gradle'
apply from: '../../config/dependencies.gradle'

import com.android.ddmlib.DdmPreferences

// Build numbers were manually set until 1067
def LEGACY_BUILD_NUMBER_OFFSET = 1067

Expand Down Expand Up @@ -181,6 +179,8 @@ dependencies {
implementation packages.bikram_sambat
implementation packages.rarepebble_colorpicker
implementation packages.commons_io
implementation packages.rx_java
implementation packages.rx_android
implementation(packages.opencsv) {
exclude group: 'commons-logging'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import org.odk.collect.android.audio.M4AAppender;
import org.odk.collect.android.backgroundwork.InstanceSubmitScheduler;
import org.odk.collect.android.dao.helpers.InstancesDaoHelper;
import org.odk.collect.android.events.FormEventBus;
import org.odk.collect.android.exception.JavaRosaException;
import org.odk.collect.android.external.FormsContract;
import org.odk.collect.android.external.InstancesContract;
Expand Down Expand Up @@ -703,10 +704,12 @@ private void loadFromIntent(Intent intent) {
}

formPath = candidateForms.get(0).getFormFilePath();
FormEventBus.INSTANCE.formOpened(instance.getFormId());
} else if (uriMimeType != null && uriMimeType.equals(FormsContract.CONTENT_ITEM_TYPE)) {
Form form = formsRepositoryProvider.get().get(ContentUriHelper.getIdFromUri(uri));
if (form != null) {
formPath = form.getFormFilePath();
FormEventBus.INSTANCE.formOpened(form.getFormId());
}

if (formPath == null) {
Expand Down Expand Up @@ -1823,6 +1826,11 @@ private void handleSaveResult(FormSaveViewModel.SaveResult result) {
DialogFragmentUtils.dismissDialog(SaveFormProgressDialogFragment.class, getSupportFragmentManager());
DialogFragmentUtils.dismissDialog(ChangesReasonPromptDialogFragment.class, getSupportFragmentManager());

Instance instance = new InstancesRepositoryProvider(this).get().getOneByPath(getAbsoluteInstancePath());
if (instance != null) {
FormEventBus.INSTANCE.formSaved(instance.getFormId(), instance.getInstanceFilePath());
}

showShortToast(this, R.string.data_saved_ok);

if (result.getRequest().viewExiting()) {
Expand All @@ -1848,6 +1856,11 @@ private void handleSaveResult(FormSaveViewModel.SaveResult result) {
message = getString(R.string.data_saved_error);
}

instance = new InstancesRepositoryProvider(this).get().getOneByPath(getAbsoluteInstancePath());
if (instance != null) {
FormEventBus.INSTANCE.formSaveError(instance.getFormId(), message);
}

showLongToast(this, message);
formSaveViewModel.resumeFormEntry();
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.odk.collect.android.events

/**
* Events exporter class for form events. This class contains all the
* events that occur during the lifecycle of a form.
*/
object FormEventBus: ODKEventBus<FormStateEvent>() {
//TODO: make these functions internal

fun formOpened(formId: String) {
state.onNext(FormStateEvent.OnFormOpened(formId))
}

fun formOpenFailed(formId: String, errorMessage: String) {
state.onNext(FormStateEvent.OnFormOpenFailed(formId, errorMessage))
}

fun formSaved(formId: String, instancePath: String) {
state.onNext(FormStateEvent.OnFormSaved(formId, instancePath))
}

fun formSaveError(formId: String, errorMessage: String) {
state.onNext(FormStateEvent.OnFormSaveError(formId, errorMessage))
}

fun formUploaded(formId: String, instancePath: String) {
state.onNext(FormStateEvent.OnFormUploaded(formId, instancePath))
}

fun formUploadError(formId: String, errorMessage: String) {
state.onNext(FormStateEvent.OnFormUploadFailed(formId, errorMessage))
}

fun formDownloaded(formId: String) {
state.onNext(FormStateEvent.OnFormDownloaded(formId))
}

fun formDownloadFailed(formId: String, errorMessage: String) {
state.onNext(FormStateEvent.OnFormDownloadFailed(formId, errorMessage))
}
}

sealed class FormStateEvent {
/** Called when a form is opened. */
data class OnFormOpened(val formId: String): FormStateEvent()

/** Called when an error occurs while opening a form. */
data class OnFormOpenFailed(val formId: String, val errorMessage: String): FormStateEvent()

/** Called when a form is saved. */
data class OnFormSaved(val formId: String, val instancePath: String): FormStateEvent()

/** Called when a form save process errors out. */
data class OnFormSaveError(val formId: String, val errorMessage: String): FormStateEvent()

/** Called when a form upload is successful. */
data class OnFormUploaded(val formId: String, val instancePath: String): FormStateEvent()

/** Called when a form upload fails. */
data class OnFormUploadFailed(val formId: String, val errorMessage: String): FormStateEvent()

/** Called when a form is successfully downloaded. */
data class OnFormDownloaded(val formId: String): FormStateEvent()

/** Called when a form download fails. */
data class OnFormDownloadFailed(val formId: String, val errorMessage: String): FormStateEvent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.odk.collect.android.events

import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.PublishSubject

/**
* This is the base class for all the event exporters. Extend this class
* to create more concrete event exporters, for example form events.
*/
open class ODKEventBus<T : Any> {

protected val state: PublishSubject<T> = PublishSubject.create()

fun getState(): Observable<T> {
return state.hide()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.odk.collect.android.instancemanagement.autosend
import android.content.Context
import org.odk.collect.analytics.Analytics
import org.odk.collect.android.R
import org.odk.collect.android.events.FormEventBus
import org.odk.collect.android.formmanagement.InstancesAppState
import org.odk.collect.android.gdrive.GoogleAccountsManager
import org.odk.collect.android.gdrive.GoogleApiProvider
Expand Down Expand Up @@ -43,6 +44,14 @@ class InstanceAutoSender(

try {
val result: Map<Instance, FormUploadException?> = instanceSubmitter.submitInstances(toUpload)
result.entries.stream().forEach { entry ->
if (entry.value == null) {
FormEventBus.formUploaded(entry.key.formId, entry.key.instanceFilePath)
}
else {
FormEventBus.formUploadError(entry.key.formId, entry.value!!.message)
}
}
notifier.onSubmission(result, projectDependencyProvider.projectId)
} catch (e: SubmitException) {
when (e.type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import androidx.core.util.Pair;

import org.odk.collect.android.events.FormEventBus;
import org.odk.collect.android.formmanagement.ServerFormDetails;
import org.odk.collect.android.formmanagement.ServerFormsDetailsFetcher;
import org.odk.collect.android.listeners.FormListDownloaderListener;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import org.odk.collect.android.R;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.events.FormEventBus;
import org.odk.collect.android.formmanagement.FormDownloadException;
import org.odk.collect.android.formmanagement.FormDownloader;
import org.odk.collect.android.formmanagement.ServerFormDetails;
Expand Down Expand Up @@ -70,10 +71,12 @@ protected Map<ServerFormDetails, FormDownloadException> doInBackground(ArrayList
}, this::isCancelled);

results.put(serverFormDetails, null);
FormEventBus.INSTANCE.formDownloaded(serverFormDetails.getFormId());
} catch (FormDownloadException.DownloadingInterrupted e) {
return emptyMap();
} catch (FormDownloadException e) {
results.put(serverFormDetails, e);
FormEventBus.INSTANCE.formDownloadFailed(serverFormDetails.getFormId(), e.getMessage());
}

index++;
Expand Down
2 changes: 2 additions & 0 deletions odk/config/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,7 @@ ext {
robolectric : "org.robolectric:robolectric:${versions.robolectric}",
robolectric_shadows_multidex : "org.robolectric:shadows-multidex:${versions.robolectric}",
uiautomator : "androidx.test.uiautomator:uiautomator:2.2.0",
rx_java : "io.reactivex.rxjava3:rxjava:3.1.5",
rx_android :"io.reactivex.rxjava3:rxandroid:3.0.0"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.samagra.odk.collect.extension.utilities.FormsDownloadUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.odk.collect.android.events.FormEventBus
import org.odk.collect.android.formmanagement.FormDownloadException
import org.odk.collect.android.formmanagement.ServerFormDetails
import org.odk.collect.android.listeners.DownloadFormsTaskListener
Expand Down Expand Up @@ -128,6 +129,7 @@ class FormsNetworkHandler @Inject constructor(
formsDownloadUtil.downloadFormsList { formList, exception ->
if (exception != null || formList == null) {
listener.onCancelled(exception)
FormEventBus.formDownloadFailed(formId, exception.message ?: "Form list download failed!")
return@downloadFormsList
}
val requiredForms = ArrayList(formList.filter { form -> form.value.formId == formId }.values)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ 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
import io.samagra.odk.collect.extension.listeners.FormsProcessListener
import io.samagra.odk.collect.extension.listeners.ODKProcessListener
import io.samagra.odk.collect.extension.utilities.ConfigHandler
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -65,7 +64,7 @@ class ODKHandler @Inject constructor(
CoroutineScope(Job()).launch{ ConfigHandler(application).reset(listener) }
}

override fun openForm(formId: String, context: Context, listener: FormsProcessListener) {
override fun openForm(formId: String, context: Context) {
CoroutineScope(Job()).launch {
// Delete any saved instances of this form
val savedInstances = instancesRepository.getAllByFormId(formId)
Expand All @@ -76,25 +75,24 @@ class ODKHandler @Inject constructor(
}
val requiredForm = formsDatabaseInteractor.getLatestFormById(formId)
if (requiredForm == null) {
downloadAndOpenForm(formId, context, listener)
downloadAndOpenForm(formId, context)
}
else {
val xmlFile = File(requiredForm.formFilePath)
if (xmlFile.exists() && (requiredForm.formMediaPath == null || mediaExists(requiredForm))) {
listener.onProcessed()
formsInteractor.openFormWithFormId(formId, context)
}
else {
requiredForm.formMediaPath?.let { File(it).deleteRecursively() }
xmlFile.delete()
formsDatabaseInteractor.deleteByFormId(formId)
downloadAndOpenForm(formId, context, listener)
downloadAndOpenForm(formId, context)
}
}
}
}

override fun openSavedForm(formId: String, context: Context, listener: FormsProcessListener) {
override fun openSavedForm(formId: String, context: Context) {
CoroutineScope(Job()).launch {
val formInstances = instancesRepository.getAllByFormId(formId)
var savedInstance: Instance? = null
Expand All @@ -104,7 +102,7 @@ class ODKHandler @Inject constructor(
}
}
if (savedInstance == null) {
openForm(formId, context, listener)
openForm(formId, context)
}
else {
val currentProjectProvider = DaggerAppDependencyComponent.builder().application(application).build().currentProjectProvider()
Expand All @@ -115,21 +113,15 @@ class ODKHandler @Inject constructor(
intent.putExtra(ApplicationConstants.BundleKeys.FORM_MODE, ApplicationConstants.FormModes.EDIT_SAVED)
intent.putExtra(FormHierarchyActivity.EXTRA_JUMP_TO_BEGINNING, true)
context.startActivity(intent)
listener.onProcessed()
}
}
}

private fun downloadAndOpenForm(formId: String, context: Context, listener: FormsProcessListener) {
private fun downloadAndOpenForm(formId: String, context: Context) {
formsNetworkInteractor.downloadFormById(formId, object : FileDownloadListener {
override fun onProgress(progress: Int) {}
override fun onComplete(downloadedFile: File) {
listener.onProcessed()
formsInteractor.openFormWithFormId(formId, context)
}
override fun onCancelled(exception: Exception) {
listener.onProcessingError(exception)
}
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ interface ODKInteractor {

/** 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, listener: FormsProcessListener)
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, listener: FormsProcessListener)
fun openSavedForm(formId: String, context: Context)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import java.io.File
interface FileDownloadListener {

/** Called when there is update in download progress. */
fun onProgress(progress: Int)
fun onProgress(progress: Int) {}

/** Called when the download is complete.
* Takes the downloaded file as an argument. */
fun onComplete(downloadedFile: File)
fun onComplete(downloadedFile: File) {}

/** Called if the download is cancelled due to any error.
* Takes the exception occurred as an argument. */
fun onCancelled(exception: Exception)
fun onCancelled(exception: Exception) {}
}
2 changes: 2 additions & 0 deletions sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ dependencies {
androidTestImplementation Dependencies.androidx_work_testing
androidTestImplementation Dependencies.uiautomator
implementation project(':odk:extension')
implementation "io.reactivex.rxjava3:rxjava:3.1.5"
implementation "io.reactivex.rxjava3:rxandroid:3.0.0"

// Real LeakCanary for debug builds only: notifications, analysis, etc
// debugImplementation Dependencies.leakcanary
Expand Down
Loading

0 comments on commit ad3397f

Please sign in to comment.