BGSourceFragment UI tweaks and recognize history change

This commit is contained in:
AdrianLxM 2020-11-28 23:42:59 +01:00
parent 7bc7388f06
commit fe53b733d0
3 changed files with 128 additions and 11 deletions

View file

@ -417,6 +417,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
try { try {
getDaoBgReadings().update(bgReading); getDaoBgReadings().update(bgReading);
openHumansUploader.enqueueBGReading(bgReading); openHumansUploader.enqueueBGReading(bgReading);
aapsLogger.debug(LTag.DATABASE, "BG: Updating record from: "+ bgReading.toString());
scheduleBgHistoryChange(bgReading.date); // trigger cache invalidation
} catch (SQLException e) { } catch (SQLException e) {
aapsLogger.error("Unhandled exception", e); aapsLogger.error("Unhandled exception", e);
} }

View file

@ -7,22 +7,27 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.db.BgReading import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryBgData
import info.nightscout.androidaps.plugins.source.BGSourceFragment.RecyclerViewAdapter.BgReadingsViewHolder import info.nightscout.androidaps.plugins.source.BGSourceFragment.RecyclerViewAdapter.BgReadingsViewHolder
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ListDiffCallback
import info.nightscout.androidaps.utils.ListUpdateCallbackHelper
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
@ -30,6 +35,7 @@ import kotlinx.android.synthetic.main.bgsource_fragment.*
import javax.inject.Inject import javax.inject.Inject
class BGSourceFragment : DaggerFragment() { class BGSourceFragment : DaggerFragment() {
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@ -51,14 +57,24 @@ class BGSourceFragment : DaggerFragment() {
bgsource_recyclerview.setHasFixedSize(true) bgsource_recyclerview.setHasFixedSize(true)
bgsource_recyclerview.layoutManager = LinearLayoutManager(view.context) bgsource_recyclerview.layoutManager = LinearLayoutManager(view.context)
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
bgsource_recyclerview.adapter = RecyclerViewAdapter(MainApp.getDbHelper().getAllBgreadingsDataFromTime(now - MILLS_TO_THE_PAST, false)) bgsource_recyclerview.adapter = RecyclerViewAdapter(getBgData(now))
}
override fun onDestroyView() {
super.onDestroyView()
bgsource_recyclerview?.adapter = null // avoid leaks
} }
@Synchronized @Synchronized
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventNewBG::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ updateGUI() }) { fabricPrivacy.logException(it) }
)
disposable.add(rxBus
.toObservable(EventNewHistoryBgData::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe({ updateGUI() }) { fabricPrivacy.logException(it) } .subscribe({ updateGUI() }) { fabricPrivacy.logException(it) }
) )
@ -73,30 +89,62 @@ class BGSourceFragment : DaggerFragment() {
private fun updateGUI() { private fun updateGUI() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
bgsource_recyclerview?.swapAdapter(RecyclerViewAdapter(MainApp.getDbHelper().getAllBgreadingsDataFromTime(now - MILLS_TO_THE_PAST, false)), true) (bgsource_recyclerview?.adapter as? RecyclerViewAdapter)?.setData(getBgData(now))
}
private fun getBgData(now: Long) = MainApp.getDbHelper()
.getAllBgreadingsDataFromTime(now - MILLS_TO_THE_PAST, false)
inner class RecyclerViewAdapter internal constructor(bgReadings: List<BgReading>) : RecyclerView.Adapter<BgReadingsViewHolder>() {
private var callbackHelper = ListUpdateCallbackHelper(this) { bgsource_recyclerview?.smoothScrollToPosition(0) }
private val currentData: MutableList<BgReading> = mutableListOf<BgReading>().also { it.addAll(bgReadings) }
fun setData(newList: List<BgReading>) {
val diffResult = DiffUtil.calculateDiff(getListDiffCallback(ArrayList(newList), ArrayList(currentData)))
currentData.clear()
currentData.addAll(newList)
diffResult.dispatchUpdatesTo(callbackHelper)
}
private fun getListDiffCallback(newItems: List<BgReading>, oldItems: List<BgReading>): ListDiffCallback<BgReading> =
object : ListDiffCallback<BgReading>(newItems, oldItems) {
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val new = newItems[newItemPosition]
val old = oldItems[oldItemPosition]
return new.hasValidNS == old.hasValidNS &&
new.isValid == old.isValid &&
new.date == old.date &&
new.value == old.value
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
newItems[newItemPosition].date == oldItems[oldItemPosition].date
} }
inner class RecyclerViewAdapter internal constructor(private var bgReadings: List<BgReading>) : RecyclerView.Adapter<BgReadingsViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): BgReadingsViewHolder { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): BgReadingsViewHolder {
val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.bgsource_item, viewGroup, false) val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.bgsource_item, viewGroup, false)
return BgReadingsViewHolder(v) return BgReadingsViewHolder(v)
} }
override fun onBindViewHolder(holder: BgReadingsViewHolder, position: Int) { override fun onBindViewHolder(holder: BgReadingsViewHolder, position: Int) {
val bgReading = bgReadings[position] val bgReading = currentData[position]
holder.ns.visibility = if (NSUpload.isIdValid(bgReading._id)) View.VISIBLE else View.GONE holder.ns.visibility = (NSUpload.isIdValid(bgReading._id)).toVisibility()
holder.invalid.visibility = if (!bgReading.isValid) View.VISIBLE else View.GONE holder.invalid.visibility = bgReading.isValid.not().toVisibility()
holder.date.text = dateUtil.dateAndTimeString(bgReading.date) holder.date.text = dateUtil.dateAndTimeString(bgReading.date)
holder.value.text = bgReading.valueToUnitsToString(profileFunction.getUnits()) holder.value.text = bgReading.valueToUnitsToString(profileFunction.getUnits())
holder.direction.setImageResource(bgReading.directionToIcon(databaseHelper)) holder.direction.setImageResource(bgReading.directionToIcon(databaseHelper))
holder.remove.tag = bgReading holder.remove.tag = bgReading
holder.remove.visibility = bgReading.isValid.toVisibility()
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return bgReadings.size return currentData.size
} }
inner class BgReadingsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class BgReadingsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var date: TextView = itemView.findViewById(R.id.bgsource_date) var date: TextView = itemView.findViewById(R.id.bgsource_date)
var value: TextView = itemView.findViewById(R.id.bgsource_value) var value: TextView = itemView.findViewById(R.id.bgsource_value)
var direction: ImageView = itemView.findViewById(R.id.bgsource_direction) var direction: ImageView = itemView.findViewById(R.id.bgsource_direction)
@ -112,7 +160,6 @@ class BGSourceFragment : DaggerFragment() {
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord), text, Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord), text, Runnable {
bgReading.isValid = false bgReading.isValid = false
MainApp.getDbHelper().update(bgReading) MainApp.getDbHelper().update(bgReading)
updateGUI()
}) })
} }
} }
@ -121,3 +168,6 @@ class BGSourceFragment : DaggerFragment() {
} }
} }
} }
val BgReading.hasValidNS
get() = NSUpload.isIdValid(this._id)

