Merge pull request #86 from nightscout/adrian/recycler-adapter

BGSourceFragment UI tweaks and recognize history change
This commit is contained in:
Milos Kozak 2020-11-29 19:41:51 +01:00 committed by GitHub
commit 5c9a8d2f60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 128 additions and 11 deletions

View file

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

View file

@ -7,22 +7,27 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
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.utils.DateUtil
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.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
@ -30,6 +35,7 @@ import kotlinx.android.synthetic.main.bgsource_fragment.*
import javax.inject.Inject
class BGSourceFragment : DaggerFragment() {
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper
@ -51,14 +57,24 @@ class BGSourceFragment : DaggerFragment() {
bgsource_recyclerview.setHasFixedSize(true)
bgsource_recyclerview.layoutManager = LinearLayoutManager(view.context)
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
override fun onResume() {
super.onResume()
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())
.subscribe({ updateGUI() }) { fabricPrivacy.logException(it) }
)
@ -73,30 +89,62 @@ class BGSourceFragment : DaggerFragment() {
private fun updateGUI() {
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))
}
inner class RecyclerViewAdapter internal constructor(private var bgReadings: List<BgReading>) : RecyclerView.Adapter<BgReadingsViewHolder>() {
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
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): BgReadingsViewHolder {
val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.bgsource_item, viewGroup, false)
return BgReadingsViewHolder(v)
}
override fun onBindViewHolder(holder: BgReadingsViewHolder, position: Int) {
val bgReading = bgReadings[position]
holder.ns.visibility = if (NSUpload.isIdValid(bgReading._id)) View.VISIBLE else View.GONE
holder.invalid.visibility = if (!bgReading.isValid) View.VISIBLE else View.GONE
val bgReading = currentData[position]
holder.ns.visibility = (NSUpload.isIdValid(bgReading._id)).toVisibility()
holder.invalid.visibility = bgReading.isValid.not().toVisibility()
holder.date.text = dateUtil.dateAndTimeString(bgReading.date)
holder.value.text = bgReading.valueToUnitsToString(profileFunction.getUnits())
holder.direction.setImageResource(bgReading.directionToIcon(databaseHelper))
holder.remove.tag = bgReading
holder.remove.visibility = bgReading.isValid.toVisibility()
}
override fun getItemCount(): Int {
return bgReadings.size
return currentData.size
}
inner class BgReadingsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var date: TextView = itemView.findViewById(R.id.bgsource_date)
var value: TextView = itemView.findViewById(R.id.bgsource_value)
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 {
bgReading.isValid = false
MainApp.getDbHelper().update(bgReading)
updateGUI()
})
}
}
@ -120,4 +167,7 @@ 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)
}
}