From 43c4d32e541e0ec56f64713a1de15adf6fb08b64 Mon Sep 17 00:00:00 2001 From: "S. Grimault" Date: Tue, 25 Aug 2020 20:22:28 +0200 Subject: [PATCH 01/11] fix: single task mode for main activity --- occtax/src/main/AndroidManifest.xml | 2 +- occtax/src/main/res/values/strings.xml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/occtax/src/main/AndroidManifest.xml b/occtax/src/main/AndroidManifest.xml index e881e5ac..62510b02 100644 --- a/occtax/src/main/AndroidManifest.xml +++ b/occtax/src/main/AndroidManifest.xml @@ -16,7 +16,7 @@ + android:launchMode="singleTask"> diff --git a/occtax/src/main/res/values/strings.xml b/occtax/src/main/res/values/strings.xml index e08f971d..11f82600 100644 --- a/occtax/src/main/res/values/strings.xml +++ b/occtax/src/main/res/values/strings.xml @@ -4,7 +4,6 @@ Occtax %1$s (%2$d)\n%3$s - @string/app_name Settings Dataset Observers From 313ba8cb54d65aeb763ef66718581549044084c9 Mon Sep 17 00:00:00 2001 From: "S. Grimault" Date: Sat, 5 Sep 2020 15:21:12 +0200 Subject: [PATCH 02/11] feat(#7): allow to configure default properties to show from information and counting fragments --- .../occtax/input/CountingMetadata.kt | 20 ++- .../fr/geonature/occtax/input/InputTaxon.kt | 47 ++++--- .../geonature/occtax/settings/AppSettings.kt | 12 +- .../occtax/settings/NomenclatureSettings.kt | 52 +++++++ .../occtax/settings/PropertySettings.kt | 47 +++++++ .../io/OnAppSettingsJsonReaderListenerImpl.kt | 132 ++++++++++++++++-- .../ui/input/InputPagerFragmentActivity.kt | 10 +- .../ui/input/counting/CountingFragment.kt | 23 ++- .../counting/EditCountingMetadataActivity.kt | 14 +- .../counting/EditCountingMetadataFragment.kt | 18 ++- .../NomenclatureTypesRecyclerViewAdapter.kt | 58 ++++++-- .../input/informations/InformationFragment.kt | 25 +++- .../NomenclatureTypesRecyclerViewAdapter.kt | 43 +++--- occtax/src/main/res/values-fr/strings.xml | 2 + occtax/src/main/res/values/strings.xml | 2 + .../settings/io/AppSettingsJsonReaderTest.kt | 68 +++++++++ .../resources/fixtures/settings_occtax.json | 34 +++++ 17 files changed, 530 insertions(+), 77 deletions(-) create mode 100644 occtax/src/main/java/fr/geonature/occtax/settings/NomenclatureSettings.kt create mode 100644 occtax/src/main/java/fr/geonature/occtax/settings/PropertySettings.kt diff --git a/occtax/src/main/java/fr/geonature/occtax/input/CountingMetadata.kt b/occtax/src/main/java/fr/geonature/occtax/input/CountingMetadata.kt index de5d2af6..60b00980 100644 --- a/occtax/src/main/java/fr/geonature/occtax/input/CountingMetadata.kt +++ b/occtax/src/main/java/fr/geonature/occtax/input/CountingMetadata.kt @@ -16,7 +16,7 @@ class CountingMetadata() : Parcelable { internal set val properties: SortedMap = - TreeMap(Comparator { o1, o2 -> + TreeMap { o1, o2 -> val i1 = defaultMnemonic.indexOfFirst { it.first == o1 } val i2 = defaultMnemonic.indexOfFirst { it.first == o2 } @@ -25,9 +25,19 @@ class CountingMetadata() : Parcelable { i2 == -1 -> -1 else -> i1 - i2 } - }) + } var min: Int = 1 + get() = field.coerceAtLeast(0) + set(value) { + field = value.coerceAtLeast(0) + if (field > max) max = field + } var max: Int = 1 + get() = field.coerceAtLeast(0) + set(value) { + field = value.coerceAtLeast(0) + if (field < min) min = field + } constructor(source: Parcel) : this() { index = source.readInt() @@ -84,6 +94,12 @@ class CountingMetadata() : Parcelable { companion object { + /** + * default properties as pair: + * + * * first value: mnemonic code from nomenclature type + * * second value: the corresponding view type + */ val defaultMnemonic = arrayOf( Pair( "STADE_VIE", diff --git a/occtax/src/main/java/fr/geonature/occtax/input/InputTaxon.kt b/occtax/src/main/java/fr/geonature/occtax/input/InputTaxon.kt index b97a786d..a981af62 100644 --- a/occtax/src/main/java/fr/geonature/occtax/input/InputTaxon.kt +++ b/occtax/src/main/java/fr/geonature/occtax/input/InputTaxon.kt @@ -96,38 +96,53 @@ class InputTaxon : AbstractInputTaxon { companion object { + /** + * default properties as triple: + * + * * first value: mnemonic code from nomenclature type + * * second value: the corresponding view type + * * third value: if this property is visible by default + */ val defaultPropertiesMnemonic = arrayOf( - Pair( + Triple( "METH_OBS", - NomenclatureTypeViewType.NOMENCLATURE_TYPE + NomenclatureTypeViewType.NOMENCLATURE_TYPE, + true ), - Pair( + Triple( "ETA_BIO", - NomenclatureTypeViewType.NOMENCLATURE_TYPE + NomenclatureTypeViewType.NOMENCLATURE_TYPE, + true ), - Pair( + Triple( "METH_DETERMIN", - NomenclatureTypeViewType.NOMENCLATURE_TYPE + NomenclatureTypeViewType.NOMENCLATURE_TYPE, + false ), - Pair( + Triple( "DETERMINER", - NomenclatureTypeViewType.TEXT_SIMPLE + NomenclatureTypeViewType.TEXT_SIMPLE, + false ), - Pair( + Triple( "STATUT_BIO", - NomenclatureTypeViewType.NOMENCLATURE_TYPE + NomenclatureTypeViewType.NOMENCLATURE_TYPE, + false ), - Pair( + Triple( "NATURALITE", - NomenclatureTypeViewType.NOMENCLATURE_TYPE + NomenclatureTypeViewType.NOMENCLATURE_TYPE, + false ), - Pair( + Triple( "PREUVE_EXIST", - NomenclatureTypeViewType.NOMENCLATURE_TYPE + NomenclatureTypeViewType.NOMENCLATURE_TYPE, + false ), - Pair( + Triple( "COMMENT", - NomenclatureTypeViewType.TEXT_MULTIPLE + NomenclatureTypeViewType.TEXT_MULTIPLE, + false ) ) diff --git a/occtax/src/main/java/fr/geonature/occtax/settings/AppSettings.kt b/occtax/src/main/java/fr/geonature/occtax/settings/AppSettings.kt index e81cdbe7..c656e64d 100644 --- a/occtax/src/main/java/fr/geonature/occtax/settings/AppSettings.kt +++ b/occtax/src/main/java/fr/geonature/occtax/settings/AppSettings.kt @@ -12,12 +12,14 @@ import fr.geonature.maps.settings.MapSettings */ data class AppSettings( var areaObservationDuration: Int = DEFAULT_AREA_OBSERVATION_DURATION, - var mapSettings: MapSettings? = null + var mapSettings: MapSettings? = null, + var nomenclatureSettings: NomenclatureSettings? = null ) : IAppSettings { private constructor(source: Parcel) : this( source.readInt(), - source.readParcelable(MapSettings::class.java.classLoader) as MapSettings? + source.readParcelable(MapSettings::class.java.classLoader) as MapSettings?, + source.readParcelable(NomenclatureSettings::class.java.classLoader) as NomenclatureSettings? ) override fun describeContents(): Int { @@ -34,6 +36,10 @@ data class AppSettings( mapSettings, 0 ) + it.writeParcelable( + nomenclatureSettings, + 0 + ) } } @@ -43,6 +49,7 @@ data class AppSettings( if (areaObservationDuration != other.areaObservationDuration) return false if (mapSettings != other.mapSettings) return false + if (nomenclatureSettings != other.nomenclatureSettings) return false return true } @@ -50,6 +57,7 @@ data class AppSettings( override fun hashCode(): Int { var result = areaObservationDuration result = 31 * result + (mapSettings?.hashCode() ?: 0) + result = 31 * result + (nomenclatureSettings?.hashCode() ?: 0) return result } diff --git a/occtax/src/main/java/fr/geonature/occtax/settings/NomenclatureSettings.kt b/occtax/src/main/java/fr/geonature/occtax/settings/NomenclatureSettings.kt new file mode 100644 index 00000000..bc934544 --- /dev/null +++ b/occtax/src/main/java/fr/geonature/occtax/settings/NomenclatureSettings.kt @@ -0,0 +1,52 @@ +package fr.geonature.occtax.settings + +import android.os.Parcel +import android.os.Parcelable + +/** + * Nomenclature settings. + * + * @author [S. Grimault](mailto:sebastien.grimault@gmail.com) + */ +data class NomenclatureSettings( + val information: List, + val counting: List +) : Parcelable { + private constructor(source: Parcel) : this( + mutableListOf(), + mutableListOf() + ) { + source.readTypedList( + information, + PropertySettings.CREATOR + ) + source.readTypedList( + counting, + PropertySettings.CREATOR + ) + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel( + dest: Parcel?, + flags: Int + ) { + dest?.also { + it.writeTypedList(information) + it.writeTypedList(counting) + } + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): NomenclatureSettings { + return NomenclatureSettings(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/occtax/src/main/java/fr/geonature/occtax/settings/PropertySettings.kt b/occtax/src/main/java/fr/geonature/occtax/settings/PropertySettings.kt new file mode 100644 index 00000000..10ee56ed --- /dev/null +++ b/occtax/src/main/java/fr/geonature/occtax/settings/PropertySettings.kt @@ -0,0 +1,47 @@ +package fr.geonature.occtax.settings + +import android.os.Parcel +import android.os.Parcelable +import androidx.core.os.ParcelCompat + +/** + * Property settings. + * + * @author [S. Grimault](mailto:sebastien.grimault@gmail.com) + */ +data class PropertySettings( + val key: String, + val visible: Boolean, + val default: Boolean +): Parcelable { + private constructor(source: Parcel) : this( + source.readString()!!, + ParcelCompat.readBoolean(source), + ParcelCompat.readBoolean(source) + ) + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel( + dest: Parcel?, + flags: Int + ) { + dest?.also { + it.writeString(key) + ParcelCompat.writeBoolean(it, visible) + ParcelCompat.writeBoolean(it, default) + } + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): PropertySettings { + return PropertySettings(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/occtax/src/main/java/fr/geonature/occtax/settings/io/OnAppSettingsJsonReaderListenerImpl.kt b/occtax/src/main/java/fr/geonature/occtax/settings/io/OnAppSettingsJsonReaderListenerImpl.kt index ce41535e..fac0834c 100644 --- a/occtax/src/main/java/fr/geonature/occtax/settings/io/OnAppSettingsJsonReaderListenerImpl.kt +++ b/occtax/src/main/java/fr/geonature/occtax/settings/io/OnAppSettingsJsonReaderListenerImpl.kt @@ -2,9 +2,14 @@ package fr.geonature.occtax.settings.io import android.util.JsonReader import android.util.JsonToken +import android.util.Log import fr.geonature.commons.settings.io.AppSettingsJsonReader +import fr.geonature.maps.settings.MapSettings import fr.geonature.maps.settings.io.MapSettingsReader import fr.geonature.occtax.settings.AppSettings +import fr.geonature.occtax.settings.NomenclatureSettings +import fr.geonature.occtax.settings.PropertySettings +import java.io.IOException /** * Default implementation of [AppSettingsJsonReader.OnAppSettingsJsonReaderListener]. @@ -25,24 +30,123 @@ class OnAppSettingsJsonReaderListenerImpl : ) { when (keyName) { "area_observation_duration" -> appSettings.areaObservationDuration = reader.nextInt() - "map" -> { - if (reader.peek() == JsonToken.BEGIN_OBJECT) { - readMapSettings( - reader, - appSettings - ) - } else { - reader.skipValue() + "map" -> appSettings.mapSettings = readMapSettings(reader) + "nomenclature" -> appSettings.nomenclatureSettings = readNomenclatureSettings(reader) + else -> reader.skipValue() + } + } + + private fun readMapSettings(reader: JsonReader): MapSettings? { + if (reader.peek() != JsonToken.BEGIN_OBJECT) { + reader.skipValue() + + return null + } + + return MapSettingsReader().read(reader) + } + + private fun readNomenclatureSettings(reader: JsonReader): NomenclatureSettings? { + if (reader.peek() != JsonToken.BEGIN_OBJECT) { + reader.skipValue() + + return null + } + + val information = mutableListOf() + val counting = mutableListOf() + + reader.beginObject() + + while (reader.hasNext()) { + when (reader.nextName()) { + "information" -> information.addAll(readPropertySettingsAsList(reader)) + "counting" -> counting.addAll(readPropertySettingsAsList(reader)) + } + } + + reader.endObject() + + return NomenclatureSettings( + information, + counting + ) + } + + private fun readPropertySettingsAsList(reader: JsonReader): List { + if (reader.peek() != JsonToken.BEGIN_ARRAY) { + reader.skipValue() + + return emptyList() + } + + val propertySettingsList = mutableListOf() + + reader.beginArray() + + while (reader.hasNext()) { + try { + readPropertySettings(reader)?.also { + propertySettingsList.add(it) } + } catch (ioe: IOException) { + Log.w( + TAG, + ioe + ) } - else -> reader.skipValue() } + + reader.endArray() + + return propertySettingsList } - private fun readMapSettings( - reader: JsonReader, - appSettings: AppSettings - ) { - appSettings.mapSettings = MapSettingsReader().read(reader) + @Throws(Exception::class) + private fun readPropertySettings(reader: JsonReader): PropertySettings? { + return when (reader.peek()) { + JsonToken.BEGIN_OBJECT -> { + reader.beginObject() + + var key: String? = null + var visible = true + var default = true + + while (reader.hasNext()) { + when (reader.nextName()) { + "key" -> key = reader.nextString() + "visible" -> visible = reader.nextBoolean() + "default" -> default = reader.nextBoolean() + else -> reader.skipValue() + } + } + + reader.endObject() + + if (key.isNullOrEmpty()) { + return null + } + + return PropertySettings( + key, + visible, + default + ) + } + JsonToken.STRING -> PropertySettings( + reader.nextString(), + visible = true, + default = true + ) + else -> { + reader.skipValue() + null + } + } + } + + companion object { + + private val TAG = OnAppSettingsJsonReaderListenerImpl::class.java.name } } diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/InputPagerFragmentActivity.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/InputPagerFragmentActivity.kt index 7833b1ab..28f94521 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/InputPagerFragmentActivity.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/InputPagerFragmentActivity.kt @@ -46,7 +46,7 @@ class InputPagerFragmentActivity : AbstractNavigationHistoryPagerFragmentActivit InputViewModel::class.java ) - appSettings = intent.getParcelableExtra(EXTRA_APP_SETTINGS) + appSettings = intent.getParcelableExtra(EXTRA_APP_SETTINGS)!! input = intent.getParcelableExtra(EXTRA_INPUT) ?: Input() val lastAddedInputTaxon = input.getLastAddedInputTaxon() @@ -86,11 +86,15 @@ class InputPagerFragmentActivity : AbstractNavigationHistoryPagerFragmentActivit ) put( R.string.pager_fragment_information_title, - InformationFragment.newInstance() + InformationFragment.newInstance( + *appSettings.nomenclatureSettings?.information?.toTypedArray() ?: emptyArray() + ) ) put( R.string.pager_fragment_counting_title, - CountingFragment.newInstance() + CountingFragment.newInstance( + *appSettings.nomenclatureSettings?.counting?.toTypedArray() ?: emptyArray() + ) ) put( R.string.pager_fragment_summary_title, diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/CountingFragment.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/CountingFragment.kt index 9372925e..1909191b 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/CountingFragment.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/CountingFragment.kt @@ -20,6 +20,7 @@ import fr.geonature.occtax.R import fr.geonature.occtax.input.CountingMetadata import fr.geonature.occtax.input.Input import fr.geonature.occtax.input.InputTaxon +import fr.geonature.occtax.settings.PropertySettings import fr.geonature.occtax.ui.input.IInputFragment import fr.geonature.viewpager.ui.AbstractPagerFragmentActivity import fr.geonature.viewpager.ui.IValidateFragment @@ -70,7 +71,11 @@ class CountingFragment : Fragment(), ?: Taxonomy( Taxonomy.ANY, Taxonomy.ANY - ) + ), + null, + *(arguments?.getParcelableArray(ARG_PROPERTIES) + ?.map { it as PropertySettings } + ?.toTypedArray() ?: emptyArray()) ), 0 ) @@ -88,7 +93,10 @@ class CountingFragment : Fragment(), Taxonomy.ANY, Taxonomy.ANY ), - item + item, + *(arguments?.getParcelableArray(ARG_PROPERTIES) + ?.map { it as PropertySettings } + ?.toTypedArray() ?: emptyArray()) ), 0 ) @@ -245,12 +253,21 @@ class CountingFragment : Fragment(), companion object { + private const val ARG_PROPERTIES = "arg_properties" + /** * Use this factory method to create a new instance of [CountingFragment]. * * @return A new instance of [CountingFragment] */ @JvmStatic - fun newInstance() = CountingFragment() + fun newInstance(vararg propertySettings: PropertySettings) = CountingFragment().apply { + arguments = Bundle().apply { + putParcelableArray( + ARG_PROPERTIES, + propertySettings + ) + } + } } } diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt index 764ed25c..9bf4a909 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt @@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity import fr.geonature.commons.data.Taxonomy import fr.geonature.occtax.R import fr.geonature.occtax.input.CountingMetadata +import fr.geonature.occtax.settings.PropertySettings /** * Edit additional counting information Activity. @@ -40,7 +41,10 @@ class EditCountingMetadataActivity : AppCompatActivity(), Taxonomy.ANY, Taxonomy.ANY ), - countingMetadata + countingMetadata, + *(intent.getParcelableArrayExtra(EXTRA_PROPERTIES) + ?.map { it as PropertySettings } + ?.toTypedArray() ?: emptyArray()) ) ) .commit() @@ -82,11 +86,13 @@ class EditCountingMetadataActivity : AppCompatActivity(), const val EXTRA_TAXONOMY = "extra_taxonomy" const val EXTRA_COUNTING_METADATA = "extra_counting_metadata" + const val EXTRA_PROPERTIES = "extra_properties" fun newIntent( context: Context, taxonomy: Taxonomy, - countingMetadata: CountingMetadata? = null + countingMetadata: CountingMetadata? = null, + vararg propertySettings: PropertySettings ): Intent { return Intent( context, @@ -102,6 +108,10 @@ class EditCountingMetadataActivity : AppCompatActivity(), countingMetadata ) } + putExtra( + EXTRA_PROPERTIES, + propertySettings + ) } } } diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt index 09c37701..f661040c 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt @@ -26,6 +26,7 @@ import fr.geonature.occtax.R import fr.geonature.occtax.input.CountingMetadata import fr.geonature.occtax.input.Input import fr.geonature.occtax.input.PropertyValue +import fr.geonature.occtax.settings.PropertySettings import fr.geonature.occtax.ui.input.dialog.ChooseNomenclatureDialogFragment /** @@ -86,7 +87,14 @@ class EditCountingMetadataFragment : Fragment(), when (loader.id) { LOADER_NOMENCLATURE_TYPES -> { - adapter?.bind(data) + val defaultProperties = arguments?.getParcelableArray(ARG_PROPERTIES) + ?.map { it as PropertySettings } + ?.toTypedArray() ?: emptyArray() + + adapter?.bind( + data, + *defaultProperties + ) loadDefaultNomenclatureValues() } LOADER_DEFAULT_NOMENCLATURE_VALUES -> { @@ -256,6 +264,7 @@ class EditCountingMetadataFragment : Fragment(), const val ARG_TAXONOMY = "arg_taxonomy" const val ARG_COUNTING_METADATA = "arg_counting_metadata" + const val ARG_PROPERTIES = "arg_properties" private const val LOADER_NOMENCLATURE_TYPES = 1 private const val LOADER_DEFAULT_NOMENCLATURE_VALUES = 2 @@ -270,7 +279,8 @@ class EditCountingMetadataFragment : Fragment(), @JvmStatic fun newInstance( taxonomy: Taxonomy, - countingMetadata: CountingMetadata? = null + countingMetadata: CountingMetadata? = null, + vararg propertySettings: PropertySettings ) = EditCountingMetadataFragment().apply { arguments = Bundle().apply { putParcelable( @@ -283,6 +293,10 @@ class EditCountingMetadataFragment : Fragment(), countingMetadata ) } + putParcelableArray( + ARG_PROPERTIES, + propertySettings + ) } } } diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/NomenclatureTypesRecyclerViewAdapter.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/NomenclatureTypesRecyclerViewAdapter.kt index 64b21913..e7407eb6 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/NomenclatureTypesRecyclerViewAdapter.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/NomenclatureTypesRecyclerViewAdapter.kt @@ -19,6 +19,7 @@ import fr.geonature.occtax.R import fr.geonature.occtax.input.CountingMetadata import fr.geonature.occtax.input.NomenclatureTypeViewType import fr.geonature.occtax.input.PropertyValue +import fr.geonature.occtax.settings.PropertySettings import java.util.Locale /** @@ -53,7 +54,8 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT } override fun getItemCount(): Int { - return (properties.size - 1).coerceAtLeast(0) + return properties.filter { p -> mnemonicFilter.find { it.first == p.code }?.second != NomenclatureTypeViewType.MIN_MAX }.size + + properties.filter { p -> mnemonicFilter.find { it.first == p.code }?.second == NomenclatureTypeViewType.MIN_MAX }.size.coerceAtMost(1) } override fun onBindViewHolder( @@ -75,7 +77,7 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT .toList() } - fun bind(cursor: Cursor?) { + fun bind(cursor: Cursor?, vararg defaultPropertySettings: PropertySettings) { availableNomenclatureTypes.clear() cursor?.run { @@ -86,17 +88,23 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT while (!this.isAfterLast) { NomenclatureType.fromCursor(this) ?.run { - val validNomenclatureType = - mnemonicFilter.find { it.first == this.mnemonic } - if (validNomenclatureType != null) { - availableNomenclatureTypes.add(validNomenclatureType) + (if (defaultPropertySettings.isEmpty()) { + mnemonicFilter.find { it.first == mnemonic } + } else { + defaultPropertySettings.find { it.key == mnemonic && it.visible } + ?.let { property -> mnemonicFilter.find { it.first == property.key } } + })?.also { + availableNomenclatureTypes.add(it) } } cursor.moveToNext() } // add default mnemonic filters - availableNomenclatureTypes.addAll(mnemonicFilter.filter { it.second != NomenclatureTypeViewType.NOMENCLATURE_TYPE }) + availableNomenclatureTypes.addAll(mnemonicFilter.filter { + it.second != NomenclatureTypeViewType.NOMENCLATURE_TYPE && + (defaultPropertySettings.isEmpty() || defaultPropertySettings.any { p -> p.key == it.first }) + }) } availableNomenclatureTypes.sortWith(Comparator { o1, o2 -> @@ -319,6 +327,7 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT } inner class MinMaxViewHolder(parent: ViewGroup) : AbstractCardViewHolder(parent) { + private var title: TextView = contentView.findViewById(android.R.id.title) private var editMin: EditText = contentView.findViewById(R.id.editMin) private var editMax: EditText = contentView.findViewById(R.id.editMax) @@ -388,6 +397,7 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT init { with(editMin) { + visibility = View.GONE addTextChangedListener(minTextWatcher) setOnFocusChangeListener { v, hasFocus -> if (!hasFocus) { @@ -398,6 +408,7 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT } with(editMax) { + visibility = View.GONE addTextChangedListener(maxTextWatcher) setOnFocusChangeListener { v, hasFocus -> if (!hasFocus) { @@ -418,13 +429,32 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT } override fun onBind(property: PropertyValue) { - val minProperty = properties.firstOrNull { it.code == "MIN" } - val maxProperty = properties.firstOrNull { it.code == "MAX" } + setTitle(property) + + editMin.visibility = + if (hasMinAndMaxPropertyValues() || property.code == "MIN") View.VISIBLE else View.GONE + editMax.visibility = + if (hasMinAndMaxPropertyValues() || property.code == "MAX") View.VISIBLE else View.GONE + + (properties.firstOrNull { it.code == "MIN" }?.value as Int?)?.also { + setMinValue(it) + } - if (minProperty == null || maxProperty == null) return + (properties.firstOrNull { it.code == "MAX" }?.value as Int?)?.also { + setMaxValue(it) + } + } - setMinValue(minProperty.value as Int) - setMaxValue(maxProperty.value as Int) + private fun setTitle(property: PropertyValue) { + title.setText(if (hasMinAndMaxPropertyValues()) { + R.string.counting_min_max_title + } else { + contentView.resources.getIdentifier( + "counting_${property.code.toLowerCase(Locale.ROOT)}_title", + "string", + contentView.context.packageName + ).takeIf { it > 0 } ?: R.string.counting_min_max_title + }) } private fun setMinValue(min: Int = 0) { @@ -446,6 +476,10 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT it.addTextChangedListener(maxTextWatcher) } } + + private fun hasMinAndMaxPropertyValues(): Boolean { + return properties.filter { p -> mnemonicFilter.find { it.first == p.code }?.second == NomenclatureTypeViewType.MIN_MAX }.size == 2 + } } /** diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/InformationFragment.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/InformationFragment.kt index 23a65919..d92c896e 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/InformationFragment.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/InformationFragment.kt @@ -22,6 +22,7 @@ import fr.geonature.occtax.R import fr.geonature.occtax.input.Input import fr.geonature.occtax.input.InputTaxon import fr.geonature.occtax.input.PropertyValue +import fr.geonature.occtax.settings.PropertySettings import fr.geonature.occtax.ui.input.IInputFragment import fr.geonature.occtax.ui.input.dialog.ChooseNomenclatureDialogFragment import fr.geonature.viewpager.ui.IValidateFragment @@ -85,7 +86,14 @@ class InformationFragment : Fragment(), when (loader.id) { LOADER_NOMENCLATURE_TYPES -> { - adapter?.bind(data) + val defaultProperties = arguments?.getParcelableArray(ARG_PROPERTIES) + ?.map { it as PropertySettings } + ?.toTypedArray() ?: emptyArray() + + adapter?.bind( + data, + *defaultProperties + ) loadDefaultNomenclatureValues() } LOADER_DEFAULT_NOMENCLATURE_VALUES -> { @@ -138,7 +146,10 @@ class InformationFragment : Fragment(), adapter = NomenclatureTypesRecyclerViewAdapter(object : NomenclatureTypesRecyclerViewAdapter.OnNomenclatureTypesRecyclerViewAdapterListener { override fun showMore() { - savedState.putBoolean(KEY_SHOW_ALL_NOMENCLATURE_TYPES, true) + savedState.putBoolean( + KEY_SHOW_ALL_NOMENCLATURE_TYPES, + true + ) setPropertyValues() } @@ -274,6 +285,7 @@ class InformationFragment : Fragment(), companion object { private val TAG = InformationFragment::class.java.name + private const val ARG_PROPERTIES = "arg_properties" private const val LOADER_NOMENCLATURE_TYPES = 1 private const val LOADER_DEFAULT_NOMENCLATURE_VALUES = 2 private const val CHOOSE_NOMENCLATURE_DIALOG_FRAGMENT = @@ -287,6 +299,13 @@ class InformationFragment : Fragment(), * @return A new instance of [InformationFragment] */ @JvmStatic - fun newInstance() = InformationFragment() + fun newInstance(vararg propertySettings: PropertySettings) = InformationFragment().apply { + arguments = Bundle().apply { + putParcelableArray( + ARG_PROPERTIES, + propertySettings + ) + } + } } } diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/NomenclatureTypesRecyclerViewAdapter.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/NomenclatureTypesRecyclerViewAdapter.kt index 598d156a..2be08e13 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/NomenclatureTypesRecyclerViewAdapter.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/NomenclatureTypesRecyclerViewAdapter.kt @@ -20,6 +20,7 @@ import fr.geonature.occtax.R import fr.geonature.occtax.input.InputTaxon import fr.geonature.occtax.input.NomenclatureTypeViewType import fr.geonature.occtax.input.PropertyValue +import fr.geonature.occtax.settings.PropertySettings import java.util.Locale /** @@ -31,18 +32,14 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT RecyclerView.Adapter() { private val mnemonicFilter = InputTaxon.defaultPropertiesMnemonic - private val moreViewType = Pair( + private val moreViewType = Triple( "MORE", - NomenclatureTypeViewType.MORE - ) - private val defaultMnemonicFilter = mnemonicFilter.slice( - IntRange( - 0, - 1 - ) + NomenclatureTypeViewType.MORE, + true ) - private val availableNomenclatureTypes = mutableListOf>() + private val availableNomenclatureTypes = + mutableListOf>() private val properties = mutableListOf() private var showAllNomenclatureTypes = false @@ -93,7 +90,7 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT .toList() } - fun bind(cursor: Cursor?) { + fun bind(cursor: Cursor?, vararg defaultPropertySettings: PropertySettings) { availableNomenclatureTypes.clear() cursor?.run { @@ -104,16 +101,23 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT while (!this.isAfterLast) { NomenclatureType.fromCursor(this) ?.run { - val validNomenclatureType = - mnemonicFilter.find { it.first == this.mnemonic } - if (validNomenclatureType != null) { - availableNomenclatureTypes.add(validNomenclatureType) + (if (defaultPropertySettings.isEmpty()) { + mnemonicFilter.find { it.first == mnemonic } + } else { + defaultPropertySettings.find { it.key == mnemonic && it.visible } + ?.let { property -> mnemonicFilter.find { it.first == property.key } } + })?.also { + availableNomenclatureTypes.add(it) } } cursor.moveToNext() } - availableNomenclatureTypes.addAll(mnemonicFilter.filter { it.second != NomenclatureTypeViewType.NOMENCLATURE_TYPE }) + // add default mnemonic filters + availableNomenclatureTypes.addAll(mnemonicFilter.filter { + it.second != NomenclatureTypeViewType.NOMENCLATURE_TYPE && + (defaultPropertySettings.isEmpty() || defaultPropertySettings.any { p -> p.key == it.first }) + }) } availableNomenclatureTypes.sortWith(Comparator { o1, o2 -> @@ -131,10 +135,13 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT availableNomenclatureTypes } else { val defaultNomenclatureTypes = - availableNomenclatureTypes.filter { availableNomenclatureType -> defaultMnemonicFilter.any { it.first == availableNomenclatureType.first } } + availableNomenclatureTypes.filter { availableNomenclatureType -> + if (defaultPropertySettings.isEmpty()) availableNomenclatureType.third + else defaultPropertySettings.any { it.key == availableNomenclatureType.first && it.default } + } // add MORE ViewType if default nomenclature types are presents - if (defaultNomenclatureTypes.size == defaultMnemonicFilter.size) { + if (defaultNomenclatureTypes.size < availableNomenclatureTypes.size) { listOf( *defaultNomenclatureTypes.toTypedArray(), moreViewType @@ -190,7 +197,7 @@ class NomenclatureTypesRecyclerViewAdapter(private val listener: OnNomenclatureT setNomenclatureTypes(availableNomenclatureTypes) } - private fun setNomenclatureTypes(nomenclatureTypes: List>) { + private fun setNomenclatureTypes(nomenclatureTypes: List>) { if (this.properties.isEmpty()) { this.properties.addAll(nomenclatureTypes.map { when (it.second) { diff --git a/occtax/src/main/res/values-fr/strings.xml b/occtax/src/main/res/values-fr/strings.xml index 9ce5fd0c..e977cc0b 100644 --- a/occtax/src/main/res/values-fr/strings.xml +++ b/occtax/src/main/res/values-fr/strings.xml @@ -116,6 +116,8 @@ Dénombrement supprimé Annuler Aucune information à sauvegarder + Min + Max Min & Max Min Max diff --git a/occtax/src/main/res/values/strings.xml b/occtax/src/main/res/values/strings.xml index 11f82600..09eae017 100644 --- a/occtax/src/main/res/values/strings.xml +++ b/occtax/src/main/res/values/strings.xml @@ -119,6 +119,8 @@ Counting deleted Undo Empty counting: Nothing to save + Min + Max Min & Max Min Max diff --git a/occtax/src/test/java/fr/geonature/occtax/settings/io/AppSettingsJsonReaderTest.kt b/occtax/src/test/java/fr/geonature/occtax/settings/io/AppSettingsJsonReaderTest.kt index c13106ce..1fe94931 100644 --- a/occtax/src/test/java/fr/geonature/occtax/settings/io/AppSettingsJsonReaderTest.kt +++ b/occtax/src/test/java/fr/geonature/occtax/settings/io/AppSettingsJsonReaderTest.kt @@ -5,6 +5,8 @@ import fr.geonature.maps.settings.LayerSettings import fr.geonature.maps.settings.MapSettings import fr.geonature.occtax.FixtureHelper.getFixture import fr.geonature.occtax.settings.AppSettings +import fr.geonature.occtax.settings.NomenclatureSettings +import fr.geonature.occtax.settings.PropertySettings import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull @@ -72,6 +74,62 @@ class AppSettingsJsonReaderTest { 47.225827, -1.554470 ) + ), + NomenclatureSettings( + arrayListOf( + PropertySettings( + "METH_OBS", + visible = true, + default = true + ), + PropertySettings( + "ETA_BIO", + visible = true, + default = true + ), + PropertySettings( + "METH_DETERMIN", + visible = true, + default = false + ), + PropertySettings( + "STATUT_BIO", + visible = true, + default = false + ), + PropertySettings( + "NATURALITE", + visible = true, + default = false + ), + PropertySettings( + "PREUVE_EXIST", + visible = true, + default = false + ) + ), + arrayListOf( + PropertySettings( + "STADE_VIE", + visible = true, + default = true + ), + PropertySettings( + "SEXE", + visible = true, + default = true + ), + PropertySettings( + "OBJ_DENBR", + visible = true, + default = true + ), + PropertySettings( + "TYP_DENBR", + visible = true, + default = true + ) + ) ) ), appSettings @@ -96,4 +154,14 @@ class AppSettingsJsonReaderTest { assertNotNull(appSettings) assertNull(appSettings?.mapSettings) } + + @Test + fun testReadAppSettingsFromJsonStringWithNoNomenclatureSettings() { + // when read an empty JSON as AppSettings + val appSettings = appSettingsJsonReader.read("{\"nomenclature\":null}") + + // then + assertNotNull(appSettings) + assertNull(appSettings?.nomenclatureSettings) + } } diff --git a/occtax/src/test/resources/fixtures/settings_occtax.json b/occtax/src/test/resources/fixtures/settings_occtax.json index 88983e57..1ac7ac45 100644 --- a/occtax/src/test/resources/fixtures/settings_occtax.json +++ b/occtax/src/test/resources/fixtures/settings_occtax.json @@ -28,5 +28,39 @@ } ] }, + "nomenclature": { + "information": [ + "METH_OBS", + { + "key": "ETA_BIO" + }, + { + "key": "METH_DETERMIN", + "visible": true, + "default": false + }, + { + "key": "STATUT_BIO", + "visible": true, + "default": false + }, + { + "key": "NATURALITE", + "visible": true, + "default": false + }, + { + "key": "PREUVE_EXIST", + "visible": true, + "default": false + } + ], + "counting": [ + "STADE_VIE", + "SEXE", + "OBJ_DENBR", + "TYP_DENBR" + ] + }, "no_such_property": "no_such_value" } \ No newline at end of file From 2ee8b7bfa2a13ffd7939a9fb2f4e6694c0249425 Mon Sep 17 00:00:00 2001 From: "S. Grimault" Date: Sat, 5 Sep 2020 16:38:34 +0200 Subject: [PATCH 03/11] feat: vibrate once on deleting item --- occtax/src/main/AndroidManifest.xml | 2 ++ .../java/fr/geonature/occtax/ui/home/HomeFragment.kt | 10 ++++++++++ .../occtax/ui/input/counting/CountingFragment.kt | 10 ++++++++++ 3 files changed, 22 insertions(+) diff --git a/occtax/src/main/AndroidManifest.xml b/occtax/src/main/AndroidManifest.xml index 62510b02..22d9ef2f 100644 --- a/occtax/src/main/AndroidManifest.xml +++ b/occtax/src/main/AndroidManifest.xml @@ -5,6 +5,8 @@ package="fr.geonature.occtax" android:sharedUserId="@string/sharedUserId"> + + Date: Sat, 5 Sep 2020 16:40:52 +0200 Subject: [PATCH 04/11] feat(#59): confirm dialog before quit and add an explicit 'save' button --- .../counting/EditCountingMetadataActivity.kt | 52 +++++++++++++++++-- .../counting/EditCountingMetadataFragment.kt | 1 + occtax/src/main/res/drawable/ic_close.xml | 11 ++++ occtax/src/main/res/menu/save.xml | 10 ++++ occtax/src/main/res/values-fr/strings.xml | 5 ++ occtax/src/main/res/values/strings.xml | 5 ++ 6 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 occtax/src/main/res/drawable/ic_close.xml create mode 100644 occtax/src/main/res/menu/save.xml diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt index 9bf4a909..202d9e5d 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt @@ -4,7 +4,9 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle +import android.view.Menu import android.view.MenuItem +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import fr.geonature.commons.data.Taxonomy import fr.geonature.occtax.R @@ -23,13 +25,23 @@ class EditCountingMetadataActivity : AppCompatActivity(), private lateinit var countingMetadata: CountingMetadata + // whether the current counting metadata is new or not + private var isNew: Boolean = true + + // whether the current counting metadata has been modified or not + private var isDirty: Boolean = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.run { + setDisplayHomeAsUpEnabled(true) + setHomeAsUpIndicator(R.drawable.ic_close) + } countingMetadata = intent.getParcelableExtra(EXTRA_COUNTING_METADATA) ?: CountingMetadata() + isNew = countingMetadata.isEmpty() setTitle(if (countingMetadata.isEmpty()) R.string.activity_counting_add_title else R.string.activity_counting_edit_title) if (savedInstanceState == null) { @@ -51,9 +63,22 @@ class EditCountingMetadataActivity : AppCompatActivity(), } } + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate( + R.menu.save, + menu + ) + + return true + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { + confirmBeforeQuit() + true + } + R.id.action_save -> { sendResult() finish() true @@ -63,13 +88,12 @@ class EditCountingMetadataActivity : AppCompatActivity(), } override fun onBackPressed() { - sendResult() - - super.onBackPressed() + confirmBeforeQuit() } override fun onCountingMetadata(countingMetadata: CountingMetadata) { this.countingMetadata = countingMetadata + this.isDirty = true } private fun sendResult() { @@ -82,6 +106,26 @@ class EditCountingMetadataActivity : AppCompatActivity(), }) } + private fun confirmBeforeQuit() { + if (!isDirty) { + finish() + return + } + + AlertDialog.Builder(this) + .setTitle(if (isNew && isDirty) R.string.alert_counting_title_discard else R.string.alert_counting_title_discard_changes) + .setPositiveButton( + R.string.alert_counting_action_discard + ) { dialog, _ -> + dialog.dismiss() + finish() + } + .setNegativeButton( + R.string.alert_counting_action_keep + ) { dialog, _ -> dialog.dismiss() } + .show() + } + companion object { const val EXTRA_TAXONOMY = "extra_taxonomy" diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt index f661040c..504ffd9c 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt @@ -236,6 +236,7 @@ class EditCountingMetadataFragment : Fragment(), nomenclature ) adapter?.setCountingMetata(countingMetadata) + listener?.onCountingMetadata(countingMetadata) } private fun loadDefaultNomenclatureValues() { diff --git a/occtax/src/main/res/drawable/ic_close.xml b/occtax/src/main/res/drawable/ic_close.xml new file mode 100644 index 00000000..2bc7934e --- /dev/null +++ b/occtax/src/main/res/drawable/ic_close.xml @@ -0,0 +1,11 @@ + + + diff --git a/occtax/src/main/res/menu/save.xml b/occtax/src/main/res/menu/save.xml new file mode 100644 index 00000000..a81d66d0 --- /dev/null +++ b/occtax/src/main/res/menu/save.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/occtax/src/main/res/values-fr/strings.xml b/occtax/src/main/res/values-fr/strings.xml index e977cc0b..b1461b97 100644 --- a/occtax/src/main/res/values-fr/strings.xml +++ b/occtax/src/main/res/values-fr/strings.xml @@ -38,9 +38,14 @@ %d sélectionné %d sélectionnés + Save Ajouter un commentaire Supprimer un commentaire + Supprimer ce dénombrement ? + Supprimer les changements apportés à ce dénombrement ? + Supprimer + Continuer la saisie Ajouter un commentaire Editer un commentaire Ajouter un commentaire diff --git a/occtax/src/main/res/values/strings.xml b/occtax/src/main/res/values/strings.xml index 09eae017..64bf1cf0 100644 --- a/occtax/src/main/res/values/strings.xml +++ b/occtax/src/main/res/values/strings.xml @@ -41,9 +41,14 @@ %d selected %d selected + Save Add comment Edit comment + Discard this counting? + Discard changes to this counting? + Discard + Keep editing Add a comment Edit a comment Add a comment From 7e8850e98d5f401535d1aeeef0d4b827a0eefd45 Mon Sep 17 00:00:00 2001 From: "S. Grimault" Date: Sun, 6 Sep 2020 14:41:35 +0200 Subject: [PATCH 05/11] fix(#49): show taxon common name, remove pseudo filter about common name --- .../geonature/occtax/ui/input/taxa/Filter.kt | 1 - .../occtax/ui/input/taxa/FilterName.kt | 52 --------------- .../taxa/FilterNameRecyclerViewAdapter.kt | 65 ------------------- .../input/taxa/FilterRecyclerViewAdapter.kt | 44 ++++--------- .../occtax/ui/input/taxa/TaxaFragment.kt | 59 +---------------- .../ui/input/taxa/TaxaRecyclerViewAdapter.kt | 42 +++++------- .../src/main/res/layout/list_item_taxon.xml | 26 ++++++-- 7 files changed, 49 insertions(+), 240 deletions(-) delete mode 100644 occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterName.kt delete mode 100644 occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterNameRecyclerViewAdapter.kt diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/Filter.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/Filter.kt index fe5c1cdf..63479380 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/Filter.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/Filter.kt @@ -58,7 +58,6 @@ open class Filter( * @author [S. Grimault](mailto:sebastien.grimault@gmail.com) */ enum class FilterType { - NAME, AREA_OBSERVATION, TAXONOMY } diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterName.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterName.kt deleted file mode 100644 index f3c6de0a..00000000 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterName.kt +++ /dev/null @@ -1,52 +0,0 @@ -package fr.geonature.occtax.ui.input.taxa - -import android.os.Parcel -import android.os.Parcelable - -/** - * Name filter. - * - * @author [S. Grimault](mailto:sebastien.grimault@gmail.com) - */ -class FilterName(value: Name) : Filter( - FilterType.NAME, - value -) { - class Name(val type: NameType) : - Parcelable { - - private constructor(source: Parcel) : this( - NameType.valueOf(source.readString() ?: NameType.SCIENTIFIC.name) - ) - - override fun describeContents(): Int { - return 0 - } - - override fun writeToParcel(dest: Parcel?, flags: Int) { - dest?.also { - it.writeString(type.name) - } - } - - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): Name { - return Name(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } - } - - /** - * Taxon name types. - * - * @author [S. Grimault](mailto:sebastien.grimault@gmail.com) - */ - enum class NameType { - SCIENTIFIC, - COMMON - } -} \ No newline at end of file diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterNameRecyclerViewAdapter.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterNameRecyclerViewAdapter.kt deleted file mode 100644 index d0a1e837..00000000 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterNameRecyclerViewAdapter.kt +++ /dev/null @@ -1,65 +0,0 @@ -package fr.geonature.occtax.ui.input.taxa - -import android.view.View -import android.widget.Switch -import fr.geonature.commons.ui.adapter.AbstractListItemRecyclerViewAdapter -import fr.geonature.occtax.R - -/** - * Default RecyclerView Adapter for [FilterName]. - * - * @author [S. Grimault](mailto:sebastien.grimault@gmail.com) - */ -class FilterNameRecyclerViewAdapter(val listener: FilterRecyclerViewAdapterListener) : - AbstractListItemRecyclerViewAdapter() { - - init { - setItems(listOf(FilterName(FilterName.Name(FilterName.NameType.SCIENTIFIC)))) - } - - override fun getViewHolder(view: View, viewType: Int): AbstractViewHolder { - return ViewHolder(view) - } - - override fun getLayoutResourceId(position: Int, item: FilterName): Int { - return R.layout.list_item_filter_name - } - - override fun areItemsTheSame( - oldItems: List, - newItems: List, - oldItemPosition: Int, - newItemPosition: Int - ): Boolean { - return oldItems[oldItemPosition] == newItems[newItemPosition] - } - - override fun areContentsTheSame( - oldItems: List, - newItems: List, - oldItemPosition: Int, - newItemPosition: Int - ): Boolean { - return oldItems[oldItemPosition] == newItems[newItemPosition] - } - - fun setSelectedFilter(filter: FilterName) { - this.setItems(listOf(filter)) - - notifyDataSetChanged() - } - - inner class ViewHolder(itemView: View) : - AbstractListItemRecyclerViewAdapter.AbstractViewHolder(itemView) { - - private val switch: Switch = itemView.findViewById(R.id.switch_name) - - override fun onBind(item: FilterName) { - switch.setOnClickListener { - listener.onSelectedFilters(if (switch.isChecked) FilterName(FilterName.Name(FilterName.NameType.COMMON)) else FilterName(FilterName.Name(FilterName.NameType.SCIENTIFIC))) - } - - switch.isChecked = item.value.type == FilterName.NameType.COMMON - } - } -} \ No newline at end of file diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterRecyclerViewAdapter.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterRecyclerViewAdapter.kt index 72a43742..e8c7d6a9 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterRecyclerViewAdapter.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/FilterRecyclerViewAdapter.kt @@ -13,12 +13,11 @@ import fr.geonature.commons.ui.adapter.StickyHeaderItemDecorator import fr.geonature.occtax.R /** - * Default RecyclerView Adapter used by [TaxaFilterFragment], combining [FilterNameRecyclerViewAdapter], + * Default RecyclerView Adapter used by [TaxaFilterFragment], combining * [FilterAreaObservationRecyclerViewAdapter] and [FilterTaxonomyRecyclerViewAdapter]. * * @author [S. Grimault](mailto:sebastien.grimault@gmail.com) * - * @see FilterNameRecyclerViewAdapter * @see FilterAreaObservationRecyclerViewAdapter * @see FilterTaxonomyRecyclerViewAdapter */ @@ -26,16 +25,6 @@ class FilterRecyclerViewAdapter(val listener: FilterRecyclerViewAdapterListener< RecyclerView.Adapter(), IStickyRecyclerViewAdapter { - private val filterNameRecyclerViewAdapter: FilterNameRecyclerViewAdapter = - FilterNameRecyclerViewAdapter(object : FilterRecyclerViewAdapterListener { - override fun onSelectedFilters(vararg filter: FilterName) { - val existingFilters = - selectedFilters.filter { it.type != Filter.FilterType.NAME } - selectedFilters.clear() - selectedFilters.addAll(existingFilters + filter) - listener.onSelectedFilters(*selectedFilters.toTypedArray()) - } - }) private val filterTitleAreaObservationRecyclerViewAdapter = FilterTitleRecyclerViewAdapter() private val filterAreaObservationRecyclerViewAdapter: FilterAreaObservationRecyclerViewAdapter = FilterAreaObservationRecyclerViewAdapter(object : @@ -61,7 +50,6 @@ class FilterRecyclerViewAdapter(val listener: FilterRecyclerViewAdapterListener< } }) private val mergeAdapter = MergeAdapter( - filterNameRecyclerViewAdapter, filterTitleAreaObservationRecyclerViewAdapter, filterAreaObservationRecyclerViewAdapter, filterTitleTaxonomyRecyclerViewAdapter, @@ -180,30 +168,24 @@ class FilterRecyclerViewAdapter(val listener: FilterRecyclerViewAdapterListener< } override fun getHeaderPositionForItem(itemPosition: Int): Int { - // the first adapter has no header - if (itemPosition < filterNameRecyclerViewAdapter.itemCount) { - - return -1 - } - // if filter area observation have some items - if (filterAreaObservationRecyclerViewAdapter.itemCount > 0 && itemPosition < (filterNameRecyclerViewAdapter.itemCount + filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount)) { - return filterNameRecyclerViewAdapter.itemCount + if (filterAreaObservationRecyclerViewAdapter.itemCount > 0 && itemPosition < (filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount)) { + return 0 } - if (itemPosition == filterNameRecyclerViewAdapter.itemCount + filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount - 1) { + if (itemPosition == filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount - 1) { return itemPosition } val currentFilterTaxonomy = - filterTaxonomyRecyclerViewAdapter.items.getOrNull(itemPosition - (filterNameRecyclerViewAdapter.itemCount + filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount)) - ?: return filterNameRecyclerViewAdapter.itemCount + filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount + filterTaxonomyRecyclerViewAdapter.items.getOrNull(itemPosition - (filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount)) + ?: return filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount return filterTaxonomyRecyclerViewAdapter.items.indexOfFirst { it.value.kingdom == currentFilterTaxonomy.value.kingdom && it.value.group == Taxonomy.ANY }.takeIf { it >= 0 } - ?.plus(filterNameRecyclerViewAdapter.itemCount + filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount) - ?: filterNameRecyclerViewAdapter.itemCount + filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount - 1 + ?.plus(filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount) + ?: filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount - 1 } override fun onBindHeaderViewHolder(holder: HeaderViewHolder, headerPosition: Int) { @@ -231,10 +213,6 @@ class FilterRecyclerViewAdapter(val listener: FilterRecyclerViewAdapterListener< this.selectedFilters.clear() this.selectedFilters.addAll(filter) - (filter.find { it.type == Filter.FilterType.NAME } as FilterName?)?.also { - this.filterNameRecyclerViewAdapter.setSelectedFilter(it) - } - this.filterAreaObservationRecyclerViewAdapter.setSelectedFilters(*filter.filter { it.type == Filter.FilterType.AREA_OBSERVATION } .map { FilterAreaObservation(it.value as FilterAreaObservation.AreaObservation) } .toTypedArray()) @@ -266,18 +244,18 @@ class FilterRecyclerViewAdapter(val listener: FilterRecyclerViewAdapterListener< Typeface.BOLD ) - if (filterAreaObservationRecyclerViewAdapter.itemCount > 0 && position < (filterNameRecyclerViewAdapter.itemCount + filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount)) { + if (filterAreaObservationRecyclerViewAdapter.itemCount > 0 && position < (filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount)) { label.text = filterTitleAreaObservationRecyclerViewAdapter.items.getOrNull(0) return } - if (position == filterNameRecyclerViewAdapter.itemCount + filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount - 1) { + if (position == filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount - 1) { label.text = filterTitleTaxonomyRecyclerViewAdapter.items.getOrNull(0) return } val taxonomyAsHeader = - filterTaxonomyRecyclerViewAdapter.items.getOrNull(position - (filterNameRecyclerViewAdapter.itemCount + filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount)) + filterTaxonomyRecyclerViewAdapter.items.getOrNull(position - (filterAreaObservationRecyclerViewAdapter.itemCount + filterTitleAreaObservationRecyclerViewAdapter.itemCount + filterTitleTaxonomyRecyclerViewAdapter.itemCount)) if (taxonomyAsHeader == null) { label.text = filterTitleTaxonomyRecyclerViewAdapter.items.getOrNull(0) diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/TaxaFragment.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/TaxaFragment.kt index 8b86b29e..ac41d0f1 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/TaxaFragment.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/TaxaFragment.kt @@ -79,14 +79,13 @@ class TaxaFragment : Fragment(), "load taxa with selected feature ID: $selectedFeatureId" ) - adapter?.toggleCommonName((selectedFilters.find { it.type == Filter.FilterType.NAME }?.value as FilterName.Name?)?.type == FilterName.NameType.COMMON) - val taxonFilter = TaxonWithArea.Filter() .byNameOrDescriptionOrRank(args?.getString(KEY_FILTER_BY_NAME)) .also { val filterByAreaObservation = - selectedFilters.asSequence() + selectedFilters + .asSequence() .filter { filter -> filter.type == Filter.FilterType.AREA_OBSERVATION } .map { filter -> filter.value as FilterAreaObservation.AreaObservation } .map { areaObservation -> @@ -117,9 +116,7 @@ class TaxaFragment : Fragment(), null, taxonFilter.first, taxonFilter.second.map { it.toString() }.toTypedArray(), - (if ((selectedFilters.find { it.type == Filter.FilterType.NAME }?.value as FilterName.Name?)?.type == FilterName.NameType.COMMON) TaxonWithArea.OrderBy() - .byCommonName() else TaxonWithArea.OrderBy() - .by(AbstractTaxon.COLUMN_NAME)).build() + TaxonWithArea.OrderBy().by(AbstractTaxon.COLUMN_NAME).build() ) } LOADER_TAXON -> { @@ -451,68 +448,18 @@ class TaxaFragment : Fragment(), filter ) - val filterName = - filter.find { it.type == Filter.FilterType.NAME }?.value as FilterName.Name? val selectedAreaObservation = filter.filter { it.type == Filter.FilterType.AREA_OBSERVATION } .map { it.value as FilterAreaObservation.AreaObservation } val selectedTaxonomy = filter.find { it.type == Filter.FilterType.TAXONOMY }?.value as Taxonomy? - filterByName(filterName) filterByAreaObservation(*selectedAreaObservation.toTypedArray()) filterByTaxonomy(selectedTaxonomy) loadTaxa() } - private fun filterByName(filterName: FilterName.Name?) { - val filterChipGroup = filterChipGroup ?: return - val context = context ?: return - - val nameChipsToDelete = arrayListOf() - - for (i in 0 until filterChipGroup.childCount) { - with(filterChipGroup[i]) { - if (this is Chip && tag is FilterName.Name) { - nameChipsToDelete.add(this) - } - } - } - - nameChipsToDelete.forEach { - filterChipGroup.removeView(it) - } - - filterChipGroup.visibility = if (filterChipGroup.childCount > 0) View.VISIBLE else View.GONE - - if (filterName != null && filterName.type == FilterName.NameType.COMMON) { - filterChipGroup.visibility = View.VISIBLE - - // build name filter chip - with( - LayoutInflater.from(context).inflate( - R.layout.chip, - filterChipGroup, - false - ) as Chip - ) { - tag = filterName - text = context.getText(R.string.taxa_filter_name_short) - setOnClickListener { - applyFilters(*getSelectedFilters().filter { it.type != Filter.FilterType.NAME } - .toTypedArray()) - } - setOnCloseIconClickListener { - applyFilters(*getSelectedFilters().filter { it.type != Filter.FilterType.NAME } - .toTypedArray()) - } - - filterChipGroup.addView(this) - } - } - } - private fun filterByAreaObservation(vararg areaObservation: FilterAreaObservation.AreaObservation) { val filterChipGroup = filterChipGroup ?: return val context = context ?: return diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/TaxaRecyclerViewAdapter.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/TaxaRecyclerViewAdapter.kt index fa58d62e..df2f6a43 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/TaxaRecyclerViewAdapter.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/taxa/TaxaRecyclerViewAdapter.kt @@ -28,7 +28,6 @@ class TaxaRecyclerViewAdapter(private val listener: OnTaxaRecyclerViewAdapterLis RecyclerView.Adapter(), FastScroller.SectionIndexer { private var cursor: Cursor? = null - private var showCommonName = false private var selectedTaxon: AbstractTaxon? = null private val onClickListener: View.OnClickListener @@ -84,8 +83,8 @@ class TaxaRecyclerViewAdapter(private val listener: OnTaxaRecyclerViewAdapterLis val checkbox: CheckBox = v.findViewById(android.R.id.checkbox) checkbox.isChecked = !checkbox.isChecked - val text2: TextView = v.findViewById(android.R.id.text2) - text2.isSelected = true + val summary: TextView = v.findViewById(android.R.id.summary) + summary.isSelected = true val taxon = v.tag as AbstractTaxon @@ -137,8 +136,7 @@ class TaxaRecyclerViewAdapter(private val listener: OnTaxaRecyclerViewAdapterLis val taxon = Taxon.fromCursor(cursor) ?: return "" - return taxonName(taxon).elementAt(0) - .toString() + return taxon.name.elementAt(0).toString() } fun setSelectedTaxon(selectedTaxon: Taxon) { @@ -153,15 +151,6 @@ class TaxaRecyclerViewAdapter(private val listener: OnTaxaRecyclerViewAdapterLis scrollToFirstItemSelected() } - fun toggleCommonName(toggle: Boolean = false, notify: Boolean = false) { - this.showCommonName = toggle - - if (notify) { - notifyDataSetChanged() - scrollToFirstItemSelected() - } - } - private fun getItemPosition(taxon: AbstractTaxon?): Int { var itemPosition = -1 val cursor = cursor ?: return itemPosition @@ -193,10 +182,6 @@ class TaxaRecyclerViewAdapter(private val listener: OnTaxaRecyclerViewAdapterLis } } - private fun taxonName(taxon: AbstractTaxon): String { - return if (showCommonName) taxon.commonName ?: taxon.name else taxon.name - } - inner class ViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( LayoutInflater.from(parent.context).inflate( R.layout.list_item_taxon, @@ -208,9 +193,11 @@ class TaxaRecyclerViewAdapter(private val listener: OnTaxaRecyclerViewAdapterLis private val title: TextView = itemView.findViewById(android.R.id.title) private val text1: TextView = itemView.findViewById(android.R.id.text1) private val text2: TextView = itemView.findViewById(android.R.id.text2) + private val summary: TextView = itemView.findViewById(android.R.id.summary) private val checkbox: CheckBox = itemView.findViewById(android.R.id.checkbox) private val taxonColorView: View = itemView.findViewById(R.id.taxon_color_view) - private val taxonObserversImageView: View = itemView.findViewById(R.id.taxon_observers_image_view) + private val taxonObserversImageView: View = + itemView.findViewById(R.id.taxon_observers_image_view) private val taxonObserversView: TextView = itemView.findViewById(R.id.taxon_observers_view) private val taxonLastUpdatedAtView: TextView = itemView.findViewById(R.id.taxon_last_updated_at_view) @@ -224,23 +211,24 @@ class TaxaRecyclerViewAdapter(private val listener: OnTaxaRecyclerViewAdapterLis val previousTitle = if (position > 0) { cursor.moveToPosition(position - 1) - TaxonWithArea.fromCursor(cursor) - ?.let { taxonName(it).elementAt(0).toString() } ?: "" + TaxonWithArea.fromCursor(cursor)?.name?.elementAt(0)?.toString() ?: "" } else { "" } if (taxon != null) { - val taxonName = taxonName(taxon) - val currentTitle = taxonName.elementAt(0) - .toString() + val currentTitle = taxon.name.elementAt(0).toString() title.text = if (previousTitle == currentTitle) "" else currentTitle - text1.text = taxonName - text2.text = HtmlCompat.fromHtml( + + text1.text = taxon.name + text2.text = taxon.commonName + + summary.text = HtmlCompat.fromHtml( "${if (taxon.description.isNullOrBlank()) "" else "${taxon.description ?: ""}"}${if (taxon.rank.isNullOrBlank()) "" else "${if (taxon.description.isNullOrBlank()) "" else " - [${taxon.rank}]"} "}", HtmlCompat.FROM_HTML_MODE_COMPACT ) - text2.isSelected = selectedTaxon?.id == taxon.id + summary.isSelected = selectedTaxon?.id == taxon.id + checkbox.isChecked = selectedTaxon?.id == taxon.id with(taxon.taxonArea) { diff --git a/occtax/src/main/res/layout/list_item_taxon.xml b/occtax/src/main/res/layout/list_item_taxon.xml index 39de7ec7..553e58e7 100644 --- a/occtax/src/main/res/layout/list_item_taxon.xml +++ b/occtax/src/main/res/layout/list_item_taxon.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/selectableItemBackground" - android:minHeight="?attr/listPreferredItemHeight"> + android:minHeight="?attr/listPreferredItemHeightLarge"> + + + app:layout_constraintTop_toBottomOf="@android:id/text2" + tools:text="@tools:sample/lorem/random" /> Date: Sun, 13 Sep 2020 16:18:36 +0200 Subject: [PATCH 06/11] feat(#7): add new field 'Behavior of observed occurrences' (OCC_COMPORTEMENT) from nomenclature. --- README.md | 134 +++++++++++++++++- .../java/fr/geonature/occtax/input/Input.kt | 6 +- .../fr/geonature/occtax/input/InputTaxon.kt | 5 + .../input/io/OnInputJsonWriterListenerImpl.kt | 29 ++-- .../src/main/res/values-fr/nomenclatures.xml | 4 +- occtax/src/main/res/values/nomenclatures.xml | 4 +- .../fr/geonature/occtax/input/InputTest.kt | 5 - .../occtax/input/io/InputJsonReaderTest.kt | 13 +- .../occtax/input/io/InputJsonWriterTest.kt | 10 +- .../test/resources/fixtures/input_simple.json | 10 +- 10 files changed, 176 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index edfbd791..cf4e6c35 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ Installation documentation (French) : https://github.com/PnX-SI/gn_mobile_occtax ## Launcher icons -| Name | Flavor | Launcher icon | -| -------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------- | +| Name | Flavor | Launcher icon | +| -------------------------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------- | | Default | _generic_ | ![PNX](https://raw.githubusercontent.com/PnX-SI/gn_mobile_occtax/develop/occtax/src/main/res/mipmap-xhdpi/ic_launcher.png) | | [Parc National des Cévennes](http://www.cevennes-parcnational.fr) | _pnc_ | ![PNC](https://raw.githubusercontent.com/PnX-SI/gn_mobile_occtax/develop/occtax/src/pnc/res/mipmap-xhdpi/ic_launcher.png) | | [Parc National des Écrins](http://www.ecrins-parcnational.fr) | _pne_ | ![PNE](https://raw.githubusercontent.com/PnX-SI/gn_mobile_occtax/develop/occtax/src/pne/res/mipmap-xhdpi/ic_launcher.png) | @@ -49,6 +49,136 @@ Example: | --------------------------- | ------- | ------------------------------------------------------------------------------ | ------------- | | `area_observation_duration` | ☐ | Area observation duration period (in days) | 365 | | `map` | ☐ | Maps settings (cf. https://github.com/PnX-SI/gn_mobile_maps/tree/develop/maps) | | +| `nomenclature` | ☐ | Nomemclature settings | | +| `nomenclature/information` | ☐ | Information settings (as array) | | +| `nomenclature/counting` | ☐ | Counting settings (as array) | | + +#### Nomenclature settings + +All these settings may be not defined and the default values will be used instead: + +**Information settings** + +| Nomemclature | Label | Visible by default | Editable | +| ---------------- | -------------------------------- | ------------------ | -------- | +| METH_OBS | Observation methods | `true` | `true` | +| ETA_BIO | Biological state of observation | `true` | `true` | +| METH_DETERMIN | Determination method | `false` | `true` | +| DETERMINER | Determiner | `false` | `true` | +| STATUT_BIO | Biological status | `false` | `true` | +| OCC_COMPORTEMENT | Behavior of observed occurrences | `false` | `true` | +| NATURALITE | Level of naturalness | `false` | `true` | +| PREUVE_EXIST | Proof of existence | `false` | `true` | +| COMMENT | Comment | `false` | `true` | + +**Counting settings** + +| Nomemclature | Label | Visible by default | Editable | +| ------------ | -------------------------- | ------------------ | -------- | +| STADE_VIE | Life stage | `true` | `true` | +| SEXE | Sex | `true` | `true` | +| OBJ_DENBR | Purpose of the enumeration | `true` | `true` | +| TYP_DENBR | Type of enumeration | `true` | `true` | +| MIN | Min | `true` | `true` | +| MAX | Max | `true` | `true` | + +**Note:** Any unknown nomenclature attribute added will be simply ignored at startup. + +You can override these default settings by adding a property for each nomenclature settings, e.g: + +```json +{ + "nomenclature": { + "information": [ + "METH_OBS", + { + "key": "ETA_BIO" + }, + { + "key": "METH_DETERMIN", + "visible": true, + "default": true + }, + { + "key": "STATUT_BIO", + "visible": true, + "default": false + }, + { + "key": "OCC_COMPORTEMENT", + "visible": true, + "default": false + }, + { + "key": "NATURALITE", + "visible": true, + "default": false + }, + { + "key": "PREUVE_EXIST", + "visible": true, + "default": false + } + ], + "counting": ["STADE_VIE", "SEXE", "OBJ_DENBR", "TYP_DENBR"] + } +} +``` + +Each property may be a simple string representing the nomenclature attribute to show or an object with the following properties: + +| Property | Description | Mandatory | +| --------- | --------------------------------------------------------------------- | --------- | +| `key` | The nomenclature attribute | ☑ | +| `visible` | If this attribute is visible (thus editable) or not (default: `true`) | ☐ | +| `default` | If this attribute is shown by default (default: `true`) | ☐ | + +**Example:** + +- `"METH_OBS"` has the same meaning like + + ```json + { + "key": "METH_OBS" + } + ``` + + or + + ```json + { + "key": "METH_OBS", + "visible": true + } + ``` + + or + + ```json + { + "key": "METH_OBS", + "default": true + } + ``` + + or + + ```json + { + "key": "METH_OBS", + "visible": true, + "default": true + } + ``` + +- An omitted property (e.g. `METH_OBS`) has the same meaning like + + ```json + { + "key": "METH_OBS", + "visible": false + } + ``` ## Upgrade git sub modules diff --git a/occtax/src/main/java/fr/geonature/occtax/input/Input.kt b/occtax/src/main/java/fr/geonature/occtax/input/Input.kt index 2199eefb..e6364b7c 100644 --- a/occtax/src/main/java/fr/geonature/occtax/input/Input.kt +++ b/occtax/src/main/java/fr/geonature/occtax/input/Input.kt @@ -4,9 +4,9 @@ import android.os.Parcel import android.os.Parcelable import fr.geonature.commons.input.AbstractInput import fr.geonature.commons.input.AbstractInputTaxon +import org.locationtech.jts.geom.Geometry import java.util.SortedMap import java.util.TreeMap -import org.locationtech.jts.geom.Geometry /** * Describes a current input. @@ -86,10 +86,6 @@ class Input : AbstractInput { companion object { val defaultPropertiesMnemonic = arrayOf( - Pair( - "TECHNIQUE_OBS", - NomenclatureTypeViewType.NOMENCLATURE_TYPE - ), Pair( "TYP_GRP", NomenclatureTypeViewType.NOMENCLATURE_TYPE diff --git a/occtax/src/main/java/fr/geonature/occtax/input/InputTaxon.kt b/occtax/src/main/java/fr/geonature/occtax/input/InputTaxon.kt index a981af62..1639f0dc 100644 --- a/occtax/src/main/java/fr/geonature/occtax/input/InputTaxon.kt +++ b/occtax/src/main/java/fr/geonature/occtax/input/InputTaxon.kt @@ -129,6 +129,11 @@ class InputTaxon : AbstractInputTaxon { NomenclatureTypeViewType.NOMENCLATURE_TYPE, false ), + Triple( + "OCC_COMPORTEMENT", + NomenclatureTypeViewType.NOMENCLATURE_TYPE, + false + ), Triple( "NATURALITE", NomenclatureTypeViewType.NOMENCLATURE_TYPE, diff --git a/occtax/src/main/java/fr/geonature/occtax/input/io/OnInputJsonWriterListenerImpl.kt b/occtax/src/main/java/fr/geonature/occtax/input/io/OnInputJsonWriterListenerImpl.kt index 2d8313f3..d06944dc 100644 --- a/occtax/src/main/java/fr/geonature/occtax/input/io/OnInputJsonWriterListenerImpl.kt +++ b/occtax/src/main/java/fr/geonature/occtax/input/io/OnInputJsonWriterListenerImpl.kt @@ -122,7 +122,6 @@ class OnInputJsonWriterListenerImpl : InputJsonWriter.OnInputJsonWriterListener< if (it.value.isEmpty()) return@forEach when (it.key) { - "TECHNIQUE_OBS" -> writer.name("id_nomenclature_obs_technique").value(it.value.value as Long) "TYP_GRP" -> writer.name("id_nomenclature_grp_typ").value(it.value.value as Long) } } @@ -228,13 +227,20 @@ class OnInputJsonWriterListenerImpl : InputJsonWriter.OnInputJsonWriterListener< if (it.value.isEmpty()) return@forEach when (it.key) { - "METH_OBS" -> writer.name("id_nomenclature_obs_meth").value(it.value.value as Long) - "ETA_BIO" -> writer.name("id_nomenclature_bio_condition").value(it.value.value as Long) - "METH_DETERMIN" -> writer.name("id_nomenclature_determination_method").value(it.value.value as Long) + "METH_OBS" -> writer.name("id_nomenclature_obs_technique").value(it.value.value as Long) + "ETA_BIO" -> writer.name("id_nomenclature_bio_condition") + .value(it.value.value as Long) + "METH_DETERMIN" -> writer.name("id_nomenclature_determination_method") + .value(it.value.value as Long) "DETERMINER" -> writer.name("determiner").value(it.value.value as String?) - "STATUT_BIO" -> writer.name("id_nomenclature_bio_status").value(it.value.value as Long) - "NATURALITE" -> writer.name("id_nomenclature_naturalness").value(it.value.value as Long) - "PREUVE_EXIST" -> writer.name("id_nomenclature_exist_proof").value(it.value.value as Long) + "STATUT_BIO" -> writer.name("id_nomenclature_bio_status") + .value(it.value.value as Long) + "OCC_COMPORTEMENT" -> writer.name("id_nomenclature_behaviour") + .value(it.value.value as Long) + "NATURALITE" -> writer.name("id_nomenclature_naturalness") + .value(it.value.value as Long) + "PREUVE_EXIST" -> writer.name("id_nomenclature_exist_proof") + .value(it.value.value as Long) "COMMENT" -> writer.name("comment").value(it.value.value as String?) } } @@ -249,10 +255,13 @@ class OnInputJsonWriterListenerImpl : InputJsonWriter.OnInputJsonWriterListener< c.properties.forEach { p -> when (p.key) { - "STADE_VIE" -> writer.name("id_nomenclature_life_stage").value(p.value.value as Long) + "STADE_VIE" -> writer.name("id_nomenclature_life_stage") + .value(p.value.value as Long) "SEXE" -> writer.name("id_nomenclature_sex").value(p.value.value as Long) - "OBJ_DENBR" -> writer.name("id_nomenclature_obj_count").value(p.value.value as Long) - "TYP_DENBR" -> writer.name("id_nomenclature_type_count").value(p.value.value as Long) + "OBJ_DENBR" -> writer.name("id_nomenclature_obj_count") + .value(p.value.value as Long) + "TYP_DENBR" -> writer.name("id_nomenclature_type_count") + .value(p.value.value as Long) } } diff --git a/occtax/src/main/res/values-fr/nomenclatures.xml b/occtax/src/main/res/values-fr/nomenclatures.xml index b0090533..49172f76 100644 --- a/occtax/src/main/res/values-fr/nomenclatures.xml +++ b/occtax/src/main/res/values-fr/nomenclatures.xml @@ -1,13 +1,13 @@ - Techniques d\'observation - Méthodes d\'observation + Techniques d\'observation État biologique de l\'observation Informations avancées Méthode de détermination Déterminateur Statut biologique + Comportement des occurrences observées Niveau de naturalité Preuve d\'existence Commentaire diff --git a/occtax/src/main/res/values/nomenclatures.xml b/occtax/src/main/res/values/nomenclatures.xml index 176db29d..162e9515 100644 --- a/occtax/src/main/res/values/nomenclatures.xml +++ b/occtax/src/main/res/values/nomenclatures.xml @@ -1,13 +1,13 @@ - Technical observation - Observation methods + Technical observation Biological state of observation More information Determination method Determiner Biological status + Behavior of observed occurrences Level of naturalness Proof of existence Comment diff --git a/occtax/src/test/java/fr/geonature/occtax/input/InputTest.kt b/occtax/src/test/java/fr/geonature/occtax/input/InputTest.kt index eecc7740..8303a9f4 100644 --- a/occtax/src/test/java/fr/geonature/occtax/input/InputTest.kt +++ b/occtax/src/test/java/fr/geonature/occtax/input/InputTest.kt @@ -24,11 +24,6 @@ class InputTest { val input = Input().apply { id = 1234 datasetId = 17 - properties["TECHNIQUE_OBS"] = PropertyValue( - "TECHNIQUE_OBS", - null, - 317 - ) properties["TYP_GRP"] = PropertyValue( "TYP_GRP", null, diff --git a/occtax/src/test/java/fr/geonature/occtax/input/io/InputJsonReaderTest.kt b/occtax/src/test/java/fr/geonature/occtax/input/io/InputJsonReaderTest.kt index 81a5565e..7c4cd8c3 100644 --- a/occtax/src/test/java/fr/geonature/occtax/input/io/InputJsonReaderTest.kt +++ b/occtax/src/test/java/fr/geonature/occtax/input/io/InputJsonReaderTest.kt @@ -117,14 +117,6 @@ class InputJsonReaderTest { ) assertEquals( mapOf( - Pair( - "TECHNIQUE_OBS", - PropertyValue( - "TECHNIQUE_OBS", - null, - 317L - ) - ), Pair( "TYP_GRP", PropertyValue( @@ -172,6 +164,11 @@ class InputJsonReaderTest { null, 29L ) + properties["OCC_COMPORTEMENT"] = PropertyValue( + "OCC_COMPORTEMENT", + null, + 580L + ) properties["NATURALITE"] = PropertyValue( "NATURALITE", null, diff --git a/occtax/src/test/java/fr/geonature/occtax/input/io/InputJsonWriterTest.kt b/occtax/src/test/java/fr/geonature/occtax/input/io/InputJsonWriterTest.kt index 5edffc1f..b7076175 100644 --- a/occtax/src/test/java/fr/geonature/occtax/input/io/InputJsonWriterTest.kt +++ b/occtax/src/test/java/fr/geonature/occtax/input/io/InputJsonWriterTest.kt @@ -38,11 +38,6 @@ class InputJsonWriterTest { val input = Input().apply { id = 1234 datasetId = 17 - properties["TECHNIQUE_OBS"] = PropertyValue( - "TECHNIQUE_OBS", - null, - 317L - ) properties["TYP_GRP"] = PropertyValue( "TYP_GRP", null, @@ -89,6 +84,11 @@ class InputJsonWriterTest { null, 29L ) + properties["OCC_COMPORTEMENT"] = PropertyValue( + "OCC_COMPORTEMENT", + null, + 580L + ) properties["NATURALITE"] = PropertyValue( "NATURALITE", null, diff --git a/occtax/src/test/resources/fixtures/input_simple.json b/occtax/src/test/resources/fixtures/input_simple.json index 85030cf4..7ad6b5c6 100644 --- a/occtax/src/test/resources/fixtures/input_simple.json +++ b/occtax/src/test/resources/fixtures/input_simple.json @@ -16,14 +16,10 @@ ], "comment": "Global comment", "default": { - "technique_obs": { - "value": 317 - }, "typ_grp": { "value": 133 } }, - "id_nomenclature_obs_technique": 317, "id_nomenclature_grp_typ": 133, "t_occurrences_occtax": [ { @@ -47,6 +43,9 @@ "statut_bio": { "value": 29 }, + "occ_comportement": { + "value": 580 + }, "naturalite": { "value": 160 }, @@ -76,11 +75,12 @@ } ] }, - "id_nomenclature_obs_meth": 41, + "id_nomenclature_obs_technique": 41, "id_nomenclature_bio_condition": 29, "id_nomenclature_determination_method": 445, "determiner": "Determiner value", "id_nomenclature_bio_status": 29, + "id_nomenclature_behaviour": 580, "id_nomenclature_naturalness": 160, "id_nomenclature_exist_proof": 81, "comment": "Some comment", From 8859b5c770bfde2c5795e09ba4723d9c37ff2558 Mon Sep 17 00:00:00 2001 From: "S. Grimault" Date: Sun, 13 Sep 2020 16:21:19 +0200 Subject: [PATCH 07/11] chore: IDE global settings --- .idea/codeStyles/Project.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 8f6b0bce..38ea6b0f 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -20,7 +20,16 @@ +