From 3841184c541f9fb9bd65d7ce30e2f9bcf9d9ceaa Mon Sep 17 00:00:00 2001 From: chaneylc Date: Tue, 10 Dec 2024 14:37:58 -0600 Subject: [PATCH 1/2] closes #1027 fix validating categorical data from barcode scans --- .../tracker/activities/CollectActivity.java | 40 +++++------ .../tracker/objects/TraitObject.java | 67 +++++++++---------- .../tracker/traits/AudioTraitLayout.java | 4 +- .../tracker/traits/BaseTraitLayout.java | 14 +++- .../traits/CategoricalTraitLayout.java | 19 ++++++ .../tracker/traits/CounterTraitLayout.java | 2 +- .../tracker/traits/DateTraitLayout.java | 2 +- .../traits/DiseaseRatingTraitLayout.java | 2 +- .../tracker/traits/MultiCatTraitLayout.java | 32 +++++++++ .../tracker/traits/NumericTraitLayout.java | 65 +++++++++++++++++- .../tracker/traits/PercentTraitLayout.java | 2 +- .../fieldbook/tracker/views/TraitBoxView.kt | 11 +-- app/src/main/res/values/strings.xml | 1 + 13 files changed, 184 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java b/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java index b411a760f..dcafd758d 100644 --- a/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java +++ b/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java @@ -623,30 +623,20 @@ public void refreshMain() { */ @Override public boolean validateData() { - final String strValue = collectInputView.getText(); + final String data = collectInputView.getText(); final TraitObject currentTrait = traitBox.getCurrentTrait(); if (currentTrait == null) return false; - if (strValue.equals("NA")) return true; + if (data.equals("NA")) return true; - final String trait = currentTrait.getName(); + if (data.isEmpty()) return true; - if (traitBox.existsNewTraits() - && traitBox.getCurrentTrait() != null - && strValue.length() > 0 - && !traitBox.getCurrentTrait().isValidValue(strValue)) { + BaseTraitLayout layout = traitLayouts.getTraitLayout(currentTrait.getFormat()); + if (!layout.validate(data)) { - //checks if the trait is numerical and within the bounds (otherwise returns false) - if (currentTrait.isOver(strValue)) { - Utils.makeToast(getApplicationContext(),getString(R.string.trait_error_maximum_value) - + ": " + currentTrait.getMaximum()); - } else if (currentTrait.isUnder(strValue)) { - Utils.makeToast(getApplicationContext(),getString(R.string.trait_error_minimum_value) - + ": " + currentTrait.getMinimum()); - } + removeTrait(currentTrait); - removeTrait(trait); collectInputView.clear(); soundHelper.playError(); @@ -720,7 +710,7 @@ private void initToolbars() { // if a brapi observation that has been synced, don't allow deleting String format = getTraitFormat(); if (status && !Formats.Companion.isCameraTrait(format)) { - brapiDelete(getTraitName(), false); + brapiDelete(getCurrentTrait(), false); } else { traitLayouts.deleteTraitListener(getTraitFormat()); } @@ -1249,9 +1239,8 @@ public String getLocationByPreferences() { .getLocationByCollectMode(this, preferences, expId, obsUnit, geoNavHelper.getMInternalLocation(), geoNavHelper.getMExternalLocation(), database); } - private void brapiDelete(String parent, Boolean hint) { + private void brapiDelete(TraitObject trait, Boolean hint) { Utils.makeToast(this, getString(R.string.brapi_delete_message)); - TraitObject trait = traitBox.getCurrentTrait(); updateObservation(trait, getString(R.string.brapi_na), null); if (hint) { setNaTextBrapiEmptyField(); @@ -1261,19 +1250,20 @@ private void brapiDelete(String parent, Boolean hint) { } // Delete trait, including from database - public void removeTrait(String parent) { + public void removeTrait(TraitObject trait) { + if (rangeBox.isEmpty()) { return; } - String exp_id = Integer.toString(preferences.getInt(GeneralKeys.SELECTED_FIELD_ID, 0)); - TraitObject trait = traitBox.getCurrentTrait(); - if (database.isBrapiSynced(exp_id, getObservationUnit(), trait.getId(), getRep())) { - brapiDelete(parent, true); + String fieldId = Integer.toString(preferences.getInt(GeneralKeys.SELECTED_FIELD_ID, 0)); + + if (database.isBrapiSynced(fieldId, getObservationUnit(), trait.getId(), getRep())) { + brapiDelete(trait, true); } else { // Always remove existing trait before inserting again // Based on plot_id, prevent duplicate - traitBox.remove(parent, getObservationUnit(), getRep()); + traitBox.remove(trait, getObservationUnit(), getRep()); } } diff --git a/app/src/main/java/com/fieldbook/tracker/objects/TraitObject.java b/app/src/main/java/com/fieldbook/tracker/objects/TraitObject.java index cc0b5bfed..8e4dd5f24 100644 --- a/app/src/main/java/com/fieldbook/tracker/objects/TraitObject.java +++ b/app/src/main/java/com/fieldbook/tracker/objects/TraitObject.java @@ -1,9 +1,15 @@ package com.fieldbook.tracker.objects; +import android.graphics.Color; import android.util.Log; import androidx.annotation.NonNull; +import com.fieldbook.tracker.utilities.CategoryJsonUtil; + +import org.brapi.v2.model.pheno.BrAPIScaleValidValuesCategories; + +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -144,47 +150,38 @@ public void setObservationLevelNames(List observationLevelNames) { this.observationLevelNames = observationLevelNames; } - public boolean isValidValue(final String s) { - // this code is not perfect. - // I think that it is necessary to check - // the minimum and the maximum values - return !isUnder(s) && !isOver(s); - } - public boolean isUnder(final String s) { - if (!(format.equals("numeric"))) - return false; - Log.d("FB",s); - if (minimum.length() > 0) { // minimum exists - try { - final double v = Double.parseDouble(s); - final double lowerValue = Double.parseDouble(minimum); - return v < lowerValue; - } catch (NumberFormatException e) { - return true; - } - } else { - return false; - } - } + public boolean isValidCategoricalValue(final String inputCategory) { + + //check if its the new json + try { - public boolean isOver(final String s) { - if (!(format.equals("numeric"))) - return false; + ArrayList c = CategoryJsonUtil.Companion.decode(inputCategory); - Log.d("FB",s); - if (maximum.length() > 0) { // maximum exists - try { - final double v = Double.parseDouble(s); - final double upperValue = Double.parseDouble(maximum); - return v > upperValue; - } catch (NumberFormatException e) { - return true; + if (!c.isEmpty()) { + + //get the value from the single-sized array + BrAPIScaleValidValuesCategories labelVal = c.get(0); + + //check that this pair is a valid label/val pair in the category, + //if it is then set the text based on the preference + return CategoryJsonUtil.Companion.contains(c, labelVal); } - } else { - return false; + + } catch (Exception e) { + + e.printStackTrace(); //if it fails to decode, assume its an old string + +// if (CategoryJsonUtil.Companion.contains(cats, value)) { +// +// getCollectInputView().setText(value); +// +// getCollectInputView().setTextColor(Color.parseColor(getDisplayColor())); +// } } + + return false; } @Override diff --git a/app/src/main/java/com/fieldbook/tracker/traits/AudioTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/AudioTraitLayout.java index 9169f8428..900d7ea88 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/AudioTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/AudioTraitLayout.java @@ -136,7 +136,7 @@ private void refreshButtonState() { @Override public void deleteTraitListener() { deleteRecording(); - removeTrait(getCurrentTrait().getName()); + removeTrait(getCurrentTrait()); super.deleteTraitListener(); recordingLocation = null; mediaPlayer = null; @@ -263,7 +263,7 @@ private void stopPlayback() { private void startRecording() { try { - removeTrait(getCurrentTrait().getName()); + removeTrait(getCurrentTrait()); audioRecordingText.setText(""); fieldAudioHelper.startRecording(false); } catch (Exception e) { diff --git a/app/src/main/java/com/fieldbook/tracker/traits/BaseTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/BaseTraitLayout.java index 435cb2d03..5bbba7886 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/BaseTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/BaseTraitLayout.java @@ -10,6 +10,7 @@ import android.widget.EditText; import android.widget.LinearLayout; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; @@ -69,6 +70,15 @@ public boolean isTraitType(String trait) { public abstract void init(Activity act); + /** + * validate is used in collect activity to check if the collected data is within the + * specification of the trait, such as min/max. + */ + @NonNull + public Boolean validate(String data) { + return true; + } + /** * Override to block multi-measure navigation with specific condition */ @@ -303,8 +313,8 @@ public void updateObservation(TraitObject trait, String value) { ((CollectActivity) getContext()).updateObservation(trait, value, null); } - public void removeTrait(String parent) { - ((CollectActivity) getContext()).removeTrait(parent); + public void removeTrait(TraitObject trait) { + ((CollectActivity) getContext()).removeTrait(trait); } public void triggerTts(String text) { diff --git a/app/src/main/java/com/fieldbook/tracker/traits/CategoricalTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/CategoricalTraitLayout.java index 3008ee7d2..77deb9b69 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/CategoricalTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/CategoricalTraitLayout.java @@ -8,6 +8,7 @@ import android.widget.Button; import android.widget.LinearLayout; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; @@ -15,6 +16,7 @@ import com.fieldbook.tracker.activities.CollectActivity; import com.fieldbook.tracker.preferences.GeneralKeys; import com.fieldbook.tracker.utilities.CategoryJsonUtil; +import com.fieldbook.tracker.utilities.Utils; import com.google.android.flexbox.AlignItems; import com.google.android.flexbox.FlexDirection; import com.google.android.flexbox.FlexWrap; @@ -317,4 +319,21 @@ public String decodeValue(String value) { } else return scale.get(0).getLabel(); } else return ""; } + + @NonNull + @Override + public Boolean validate(String data) { + + //check if the data is in the list of categories + ArrayList cats = getCategories(); + if (CategoryJsonUtil.Companion.contains(cats, data)) { + return true; + } else { + getCollectActivity().runOnUiThread(() -> { + //Utils.makeToast(controller.getContext(), + // controller.getContext().getString(R.string.trait_error_invalid_value)); + }); + return false; + } + } } diff --git a/app/src/main/java/com/fieldbook/tracker/traits/CounterTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/CounterTraitLayout.java index efe80c03a..0670548b4 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/CounterTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/CounterTraitLayout.java @@ -103,7 +103,7 @@ public void afterLoadNotExists(CollectActivity act) { @Override public void deleteTraitListener() { - removeTrait(getCurrentTrait().getName()); + removeTrait(getCurrentTrait()); super.deleteTraitListener(); ObservationModel model = getCurrentObservation(); if (model != null) { diff --git a/app/src/main/java/com/fieldbook/tracker/traits/DateTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/DateTraitLayout.java index f31d7fa6e..7e62c047f 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/DateTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/DateTraitLayout.java @@ -369,7 +369,7 @@ public void afterLoadNotExists(CollectActivity act) { @Override public void deleteTraitListener() { - removeTrait(getCurrentTrait().getName()); + removeTrait(getCurrentTrait()); super.deleteTraitListener(); diff --git a/app/src/main/java/com/fieldbook/tracker/traits/DiseaseRatingTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/DiseaseRatingTraitLayout.java index ced56594b..47c4f778f 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/DiseaseRatingTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/DiseaseRatingTraitLayout.java @@ -148,7 +148,7 @@ public void loadLayout() { @Override public void deleteTraitListener() { - removeTrait(getCurrentTrait().getName()); + removeTrait(getCurrentTrait()); super.deleteTraitListener(); ObservationModel model = getCurrentObservation(); diff --git a/app/src/main/java/com/fieldbook/tracker/traits/MultiCatTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/MultiCatTraitLayout.java index ecc345e2c..7bb257a65 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/MultiCatTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/MultiCatTraitLayout.java @@ -8,6 +8,7 @@ import android.widget.Button; import android.widget.LinearLayout; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; @@ -15,6 +16,7 @@ import com.fieldbook.tracker.activities.CollectActivity; import com.fieldbook.tracker.preferences.GeneralKeys; import com.fieldbook.tracker.utilities.CategoryJsonUtil; +import com.fieldbook.tracker.utilities.Utils; import com.google.android.flexbox.AlignItems; import com.google.android.flexbox.FlexDirection; import com.google.android.flexbox.FlexWrap; @@ -23,6 +25,7 @@ import org.brapi.v2.model.pheno.BrAPIScaleValidValuesCategories; import java.util.ArrayList; +import java.util.Arrays; import java.util.StringJoiner; public class MultiCatTraitLayout extends BaseTraitLayout { @@ -355,4 +358,33 @@ public String decodeValue(String value) { } return joiner.toString(); } + + @NonNull + @Override + public Boolean validate(String data) { + + String[] classTokens = data.split(":"); + + boolean valid = false; + + ArrayList cats = new ArrayList<>(Arrays.asList(getCategories())); + + for (String token : classTokens) { + + BrAPIScaleValidValuesCategories validValue = new BrAPIScaleValidValuesCategories() + .label(token) + .value(token); + + valid = hasCategory(validValue); + } + + //check if the data is in the list of categories + if (!valid) { + getCollectActivity().runOnUiThread(() -> + Utils.makeToast(controller.getContext(), + controller.getContext().getString(R.string.trait_error_invalid_multicat_value))); + } + + return valid; + } } diff --git a/app/src/main/java/com/fieldbook/tracker/traits/NumericTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/NumericTraitLayout.java index b70564963..7e6927e0a 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/NumericTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/NumericTraitLayout.java @@ -3,11 +3,15 @@ import android.app.Activity; import android.content.Context; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.widget.Button; +import androidx.annotation.NonNull; + import com.fieldbook.tracker.R; import com.fieldbook.tracker.activities.CollectActivity; +import com.fieldbook.tracker.utilities.Utils; import java.util.LinkedHashMap; import java.util.Map; @@ -71,7 +75,7 @@ public void init(Activity act) { @Override public boolean onLongClick(View v) { getCollectInputView().setText(""); - removeTrait(getCurrentTrait().getName()); + removeTrait(getCurrentTrait()); return false; } }); @@ -96,6 +100,65 @@ public void deleteTraitListener() { super.deleteTraitListener(); } + @NonNull + @Override + public Boolean validate(String data) { + + if (isUnder(data) || isOver(data)) { + + getCollectActivity().runOnUiThread(() -> { + + if (isOver(data)) { + Utils.makeToast(controller.getContext(), + controller.getContext().getString(R.string.trait_error_maximum_value) + + ": " + getCurrentTrait().getMaximum()); + } else if (isUnder(data)) { + Utils.makeToast(controller.getContext(), + controller.getContext().getString(R.string.trait_error_minimum_value) + + ": " + getCurrentTrait().getMinimum()); + } + }); + + return false; + } + + return true; + } + + public boolean isUnder(final String s) { + + String minimum = getCurrentTrait().getMinimum(); + + if (!minimum.isEmpty()) { + try { + final double v = Double.parseDouble(s); + final double lowerValue = Double.parseDouble(minimum); + return v < lowerValue; + } catch (NumberFormatException e) { + return true; + } + } else { + return false; + } + } + + public boolean isOver(final String s) { + + String maximum = getCurrentTrait().getMaximum(); + + if (!maximum.isEmpty()) { + try { + final double v = Double.parseDouble(s); + final double upperValue = Double.parseDouble(maximum); + return v > upperValue; + } catch (NumberFormatException e) { + return true; + } + } else { + return false; + } + } + private class NumberButtonOnClickListener implements OnClickListener { @Override diff --git a/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java index b7b496c3f..59fa2a1b5 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java @@ -222,7 +222,7 @@ private void setCurrentValueText(String value, int color) { @Override public void deleteTraitListener() { - removeTrait(getCurrentTrait().getName()); + removeTrait(getCurrentTrait()); super.deleteTraitListener(); ObservationModel model = getCurrentObservation(); seekBar.setOnSeekBarChangeListener(null); diff --git a/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt b/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt index a6359863c..611f9491e 100644 --- a/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt +++ b/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt @@ -400,16 +400,11 @@ class TraitBoxView : ConstraintLayout { * @param traitName the observation variable name * @param plotID the unique plot identifier to remove the observations from */ - fun remove(traitName: String, plotID: String, rep: String) { - if (newTraits.containsKey(traitName)) newTraits.remove(traitName) + fun remove(trait: TraitObject, plotID: String, rep: String) { + if (newTraits.containsKey(trait.name)) newTraits.remove(trait.name) val studyId = controller.getPreferences().getInt(GeneralKeys.SELECTED_FIELD_ID, 0).toString() - val traitDbId = controller.getDatabase().getTraitByName(traitName).id - controller.getDatabase().deleteTrait(studyId, plotID, traitDbId, rep) - } - - fun remove(trait: TraitObject, plotID: String, rep: String) { - remove(trait.name, plotID, rep) + controller.getDatabase().deleteTrait(studyId, plotID, trait.id, rep) } private fun createTraitOnTouchListener( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6890ffdcc..25da8164a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1380,4 +1380,5 @@ GeoNav automatically stops when distance from all plots exceeds this value in kilometers. Proximity Check GeoNav Stopping, outside proximity of coordinates + Invalid categorical data was entered. \ No newline at end of file From 421a092b480cf1d571e5c4c50164ed6885d17e3e Mon Sep 17 00:00:00 2001 From: chaneylc Date: Fri, 13 Dec 2024 14:55:51 -0600 Subject: [PATCH 2/2] added barcode validation for various traits added Scannable interface that shows a trait format is scannable from a barcode --- .../tracker/activities/CollectActivity.java | 32 ++++++++++++++---- .../tracker/interfaces/CollectController.kt | 2 ++ .../interfaces/CollectRangeController.kt | 2 +- .../interfaces/CollectTraitController.kt | 2 +- .../tracker/traits/BooleanTraitLayout.java | 13 ++++++++ .../traits/CategoricalTraitLayout.java | 18 +++++----- .../tracker/traits/CounterTraitLayout.java | 13 ++++++++ .../tracker/traits/DateTraitLayout.java | 12 +++++++ .../tracker/traits/GNSSTraitLayout.kt | 22 +++++++++++++ .../tracker/traits/MultiCatTraitLayout.java | 33 ++++++++++++++----- .../tracker/traits/NumericTraitLayout.java | 17 ++++++---- .../tracker/traits/PercentTraitLayout.java | 19 +++++++++++ .../tracker/traits/formats/BooleanFormat.kt | 4 +-- .../traits/formats/CategoricalFormat.kt | 5 ++- .../tracker/traits/formats/CounterFormat.kt | 2 +- .../tracker/traits/formats/DateFormat.kt | 2 +- .../traits/formats/DiseaseRatingFormat.kt | 2 +- .../tracker/traits/formats/GnssFormat.kt | 2 +- .../tracker/traits/formats/LocationFormat.kt | 2 +- .../traits/formats/MultiCategoricalFormat.kt | 2 +- .../tracker/traits/formats/NumericFormat.kt | 2 +- .../tracker/traits/formats/PercentFormat.kt | 2 +- .../tracker/traits/formats/Scannable.kt | 17 ++++++++++ .../tracker/traits/formats/TextFormat.kt | 2 +- .../fieldbook/tracker/views/RangeBoxView.kt | 6 ++-- .../fieldbook/tracker/views/TraitBoxView.kt | 2 +- 26 files changed, 188 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/com/fieldbook/tracker/traits/formats/Scannable.kt diff --git a/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java b/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java index dcafd758d..d934f8ad2 100644 --- a/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java +++ b/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java @@ -72,6 +72,7 @@ import com.fieldbook.tracker.traits.PhotoTraitLayout; import com.fieldbook.tracker.traits.formats.TraitFormat; import com.fieldbook.tracker.traits.formats.coders.StringCoder; +import com.fieldbook.tracker.traits.formats.Scannable; import com.fieldbook.tracker.traits.formats.presenters.ValuePresenter; import com.fieldbook.tracker.utilities.CameraXFacade; import com.fieldbook.tracker.utilities.BluetoothHelper; @@ -622,18 +623,26 @@ public void refreshMain() { * @return boolean flag false when data is out of bounds, true otherwise */ @Override - public boolean validateData() { - final String data = collectInputView.getText(); + public boolean validateData(@Nullable String data) { final TraitObject currentTrait = traitBox.getCurrentTrait(); if (currentTrait == null) return false; + if (data == null) return true; + if (data.equals("NA")) return true; if (data.isEmpty()) return true; BaseTraitLayout layout = traitLayouts.getTraitLayout(currentTrait.getFormat()); - if (!layout.validate(data)) { + TraitFormat format = Formats.Companion.findTrait(currentTrait.getFormat()); + + String value = data; + if (format instanceof Scannable) { + value = ((Scannable) format).preprocess(data); + } + + if (!layout.validate(value)) { removeTrait(currentTrait); @@ -2005,12 +2014,21 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { TraitObject currentTrait = traitBox.getCurrentTrait(); BaseTraitLayout currentTraitLayout = traitLayouts.getTraitLayout(currentTrait.getFormat()); - currentTraitLayout.loadLayout(); + TraitFormat traitFormat = Formats.Companion.findTrait(currentTrait.getFormat()); + String oldValue = ""; + ObservationModel currentObs = getCurrentObservation(); + if (currentObs != null) { + oldValue = currentObs.getValue(); + } + + if (scannedBarcode != null && traitFormat instanceof Scannable && validateData(scannedBarcode)) { + updateObservation(currentTrait, ((Scannable) traitFormat).preprocess(scannedBarcode), null); + } else { + updateObservation(currentTrait, oldValue, null); + } - updateObservation(currentTrait, scannedBarcode, null); currentTraitLayout.loadLayout(); - validateData(); } break; case PhotoTraitLayout.PICTURE_REQUEST_CODE: @@ -2580,7 +2598,7 @@ public void showObservationMetadataDialog(){ } } - private ObservationModel getCurrentObservation() { + public ObservationModel getCurrentObservation() { String rep = getCollectInputView().getRep(); List models = Arrays.asList(getDatabase().getRepeatedValues(getStudyId(), getObservationUnit(), getTraitDbId())); for (ObservationModel m : models) { diff --git a/app/src/main/java/com/fieldbook/tracker/interfaces/CollectController.kt b/app/src/main/java/com/fieldbook/tracker/interfaces/CollectController.kt index 1fca5e2c0..fcbe6ea73 100644 --- a/app/src/main/java/com/fieldbook/tracker/interfaces/CollectController.kt +++ b/app/src/main/java/com/fieldbook/tracker/interfaces/CollectController.kt @@ -3,6 +3,7 @@ package com.fieldbook.tracker.interfaces import android.content.Context import android.location.Location import android.os.Handler +import com.fieldbook.tracker.database.models.ObservationModel import com.fieldbook.tracker.devices.camera.UsbCameraApi import com.fieldbook.tracker.devices.camera.GoProApi import com.fieldbook.tracker.devices.camera.CanonApi @@ -58,4 +59,5 @@ interface CollectController: FieldController { fun getFfmpegHelper(): FfmpegHelper fun getCanonApi(): CanonApi fun takePicture() + fun getCurrentObservation(): ObservationModel? } \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/interfaces/CollectRangeController.kt b/app/src/main/java/com/fieldbook/tracker/interfaces/CollectRangeController.kt index 7a458be9e..e7a663381 100644 --- a/app/src/main/java/com/fieldbook/tracker/interfaces/CollectRangeController.kt +++ b/app/src/main/java/com/fieldbook/tracker/interfaces/CollectRangeController.kt @@ -3,7 +3,7 @@ package com.fieldbook.tracker.interfaces import com.fieldbook.tracker.views.CollectInputView interface CollectRangeController: CollectController { - fun validateData(): Boolean + fun validateData(data: String?): Boolean fun initWidgets(rangeSuppress: Boolean) fun cancelAndFinish() fun callFinish() diff --git a/app/src/main/java/com/fieldbook/tracker/interfaces/CollectTraitController.kt b/app/src/main/java/com/fieldbook/tracker/interfaces/CollectTraitController.kt index e53335028..295514084 100644 --- a/app/src/main/java/com/fieldbook/tracker/interfaces/CollectTraitController.kt +++ b/app/src/main/java/com/fieldbook/tracker/interfaces/CollectTraitController.kt @@ -5,7 +5,7 @@ import com.fieldbook.tracker.traits.LayoutCollections import com.fieldbook.tracker.views.CollectInputView interface CollectTraitController: CollectController { - fun validateData(): Boolean + fun validateData(data: String?): Boolean fun getTraitLayouts(): LayoutCollections fun isCyclingTraitsAdvances(): Boolean fun refreshLock() diff --git a/app/src/main/java/com/fieldbook/tracker/traits/BooleanTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/BooleanTraitLayout.java index 1a13aea20..19c0d5c58 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/BooleanTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/BooleanTraitLayout.java @@ -7,11 +7,14 @@ import android.widget.ImageView; import android.widget.SeekBar; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.fieldbook.tracker.R; import com.fieldbook.tracker.activities.CollectActivity; +import java.util.Locale; + public class BooleanTraitLayout extends BaseTraitLayout implements SeekBar.OnSeekBarChangeListener { private SeekBar threeStateSeekBar; @@ -93,6 +96,16 @@ public void deleteTraitListener() { //resetToDefault(); } + @NonNull + @Override + public Boolean validate(String data) { + try { + return data.toLowerCase(Locale.ROOT).equals("true") || data.toLowerCase(Locale.ROOT).equals("false"); + } catch (Exception e) { + return false; + } + } + private void resetToDefault() { String value = getCurrentTrait().getDefaultValue().trim(); diff --git a/app/src/main/java/com/fieldbook/tracker/traits/CategoricalTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/CategoricalTraitLayout.java index 77deb9b69..c0307a478 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/CategoricalTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/CategoricalTraitLayout.java @@ -324,15 +324,15 @@ public String decodeValue(String value) { @Override public Boolean validate(String data) { - //check if the data is in the list of categories - ArrayList cats = getCategories(); - if (CategoryJsonUtil.Companion.contains(cats, data)) { - return true; - } else { - getCollectActivity().runOnUiThread(() -> { - //Utils.makeToast(controller.getContext(), - // controller.getContext().getString(R.string.trait_error_invalid_value)); - }); + try { + ArrayList userChosenCats = CategoryJsonUtil.Companion.decode(data); + if (userChosenCats.isEmpty()) { + return true; + } else { + ArrayList cats = getCategories(); + return CategoryJsonUtil.Companion.contains(cats, userChosenCats.get(0)); + } + } catch (Exception e) { return false; } } diff --git a/app/src/main/java/com/fieldbook/tracker/traits/CounterTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/CounterTraitLayout.java index 0670548b4..267e325a8 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/CounterTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/CounterTraitLayout.java @@ -4,6 +4,8 @@ import android.content.Context; import android.util.AttributeSet; +import androidx.annotation.NonNull; + import com.fieldbook.tracker.R; import com.fieldbook.tracker.activities.CollectActivity; import com.fieldbook.tracker.database.models.ObservationModel; @@ -112,4 +114,15 @@ public void deleteTraitListener() { getCollectInputView().setText("0"); } } + + @NonNull + @Override + public Boolean validate(String data) { + try { + Integer.parseInt(data); + return true; + } catch (Exception e) { + return false; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/DateTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/DateTraitLayout.java index 7e62c047f..d2a76c1f6 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/DateTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/DateTraitLayout.java @@ -7,6 +7,7 @@ import android.util.Log; import android.widget.ImageButton; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; @@ -423,4 +424,15 @@ public String decodeValue(String value) { } return getMonthForInt(c.get(Calendar.MONTH)) + " " + String.format(Locale.getDefault(), "%02d", c.get(Calendar.DAY_OF_MONTH)); } + + @NonNull + @Override + public Boolean validate(String data) { + try { + Date d = dateFormat.parse(data); + return true; + } catch (Exception e) { + return false; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/GNSSTraitLayout.kt b/app/src/main/java/com/fieldbook/tracker/traits/GNSSTraitLayout.kt index 45cfad9bf..a542e6e73 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/GNSSTraitLayout.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/GNSSTraitLayout.kt @@ -1025,4 +1025,26 @@ class GNSSTraitLayout : BaseTraitLayout, GPSTracker.GPSTrackerListener { update() } + + override fun validate(data: String?): Boolean { + + if (data == null) return true + + try { + val gson = GeoJsonUtil.decode(data) + return true + } catch (e: Exception) { + if (";" in data) { + val parts = data.split(";") + if (parts.size >= 2) { + val lat = parts[0].toDoubleOrNull() + val lng = parts[1].toDoubleOrNull() + if (lat != null && lng != null) { + return true + } + } + } + return false + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/MultiCatTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/MultiCatTraitLayout.java index 7bb257a65..49158d725 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/MultiCatTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/MultiCatTraitLayout.java @@ -16,11 +16,13 @@ import com.fieldbook.tracker.activities.CollectActivity; import com.fieldbook.tracker.preferences.GeneralKeys; import com.fieldbook.tracker.utilities.CategoryJsonUtil; +import com.fieldbook.tracker.utilities.JsonUtil; import com.fieldbook.tracker.utilities.Utils; import com.google.android.flexbox.AlignItems; import com.google.android.flexbox.FlexDirection; import com.google.android.flexbox.FlexWrap; import com.google.android.flexbox.FlexboxLayoutManager; +import com.google.gson.JsonParseException; import org.brapi.v2.model.pheno.BrAPIScaleValidValuesCategories; @@ -363,19 +365,34 @@ public String decodeValue(String value) { @Override public Boolean validate(String data) { - String[] classTokens = data.split(":"); + ArrayList cats = new ArrayList<>(Arrays.asList(getCategories())); - boolean valid = false; + ArrayList userChosenCats = new ArrayList<>(); - ArrayList cats = new ArrayList<>(Arrays.asList(getCategories())); + try { - for (String token : classTokens) { + if (JsonUtil.Companion.isJsonValid(data)) { - BrAPIScaleValidValuesCategories validValue = new BrAPIScaleValidValuesCategories() - .label(token) - .value(token); + userChosenCats.addAll(CategoryJsonUtil.Companion.decode(data)); + + } else throw new RuntimeException(); + + } catch (Exception e) { + + String[] classTokens = data.split(":"); + + for (String token : classTokens) { + + userChosenCats.add(new BrAPIScaleValidValuesCategories() + .label(token) + .value(token)); + } + } - valid = hasCategory(validValue); + boolean valid = true; + for (BrAPIScaleValidValuesCategories cat : userChosenCats) { + valid = CategoryJsonUtil.Companion.contains(cats, cat); + if (!valid) break; } //check if the data is in the list of categories diff --git a/app/src/main/java/com/fieldbook/tracker/traits/NumericTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/NumericTraitLayout.java index 7e6927e0a..2d64e0ab9 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/NumericTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/NumericTraitLayout.java @@ -11,6 +11,7 @@ import com.fieldbook.tracker.R; import com.fieldbook.tracker.activities.CollectActivity; +import com.fieldbook.tracker.objects.TraitObject; import com.fieldbook.tracker.utilities.Utils; import java.util.LinkedHashMap; @@ -104,15 +105,17 @@ public void deleteTraitListener() { @Override public Boolean validate(String data) { - if (isUnder(data) || isOver(data)) { + TraitObject trait = getCurrentTrait(); + + if (isUnder(trait, data) || isOver(trait, data)) { getCollectActivity().runOnUiThread(() -> { - if (isOver(data)) { + if (isOver(trait, data)) { Utils.makeToast(controller.getContext(), controller.getContext().getString(R.string.trait_error_maximum_value) + ": " + getCurrentTrait().getMaximum()); - } else if (isUnder(data)) { + } else if (isUnder(trait, data)) { Utils.makeToast(controller.getContext(), controller.getContext().getString(R.string.trait_error_minimum_value) + ": " + getCurrentTrait().getMinimum()); @@ -125,9 +128,9 @@ public Boolean validate(String data) { return true; } - public boolean isUnder(final String s) { + public static boolean isUnder(TraitObject trait, final String s) { - String minimum = getCurrentTrait().getMinimum(); + String minimum = trait.getMinimum(); if (!minimum.isEmpty()) { try { @@ -142,9 +145,9 @@ public boolean isUnder(final String s) { } } - public boolean isOver(final String s) { + public static boolean isOver(TraitObject trait, final String s) { - String maximum = getCurrentTrait().getMaximum(); + String maximum = trait.getMaximum(); if (!maximum.isEmpty()) { try { diff --git a/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java b/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java index 59fa2a1b5..7bffedb82 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java @@ -1,5 +1,8 @@ package com.fieldbook.tracker.traits; +import static com.fieldbook.tracker.traits.NumericTraitLayout.isOver; +import static com.fieldbook.tracker.traits.NumericTraitLayout.isUnder; + import android.app.Activity; import android.content.Context; import android.graphics.Color; @@ -8,10 +11,15 @@ import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; + import com.fieldbook.tracker.R; import com.fieldbook.tracker.activities.CollectActivity; import com.fieldbook.tracker.database.models.ObservationModel; import com.fieldbook.tracker.objects.TraitObject; +import com.fieldbook.tracker.traits.formats.Formats; +import com.fieldbook.tracker.traits.formats.Scannable; +import com.fieldbook.tracker.traits.formats.TraitFormat; public class PercentTraitLayout extends BaseTraitLayout { private SeekBar seekBar; @@ -184,6 +192,17 @@ public void refreshLock() { } } + @NonNull + @Override + public Boolean validate(String data) { + try { + TraitObject trait = getCurrentTrait(); + return !(isUnder(trait, data) || isOver(trait, data)); + } catch (Exception e) { + return false; + } + } + private void updateLoadBar() { String max = getCurrentTrait().getMaximum(); if (!max.isEmpty()) { diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/BooleanFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/BooleanFormat.kt index 25e9796a5..73f864438 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/BooleanFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/BooleanFormat.kt @@ -15,5 +15,5 @@ class BooleanFormat : TraitFormat( stringNameAux = null, NameParameter(), DefaultToggleValueParameter(), - DetailsParameter() -) \ No newline at end of file + DetailsParameter(), +), Scannable \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/CategoricalFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/CategoricalFormat.kt index b3074eefe..48579239c 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/CategoricalFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/CategoricalFormat.kt @@ -23,4 +23,7 @@ class CategoricalFormat : TraitFormat( NameParameter(), DetailsParameter(), CategoriesParameter() -), StringCoder by CategoricalJsonCoder(), ValuePresenter by CategoricalValuePresenter() \ No newline at end of file +), + StringCoder by CategoricalJsonCoder(), + ValuePresenter by CategoricalValuePresenter(), + Scannable \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/CounterFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/CounterFormat.kt index 2f67bab58..e73e7862b 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/CounterFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/CounterFormat.kt @@ -14,4 +14,4 @@ class CounterFormat : TraitFormat( stringNameAux = null, NameParameter(), DetailsParameter() -) \ No newline at end of file +), Scannable \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/DateFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/DateFormat.kt index 56c0775a6..57bcb4b3a 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/DateFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/DateFormat.kt @@ -14,4 +14,4 @@ class DateFormat : TraitFormat( stringNameAux = null, NameParameter(), DetailsParameter() -) \ No newline at end of file +), Scannable \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/DiseaseRatingFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/DiseaseRatingFormat.kt index 933b3e45b..b1892118e 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/DiseaseRatingFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/DiseaseRatingFormat.kt @@ -14,4 +14,4 @@ class DiseaseRatingFormat : TraitFormat( stringNameAux = null, NameParameter(), DetailsParameter() -) \ No newline at end of file +), Scannable \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/GnssFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/GnssFormat.kt index 35aefb101..c9f2680c1 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/GnssFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/GnssFormat.kt @@ -14,4 +14,4 @@ class GnssFormat : TraitFormat( stringNameAux = null, NameParameter(), DetailsParameter() -) \ No newline at end of file +), Scannable \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/LocationFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/LocationFormat.kt index a3695e8bf..d023b11cc 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/LocationFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/LocationFormat.kt @@ -14,4 +14,4 @@ class LocationFormat : TraitFormat( stringNameAux = null, NameParameter(), DetailsParameter() -) \ No newline at end of file +), Scannable \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/MultiCategoricalFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/MultiCategoricalFormat.kt index 84c2a8474..00325abed 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/MultiCategoricalFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/MultiCategoricalFormat.kt @@ -20,4 +20,4 @@ class MultiCategoricalFormat : TraitFormat( NameParameter(), DetailsParameter(), CategoriesParameter() -), StringCoder by CategoricalJsonCoder(), ValuePresenter by CategoricalValuePresenter() \ No newline at end of file +), StringCoder by CategoricalJsonCoder(), ValuePresenter by CategoricalValuePresenter(), Scannable \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/NumericFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/NumericFormat.kt index 4a4d2295c..5d01fa8d7 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/NumericFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/NumericFormat.kt @@ -36,7 +36,7 @@ open class NumericFormat( iconDrawableResourceId = iconDrawableResourceId, stringNameAux = null, *parameters -) { +), Scannable { override fun validate( context: Context, diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/PercentFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/PercentFormat.kt index 90097d64e..9b89fbc9e 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/PercentFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/PercentFormat.kt @@ -28,4 +28,4 @@ class PercentFormat : NumericFormat( isRequired = true ), DetailsParameter() -) \ No newline at end of file +), Scannable by PercentageScannable() \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/Scannable.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/Scannable.kt new file mode 100644 index 000000000..bee436fcd --- /dev/null +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/Scannable.kt @@ -0,0 +1,17 @@ +package com.fieldbook.tracker.traits.formats + +/** + * Interface for defining traits that can be scanned in from a barcode. + */ +interface Scannable { + + fun preprocess(barcodeValue: String): String { + return barcodeValue + } +} + +class PercentageScannable : Scannable { + override fun preprocess(barcodeValue: String): String { + return barcodeValue.replace("%", "") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/traits/formats/TextFormat.kt b/app/src/main/java/com/fieldbook/tracker/traits/formats/TextFormat.kt index d3eac364c..28b33bb86 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/formats/TextFormat.kt +++ b/app/src/main/java/com/fieldbook/tracker/traits/formats/TextFormat.kt @@ -18,4 +18,4 @@ class TextFormat : TraitFormat( DefaultValueParameter(), DetailsParameter(), CloseKeyboardParameter(false) -) \ No newline at end of file +), Scannable \ No newline at end of file diff --git a/app/src/main/java/com/fieldbook/tracker/views/RangeBoxView.kt b/app/src/main/java/com/fieldbook/tracker/views/RangeBoxView.kt index d4ddc66d8..959701ec1 100644 --- a/app/src/main/java/com/fieldbook/tracker/views/RangeBoxView.kt +++ b/app/src/main/java/com/fieldbook/tracker/views/RangeBoxView.kt @@ -380,7 +380,7 @@ class RangeBoxView : ConstraintLayout { // Simulate range right key press fun repeatKeyPress(directionStr: String) { val left = directionStr.equals("left", ignoreCase = true) - if (!controller.validateData()) { + if (!controller.validateData(controller.getCurrentObservation()?.value)) { return } if (rangeID.isNotEmpty()) { @@ -546,7 +546,7 @@ class RangeBoxView : ConstraintLayout { ///// paging ///// fun moveEntryLeft() { - if (!controller.validateData()) { + if (!controller.validateData(controller.getCurrentObservation()?.value)) { return } if (controller.getPreferences().getBoolean(GeneralKeys.ENTRY_NAVIGATION_SOUND, false) @@ -570,7 +570,7 @@ class RangeBoxView : ConstraintLayout { fun moveEntryRight() { val traitBox = controller.getTraitBox() - if (!controller.validateData()) { + if (!controller.validateData(controller.getCurrentObservation()?.value)) { return } if (controller.getPreferences().getBoolean(GeneralKeys.ENTRY_NAVIGATION_SOUND, false) diff --git a/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt b/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt index 611f9491e..b9689fba1 100644 --- a/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt +++ b/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt @@ -431,7 +431,7 @@ class TraitBoxView : ConstraintLayout { if (visibleTraitsList == null) return var pos = 0 - if (!controller.validateData()) { + if (!controller.validateData(controller.getCurrentObservation()?.value)) { return }