diff --git a/buildSrc/src/main/java/dependencies/Dependencies.kt b/buildSrc/src/main/java/dependencies/Dependencies.kt
index 5def5c71b8b..680b88b2a29 100644
--- a/buildSrc/src/main/java/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/java/dependencies/Dependencies.kt
@@ -43,7 +43,7 @@ object Dependencies {
const val rarepebble_colorpicker = "com.github.martin-stone:hsv-alpha-color-picker-android:3.0.1"
const val commons_io = "commons-io:commons-io:2.5" // Commons 2.6+ introduce java.nio usage that we can't access until our minSdkVersion >= 26 (https://developer.android.com/reference/java/io/File#toPath())
const val opencsv = "com.opencsv:opencsv:5.7.1"
- const val javarosa = "org.getodk:javarosa:4.1.0"
+ const val javarosa = "org.getodk:javarosa:4.2.0-SNAPSHOT"
const val javarosa_local = "org.getodk:javarosa:local"
const val karumi_dexter = "com.karumi:dexter:6.2.3"
const val zxing_android_embedded = "com.journeyapps:zxing-android-embedded:4.3.0"
diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormNavigationTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormNavigationTest.java
index e0253031479..1ddcdf91dd8 100644
--- a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormNavigationTest.java
+++ b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormNavigationTest.java
@@ -16,11 +16,15 @@
package org.odk.collect.android.instrumented.forms;
+import static junit.framework.Assert.assertEquals;
+
import android.app.Application;
import androidx.test.core.app.ApplicationProvider;
import org.javarosa.core.model.FormDef;
+import org.javarosa.form.api.FormEntryController;
+import org.javarosa.form.api.FormEntryModel;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
@@ -46,8 +50,6 @@
import timber.log.Timber;
-import static junit.framework.Assert.assertEquals;
-
/**
* This test has been created in order to check indices while navigating through a form.
* It's especially important while navigating through a form that contains nested groups and if we
@@ -59,6 +61,13 @@
@RunWith(Parameterized.class)
public class FormNavigationTest {
+ private final FormLoaderTask.FormEntryControllerFactory formEntryControllerFactory = new FormLoaderTask.FormEntryControllerFactory() {
+ @Override
+ public FormEntryController create(FormDef formDef) {
+ return new FormEntryController(new FormEntryModel(formDef));
+ }
+ };
+
@Rule
public RuleChain copyFormChain = TestRuleChain.chain()
.around(new RunnableRule(() -> {
@@ -120,7 +129,7 @@ private void testIndices(String formName, String[] expectedIndices) throws Execu
Timber.i(e);
}
- FormLoaderTask formLoaderTask = new FormLoaderTask(formPath(formName), null, null);
+ FormLoaderTask formLoaderTask = new FormLoaderTask(formPath(formName), null, null, formEntryControllerFactory);
formLoaderTask.setFormLoaderListener(new FormLoaderListener() {
@Override
public void loadingComplete(FormLoaderTask task, FormDef fd, String warningMsg) {
diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormUtilsTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormUtilsTest.java
index 69d4457c333..dc29b71aee0 100644
--- a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormUtilsTest.java
+++ b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormUtilsTest.java
@@ -1,18 +1,21 @@
package org.odk.collect.android.instrumented.forms;
+import org.javarosa.core.model.FormDef;
import org.javarosa.core.reference.RootTranslator;
+import org.javarosa.form.api.FormEntryController;
+import org.javarosa.form.api.FormEntryModel;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
-import org.odk.collect.android.support.rules.CollectTestRule;
-import org.odk.collect.android.support.rules.TestRuleChain;
-import org.odk.collect.android.utilities.FormUtils;
import org.odk.collect.android.storage.StoragePathProvider;
import org.odk.collect.android.storage.StorageSubdirectory;
+import org.odk.collect.android.support.rules.CollectTestRule;
+import org.odk.collect.android.support.rules.TestRuleChain;
import org.odk.collect.android.tasks.FormLoaderTask;
import org.odk.collect.android.utilities.FileUtils;
+import org.odk.collect.android.utilities.FormUtils;
import java.io.File;
import java.util.List;
@@ -21,6 +24,13 @@ public class FormUtilsTest {
private static final String BASIC_FORM = "basic.xml";
private final CollectTestRule rule = new CollectTestRule();
+ private final FormLoaderTask.FormEntryControllerFactory formEntryControllerFactory = new FormLoaderTask.FormEntryControllerFactory() {
+ @Override
+ public FormEntryController create(FormDef formDef) {
+ return new FormEntryController(new FormEntryModel(formDef));
+ }
+ };
+
@Rule
public RuleChain copyFormChain = TestRuleChain.chain()
.around(rule);
@@ -38,7 +48,7 @@ public void setUp() {
public void sessionRootTranslatorOrderDoesNotMatter() throws Exception {
final String formPath = new StoragePathProvider().getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + BASIC_FORM;
// Load the form in order to populate the ReferenceManager
- FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null);
+ FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory);
formLoaderTask.execute(formPath).get();
final File formXml = new File(formPath);
diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/tasks/FormLoaderTaskTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/tasks/FormLoaderTaskTest.java
index 928c10b4bb2..af2476aef5b 100644
--- a/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/tasks/FormLoaderTaskTest.java
+++ b/collect_app/src/androidTest/java/org/odk/collect/android/instrumented/tasks/FormLoaderTaskTest.java
@@ -1,5 +1,15 @@
package org.odk.collect.android.instrumented.tasks;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.notNullValue;
+
+import android.app.Application;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.javarosa.core.model.FormDef;
+import org.javarosa.form.api.FormEntryController;
+import org.javarosa.form.api.FormEntryModel;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
@@ -12,6 +22,7 @@
import org.odk.collect.android.support.rules.RunnableRule;
import org.odk.collect.android.support.rules.TestRuleChain;
import org.odk.collect.android.tasks.FormLoaderTask;
+import org.odk.collect.android.tasks.FormLoaderTask.FormEntryControllerFactory;
import org.odk.collect.projects.Project;
import java.io.File;
@@ -19,13 +30,6 @@
import java.util.Arrays;
import java.util.Collections;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.notNullValue;
-
-import android.app.Application;
-
-import androidx.test.core.app.ApplicationProvider;
-
public class FormLoaderTaskTest {
private final StoragePathProvider storagePathProvider = new StoragePathProvider();
@@ -35,6 +39,13 @@ public class FormLoaderTaskTest {
private static final String SIMPLE_SEARCH_EXTERNAL_CSV_FILE = "simple-search-external-csv-fruits.csv";
private static final String SIMPLE_SEARCH_EXTERNAL_DB_FILE = "simple-search-external-csv-fruits.db";
+ private final FormEntryControllerFactory formEntryControllerFactory = new FormEntryControllerFactory() {
+ @Override
+ public FormEntryController create(FormDef formDef) {
+ return new FormEntryController(new FormEntryModel(formDef));
+ }
+ };
+
@Rule
public RuleChain copyFormChain = TestRuleChain.chain()
.around(new RunnableRule(() -> {
@@ -55,7 +66,7 @@ public class FormLoaderTaskTest {
@Test
public void loadFormWithSecondaryCSV() throws Exception {
final String formPath = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + SECONDARY_INSTANCE_EXTERNAL_CSV_FORM;
- FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null);
+ FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory);
FormLoaderTask.FECWrapper wrapper = formLoaderTask.execute(formPath).get();
Assert.assertNotNull(wrapper);
}
@@ -64,7 +75,7 @@ public void loadFormWithSecondaryCSV() throws Exception {
@Test
public void loadSearchFromExternalCSV() throws Exception {
final String formPath = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + SIMPLE_SEARCH_EXTERNAL_CSV_FORM;
- FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null);
+ FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory);
FormLoaderTask.FECWrapper wrapper = formLoaderTask.execute(formPath).get();
assertThat(wrapper, notNullValue());
}
@@ -72,7 +83,7 @@ public void loadSearchFromExternalCSV() throws Exception {
@Test
public void loadSearchFromexternalCsvLeavesFileUnchanged() throws Exception {
final String formPath = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + SIMPLE_SEARCH_EXTERNAL_CSV_FORM;
- FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null);
+ FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory);
FormLoaderTask.FECWrapper wrapper = formLoaderTask.execute(formPath).get();
Assert.assertNotNull(wrapper);
Assert.assertNotNull(wrapper.getController());
@@ -87,7 +98,7 @@ public void loadSearchFromexternalCsvLeavesFileUnchanged() throws Exception {
public void loadSearchFromExternalCSVmultipleTimes() throws Exception {
final String formPath = storagePathProvider.getOdkDirPath(StorageSubdirectory.FORMS) + File.separator + SIMPLE_SEARCH_EXTERNAL_CSV_FORM;
// initial load with side effects
- FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null);
+ FormLoaderTask formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory);
FormLoaderTask.FECWrapper wrapper = formLoaderTask.execute(formPath).get();
Assert.assertNotNull(wrapper);
Assert.assertNotNull(wrapper.getController());
@@ -98,7 +109,7 @@ public void loadSearchFromExternalCSVmultipleTimes() throws Exception {
long dbLastModified = dbFile.lastModified();
// subsequent load should succeed despite side effects from import
- formLoaderTask = new FormLoaderTask(formPath, null, null);
+ formLoaderTask = new FormLoaderTask(formPath, null, null, formEntryControllerFactory);
wrapper = formLoaderTask.execute(formPath).get();
Assert.assertNotNull(wrapper);
Assert.assertNotNull(wrapper.getController());
diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/FormEntryActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/FormEntryActivity.java
index 97027c0fb87..e1f7ce55101 100644
--- a/collect_app/src/main/java/org/odk/collect/android/activities/FormEntryActivity.java
+++ b/collect_app/src/main/java/org/odk/collect/android/activities/FormEntryActivity.java
@@ -359,6 +359,9 @@ enum AnimationType {
@Inject
public AudioHelperFactory audioHelperFactory;
+ @Inject
+ public FormLoaderTask.FormEntryControllerFactory formEntryControllerFactory;
+
private final LocationProvidersReceiver locationProvidersReceiver = new LocationProvidersReceiver();
private SwipeHandler swipeHandler;
@@ -634,7 +637,7 @@ private void loadForm() {
formEntryViewModel.refresh();
} else {
Timber.w("Reloading form and restoring state.");
- formLoaderTask = new FormLoaderTask(instancePath, startingXPath, waitingXPath);
+ formLoaderTask = new FormLoaderTask(instancePath, startingXPath, waitingXPath, formEntryControllerFactory);
showIfNotShowing(FormLoadingDialogFragment.class, getSupportFragmentManager());
formLoaderTask.execute(formPath);
}
@@ -713,7 +716,7 @@ private void loadFromIntent(Intent intent) {
return;
}
- formLoaderTask = new FormLoaderTask(instancePath, null, null);
+ formLoaderTask = new FormLoaderTask(instancePath, null, null, formEntryControllerFactory);
formLoaderTask.setFormLoaderListener(this);
showIfNotShowing(FormLoadingDialogFragment.class, getSupportFragmentManager());
formLoaderTask.execute(formPath);
diff --git a/collect_app/src/main/java/org/odk/collect/android/formmanagement/CollectFormEntryControllerFactory.kt b/collect_app/src/main/java/org/odk/collect/android/formmanagement/CollectFormEntryControllerFactory.kt
new file mode 100644
index 00000000000..eb31fa179f3
--- /dev/null
+++ b/collect_app/src/main/java/org/odk/collect/android/formmanagement/CollectFormEntryControllerFactory.kt
@@ -0,0 +1,22 @@
+package org.odk.collect.android.formmanagement
+
+import org.javarosa.core.model.FormDef
+import org.javarosa.entities.EntityFormFinalizationProcessor
+import org.javarosa.form.api.FormEntryController
+import org.javarosa.form.api.FormEntryModel
+import org.odk.collect.android.tasks.FormLoaderTask.FormEntryControllerFactory
+import org.odk.collect.settings.keys.ProjectKeys
+import org.odk.collect.shared.settings.Settings
+
+class CollectFormEntryControllerFactory constructor(private val settings: Settings) :
+ FormEntryControllerFactory {
+ override fun create(formDef: FormDef): FormEntryController {
+ return FormEntryController(FormEntryModel(formDef)).also {
+ it.addPostProcessor(EntityFormFinalizationProcessor())
+
+ if (!settings.getBoolean(ProjectKeys.KEY_PREDICATE_CACHING)) {
+ it.disablePredicateCaching()
+ }
+ }
+ }
+}
diff --git a/collect_app/src/main/java/org/odk/collect/android/injection/config/AppDependencyModule.java b/collect_app/src/main/java/org/odk/collect/android/injection/config/AppDependencyModule.java
index 03c9eb354eb..bd488b36675 100644
--- a/collect_app/src/main/java/org/odk/collect/android/injection/config/AppDependencyModule.java
+++ b/collect_app/src/main/java/org/odk/collect/android/injection/config/AppDependencyModule.java
@@ -50,6 +50,7 @@
import org.odk.collect.android.formentry.media.AudioHelperFactory;
import org.odk.collect.android.formentry.media.ScreenContextAudioHelperFactory;
import org.odk.collect.android.formlists.blankformlist.BlankFormListViewModel;
+import org.odk.collect.android.formmanagement.CollectFormEntryControllerFactory;
import org.odk.collect.android.formmanagement.FormDownloader;
import org.odk.collect.android.formmanagement.FormMetadataParser;
import org.odk.collect.android.formmanagement.FormSourceProvider;
@@ -84,6 +85,7 @@
import org.odk.collect.android.projects.ProjectDependencyProviderFactory;
import org.odk.collect.android.storage.StoragePathProvider;
import org.odk.collect.android.storage.StorageSubdirectory;
+import org.odk.collect.android.tasks.FormLoaderTask;
import org.odk.collect.android.utilities.AdminPasswordProvider;
import org.odk.collect.android.utilities.AndroidUserAgent;
import org.odk.collect.android.utilities.ChangeLockProvider;
@@ -633,4 +635,9 @@ public BlankFormListViewModel.Factory providesBlankFormListViewModel(FormsReposi
public ImageCompressionController providesImageCompressorManager() {
return new ImageCompressionController(ImageCompressor.INSTANCE);
}
+
+ @Provides
+ public FormLoaderTask.FormEntryControllerFactory formEntryControllerFactory(SettingsProvider settingsProvider) {
+ return new CollectFormEntryControllerFactory(settingsProvider.getUnprotectedSettings());
+ }
}
diff --git a/collect_app/src/main/java/org/odk/collect/android/preferences/Defaults.kt b/collect_app/src/main/java/org/odk/collect/android/preferences/Defaults.kt
index c29f98c33b7..cf835caed05 100644
--- a/collect_app/src/main/java/org/odk/collect/android/preferences/Defaults.kt
+++ b/collect_app/src/main/java/org/odk/collect/android/preferences/Defaults.kt
@@ -55,6 +55,8 @@ object Defaults {
hashMap[ProjectKeys.KEY_USGS_MAP_STYLE] = "topographic"
hashMap[ProjectKeys.KEY_GOOGLE_MAP_STYLE] = GoogleMap.MAP_TYPE_NORMAL.toString()
hashMap[ProjectKeys.KEY_MAPBOX_MAP_STYLE] = "mapbox://styles/mapbox/streets-v11"
+ // experimental_preferences.xml
+ hashMap[ProjectKeys.KEY_PREDICATE_CACHING] = false
return hashMap
}
diff --git a/collect_app/src/main/java/org/odk/collect/android/tasks/FormLoaderTask.java b/collect_app/src/main/java/org/odk/collect/android/tasks/FormLoaderTask.java
index 6f7f565dc9a..ebe38699699 100644
--- a/collect_app/src/main/java/org/odk/collect/android/tasks/FormLoaderTask.java
+++ b/collect_app/src/main/java/org/odk/collect/android/tasks/FormLoaderTask.java
@@ -22,6 +22,8 @@
import android.database.SQLException;
import android.os.AsyncTask;
+import androidx.annotation.NonNull;
+
import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvValidationException;
@@ -32,9 +34,7 @@
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.model.instance.utils.DefaultAnswerResolver;
import org.javarosa.core.reference.ReferenceManager;
-import org.javarosa.entities.EntityFormFinalizationProcessor;
import org.javarosa.form.api.FormEntryController;
-import org.javarosa.form.api.FormEntryModel;
import org.javarosa.xform.parse.XFormParser;
import org.javarosa.xform.util.XFormUtils;
import org.javarosa.xpath.XPathTypeMismatchException;
@@ -81,6 +81,7 @@ public class FormLoaderTask extends AsyncTask
+
+
Crash app
Force an uncaught exception causing the app to crash
+ Predicate caching