Merge pull request #1437 from Andries-Smit/feat/bg-source-option-menu

Feat: BG source option menu
This commit is contained in:
Milos Kozak 2022-03-13 10:34:19 +01:00 committed by GitHub
commit 4a550f237d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 167 additions and 50 deletions

View file

@ -1,10 +1,10 @@
package info.nightscout.androidaps.plugins.source package info.nightscout.androidaps.plugins.source
import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.util.SparseArray
import android.view.View import android.view.*
import android.view.ViewGroup import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach
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
@ -54,11 +54,12 @@ class BGSourceFragment : DaggerFragment() {
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private val millsToThePast = T.hours(36).msecs() private val millsToThePast = T.hours(36).msecs()
private var selectedItems: SparseArray<GlucoseValue> = SparseArray()
private var toolbar: Toolbar? = null
private var removeActionMode: ActionMode? = null
private var _binding: BgsourceFragmentBinding? = null private var _binding: BgsourceFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
@ -66,6 +67,8 @@ class BGSourceFragment : DaggerFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
toolbar = activity?.findViewById(R.id.toolbar)
setHasOptionsMenu(true)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
@ -94,17 +97,45 @@ class BGSourceFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onPause() { override fun onPause() {
removeActionMode?.finish()
disposable.clear() disposable.clear()
super.onPause() super.onPause()
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_bgsource, menu)
}
override fun onPrepareOptionsMenu(menu: Menu) {
// Only show when tab bg source is shown
menu.findItem(R.id.nav_remove_items)?.isVisible = isResumed
super.onPrepareOptionsMenu(menu)
}
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.finish()
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.nav_remove_items -> {
if (toolbar != null) {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) // in overview
} else {
removeActionMode = activity?.startActionMode(RemoveActionModeCallback()) // in Single FragmentActivity
}
true
}
else -> false
}
}
inner class RecyclerViewAdapter internal constructor(private var glucoseValues: List<GlucoseValue>) : RecyclerView.Adapter<RecyclerViewAdapter.GlucoseValuesViewHolder>() { inner class RecyclerViewAdapter internal constructor(private var glucoseValues: List<GlucoseValue>) : RecyclerView.Adapter<RecyclerViewAdapter.GlucoseValuesViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): GlucoseValuesViewHolder { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): GlucoseValuesViewHolder {
@ -116,19 +147,43 @@ class BGSourceFragment : DaggerFragment() {
val glucoseValue = glucoseValues[position] val glucoseValue = glucoseValues[position]
holder.binding.ns.visibility = (glucoseValue.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ns.visibility = (glucoseValue.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = (!glucoseValue.isValid).toVisibility() holder.binding.invalid.visibility = (!glucoseValue.isValid).toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(glucoseValue.timestamp, glucoseValues[position-1].timestamp) val sameDayPrevious = position > 0 && dateUtil.isSameDay(glucoseValue.timestamp, glucoseValues[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(glucoseValue.timestamp) holder.binding.date.text = dateUtil.dateString(glucoseValue.timestamp)
holder.binding.time.text = dateUtil.timeString(glucoseValue.timestamp) holder.binding.time.text = dateUtil.timeString(glucoseValue.timestamp)
holder.binding.value.text = glucoseValue.valueToUnitsString(profileFunction.getUnits()) holder.binding.value.text = glucoseValue.valueToUnitsString(profileFunction.getUnits())
holder.binding.direction.setImageResource(glucoseValue.trendArrow.directionToIcon()) holder.binding.direction.setImageResource(glucoseValue.trendArrow.directionToIcon())
holder.binding.remove.tag = glucoseValue
if (position > 0) { if (position > 0) {
val previous = glucoseValues[position - 1] val previous = glucoseValues[position - 1]
val diff = previous.timestamp - glucoseValue.timestamp val diff = previous.timestamp - glucoseValue.timestamp
if (diff < T.secs(20).msecs()) if (diff < T.secs(20).msecs())
holder.binding.root.setBackgroundColor(rh.gc(R.color.errorAlertBackground)) holder.binding.root.setBackgroundColor(rh.gc(R.color.errorAlertBackground))
} }
fun updateSelection(selected: Boolean) {
if (selected) {
selectedItems.put(position, glucoseValue)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
}
holder.binding.root.setOnLongClickListener {
if (removeActionMode == null) {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
}
holder.binding.cbRemove.toggle()
updateSelection(holder.binding.cbRemove.isChecked)
true
}
holder.binding.root.setOnClickListener {
if (removeActionMode != null) {
holder.binding.cbRemove.toggle()
updateSelection(holder.binding.cbRemove.isChecked)
}
}
holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> updateSelection(value) }
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null
holder.binding.cbRemove.visibility = (removeActionMode != null).toVisibility()
} }
override fun getItemCount(): Int = glucoseValues.size override fun getItemCount(): Int = glucoseValues.size
@ -136,38 +191,76 @@ class BGSourceFragment : DaggerFragment() {
inner class GlucoseValuesViewHolder(view: View) : RecyclerView.ViewHolder(view) { inner class GlucoseValuesViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = BgsourceItemBinding.bind(view) val binding = BgsourceItemBinding.bind(view)
init {
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.remove.setOnClickListener { v: View ->
val glucoseValue = v.tag as GlucoseValue
activity?.let { activity ->
val text = dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits())
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable {
val source = when ((activePlugin.activeBgSource as PluginBase).pluginDescription.pluginName) {
R.string.dexcom_app_patched -> Sources.Dexcom
R.string.eversense -> Sources.Eversense
R.string.Glimp -> Sources.Glimp
R.string.MM640g -> Sources.MM640g
R.string.nsclientbg -> Sources.NSClientSource
R.string.poctech -> Sources.PocTech
R.string.tomato -> Sources.Tomato
R.string.glunovo -> Sources.Glunovo
R.string.xdrip -> Sources.Xdrip
else -> Sources.Unknown
}
uel.log(
Action.BG_REMOVED, source,
ValueWithUnit.Timestamp(glucoseValue.timestamp)
)
repository.runTransactionForResult(InvalidateGlucoseValueTransaction(glucoseValue.id))
.doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating BG value", it) }
.blockingGet()
.also { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bg $it") } }
})
}
}
}
} }
} }
inner class RemoveActionModeCallback : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
mode.menuInflater.inflate(R.menu.menu_delete_selection, menu)
selectedItems.clear()
mode.title = rh.gs(R.string.count_selected, selectedItems.size())
binding.recyclerview.adapter?.notifyDataSetChanged()
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.remove_selected -> {
removeSelected()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
removeActionMode = null
binding.recyclerview.adapter?.notifyDataSetChanged()
}
}
private fun getConfirmationText(): String {
if (selectedItems.size() == 1) {
val glucoseValue = selectedItems.valueAt(0)
return dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits())
}
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
}
private fun removeSelected() {
if (selectedItems.size() > 0)
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable {
selectedItems.forEach { _, glucoseValue ->
val source = when ((activePlugin.activeBgSource as PluginBase).pluginDescription.pluginName) {
R.string.dexcom_app_patched -> Sources.Dexcom
R.string.eversense -> Sources.Eversense
R.string.Glimp -> Sources.Glimp
R.string.MM640g -> Sources.MM640g
R.string.nsclientbg -> Sources.NSClientSource
R.string.poctech -> Sources.PocTech
R.string.tomato -> Sources.Tomato
R.string.glunovo -> Sources.Glunovo
R.string.xdrip -> Sources.Xdrip
else -> Sources.Unknown
}
uel.log(
Action.BG_REMOVED, source,
ValueWithUnit.Timestamp(glucoseValue.timestamp)
)
repository.runTransactionForResult(InvalidateGlucoseValueTransaction(glucoseValue.id))
.doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating BG value", it) }
.blockingGet()
.also { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bg $it") } }
}
removeActionMode?.finish()
})
}
else
removeActionMode?.finish()
}
} }

