From 3841184c541f9fb9bd65d7ce30e2f9bcf9d9ceaa Mon Sep 17 00:00:00 2001 From: chaneylc Date: Tue, 10 Dec 2024 14:37:58 -0600 Subject: [PATCH 01/22] 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 02/22] 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 } From e9eec0b90bd9720dc1849f67f51783dec4c20760 Mon Sep 17 00:00:00 2001 From: chaneylc Date: Fri, 13 Dec 2024 15:16:44 -0600 Subject: [PATCH 03/22] closes #1061 potentially resolve crash when storage directory is deleted and user tries to export --- .../main/java/com/fieldbook/tracker/utilities/ExportUtil.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/fieldbook/tracker/utilities/ExportUtil.kt b/app/src/main/java/com/fieldbook/tracker/utilities/ExportUtil.kt index 0598cf820..d98ddc787 100644 --- a/app/src/main/java/com/fieldbook/tracker/utilities/ExportUtil.kt +++ b/app/src/main/java/com/fieldbook/tracker/utilities/ExportUtil.kt @@ -524,7 +524,9 @@ class ExportUtil @Inject constructor(@ActivityContext private val context: Conte } else { if (preferences.getBoolean(GeneralKeys.EXPORT_OVERWRITE, false)) { - archivePreviousExport(filesToExport.first()) + if (filesToExport.isNotEmpty()) { + archivePreviousExport(filesToExport.first()) + } } filesToExport.firstOrNull() } From 969df447358b084ae660a39723f9da5edff4e221 Mon Sep 17 00:00:00 2001 From: chaneylc Date: Tue, 17 Dec 2024 12:01:50 -0600 Subject: [PATCH 04/22] added better error handling when storage directory doesn't exist --- .../tracker/activities/ConfigActivity.java | 9 +++++++- .../activities/FieldEditorActivity.java | 19 ++++++++++++++-- .../activities/TraitEditorActivity.java | 14 +++++++----- .../StoragePreferencesFragment.java | 22 +++++++++++++++++-- app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/fieldbook/tracker/activities/ConfigActivity.java b/app/src/main/java/com/fieldbook/tracker/activities/ConfigActivity.java index 7940cb394..855474752 100644 --- a/app/src/main/java/com/fieldbook/tracker/activities/ConfigActivity.java +++ b/app/src/main/java/com/fieldbook/tracker/activities/ConfigActivity.java @@ -55,6 +55,7 @@ import com.michaelflisar.changelog.internal.ChangelogDialogFragment; import org.apache.commons.lang3.ArrayUtils; +import org.phenoapps.utils.BaseDocumentTreeUtil; import java.util.ArrayList; import java.util.HashSet; @@ -318,7 +319,13 @@ private void loadScreen() { break; case 3: if (checkTraitsExist() < 0) return; - exportUtil.exportActiveField(); + if (BaseDocumentTreeUtil.Companion.getRoot(this) != null + && BaseDocumentTreeUtil.Companion.isEnabled(this) + && BaseDocumentTreeUtil.Companion.getDirectory(this, R.string.dir_field_export) != null) { + exportUtil.exportActiveField(); + } else { + Toast.makeText(this, R.string.error_storage_directory, Toast.LENGTH_LONG).show(); + } break; case 4: intent.setClassName(ConfigActivity.this, diff --git a/app/src/main/java/com/fieldbook/tracker/activities/FieldEditorActivity.java b/app/src/main/java/com/fieldbook/tracker/activities/FieldEditorActivity.java index 47d7643c0..93b9ced0a 100644 --- a/app/src/main/java/com/fieldbook/tracker/activities/FieldEditorActivity.java +++ b/app/src/main/java/com/fieldbook/tracker/activities/FieldEditorActivity.java @@ -395,10 +395,14 @@ private void showFileDialog() { public void onItemClick(AdapterView parent, View view, int position, long id) { switch (position) { case 0: - loadLocalPermission(); + if (checkDirectory()) { + loadLocalPermission(); + } break; case 1: - loadCloud(); + if (checkDirectory()) { + loadCloud(); + } break; case 2: FieldCreatorDialogFragment dialog = new FieldCreatorDialogFragment((ThemedActivity) FieldEditorActivity.this); @@ -456,6 +460,17 @@ public void loadBrAPI() { } } + private Boolean checkDirectory() { + if (BaseDocumentTreeUtil.Companion.getRoot(this) != null + && BaseDocumentTreeUtil.Companion.isEnabled(this) + && BaseDocumentTreeUtil.Companion.getDirectory(this, R.string.dir_field_import) != null) { + return true; + } else { + Toast.makeText(this, R.string.error_storage_directory, Toast.LENGTH_LONG).show(); + return false; + } + } + public void loadCloud() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); diff --git a/app/src/main/java/com/fieldbook/tracker/activities/TraitEditorActivity.java b/app/src/main/java/com/fieldbook/tracker/activities/TraitEditorActivity.java index 3059e3d7b..38e904f97 100644 --- a/app/src/main/java/com/fieldbook/tracker/activities/TraitEditorActivity.java +++ b/app/src/main/java/com/fieldbook/tracker/activities/TraitEditorActivity.java @@ -9,7 +9,6 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; -import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -57,8 +56,6 @@ import com.fieldbook.tracker.objects.ImportFormat; import com.fieldbook.tracker.objects.TraitObject; import com.fieldbook.tracker.preferences.GeneralKeys; -import com.fieldbook.tracker.traits.formats.Formats; -import com.fieldbook.tracker.utilities.ArrayIndexComparator; import com.fieldbook.tracker.utilities.CSVWriter; import com.fieldbook.tracker.utilities.FileUtil; import com.fieldbook.tracker.utilities.SharedPreferenceUtils; @@ -78,7 +75,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -380,7 +376,13 @@ public boolean onOptionsItemSelected(MenuItem item) { } else if (itemId == R.id.sortTrait) { showTraitSortDialog(); } else if (itemId == R.id.importexport) { - importExportDialog(); + if (BaseDocumentTreeUtil.Companion.getRoot(this) != null + && BaseDocumentTreeUtil.Companion.isEnabled(this) + && BaseDocumentTreeUtil.Companion.getDirectory(this, R.string.dir_trait) != null) { + importExportDialog(); + } else { + Toast.makeText(this, R.string.error_storage_directory, Toast.LENGTH_LONG).show(); + } } else if (itemId == R.id.toggleTrait) { changeAllVisibility(); } else if (itemId == android.R.id.home) { @@ -606,7 +608,7 @@ private void loadCloud() { try { startActivityForResult(Intent.createChooser(intent, "cloudFile"), REQUEST_CLOUD_FILE_CODE); } catch (android.content.ActivityNotFoundException ex) { - Toast.makeText(getApplicationContext(), "No suitable File Manager was found.", Toast.LENGTH_SHORT).show(); + Toast.makeText(getApplicationContext(), R.string.no_suitable_file_manager_was_found, Toast.LENGTH_SHORT).show(); } } diff --git a/app/src/main/java/com/fieldbook/tracker/preferences/StoragePreferencesFragment.java b/app/src/main/java/com/fieldbook/tracker/preferences/StoragePreferencesFragment.java index 23fb4b1fe..efa8281d2 100644 --- a/app/src/main/java/com/fieldbook/tracker/preferences/StoragePreferencesFragment.java +++ b/app/src/main/java/com/fieldbook/tracker/preferences/StoragePreferencesFragment.java @@ -19,7 +19,9 @@ import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; +import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.documentfile.provider.DocumentFile; import androidx.preference.ListPreference; @@ -104,12 +106,16 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { Preference databaseDelete = findPreference("pref_database_delete"); databaseImport.setOnPreferenceClickListener(preference -> { - importDatabaseFilePermission(); + if (checkDirectory()) { + importDatabaseFilePermission(); + } return true; }); databaseExport.setOnPreferenceClickListener(preference -> { - exportDatabaseFilePermission(); + if (checkDirectory()) { + exportDatabaseFilePermission(); + } return true; }); @@ -119,6 +125,18 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { }); } + @NonNull + private Boolean checkDirectory() { + if (BaseDocumentTreeUtil.Companion.getRoot(context) != null + && BaseDocumentTreeUtil.Companion.isEnabled(context) + && BaseDocumentTreeUtil.Companion.getDirectory(context, R.string.dir_database) != null) { + return true; + } else { + Toast.makeText(context, R.string.error_storage_directory, Toast.LENGTH_LONG).show(); + return false; + } + } + @Override public void onAttach(Context context) { super.onAttach(context); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bffcbc353..9654d9b03 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -587,6 +587,7 @@ All traits Only active traits Error during export + Storage directory is missing or corrupted. No data exists to export Export complete Exporting… @@ -1386,5 +1387,6 @@ GeoNav automatically stops when distance from all plots exceeds this value in kilometers. Proximity Check GeoNav Stopping, outside proximity of coordinates + No suitable File Manager was found. \ No newline at end of file From f33095e0183974e426788365a3c843424ce2ec96 Mon Sep 17 00:00:00 2001 From: chaneylc Date: Tue, 17 Dec 2024 16:21:55 -0600 Subject: [PATCH 05/22] closes #934 replaces old flip flop arrows implementation with one that uses constraint layout to swap UI --- .../tracker/activities/CollectActivity.java | 39 +++++++++++ .../fieldbook/tracker/views/RangeBoxView.kt | 55 ++++----------- .../fieldbook/tracker/views/TraitBoxView.kt | 68 ++++++------------- 3 files changed, 71 insertions(+), 91 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 686d5a8b4..9fdee5b1d 100644 --- a/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java +++ b/app/src/main/java/com/fieldbook/tracker/activities/CollectActivity.java @@ -34,6 +34,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.constraintlayout.widget.ConstraintSet; import androidx.documentfile.provider.DocumentFile; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentManager; @@ -553,6 +554,44 @@ private void loadScreen() { refreshInfoBarAdapter(); uvcView = findViewById(R.id.collect_activity_uvc_tv); + + handleFlipFlopPreferences(); + } + + /** + * Handles the flip flop preferences for the collect activity. + * Change from issue #934: + * Originally, the arrow functionalities were switched depending on this preference, + * now the whole UI is swapped. + */ + private void handleFlipFlopPreferences() { + + ConstraintLayout layout = findViewById(R.id.layout_main); + + ConstraintSet constraintSet = new ConstraintSet(); + constraintSet.clone(layout); + + if (preferences.getBoolean(GeneralKeys.FLIP_FLOP_ARROWS, false)) { + constraintSet.connect(R.id.act_collect_range_box, ConstraintSet.TOP, + R.id.act_collect_infobar_rv, ConstraintSet.BOTTOM, 0); + constraintSet.connect(R.id.act_collect_range_box, ConstraintSet.BOTTOM, + R.id.act_collect_trait_box, ConstraintSet.TOP, 0); + constraintSet.connect(R.id.act_collect_trait_box, ConstraintSet.TOP, + R.id.act_collect_range_box, ConstraintSet.BOTTOM, 0); + constraintSet.connect(R.id.act_collect_input_view, ConstraintSet.TOP, + R.id.act_collect_trait_box, ConstraintSet.BOTTOM, 0); + } else { + constraintSet.connect(R.id.act_collect_trait_box, ConstraintSet.TOP, + R.id.act_collect_infobar_rv, ConstraintSet.BOTTOM, 0); + constraintSet.connect(R.id.act_collect_trait_box, ConstraintSet.BOTTOM, + R.id.act_collect_range_box, ConstraintSet.TOP, 0); + constraintSet.connect(R.id.act_collect_range_box, ConstraintSet.TOP, + R.id.act_collect_trait_box, ConstraintSet.BOTTOM, 0); + constraintSet.connect(R.id.act_collect_input_view, ConstraintSet.TOP, + R.id.act_collect_range_box, ConstraintSet.BOTTOM, 0); + } + + constraintSet.applyTo(layout); } //when softkeyboard is displayed, reset the snackbar to redisplay with a calculated bottom margin 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..0aca2b330 100644 --- a/app/src/main/java/com/fieldbook/tracker/views/RangeBoxView.kt +++ b/app/src/main/java/com/fieldbook/tracker/views/RangeBoxView.kt @@ -147,17 +147,8 @@ class RangeBoxView : ConstraintLayout { fun connectTraitBox(traitBoxView: TraitBoxView) { - //determine range button function based on user-preferences - //issues217 introduces the ability to swap trait and plot arrows - val flipFlopArrows = - controller.getPreferences().getBoolean(GeneralKeys.FLIP_FLOP_ARROWS, false) - if (flipFlopArrows) { - rangeLeft = traitBoxView.getTraitLeft() - rangeRight = traitBoxView.getTraitRight() - } else { - rangeLeft = findViewById(R.id.rangeLeft) - rangeRight = findViewById(R.id.rangeRight) - } + rangeLeft = findViewById(R.id.rangeLeft) + rangeRight = findViewById(R.id.rangeRight) rangeLeft.setOnTouchListener(createOnLeftTouchListener()) rangeRight.setOnTouchListener(createOnRightTouchListener()) @@ -298,43 +289,21 @@ class RangeBoxView : ConstraintLayout { private fun createOnLeftTouchListener(): OnTouchListener { val actionLeft = createRunnable("left") - //change click-arrow based on preferences - val flipFlopArrows = - controller.getPreferences().getBoolean(GeneralKeys.FLIP_FLOP_ARROWS, false) - return if (flipFlopArrows) { - createOnTouchListener( - rangeLeft, actionLeft, - R.drawable.trait_chevron_left_pressed, - R.drawable.trait_chevron_left - ) - } else { - createOnTouchListener( - rangeLeft, actionLeft, - R.drawable.chevron_left_pressed, - R.drawable.chevron_left - ) - } + return createOnTouchListener( + rangeLeft, actionLeft, + R.drawable.chevron_left_pressed, + R.drawable.chevron_left + ) } private fun createOnRightTouchListener(): OnTouchListener { val actionRight = createRunnable("right") - //change click-arrow based on preferences - val flipFlopArrows = - controller.getPreferences().getBoolean(GeneralKeys.FLIP_FLOP_ARROWS, false) - return if (flipFlopArrows) { - createOnTouchListener( - rangeRight, actionRight, - R.drawable.trait_chevron_right_pressed, - R.drawable.trait_chevron_right - ) - } else { - createOnTouchListener( - rangeRight, actionRight, - R.drawable.chevron_right_pressed, - R.drawable.chevron_right - ) - } + return createOnTouchListener( + rangeRight, actionRight, + R.drawable.chevron_right_pressed, + R.drawable.chevron_right + ) } private fun createOnTouchListener( 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..f03ccbbbf 100644 --- a/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt +++ b/app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt @@ -87,59 +87,28 @@ class TraitBoxView : ConstraintLayout { fun connectRangeBox(rangeBoxView: RangeBoxView) { - //determine trait button function based on user-preferences - //issues217 introduces the ability to swap trait and plot arrows - val flipFlopArrows: Boolean = - controller.getPreferences().getBoolean(GeneralKeys.FLIP_FLOP_ARROWS, false) - if (flipFlopArrows) { - traitLeft = rangeBoxView.getRangeLeft() - traitRight = rangeBoxView.getRangeRight() - } else { - traitLeft = findViewById(R.id.traitLeft) - traitRight = findViewById(R.id.traitRight) - } + traitLeft = findViewById(R.id.traitLeft) + traitRight = findViewById(R.id.traitRight) traitDetails = findViewById(R.id.traitDetails) - //change click-arrow based on preferences - if (flipFlopArrows) { - traitLeft.setImageResource(R.drawable.chevron_left) - traitLeft.setOnTouchListener( - createTraitOnTouchListener( - traitLeft, R.drawable.chevron_left, - R.drawable.chevron_left_pressed - ) - ) - } else { - traitLeft.setImageResource(R.drawable.trait_chevron_left) - traitLeft.setOnTouchListener( - createTraitOnTouchListener( - traitLeft, R.drawable.trait_chevron_left, - R.drawable.trait_chevron_left_pressed - ) + traitLeft.setImageResource(R.drawable.trait_chevron_left) + traitLeft.setOnTouchListener( + createTraitOnTouchListener( + traitLeft, R.drawable.trait_chevron_left, + R.drawable.trait_chevron_left_pressed ) - } + ) // Go to previous trait traitLeft.setOnClickListener { moveTrait("left") } - //change click-arrow based on preferences - if (flipFlopArrows) { - traitRight.setImageResource(R.drawable.chevron_right) - traitRight.setOnTouchListener( - createTraitOnTouchListener( - traitRight, R.drawable.chevron_right, - R.drawable.chevron_right_pressed - ) + traitRight.setImageResource(R.drawable.trait_chevron_right) + traitRight.setOnTouchListener( + createTraitOnTouchListener( + traitRight, R.drawable.trait_chevron_right, + R.drawable.trait_chevron_right_pressed ) - } else { - traitRight.setImageResource(R.drawable.trait_chevron_right) - traitRight.setOnTouchListener( - createTraitOnTouchListener( - traitRight, R.drawable.trait_chevron_right, - R.drawable.trait_chevron_right_pressed - ) - ) - } + ) // Go to next trait traitRight.setOnClickListener { moveTrait("right") } @@ -147,11 +116,12 @@ class TraitBoxView : ConstraintLayout { fun initTraitDetails() { val traitDetails: TextView = findViewById(R.id.traitDetails) - traitDetails.setOnTouchListener { _, event -> + traitDetails.setOnTouchListener { view, event -> when (event.action) { MotionEvent.ACTION_DOWN -> traitDetails.maxLines = 10 MotionEvent.ACTION_UP -> traitDetails.maxLines = 1 } + view?.performClick() true } } @@ -416,9 +386,11 @@ class TraitBoxView : ConstraintLayout { arrow: ImageView, imageIdUp: Int, imageIdDown: Int ): OnTouchListener { - return OnTouchListener { _, event -> + return OnTouchListener { v, event -> when (event.action) { - MotionEvent.ACTION_DOWN -> arrow.setImageResource(imageIdDown) + MotionEvent.ACTION_DOWN -> { + arrow.setImageResource(imageIdDown) + } MotionEvent.ACTION_MOVE -> {} MotionEvent.ACTION_UP -> arrow.setImageResource(imageIdUp) MotionEvent.ACTION_CANCEL -> {} From b3ed55b1c4271a5bc140f76794855e013a2240b7 Mon Sep 17 00:00:00 2001 From: bellerbrock Date: Tue, 17 Dec 2024 17:38:46 -0500 Subject: [PATCH 06/22] include changelog.xml update in release action --- .github/workflows/github-release.yml | 33 +++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index bfe559c8b..2432a2f38 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -80,9 +80,40 @@ jobs: echo "patchVersion=$patchVersion" >> version.properties VERSION=$majorVersion.$minorVersion.$patchVersion + VERSION_CODE=$((majorVersion * 10000 + minorVersion * 100 + patchVersion)) echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "VERSION_CODE=$VERSION_CODE" >> $GITHUB_ENV - - name: Process Changelog Changes + - name: Update changelog.xml + id: update-changelog-xml + run: | + + # Read the Unreleased section and process notes based on type + RELEASE_CONTENT="" CURRENT_TYPE="" + while IFS= read -r note; do + if [[ "$note" == "### Added"* ]]; then + CURRENT_TYPE="new" + elif [[ "$note" == "### Changed"* ]]; then + CURRENT_TYPE="info" + elif [[ "$note" == "### Fixed"* ]]; then + CURRENT_TYPE="bugfix" + elif [[ "$note" =~ ^- ]]; then + clean_note=$(echo "$note" | sed -E 's/^- //; s/ *\(.*\)//') + pr_number=$(echo "$note" | grep -oP 'pull/\K[0-9]+' || echo "N/A") + RELEASE_CONTENT+=" <${CURRENT_TYPE}>${clean_note} (#${pr_number})\n" + fi + done < <(sed -n '/## \[Unreleased\]/,/^## /p' CHANGELOG.md | sed '1,2d' | sed '$d') + + # Generate, insert, and log the new release block + today=$(date +'%Y-%m-%d') + RELEASE_BLOCK=" \n" + RELEASE_BLOCK+="$RELEASE_CONTENT" + RELEASE_BLOCK+=" " + sed -i "//a \\\n$RELEASE_BLOCK" app/src/main/res/raw/changelog.xml + echo "Generated release block:" + echo -e "$RELEASE_BLOCK" + + - name: Process CHANGELOG.md id: process-changelog run: | unreleased_section=$(sed -n '/## \[Unreleased\]/,/^## /p' CHANGELOG.md | sed '1,2d' | sed '$d') From 5ca8bb434d19a4a0b729203a7dcc62d980011eeb Mon Sep 17 00:00:00 2001 From: bellerbrock Date: Tue, 17 Dec 2024 17:39:40 -0500 Subject: [PATCH 07/22] include updated xml file in commit --- .github/workflows/github-release.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 2432a2f38..ca737f61b 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -180,9 +180,7 @@ jobs: if: ${{ success() }} uses: EndBug/add-and-commit@v7 with: - add: | - version.properties - CHANGELOG.md + add: "version.properties CHANGELOG.md app/src/main/res/raw/changelog.xml" message: "Bump ${{ env.BUMP_TYPE }} and update changelog for v${{ env.VERSION }}" token: ${{ secrets.ACTIONS_PAT }} From 29d5a84824a10ac170d1c5931e86372a2ab9261c Mon Sep 17 00:00:00 2001 From: bellerbrock Date: Wed, 18 Dec 2024 12:55:57 -0500 Subject: [PATCH 08/22] display with default max of 100 if max not set --- .../com/fieldbook/tracker/traits/PercentTraitLayout.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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..2db6fee33 100644 --- a/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java +++ b/app/src/main/java/com/fieldbook/tracker/traits/PercentTraitLayout.java @@ -144,7 +144,10 @@ public void afterLoadExists(CollectActivity act, String value) { if (value != null && !value.equals("NA") && !value.isEmpty()) { - seekBar.setMax(Integer.parseInt(getCurrentTrait().getMaximum())); + // Default to max 100 if maximum is not set + String maxString = getCurrentTrait().getMaximum(); + int max = (maxString != null && !maxString.isEmpty()) ? Integer.parseInt(maxString) : 100; + seekBar.setMax(max); int textColor = value.equals(getDefaultValue()) ? Color.BLACK : Color.parseColor(getDisplayColor()); setCurrentValueText(value, textColor); From 7f9e6e4cc3ae3302963cfdf504acd0309644a85e Mon Sep 17 00:00:00 2001 From: Bryan Ellerbrock Date: Thu, 19 Dec 2024 12:35:54 -0500 Subject: [PATCH 09/22] remove PR # from changelog.xml notes --- .github/workflows/github-release.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index ca737f61b..577655652 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -99,8 +99,10 @@ jobs: CURRENT_TYPE="bugfix" elif [[ "$note" =~ ^- ]]; then clean_note=$(echo "$note" | sed -E 's/^- //; s/ *\(.*\)//') - pr_number=$(echo "$note" | grep -oP 'pull/\K[0-9]+' || echo "N/A") - RELEASE_CONTENT+=" <${CURRENT_TYPE}>${clean_note} (#${pr_number})\n" + RELEASE_CONTENT+=" <${CURRENT_TYPE}>${clean_note}\n" + #pr_number=$(echo "$note" | grep -oP 'pull/\K[0-9]+' || echo "N/A") + #RELEASE_CONTENT+=" <${CURRENT_TYPE}>${clean_note} (#${pr_number})\n" + fi done < <(sed -n '/## \[Unreleased\]/,/^## /p' CHANGELOG.md | sed '1,2d' | sed '$d') From d0be026f6d5ff2f55d04cabf8b35e64153716605 Mon Sep 17 00:00:00 2001 From: trife Date: Mon, 23 Dec 2024 20:17:10 +0000 Subject: [PATCH 10/22] Bump and update changelog for v6.0.3 --- CHANGELOG.md | 11 ++++++++++- version.properties | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47a7a42ff..8c8f34bbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +## [v6.0.3] - 2024-12-23 + +### Added + +### Changed + +### Fixed + ## [v6.0.2] - 2024-12-16 ### Added @@ -550,4 +558,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [v6.0.0]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.0 [v6.0.1]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.1 -[v6.0.2]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.2 \ No newline at end of file +[v6.0.2]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.2 +[v6.0.3]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.3 \ No newline at end of file diff --git a/version.properties b/version.properties index dff822e4e..89533df1b 100644 --- a/version.properties +++ b/version.properties @@ -1,3 +1,3 @@ majorVersion=6 minorVersion=0 -patchVersion=2 +patchVersion=3 From c918b643713bc87d0e8599ebe393e8a3235a429a Mon Sep 17 00:00:00 2001 From: bellerbrock Date: Mon, 30 Dec 2024 00:00:48 -0500 Subject: [PATCH 11/22] fix scheduled release /app change check --- .github/workflows/github-release.yml | 51 +++++++++++++++++----------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 577655652..19b4e9e79 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -14,61 +14,74 @@ on: types: [trigger-release] jobs: - release: + check-changes: runs-on: ubuntu-latest - + outputs: + release_required: ${{ steps.release-check.outputs.release_required }} + bump_type: ${{ steps.release-check.outputs.bump_type }} steps: - - name: Checkout Repo uses: actions/checkout@v4 with: token: ${{ secrets.ACTIONS_PAT }} fetch-depth: 0 - - name: Determine Bump Type Based on Trigger - id: determine-bump-type + - name: Determine Release Type and Necessity + id: release-check run: | if [ "${{ github.event_name }}" == "schedule" ]; then LAST_RELEASE_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) - echo "LAST_RELEASE_TAG was $LAST_RELEASE_TAG" + echo "LAST_RELEASE_TAG: $LAST_RELEASE_TAG" LAST_RELEASE_COMMIT=$(git rev-list -n 1 $LAST_RELEASE_TAG) - echo "LAST_RELEASE_COMMIT was $LAST_RELEASE_COMMIT" + echo "LAST_RELEASE_COMMIT: $LAST_RELEASE_COMMIT" + changed_files=$(git diff-tree --no-commit-id --name-only $LAST_RELEASE_COMMIT HEAD | grep '^app' || echo "none") + echo "Changed files: $changed_files" + if [ "$changed_files" != "none" ]; then echo "App directory has changed since the last release, proceeding with new release" + echo "release_required=true" >> $GITHUB_OUTPUT + echo "bump_type=patch" >> $GITHUB_OUTPUT else echo "No app directory changes since the last release. Skipping release." - exit 0 + echo "release_required=false" >> $GITHUB_OUTPUT fi - BUMP_TYPE="patch" elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - BUMP_TYPE="${{ github.event.inputs.bump_type }}" + echo "release_required=true" >> $GITHUB_OUTPUT + echo "bump_type=${{ github.event.inputs.bump_type }}" >> $GITHUB_OUTPUT elif [ "${{ github.event_name }}" == "repository_dispatch" ]; then - BUMP_TYPE="${{ github.event.client_payload.bump_type }}" + echo "release_required=true" >> $GITHUB_OUTPUT + echo "bump_type=${{ github.event.client_payload.bump_type }}" >> $GITHUB_OUTPUT else echo "Unknown trigger source." exit 1 fi + release: + runs-on: ubuntu-latest + needs: check-changes + if: needs.check-changes.outputs.release_required == 'true' + steps: - if [[ ! "$BUMP_TYPE" =~ ^(major|minor|patch)$ ]]; then - echo "Invalid bump type: $BUMP_TYPE" - exit 1 - fi - echo "BUMP_TYPE=$BUMP_TYPE" >> $GITHUB_ENV + - name: Checkout Repo + uses: actions/checkout@v4 + with: + token: ${{ secrets.ACTIONS_PAT }} + fetch-depth: 0 - name: Bump Version in version.properties id: bump-version run: | - echo "Bumping version with BUMP_TYPE: $BUMP_TYPE" + bump_type="${{ needs.check-changes.outputs.bump_type }}" + echo "Bumping version with BUMP_TYPE: $bump_type" source version.properties - if [ "$BUMP_TYPE" == "major" ]; then + if [ "$bump_type" == "major" ]; then majorVersion=$((majorVersion + 1)) minorVersion=0 patchVersion=0 - elif [ "$BUMP_TYPE" == "minor" ]; then + elif [ "$bump_type" == "minor" ]; then minorVersion=$((minorVersion + 1)) patchVersion=0 else From f1be796fba4d08c1ca65b23b76d76d08cb5bdf68 Mon Sep 17 00:00:00 2001 From: trife Date: Mon, 30 Dec 2024 20:17:15 +0000 Subject: [PATCH 12/22] Bump and update changelog for v6.0.4 --- CHANGELOG.md | 11 ++++++++++- version.properties | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c8f34bbc..121151615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +## [v6.0.4] - 2024-12-30 + +### Added + +### Changed + +### Fixed + ## [v6.0.3] - 2024-12-23 ### Added @@ -559,4 +567,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [v6.0.1]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.1 [v6.0.2]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.2 -[v6.0.3]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.3 \ No newline at end of file +[v6.0.3]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.3 +[v6.0.4]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.4 \ No newline at end of file diff --git a/version.properties b/version.properties index 89533df1b..dd0c9f5ce 100644 --- a/version.properties +++ b/version.properties @@ -1,3 +1,3 @@ majorVersion=6 minorVersion=0 -patchVersion=3 +patchVersion=4 From 02ee7caab7c75ede2e2feca4fa1f862d10aa3e88 Mon Sep 17 00:00:00 2001 From: Trevor Rife Date: Fri, 3 Jan 2025 15:38:54 -0600 Subject: [PATCH 13/22] Update version.properties --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index dd0c9f5ce..dff822e4e 100644 --- a/version.properties +++ b/version.properties @@ -1,3 +1,3 @@ majorVersion=6 minorVersion=0 -patchVersion=4 +patchVersion=2 From ed8125e8339906020d415eb08722174bea35a83b Mon Sep 17 00:00:00 2001 From: Trevor Rife Date: Fri, 3 Jan 2025 15:39:39 -0600 Subject: [PATCH 14/22] Update CHANGELOG.md --- CHANGELOG.md | 84 +++++++++++++++++++++------------------------------- 1 file changed, 34 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 121151615..5ccd60257 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,56 +5,40 @@ All notable changes to Field Book app will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] - -### Added - -### Changed - -### Fixed - -## [v6.0.4] - 2024-12-30 - -### Added - -### Changed - -### Fixed - -## [v6.0.3] - 2024-12-23 - -### Added - -### Changed - -### Fixed - -## [v6.0.2] - 2024-12-16 - -### Added -- Trial name included in BrAPI import and field details page (https://github.com/PhenoApps/Field-Book/pull/1121) -- Image EXIF tag metadata includes the device pitch, roll, and yaw at the time of capture (https://github.com/PhenoApps/Field-Book/pull/1115) -- Setting added to reset preferences to default (https://github.com/PhenoApps/Field-Book/pull/1118) - -### Changed -- Fields with different BrAPI study IDs can now have the same name (https://github.com/PhenoApps/Field-Book/pull/1095) - -### Fixed -- Min and max values now correctly saved for BrAPI traits (https://github.com/PhenoApps/Field-Book/pull/1103) -- Label print observations are now assigned to the correct entry when navigating before the print is complete (https://github.com/PhenoApps/Field-Book/pull/1109) -- Improved database export messaging (https://github.com/PhenoApps/Field-Book/pull/1112) -- Go to ID dialog now has cursor focus when opened (https://github.com/PhenoApps/Field-Book/pull/1108) - +## [Unreleased] + +### Added + +### Changed + +### Fixed + +## [v6.0.2] - 2024-12-16 + +### Added +- Trial name included in BrAPI import and field details page (https://github.com/PhenoApps/Field-Book/pull/1121) +- Image EXIF tag metadata includes the device pitch, roll, and yaw at the time of capture (https://github.com/PhenoApps/Field-Book/pull/1115) +- Setting added to reset preferences to default (https://github.com/PhenoApps/Field-Book/pull/1118) + +### Changed +- Fields with different BrAPI study IDs can now have the same name (https://github.com/PhenoApps/Field-Book/pull/1095) + +### Fixed +- Min and max values now correctly saved for BrAPI traits (https://github.com/PhenoApps/Field-Book/pull/1103) +- Label print observations are now assigned to the correct entry when navigating before the print is complete (https://github.com/PhenoApps/Field-Book/pull/1109) +- Improved database export messaging (https://github.com/PhenoApps/Field-Book/pull/1112) +- Go to ID dialog now has cursor focus when opened (https://github.com/PhenoApps/Field-Book/pull/1108) + ## [v6.0.1] - 2024-12-09 -### Added -- Accession number now included for germplasm imported via BrAPI (https://github.com/PhenoApps/Field-Book/pull/1093) -- Option added at trait creation to keep keyboard closed when navigating to text traits (https://github.com/PhenoApps/Field-Book/pull/1102) +### Added +- Accession number now included for germplasm imported via BrAPI (https://github.com/PhenoApps/Field-Book/pull/1093) +- Option added at trait creation to keep keyboard closed when navigating to text traits (https://github.com/PhenoApps/Field-Book/pull/1102) - Proximity check added to disable GeoNav when away from field (https://github.com/PhenoApps/Field-Book/pull/1098) - Experimental setting to use media keys for entry/trait navigation and picture capture (https://github.com/PhenoApps/Field-Book/pull/1089) -### Changed -- Fields and traits that have already been imported are now hidden on the BrAPI import screen (https://github.com/PhenoApps/Field-Book/pull/1091) +### Changed +- Fields and traits that have already been imported are now hidden on the BrAPI import screen (https://github.com/PhenoApps/Field-Book/pull/1091) - Long-pressing delete button on bottom toolbar will remove all observations for that trait (https://github.com/PhenoApps/Field-Book/pull/1099) - Current time now used as lastSyncedTime when downloading Observations via BrAPI (https://github.com/PhenoApps/Field-Book/pull/1071) - lastSyncedTime updated after creating/updating Observations (https://github.com/PhenoApps/Field-Book/pull/1071) @@ -564,8 +548,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [v5.6.26]: https://github.com/PhenoApps/Field-Book/releases/tag/5.6.26 [v6.0.0]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.0 - -[v6.0.1]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.1 -[v6.0.2]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.2 -[v6.0.3]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.3 -[v6.0.4]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.4 \ No newline at end of file + +[v6.0.1]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.1 +[v6.0.2]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.2 +[v6.0.3]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.3 +[v6.0.4]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.4 From 5183bce77b19c513cf5eb26d862fab0ce71dec19 Mon Sep 17 00:00:00 2001 From: Git Action Bot Date: Fri, 3 Jan 2025 21:40:33 +0000 Subject: [PATCH 15/22] Update CHANGELOG.md with release note from PR #1127 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ccd60257..6d093525e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -### Fixed +### Fixed +- Percent traits imported via BrAPI without min and max values default to 0/100 (https://github.com/PhenoApps/Field-Book/pull/1127) ## [v6.0.2] - 2024-12-16 From a53019e2b7daaa48ef7e4fce6e808b79df01fdd3 Mon Sep 17 00:00:00 2001 From: trife Date: Fri, 3 Jan 2025 16:42:40 -0500 Subject: [PATCH 16/22] string --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bffcbc353..5d4c88bdf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -752,7 +752,7 @@ Arrows skip entries that already have values for all traits Swap Navigation - Swap the behavior of the entry and trait arrows + Swap the location of the entry and trait arrows Navigate With Volume Keys Disabled From 6f0bf4b6801cfcc76a2e43d35d603c441c18b04e Mon Sep 17 00:00:00 2001 From: Git Action Bot Date: Fri, 3 Jan 2025 21:44:54 +0000 Subject: [PATCH 17/22] Update CHANGELOG.md with release note from PR #1123 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d093525e..cd32d90e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -### Changed +### Changed +- Swap navigation now changes position of entry/trait navigation instead of only behavior (https://github.com/PhenoApps/Field-Book/pull/1123) ### Fixed - Percent traits imported via BrAPI without min and max values default to 0/100 (https://github.com/PhenoApps/Field-Book/pull/1127) From 3bf77fb5635e89081e3702d06951a178b2a0fe4e Mon Sep 17 00:00:00 2001 From: Trevor Rife Date: Fri, 3 Jan 2025 16:50:45 -0500 Subject: [PATCH 18/22] New translations strings.xml (Spanish, Mexico) --- app/src/main/res/values-es-rMX/strings.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index 252f2b7d3..02336c382 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -622,7 +622,6 @@ Las flechas omiten las entradas que ya tienen valores para la variable actual Las flechas omiten las entradas que ya tienen valores para todas las variables Cambiar Navegación - Cambiar el comportamiento de las flechas de entrada y variables Navegar con las Teclas de Volumen Desactivado Teclas de volumen navegan variables From 91c399b58b4d8cc95933b0f799e7bd0f9acc1aec Mon Sep 17 00:00:00 2001 From: trife Date: Fri, 3 Jan 2025 16:54:22 -0500 Subject: [PATCH 19/22] update changelog --- app/src/main/res/raw/changelog.xml | 33 +++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/raw/changelog.xml b/app/src/main/res/raw/changelog.xml index c764f80f8..571dbbafe 100644 --- a/app/src/main/res/raw/changelog.xml +++ b/app/src/main/res/raw/changelog.xml @@ -1,7 +1,38 @@ + Trial name included in BrAPI import and field details page + Image EXIF tag metadata includes the device pitch, roll, and yaw at the time of capture + Setting added to reset preferences to default + Fields with different BrAPI study IDs can now have the same name + Min and max values now correctly saved for BrAPI traits + Label print observations are now assigned to the correct entry when navigating before the print is complete + Improved database export messaging + Go to ID dialog now has cursor focus when opened + + + + Accession number now included for germplasm imported via BrAPI + Option added at trait creation to keep keyboard closed when navigating to text traits + Proximity check added to disable GeoNav when away from field + Experimental setting to use media keys for entry/trait navigation and picture capture + Fields and traits that have already been imported are now hidden on the BrAPI import screen + Long-pressing delete button on bottom toolbar will remove all observations for that trait + Current time now used as lastSyncedTime when downloading Observations via BrAPI + lastSyncedTime updated after creating/updating Observations + Improved support for BrAPI servers not using https + "observation_time_stamp" and "last_synced_time" now correctly saved in DAO + Synced and updated records now correctly identified + + + Fields list updated to include bulk actions and individual field pages From d7b20a98c49689e361d2d192542489862b4aca29 Mon Sep 17 00:00:00 2001 From: Git Action Bot Date: Sat, 4 Jan 2025 01:47:12 +0000 Subject: [PATCH 20/22] Update CHANGELOG.md with release note from PR #1110 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd32d90e4..70b7b40a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Swap navigation now changes position of entry/trait navigation instead of only behavior (https://github.com/PhenoApps/Field-Book/pull/1123) ### Fixed +- Barcodes scanned for data entry are now correctly checked for valid values (https://github.com/PhenoApps/Field-Book/pull/1110) - Percent traits imported via BrAPI without min and max values default to 0/100 (https://github.com/PhenoApps/Field-Book/pull/1127) ## [v6.0.2] - 2024-12-16 From 2d7f5f354406426321c8a0e71a12aedcc5f0db41 Mon Sep 17 00:00:00 2001 From: Git Action Bot Date: Sat, 4 Jan 2025 01:58:40 +0000 Subject: [PATCH 21/22] Update CHANGELOG.md with release note from PR #1122 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70b7b40a5..3d39f1f57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Swap navigation now changes position of entry/trait navigation instead of only behavior (https://github.com/PhenoApps/Field-Book/pull/1123) ### Fixed +- Improved warnings for missing or corrupted storage directory (https://github.com/PhenoApps/Field-Book/pull/1122) - Barcodes scanned for data entry are now correctly checked for valid values (https://github.com/PhenoApps/Field-Book/pull/1110) - Percent traits imported via BrAPI without min and max values default to 0/100 (https://github.com/PhenoApps/Field-Book/pull/1127) From 09051c4a2632c4507e3c0d3ac0d33fc93c4cfa76 Mon Sep 17 00:00:00 2001 From: trife Date: Mon, 6 Jan 2025 20:16:32 +0000 Subject: [PATCH 22/22] Bump and update changelog for v6.0.3 --- CHANGELOG.md | 12 +++++++++++- app/src/main/res/raw/changelog.xml | 7 +++++++ version.properties | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d39f1f57..cde354913 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,15 @@ All notable changes to Field Book app will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [Unreleased] + +### Added + +### Changed + +### Fixed + +## [v6.0.3] - 2025-01-06 ### Added @@ -557,3 +565,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [v6.0.2]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.2 [v6.0.3]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.3 [v6.0.4]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.4 + +[v6.0.3]: https://github.com/PhenoApps/Field-Book/releases/tag/6.0.3 \ No newline at end of file diff --git a/app/src/main/res/raw/changelog.xml b/app/src/main/res/raw/changelog.xml index 571dbbafe..f38070724 100644 --- a/app/src/main/res/raw/changelog.xml +++ b/app/src/main/res/raw/changelog.xml @@ -1,5 +1,12 @@ + + Swap navigation now changes position of entry/trait navigation instead of only behavior + Improved warnings for missing or corrupted storage directory + Barcodes scanned for data entry are now correctly checked for valid values + Percent traits imported via BrAPI without min and max values default to 0/100 + +