Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trait progress bar changes #1059

Open
wants to merge 7 commits into
base: fields_modifications
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

import com.fieldbook.tracker.R;
import com.fieldbook.tracker.adapters.InfoBarAdapter;
import com.fieldbook.tracker.adapters.TraitsStatusAdapter;
import com.fieldbook.tracker.brapi.model.Observation;
import com.fieldbook.tracker.database.DataHelper;
import com.fieldbook.tracker.database.models.ObservationModel;
Expand Down Expand Up @@ -98,7 +99,6 @@
import com.fieldbook.tracker.views.TraitBoxView;
import com.getkeepsafe.taptargetview.TapTarget;
import com.getkeepsafe.taptargetview.TapTargetSequence;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import com.serenegiant.widget.UVCCameraTextureView;
Expand Down Expand Up @@ -724,6 +724,9 @@ private void initToolbars() {
traitLayouts.deleteTraitListener(getTraitFormat());
}

// if no more observations present, update trait status
if (getCurrentObservation() == null) updateCurrentTraitStatus(false);

triggerTts(deleteTts);
});

Expand Down Expand Up @@ -1130,6 +1133,16 @@ public void navigateToTrait(String trait) {
}
}

public void updateCurrentTraitStatus(Boolean hasObservation) {
RecyclerView traitBoxRecyclerView = traitBox.getRecyclerView();
if (traitBoxRecyclerView != null) {
TraitsStatusAdapter traitsStatusAdapter = (TraitsStatusAdapter) traitBoxRecyclerView.getAdapter();
if (traitsStatusAdapter != null){
traitsStatusAdapter.updateCurrentTraitStatus(hasObservation);
}
}
}

