From 30d826117cc63fb2ab14a50e02be8986158ec24e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne=20Martin?= Date: Thu, 4 Feb 2021 17:10:46 -0800 Subject: [PATCH] Add support for client action listeners --- .../javarosa/core/model/CoreModelModule.java | 3 +- .../javarosa/core/model/actions/Actions.java | 28 +++++++++++++++++++ .../recordaudio/RecordAudioAction.java | 6 ++++ .../recordaudio/XFormsActionListener.java | 7 +++++ .../CapturingXFormsActionListener.java | 23 +++++++++++++++ .../model/actions/RecordAudioActionTest.java | 28 +++++++++++++++++++ 6 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/javarosa/core/model/actions/recordaudio/XFormsActionListener.java create mode 100644 src/test/java/org/javarosa/core/model/actions/CapturingXFormsActionListener.java diff --git a/src/main/java/org/javarosa/core/model/CoreModelModule.java b/src/main/java/org/javarosa/core/model/CoreModelModule.java index 02829d033..bbc9526b0 100644 --- a/src/main/java/org/javarosa/core/model/CoreModelModule.java +++ b/src/main/java/org/javarosa/core/model/CoreModelModule.java @@ -51,7 +51,8 @@ public class CoreModelModule implements IModule { "org.javarosa.core.model.data.UncastData", "org.javarosa.core.model.data.helper.BasicDataPointer", "org.javarosa.core.model.actions.SetValueAction", - "org.javarosa.core.model.actions.setgeopoint.StubSetGeopointAction" + "org.javarosa.core.model.actions.setgeopoint.StubSetGeopointAction", + "org.javarosa.core.model.actions.recordaudio.RecordAudioAction" }; @Override diff --git a/src/main/java/org/javarosa/core/model/actions/Actions.java b/src/main/java/org/javarosa/core/model/actions/Actions.java index b88efe641..381bfb2b1 100644 --- a/src/main/java/org/javarosa/core/model/actions/Actions.java +++ b/src/main/java/org/javarosa/core/model/actions/Actions.java @@ -1,6 +1,10 @@ package org.javarosa.core.model.actions; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.javarosa.core.model.actions.recordaudio.RecordAudioActionHandler; +import org.javarosa.core.model.actions.recordaudio.XFormsActionListener; public class Actions { private Actions() { } @@ -41,6 +45,12 @@ private Actions() { } private static final String[] TOP_LEVEL_EVENTS = new String[]{EVENT_ODK_INSTANCE_FIRST_LOAD, EVENT_ODK_INSTANCE_LOAD, EVENT_XFORMS_READY, EVENT_XFORMS_REVALIDATE}; + /** + * Global registry of client classes that want to get updates about triggered actions. Addresses the need for some + * actions to be handled entirely client-side. + */ + private static Map actionListeners = new HashMap<>(); + public static boolean isValidEvent(String actionEventAttribute) { return Arrays.asList(ALL_EVENTS).contains(actionEventAttribute); } @@ -48,4 +58,22 @@ public static boolean isValidEvent(String actionEventAttribute) { public static boolean isTopLevelEvent(String actionEventAttribute) { return Arrays.asList(TOP_LEVEL_EVENTS).contains(actionEventAttribute); } + + + public static void registerActionListener(String actionName, XFormsActionListener listener) { + if (!actionName.equals(RecordAudioActionHandler.ELEMENT_NAME)){ + throw new IllegalArgumentException("Currently, only the recordaudio action notifies listeners"); + } + + actionListeners.put(actionName, listener); + } + + public static void unregisterActionListener(String actionName) { + actionListeners.remove(actionName); + } + + public static XFormsActionListener getActionListener(String actionName) { + return actionListeners.get(actionName); + } + } diff --git a/src/main/java/org/javarosa/core/model/actions/recordaudio/RecordAudioAction.java b/src/main/java/org/javarosa/core/model/actions/recordaudio/RecordAudioAction.java index 1a8d3c624..a1368cdc4 100644 --- a/src/main/java/org/javarosa/core/model/actions/recordaudio/RecordAudioAction.java +++ b/src/main/java/org/javarosa/core/model/actions/recordaudio/RecordAudioAction.java @@ -2,12 +2,14 @@ import org.javarosa.core.model.FormDef; import org.javarosa.core.model.actions.Action; +import org.javarosa.core.model.actions.Actions; import org.javarosa.core.model.instance.TreeReference; public class RecordAudioAction extends Action { private TreeReference targetReference; public RecordAudioAction(TreeReference targetReference) { + super(RecordAudioActionHandler.ELEMENT_NAME); this.targetReference = targetReference; } @@ -20,6 +22,10 @@ public TreeReference processAction(FormDef model, TreeReference contextRef) { TreeReference contextualizedTargetReference = contextRef == null ? this.targetReference : this.targetReference.contextualize(contextRef); + if (Actions.getActionListener(getName()) != null) { + Actions.getActionListener(getName()).actionTriggered(getName(), contextualizedTargetReference); + } + return contextualizedTargetReference; } } diff --git a/src/main/java/org/javarosa/core/model/actions/recordaudio/XFormsActionListener.java b/src/main/java/org/javarosa/core/model/actions/recordaudio/XFormsActionListener.java new file mode 100644 index 000000000..d302e787b --- /dev/null +++ b/src/main/java/org/javarosa/core/model/actions/recordaudio/XFormsActionListener.java @@ -0,0 +1,7 @@ +package org.javarosa.core.model.actions.recordaudio; + +import org.javarosa.core.model.instance.TreeReference; + +public interface XFormsActionListener { + void actionTriggered(String actionName, TreeReference absoluteTargetRef); +} \ No newline at end of file diff --git a/src/test/java/org/javarosa/core/model/actions/CapturingXFormsActionListener.java b/src/test/java/org/javarosa/core/model/actions/CapturingXFormsActionListener.java new file mode 100644 index 000000000..ff6b80427 --- /dev/null +++ b/src/test/java/org/javarosa/core/model/actions/CapturingXFormsActionListener.java @@ -0,0 +1,23 @@ +package org.javarosa.core.model.actions; + +import org.javarosa.core.model.actions.recordaudio.XFormsActionListener; +import org.javarosa.core.model.instance.TreeReference; + +public class CapturingXFormsActionListener implements XFormsActionListener { + public String actionName; + public TreeReference absoluteTargetRef; + + @Override + public void actionTriggered(String actionName, TreeReference absoluteTargetRef) { + this.actionName = actionName; + this.absoluteTargetRef = absoluteTargetRef; + } + + public String getActionName() { + return actionName; + } + + public TreeReference getAbsoluteTargetRef() { + return absoluteTargetRef; + } +} diff --git a/src/test/java/org/javarosa/core/model/actions/RecordAudioActionTest.java b/src/test/java/org/javarosa/core/model/actions/RecordAudioActionTest.java index c9254cd77..fb7169817 100644 --- a/src/test/java/org/javarosa/core/model/actions/RecordAudioActionTest.java +++ b/src/test/java/org/javarosa/core/model/actions/RecordAudioActionTest.java @@ -2,6 +2,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.javarosa.core.test.Scenario.getRef; import static org.javarosa.core.util.XFormsElement.body; import static org.javarosa.core.util.XFormsElement.head; import static org.javarosa.core.util.XFormsElement.html; @@ -12,6 +13,7 @@ import static org.javarosa.core.util.XFormsElement.title; import java.io.IOException; +import org.javarosa.core.model.actions.recordaudio.RecordAudioActionHandler; import org.javarosa.core.test.Scenario; import org.junit.Test; @@ -35,4 +37,30 @@ public void recordAudioAction_isProcessedOnFormParse() throws IOException { assertThat(scenario.getFormDef().hasAction("recordaudio"), is(true)); } + + @Test + public void recordAudioAction_callsListenerActionTriggeredWhenTriggered() throws IOException { + CapturingXFormsActionListener listener = new CapturingXFormsActionListener(); + Actions.registerActionListener(RecordAudioActionHandler.ELEMENT_NAME, listener); + + Scenario.init("Record audio form", html( + head( + title("Record audio form"), + model( + mainInstance( + t("data id=\"record-audio-form\"", + t("recording"), + t("q1") + )), + t("odk:recordaudio event=\"odk-instance-load\" ref=\"/data/recording\""))), + body( + input("/data/q1") + ) + )); + + assertThat(listener.getActionName(), is(RecordAudioActionHandler.ELEMENT_NAME)); + assertThat(listener.getAbsoluteTargetRef(), is(getRef("/data/recording"))); + + Actions.unregisterActionListener(RecordAudioActionHandler.ELEMENT_NAME); + } }