View file

@ -0,0 +1,65 @@
package info.nightscout.androidaps.utils
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback
import androidx.recyclerview.widget.RecyclerView
open class ListDiffCallback<T>(private val newItems: List<T>, private val oldItems: List<T>) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldItems.size
override fun getNewListSize(): Int = newItems.size
/**
* Called by the DiffUtil to decide whether two object represent the same Item.
* <p>
* For example, if your items have unique ids, this method should check their id equality.
*
* @param oldItemPosition The position of the item in the old list
* @param newItemPosition The position of the item in the new list
* @return True if the two items represent the same object or false if they are different.
*/
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
newItems[newItemPosition] == oldItems[oldItemPosition]
/**
* Called by the DiffUtil when it wants to check whether two items have the same data.
* DiffUtil uses this information to detect if the contents of an item has changed.
* <p>
* DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}
* so that you can change its behavior depending on your UI.
* For example, if you are using DiffUtil with a
* {@link RecyclerView.Adapter RecyclerView.Adapter}, you should
* return whether the items' visual representations are the same.
* <p>
* This method is called only if {@link #areItemsTheSame(int, int)} returns
* {@code true} for these items.
*
* @param oldItemPosition The position of the item in the old list
* @param newItemPosition The position of the item in the new list which replaces the
* oldItem
* @return True if the contents of the items are the same or false if they are different.
*/
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
newItems[newItemPosition] == oldItems[oldItemPosition]
}
class ListUpdateCallbackHelper(val adapter: RecyclerView.Adapter<*>, val insertCallback: (Int) -> Unit) : ListUpdateCallback {
override fun onChanged(position: Int, count: Int, payload: Any?) {
adapter.notifyItemRangeChanged(position, count, payload)
}
override fun onInserted(position: Int, count: Int) {
adapter.notifyItemRangeInserted(position, count)
insertCallback(position)
}
override fun onMoved(fromPosition: Int, toPosition: Int) {
adapter.notifyItemMoved(fromPosition, toPosition)
}
override fun onRemoved(position: Int, count: Int) {
adapter.notifyItemRangeRemoved(position, count)
}
}