View file

@ -68,18 +68,19 @@
android:text="@string/invalid" android:text="@string/invalid"
android:textColor="@android:color/holo_red_light" /> android:textColor="@android:color/holo_red_light" />
<TextView
android:id="@+id/remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:paddingEnd="5dp"
android:text="@string/remove_button"
android:textAlignment="viewEnd"
android:textColor="@android:color/holo_orange_light" />
</LinearLayout> </LinearLayout>
<CheckBox
android:id="@+id/cb_remove"
android:layout_width="wrap_content"
android:layout_gravity="end"
android:layout_height="19dp"
android:contentDescription="@string/select_for_removal"
android:minWidth="0dp"
android:layout_marginTop="-25dp"
android:visibility="gone"/>
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View file

@ -0,0 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/nav_remove_items"
android:orderInCategory="0"
android:title="@string/remove_bg_readings"
app:showAsAction="never" />
</menu>

View file

@ -2,45 +2,57 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"> tools:context=".MainActivity">
<item <item
android:id="@+id/nav_preferences" android:id="@+id/nav_preferences"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/nav_preferences" /> android:title="@string/nav_preferences" />
<item <item
android:id="@+id/nav_plugin_preferences" android:id="@+id/nav_plugin_preferences"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/nav_plugin_preferences" /> android:title="@string/nav_plugin_preferences" />
<item <item
android:id="@+id/nav_treatments" android:id="@+id/nav_treatments"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/treatments" /> android:title="@string/treatments" />
<item <item
android:id="@+id/nav_historybrowser" android:id="@+id/nav_historybrowser"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/nav_historybrowser" /> android:title="@string/nav_historybrowser" />
<item <item
android:id="@+id/nav_setupwizard" android:id="@+id/nav_setupwizard"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/nav_setupwizard" /> android:title="@string/nav_setupwizard" />
<item <item
android:id="@+id/nav_stats" android:id="@+id/nav_stats"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/statistics" /> android:title="@string/statistics" />
<!-- <item <!-- <item
android:id="@+id/nav_survey" android:id="@+id/nav_survey"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/nav_survey" /> android:title="@string/nav_survey" />
--> -->
<item <item
android:id="@+id/nav_defaultprofile" android:id="@+id/nav_defaultprofile"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/nav_profilehelper" /> android:title="@string/nav_profilehelper" />
<item <item
android:id="@+id/nav_about" android:id="@+id/nav_about"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/nav_about" /> android:title="@string/nav_about" />
<item <item
android:id="@+id/nav_exit" android:id="@+id/nav_exit"
android:orderInCategory="1"
app:showAsAction="never" app:showAsAction="never"
android:title="@string/nav_exit" /> android:title="@string/nav_exit" />
</menu> </menu>

View file

@ -1140,6 +1140,7 @@
<string name="errors">Errors</string> <string name="errors">Errors</string>
<string name="ns_sync_slow">Slow down uploads</string> <string name="ns_sync_slow">Slow down uploads</string>
<string name="data_status">BG data status</string> <string name="data_status">BG data status</string>
<string name="remove_bg_readings">Remove BG readings</string>
<string name="statuslights_cannula_age">cannula age</string> <string name="statuslights_cannula_age">cannula age</string>
<string name="statuslights_patch_pump_age">patch pump age</string> <string name="statuslights_patch_pump_age">patch pump age</string>
<string name="patch_pump">Patch pump</string> <string name="patch_pump">Patch pump</string>