/**
* Helper function update user data in the memory based hashmap as well as
* the database
Expand Down Expand Up @@ -1184,6 +1197,8 @@ public void updateObservation(TraitObject trait, String value, @Nullable String
database.insertObservation(obsUnit, trait.getId(), trait.getFormat(), value, person,
getLocationByPreferences(), "", studyId, observationDbId,
lastSyncedTime, rep);

updateCurrentTraitStatus(true);
}
}

Expand Down Expand Up @@ -1660,6 +1675,8 @@ public void deleteMultiMeasures(@NonNull List<ObservationModel> models) {

if (currentModels.length == 0) {

updateCurrentTraitStatus(false);

collectInputView.setText("");

} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package com.fieldbook.tracker.adapters

import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.fieldbook.tracker.R
import com.fieldbook.tracker.views.TraitBoxView

class TraitsStatusAdapter(private val traitBoxView: TraitBoxView) :
ListAdapter<TraitsStatusAdapter.TraitBoxItemModel, TraitsStatusAdapter.ViewHolder>(
DiffCallback()
) {

data class TraitBoxItemModel(val trait: String, var hasObservation: Boolean)
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var imageView: ImageView = view.findViewById(R.id.traitStatus)
}

private var currentSelection: Int = -1

// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
// Create a new view, which defines the UI of the list item
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.trait_status, viewGroup, false)

return ViewHolder(view)
}

// Replace the contents of a view (invoked by the layout manager)
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
calculateAndSetItemSize(viewHolder)

with(currentList[position]) {
if (position == currentSelection) {
if (hasObservation) {
viewHolder.imageView.setImageResource(R.drawable.square_rounded_filled)
} else {
viewHolder.imageView.setImageResource(R.drawable.square_rounded_outline)
}
viewHolder.imageView.setColorFilter(
ContextCompat.getColor(
viewHolder.imageView.context,
R.color.main_trait_percent_start_color
)
)
} else {
if (hasObservation) {
viewHolder.imageView.setImageResource(R.drawable.circle_filled)
} else {
viewHolder.imageView.setImageResource(R.drawable.circle_outline)
}
viewHolder.imageView.setColorFilter(
ContextCompat.getColor(
viewHolder.imageView.context,
R.color.main_trait_boolean_false_color
)
)
}

// viewHolder.imageView.setOnClickListener {
//// setCurrentSelection(position)
// traitBoxView.setSelection(position)
//
// traitBoxView.rangeSuppress?.let { traitBoxView.loadLayout(it) }
// }

}


}

// calculate item size and update the view
fun calculateAndSetItemSize(viewHolder: ViewHolder) {
val itemCount = itemCount

val parentWidth = traitBoxView.getRecyclerView()?.width ?: 0

val availableWidth = parentWidth - (traitBoxView.getRecyclerView()?.paddingLeft ?: 0) - (traitBoxView.getRecyclerView()?.paddingRight ?: 0)
val calculatedSize = availableWidth / itemCount

val defaultMaxSizeDp = 25 // in dp
val defaultMaxSizePx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
defaultMaxSizeDp.toFloat(),
traitBoxView.context.resources.displayMetrics
).toInt() // in px

val itemSize = minOf(calculatedSize, defaultMaxSizePx)

viewHolder.imageView.layoutParams = viewHolder.imageView.layoutParams.apply {
width = itemSize
height = itemSize
}

}

fun setCurrentSelection(newSelection: Int) {
// Only update if the new selection is different
if (currentSelection != newSelection) {
currentSelection = newSelection
notifyDataSetChanged() // Notify adapter to refresh all items
}
}

// update an item status in the list
fun updateCurrentTraitStatus(value: Boolean) {
if (currentSelection in currentList.indices) {
// Create a new list with the updated item
val updatedList = currentList.toMutableList().apply {
this[currentSelection].hasObservation = value
}
submitList(updatedList)
notifyDataSetChanged()
}
}

// Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = currentList.size

class DiffCallback : DiffUtil.ItemCallback<TraitBoxItemModel>() {

override fun areItemsTheSame(oldItem: TraitBoxItemModel, newItem: TraitBoxItemModel): Boolean {
return oldItem == newItem
}

override fun areContentsTheSame(oldItem: TraitBoxItemModel, newItem: TraitBoxItemModel): Boolean {
return oldItem == newItem
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ abstract class AbstractCameraTrait :
ui.launch {

loadAdapterItems()

// update trait status as observation was saved
(context as CollectActivity).updateCurrentTraitStatus(true)
}
}

Expand Down Expand Up @@ -647,6 +650,11 @@ abstract class AbstractCameraTrait :

}

ui.launch {
if (getImageObservations().isEmpty()){
(context as CollectActivity).updateCurrentTraitStatus(false)
}
}
(context as CollectActivity).refreshRepeatedValuesToolbarIndicator()

}
Expand Down
72 changes: 55 additions & 17 deletions app/src/main/java/com/fieldbook/tracker/views/TraitBoxView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import android.view.MotionEvent
import android.view.View.OnTouchListener
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat.startActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.fieldbook.tracker.R
import com.fieldbook.tracker.activities.TraitEditorActivity
import com.fieldbook.tracker.adapters.TraitsStatusAdapter
import com.fieldbook.tracker.interfaces.CollectTraitController
import com.fieldbook.tracker.objects.TraitObject
import com.fieldbook.tracker.preferences.GeneralKeys
Expand All @@ -33,12 +35,13 @@ class TraitBoxView : ConstraintLayout {
private var traitLeft: ImageView
private var traitRight: ImageView

private var traitsProgressBar: ProgressBar
private var traitsStatusBarRv: RecyclerView? = null
private var traitBoxItemModels: List<TraitsStatusAdapter.TraitBoxItemModel>? = null

var currentTrait: TraitObject? = null

private var visibleTraitsList: Array<String>? = null
private var rangeSuppress: Boolean? = null
var rangeSuppress: Boolean? = null

/**
* New traits is a map of observations where the key is the trait name
Expand All @@ -58,10 +61,11 @@ class TraitBoxView : ConstraintLayout {
traitLeft = v.findViewById(R.id.traitLeft)
traitRight = v.findViewById(R.id.traitRight)

traitsStatusBarRv = v.findViewById(R.id.traitsStatusBarRv)

prefixTraits = controller.getDatabase().rangeColumnNames
newTraits = HashMap()

traitsProgressBar = findViewById(R.id.traitsProgressBar)
}

constructor(ctx: Context) : super(ctx)
Expand Down Expand Up @@ -159,6 +163,21 @@ class TraitBoxView : ConstraintLayout {
this.visibleTraitsList = visibleTraits
this.rangeSuppress = rangeSuppress

traitsStatusBarRv?.adapter = TraitsStatusAdapter(this)
traitsStatusBarRv?.layoutManager = object : LinearLayoutManager(context, HORIZONTAL, false) {
override fun canScrollHorizontally(): Boolean {
return false
}
}

// recyclerView?.viewTreeObserver?.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
// override fun onGlobalLayout() {
// recyclerView?.viewTreeObserver?.removeOnGlobalLayoutListener(this)
// val ht = recyclerView?.height //height is ready
// Log.d("TAG", "onGlobalLayout: $ht")
// }
// })

// navigate to the last used trait using preferences
// if using for the first time, use the first element
traitTypeTv.text = controller.getPreferences().getString(GeneralKeys.LAST_USED_TRAIT,
Expand All @@ -173,7 +192,11 @@ class TraitBoxView : ConstraintLayout {
}
}

private fun loadLayout(rangeSuppress: Boolean){
fun getRecyclerView(): RecyclerView? {
return traitsStatusBarRv
}

fun loadLayout(rangeSuppress: Boolean) {
val traitPosition = getSelectedItemPosition()

setSelection(traitPosition)
Expand All @@ -184,6 +207,8 @@ class TraitBoxView : ConstraintLayout {
.toString()
)

updateTraitsStatusBar()

// Update last used trait so it is preserved when entry moves
controller.getPreferences().edit().putString(GeneralKeys.LAST_USED_TRAIT,traitTypeTv.text.toString()).apply()
traitTypeTv.text = currentTrait?.name
Expand Down Expand Up @@ -227,7 +252,7 @@ class TraitBoxView : ConstraintLayout {
controller.getInputView().isEnabled = true
}

updateTraitProgressBar()
updateTraitsStatusBar()
}

private fun showTraitPickerDialog(visibleTraits: Array<String>?) {
Expand Down Expand Up @@ -258,21 +283,33 @@ class TraitBoxView : ConstraintLayout {

}

private fun updateTraitProgressBar() {
var traits = controller.getDatabase().allTraitObjects
private fun updateTraitsStatusBar() {
val visibleTraits: Array<String> = controller.getDatabase().getVisibleTrait()

// images saved are not stored in newTraits hashMap
// get the data for current plot_id again
val rangeBox = controller.getRangeBox()
val traitsValue = controller.getDatabase().getUserDetail(rangeBox.getPlotID())

// a new trait object is made while assigning to currentTrait
// so instead of finding the index of currentTrait object
// we find the index of the trait name
val visibleTraits = ArrayList<String>()
for (traitObject in traits) {
if (traitObject.visible) {
visibleTraits.add(traitObject.name)
traitBoxItemModels = visibleTraits.map { trait ->
TraitsStatusAdapter.TraitBoxItemModel(
trait,
traitsValue.containsKey(trait)
)
}
(traitsStatusBarRv?.adapter as TraitsStatusAdapter).submitList(traitBoxItemModels)

// the recyclerView height was 0 initially, so calculate the icon size again
traitsStatusBarRv?.post {
for (pos in visibleTraits.indices) {
val viewHolder = traitsStatusBarRv?.findViewHolderForAdapterPosition(pos) as? TraitsStatusAdapter.ViewHolder
viewHolder?.let {
(traitsStatusBarRv?.adapter as TraitsStatusAdapter).calculateAndSetItemSize(it)
}
}
}
(traitsStatusBarRv?.adapter as TraitsStatusAdapter).notifyDataSetChanged()

traitsProgressBar.max = visibleTraits.size
traitsProgressBar.progress = visibleTraits.indexOf(currentTrait?.name) + 1
}

fun getNewTraits(): Map<String, String> {
Expand Down Expand Up @@ -319,6 +356,7 @@ class TraitBoxView : ConstraintLayout {
traitTypeTv.text
.toString()
)
(traitsStatusBarRv?.adapter as TraitsStatusAdapter).setCurrentSelection(pos)
}

private fun getSelectedItemPosition(): Int {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/drawable/circle_filled.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- drawable/circle_slice_8.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M12 5C15.87 5 19 8.13 19 12C19 15.87 15.87 19 12 19C8.13 19 5 15.87 5 12C5 8.13 8.13 5 12 5M12 2C17.5 2 22 6.5 22 12C22 17.5 17.5 22 12 22C6.5 22 2 17.5 2 12C2 6.5 6.5 2 12 2M12 4C7.58 4 4 7.58 4 12C4 16.42 7.58 20 12 20C16.42 20 20 16.42 20 12C20 7.58 16.42 4 12 4Z" /></vector>
1 change: 1 addition & 0 deletions app/src/main/res/drawable/circle_outline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" /></vector>
1 change: 1 addition & 0 deletions app/src/main/res/drawable/square_rounded_filled.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- drawable/square_rounded.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M8 3H16C18.76 3 21 5.24 21 8V16C21 18.76 18.76 21 16 21H8C5.24 21 3 18.76 3 16V8C3 5.24 5.24 3 8 3Z" /></vector>
1 change: 1 addition & 0 deletions app/src/main/res/drawable/square_rounded_outline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- drawable/square_rounded_outline.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M8 3H16C18.76 3 21 5.24 21 8V16C21 18.76 18.76 21 16 21H8C5.24 21 3 18.76 3 16V8C3 5.24 5.24 3 8 3M8 5C6.34 5 5 6.34 5 8V16C5 17.66 6.34 19 8 19H16C17.66 19 19 17.66 19 16V8C19 6.34 17.66 5 16 5H8Z" /></vector>
5 changes: 5 additions & 0 deletions app/src/main/res/layout/trait_status.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/traitStatus"
android:layout_width="25dp"
android:layout_height="25dp"/>
Loading