From 1a4f34dad7e1f98cf5fa0bcdadddbf34eff43d19 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Wed, 5 Apr 2023 15:27:11 +0200 Subject: [PATCH 1/4] Add experimental setting for predicate caching --- .../main/java/dependencies/Dependencies.kt | 2 +- .../forms/FormNavigationTest.java | 15 ++++++-- .../instrumented/forms/FormUtilsTest.java | 18 +++++++--- .../tasks/FormLoaderTaskTest.java | 35 ++++++++++++------- .../android/activities/FormEntryActivity.java | 7 ++-- .../CollectFormEntryControllerFactory.kt | 21 +++++++++++ .../injection/config/AppDependencyModule.java | 7 ++++ .../collect/android/preferences/Defaults.kt | 2 ++ .../collect/android/tasks/FormLoaderTask.java | 14 ++++---- .../main/res/xml/experimental_preferences.xml | 4 +++ 10 files changed, 97 insertions(+), 28 deletions(-) create mode 100644 collect_app/src/main/java/org/odk/collect/android/formmanagement/CollectFormEntryControllerFactory.kt 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..385df569143 --- /dev/null +++ b/collect_app/src/main/java/org/odk/collect/android/formmanagement/CollectFormEntryControllerFactory.kt @@ -0,0 +1,21 @@ +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.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("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..dfbb9238e10 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["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..64a763e4898 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 @@ -32,9 +32,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 +79,7 @@ public class FormLoaderTask extends AsyncTask + + Date: Wed, 5 Apr 2023 15:29:32 +0200 Subject: [PATCH 2/4] Pull out constant for key --- .../formmanagement/CollectFormEntryControllerFactory.kt | 3 ++- .../main/java/org/odk/collect/android/preferences/Defaults.kt | 2 +- .../src/main/java/org/odk/collect/settings/keys/ProjectKeys.kt | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) 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 index 385df569143..93bcdcfb209 100644 --- 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 @@ -5,6 +5,7 @@ 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) : @@ -13,7 +14,7 @@ class CollectFormEntryControllerFactory constructor(private val settings: Settin return FormEntryController(FormEntryModel(formDef)).also { it.addPostProcessor(EntityFormFinalizationProcessor()) - if (!settings.getBoolean("predicate_caching")) { + if (!settings.getBoolean(ProjectKeys.KEY_PREDICATE_CACHING)) { it.disablePredicateCaching() } } 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 dfbb9238e10..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 @@ -56,7 +56,7 @@ object Defaults { 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["predicate_caching"] = false + hashMap[ProjectKeys.KEY_PREDICATE_CACHING] = false return hashMap } diff --git a/settings/src/main/java/org/odk/collect/settings/keys/ProjectKeys.kt b/settings/src/main/java/org/odk/collect/settings/keys/ProjectKeys.kt index d0635a2733b..4fe5995c3ba 100644 --- a/settings/src/main/java/org/odk/collect/settings/keys/ProjectKeys.kt +++ b/settings/src/main/java/org/odk/collect/settings/keys/ProjectKeys.kt @@ -55,6 +55,9 @@ object ProjectKeys { const val KEY_BACKGROUND_LOCATION = "background_location" const val KEY_BACKGROUND_RECORDING = "background_recording" + // experimental_preferences.xml + const val KEY_PREDICATE_CACHING = "predicate_caching" + // values const val PROTOCOL_SERVER = "odk_default" const val PROTOCOL_GOOGLE_SHEETS = "google_sheets" From 30eb278cf3edd377d4b07dea3ec8d609fd5d5ed2 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Wed, 5 Apr 2023 15:33:46 +0200 Subject: [PATCH 3/4] Make parameter no null --- .../formmanagement/CollectFormEntryControllerFactory.kt | 2 +- .../java/org/odk/collect/android/tasks/FormLoaderTask.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) 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 index 93bcdcfb209..eb31fa179f3 100644 --- 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 @@ -10,7 +10,7 @@ import org.odk.collect.shared.settings.Settings class CollectFormEntryControllerFactory constructor(private val settings: Settings) : FormEntryControllerFactory { - override fun create(formDef: FormDef?): FormEntryController { + override fun create(formDef: FormDef): FormEntryController { return FormEntryController(FormEntryModel(formDef)).also { it.addPostProcessor(EntityFormFinalizationProcessor()) 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 64a763e4898..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; @@ -574,6 +576,6 @@ public FormDef getFormDef() { } public interface FormEntryControllerFactory { - FormEntryController create(FormDef formDef); + FormEntryController create(@NonNull FormDef formDef); } } From 90118ebd0228cab9c697424a79fdbd53338c9083 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Thu, 13 Apr 2023 09:34:23 +0200 Subject: [PATCH 4/4] Add translation for title --- collect_app/src/main/res/xml/experimental_preferences.xml | 2 +- strings/src/main/res/values/strings.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/collect_app/src/main/res/xml/experimental_preferences.xml b/collect_app/src/main/res/xml/experimental_preferences.xml index 43788bd2d32..408f5361f67 100644 --- a/collect_app/src/main/res/xml/experimental_preferences.xml +++ b/collect_app/src/main/res/xml/experimental_preferences.xml @@ -3,7 +3,7 @@ android:title="@string/experimental"> Crash app Force an uncaught exception causing the app to crash + Predicate caching