Merge pull request #1437 from Andries-Smit/feat/bg-source-option-menu
Feat: BG source option menu
This commit is contained in:
commit
4a550f237d
5 changed files with 167 additions and 50 deletions
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
10
app/src/main/res/menu/menu_bgsource.xml
Normal file
10
app/src/main/res/menu/menu_bgsource.xml
Normal 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>
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue