Merge pull request #1362 from Andries-Smit/feat/toolbar-treatment

Feature toolbar with options menu in Treatments
This commit is contained in:
Milos Kozak 2022-02-23 19:10:08 +01:00 committed by GitHub
commit c0dbe41899
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 1359 additions and 791 deletions

View file

@ -1,13 +1,13 @@
package info.nightscout.androidaps.activities package info.nightscout.androidaps.activities
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem
import android.view.View import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.fragments.* import info.nightscout.androidaps.activities.fragments.*
import info.nightscout.androidaps.databinding.TreatmentsFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsFragmentBinding
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import javax.inject.Inject import javax.inject.Inject
@ -23,40 +23,60 @@ class TreatmentsActivity : NoSplashAppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = TreatmentsFragmentBinding.inflate(layoutInflater) binding = TreatmentsFragmentBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
//binding.tempBasals.visibility = buildHelper.isEngineeringMode().toVisibility() //binding.tempBasals.visibility = buildHelper.isEngineeringMode().toVisibility()
//binding.extendedBoluses.visibility = (buildHelper.isEngineeringMode() && !activePlugin.activePump.isFakingTempsByExtendedBoluses).toVisibility() //binding.extendedBoluses.visibility = (buildHelper.isEngineeringMode() && !activePlugin.activePump.isFakingTempsByExtendedBoluses).toVisibility()
binding.treatments.setOnClickListener { binding.treatments.setOnClickListener {
setFragment(TreatmentsBolusCarbsFragment()) setFragment(TreatmentsBolusCarbsFragment())
setBackgroundColorOnSelected(it) setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.carbs_and_bolus)
} }
binding.extendedBoluses.setOnClickListener { binding.extendedBoluses.setOnClickListener {
setFragment(TreatmentsExtendedBolusesFragment()) setFragment(TreatmentsExtendedBolusesFragment())
setBackgroundColorOnSelected(it) setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.extended_bolus)
} }
binding.tempBasals.setOnClickListener { binding.tempBasals.setOnClickListener {
setFragment(TreatmentsTemporaryBasalsFragment()) setFragment(TreatmentsTemporaryBasalsFragment())
setBackgroundColorOnSelected(it) setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.tempbasal_label)
} }
binding.tempTargets.setOnClickListener { binding.tempTargets.setOnClickListener {
setFragment(TreatmentsTempTargetFragment()) setFragment(TreatmentsTempTargetFragment())
setBackgroundColorOnSelected(it) setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.tempt_targets)
} }
binding.profileSwitches.setOnClickListener { binding.profileSwitches.setOnClickListener {
setFragment(TreatmentsProfileSwitchFragment()) setFragment(TreatmentsProfileSwitchFragment())
setBackgroundColorOnSelected(it) setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.profile_changes)
} }
binding.careportal.setOnClickListener { binding.careportal.setOnClickListener {
setFragment(TreatmentsCareportalFragment()) setFragment(TreatmentsCareportalFragment())
setBackgroundColorOnSelected(it) setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.careportal)
} }
binding.userentry.setOnClickListener { binding.userentry.setOnClickListener {
setFragment(TreatmentsUserEntryFragment()) setFragment(TreatmentsUserEntryFragment())
setBackgroundColorOnSelected(it) setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.user_action)
} }
setFragment(TreatmentsBolusCarbsFragment()) setFragment(TreatmentsBolusCarbsFragment())
setBackgroundColorOnSelected(binding.treatments) setBackgroundColorOnSelected(binding.treatments)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.title = rh.gs(R.string.carbs_and_bolus)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
finish()
true
}
else -> false
}
} }
private fun setFragment(selectedFragment: Fragment) { private fun setFragment(selectedFragment: Fragment) {

View file

@ -3,9 +3,13 @@ package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Paint import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.util.Log
import android.view.View import android.util.SparseArray
import android.view.ViewGroup import android.view.*
import android.widget.CompoundButton
import android.view.ActionMode
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
@ -67,6 +71,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
private var _binding: TreatmentsBolusCarbsFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
class MealLink( class MealLink(
val bolus: Bolus? = null, val bolus: Bolus? = null,
val carbs: Carbs? = null, val carbs: Carbs? = null,
@ -74,14 +82,12 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
) )
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs() private val millsToThePast = T.days(30).msecs()
private var _binding: TreatmentsBolusCarbsFragmentBinding? = null private var selectedItems: SparseArray<MealLink> = SparseArray()
private var showInvalidated = false
// This property is only valid between onCreateView and private var removeActionMode: ActionMode? = null
// onDestroyView. private var toolbar: Toolbar? = null
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 =
TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@ -89,92 +95,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
toolbar = activity?.findViewById(R.id.toolbar)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.refreshFromNightscout.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") {
uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments)
disposable +=
Completable.fromAction {
repository.deleteAllBolusCalculatorResults()
repository.deleteAllBoluses()
repository.deleteAllCarbs()
}
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) },
onComplete = {
rxBus.send(EventTreatmentChange())
rxBus.send(EventNewHistoryData(0, false))
}
)
rxBus.send(EventNSClientRestart())
}
}
}
binding.deleteFutureTreatments.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), rh.gs(R.string.deletefuturetreatments) + "?", Runnable {
uel.log(Action.DELETE_FUTURE_TREATMENTS, Sources.Treatments)
repository
.getBolusesDataFromTime(dateUtil.now(), false)
.observeOn(aapsSchedulers.main)
.subscribe { list ->
list.forEach { bolus ->
disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) }
)
}
}
repository
.getCarbsDataFromTimeNotExpanded(dateUtil.now(), false)
.observeOn(aapsSchedulers.main)
.subscribe { list ->
list.forEach { carb ->
if (carb.duration == 0L)
disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
)
else {
disposable += repository.runTransactionForResult(CutCarbsTransaction(carb.id, dateUtil.now()))
.subscribe(
{ result ->
result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated (cut end) carbs $it") }
},
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
)
}
}
}
repository
.getBolusCalculatorResultsDataFromTime(dateUtil.now(), false)
.observeOn(aapsSchedulers.main)
.subscribe { list ->
list.forEach { bolusCalc ->
disposable += repository.runTransactionForResult(InvalidateBolusCalculatorResultTransaction(bolusCalc.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolusCalculatorResult $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolusCalculatorResult", it) }
)
}
}
binding.deleteFutureTreatments.visibility = View.GONE
})
}
}
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_insulin, false) || !sp.getBoolean(R.string.key_ns_receive_carbs, false) || !buildHelper.isEngineeringMode()
if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}
} }
private fun bolusMealLinksWithInvalid(now: Long) = repository private fun bolusMealLinksWithInvalid(now: Long) = repository
@ -204,36 +128,35 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
if (binding.showInvalidated.isChecked) disposable +=
disposable += carbsMealLinksWithInvalid(now) if (showInvalidated)
.zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second } carbsMealLinksWithInvalid(now)
.zipWith(calcResultMealLinksWithInvalid(now)) { first, second -> first + second } .zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second }
.map { ml -> .zipWith(calcResultMealLinksWithInvalid(now)) { first, second -> first + second }
ml.sortedByDescending { .map { ml ->
it.carbs?.timestamp ?: it.bolus?.timestamp ml.sortedByDescending {
?: it.bolusCalculatorResult?.timestamp it.carbs?.timestamp ?: it.bolus?.timestamp
?: it.bolusCalculatorResult?.timestamp
}
} }
} .observeOn(aapsSchedulers.main)
.observeOn(aapsSchedulers.main) .subscribe { list ->
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true)
binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility() else
} carbsMealLinks(now)
else .zipWith(bolusMealLinks(now)) { first, second -> first + second }
disposable += carbsMealLinks(now) .zipWith(calcResultMealLinks(now)) { first, second -> first + second }
.zipWith(bolusMealLinks(now)) { first, second -> first + second } .map { ml ->
.zipWith(calcResultMealLinks(now)) { first, second -> first + second } ml.sortedByDescending {
.map { ml -> it.carbs?.timestamp ?: it.bolus?.timestamp
ml.sortedByDescending { ?: it.bolusCalculatorResult?.timestamp
it.carbs?.timestamp ?: it.bolus?.timestamp }
?: it.bolusCalculatorResult?.timestamp }
.observeOn(aapsSchedulers.main)
.subscribe { list ->
binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true)
} }
}
.observeOn(aapsSchedulers.main)
.subscribe { list ->
binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true)
binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility()
}
} }
@ -267,13 +190,14 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
private fun timestamp(ml: MealLink): Long = ml.bolusCalculatorResult?.let { it.timestamp } ?: ml.bolus?.let { it.timestamp } ?: ml.carbs?.let { it.timestamp } ?: 0L private fun timestamp(ml: MealLink): Long = ml.bolusCalculatorResult?.timestamp ?: ml.bolus?.timestamp ?: ml.carbs?.timestamp ?: 0L
inner class RecyclerViewAdapter internal constructor(var mealLinks: List<MealLink>) : RecyclerView.Adapter<RecyclerViewAdapter.MealLinkLoadedViewHolder>() { inner class RecyclerViewAdapter internal constructor(private var mealLinks: List<MealLink>) : RecyclerView.Adapter<RecyclerViewAdapter.MealLinkLoadedViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): MealLinkLoadedViewHolder = override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): MealLinkLoadedViewHolder =
MealLinkLoadedViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_bolus_carbs_item, viewGroup, false)) MealLinkLoadedViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_bolus_carbs_item, viewGroup, false))
@ -287,13 +211,13 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
holder.binding.date.text = dateUtil.dateString(timestamp(ml)) holder.binding.date.text = dateUtil.dateString(timestamp(ml))
// Metadata // Metadata
holder.binding.metadataLayout.visibility = (ml.bolusCalculatorResult != null && (ml.bolusCalculatorResult.isValid || binding.showInvalidated.isChecked)).toVisibility() holder.binding.metadataLayout.visibility = (ml.bolusCalculatorResult != null && (ml.bolusCalculatorResult.isValid || showInvalidated)).toVisibility()
ml.bolusCalculatorResult?.let { bolusCalculatorResult -> ml.bolusCalculatorResult?.let { bolusCalculatorResult ->
holder.binding.calcTime.text = dateUtil.timeString(bolusCalculatorResult.timestamp) holder.binding.calcTime.text = dateUtil.timeString(bolusCalculatorResult.timestamp)
} }
// Bolus // Bolus
holder.binding.bolusLayout.visibility = (ml.bolus != null && (ml.bolus.isValid || binding.showInvalidated.isChecked)).toVisibility() holder.binding.bolusLayout.visibility = (ml.bolus != null && (ml.bolus.isValid || showInvalidated)).toVisibility()
ml.bolus?.let { bolus -> ml.bolus?.let { bolus ->
holder.binding.bolusTime.text = dateUtil.timeString(bolus.timestamp) holder.binding.bolusTime.text = dateUtil.timeString(bolus.timestamp)
holder.binding.insulin.text = rh.gs(R.string.formatinsulinunits, bolus.amount) holder.binding.insulin.text = rh.gs(R.string.formatinsulinunits, bolus.amount)
@ -321,7 +245,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
} }
} }
// Carbs // Carbs
holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || binding.showInvalidated.isChecked)).toVisibility() holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || showInvalidated)).toVisibility()
ml.carbs?.let { carbs -> ml.carbs?.let { carbs ->
holder.binding.carbsTime.text = dateUtil.timeString(carbs.timestamp) holder.binding.carbsTime.text = dateUtil.timeString(carbs.timestamp)
holder.binding.carbs.text = rh.gs(R.string.format_carbs, carbs.amount.toInt()) holder.binding.carbs.text = rh.gs(R.string.format_carbs, carbs.amount.toInt())
@ -330,19 +254,28 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
holder.binding.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility() holder.binding.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility()
holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility() holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility()
} }
holder.binding.cbBolusRemove.visibility = (ml.bolus?.isValid == true && removeActionMode != null).toVisibility()
holder.binding.bolusRemove.visibility = (ml.bolus?.isValid == true).toVisibility() holder.binding.cbCarbsRemove.visibility = (ml.carbs?.isValid == true && removeActionMode != null).toVisibility()
holder.binding.carbsRemove.visibility = (ml.carbs?.isValid == true).toVisibility() if (removeActionMode != null) {
holder.binding.bolusRemove.tag = ml val onChange = CompoundButton.OnCheckedChangeListener { _, value ->
holder.binding.carbsRemove.tag = ml if (value) {
selectedItems.put(position, ml)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
}
holder.binding.cbBolusRemove.setOnCheckedChangeListener(onChange)
holder.binding.cbBolusRemove.isChecked = selectedItems.get(position) != null
holder.binding.cbCarbsRemove.setOnCheckedChangeListener(onChange)
holder.binding.cbCarbsRemove.isChecked = selectedItems.get(position) != null
}
holder.binding.calculation.tag = ml holder.binding.calculation.tag = ml
val nextTimestamp = if (mealLinks.size != position + 1) timestamp(mealLinks[position + 1]) else 0L val nextTimestamp = if (mealLinks.size != position + 1) timestamp(mealLinks[position + 1]) else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(timestamp(ml), nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDay(timestamp(ml), nextTimestamp).toVisibility()
} }
override fun getItemCount(): Int { override fun getItemCount() = mealLinks.size
return mealLinks.size
}
inner class MealLinkLoadedViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) { inner class MealLinkLoadedViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) {
@ -359,35 +292,199 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
} }
} }
binding.calculation.paintFlags = binding.calculation.paintFlags or Paint.UNDERLINE_TEXT_FLAG binding.calculation.paintFlags = binding.calculation.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.bolusRemove.setOnClickListener { ml -> }
val bolus = (ml.tag as MealLink?)?.bolus ?: return@setOnClickListener }
activity?.let { activity -> }
val text = rh.gs(R.string.configbuilder_insulin) + ": " +
rh.gs(R.string.formatinsulinunits, bolus.amount) + "\n" + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(bolus.timestamp) inflater.inflate(R.menu.menu_treatments_carbs_bolus, menu)
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable { super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_insulin, false) || !sp.getBoolean(R.string.key_ns_receive_carbs, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
val hasItems = (binding.recyclerview.adapter?.itemCount ?: 0) > 0
menu.findItem(R.id.nav_delete_future)?.isVisible = hasItems
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_delete_future -> {
deleteFutureTreatments()
true
}
R.id.nav_refresh_ns -> {
refreshFromNightscout()
true
}
else -> false
}
private fun refreshFromNightscout() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") {
uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments)
disposable +=
Completable.fromAction {
repository.deleteAllBolusCalculatorResults()
repository.deleteAllBoluses()
repository.deleteAllCarbs()
}
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) },
onComplete = {
rxBus.send(EventTreatmentChange())
rxBus.send(EventNewHistoryData(0, false))
}
)
rxBus.send(EventNSClientRestart())
}
}
}
fun deleteFutureTreatments() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), rh.gs(R.string.deletefuturetreatments) + "?", Runnable {
uel.log(Action.DELETE_FUTURE_TREATMENTS, Sources.Treatments)
disposable += repository
.getBolusesDataFromTime(dateUtil.now(), false)
.observeOn(aapsSchedulers.main)
.subscribe { list ->
list.forEach { bolus ->
disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) }
)
}
}
disposable += repository
.getCarbsDataFromTimeNotExpanded(dateUtil.now(), false)
.observeOn(aapsSchedulers.main)
.subscribe { list ->
list.forEach { carb ->
if (carb.duration == 0L)
disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
)
else {
disposable += repository.runTransactionForResult(CutCarbsTransaction(carb.id, dateUtil.now()))
.subscribe(
{ result ->
result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated (cut end) carbs $it") }
},
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
)
}
}
}
disposable += repository
.getBolusCalculatorResultsDataFromTime(dateUtil.now(), false)
.observeOn(aapsSchedulers.main)
.subscribe { list ->
list.forEach { bolusCalc ->
disposable += repository.runTransactionForResult(InvalidateBolusCalculatorResultTransaction(bolusCalc.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolusCalculatorResult $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolusCalculatorResult", 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 mealLink = selectedItems.valueAt(0)
val bolus = mealLink.bolus
if (bolus != null)
return rh.gs(R.string.configbuilder_insulin) + ": " + rh.gs(R.string.formatinsulinunits, bolus.amount) + "\n" +
rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(bolus.timestamp)
val carbs = mealLink.carbs
if (carbs != null)
return rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carbs.amount.toInt()) + "\n" +
rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(carbs.timestamp)
}
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
}
fun removeSelected() {
if (selectedItems.size() > 0)
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable {
selectedItems.forEach {key, ml ->
ml.bolus?.let { bolus ->
uel.log( uel.log(
Action.BOLUS_REMOVED, Sources.Treatments, Action.BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(bolus.timestamp), ValueWithUnit.Timestamp(bolus.timestamp),
ValueWithUnit.Insulin(bolus.amount) ValueWithUnit.Insulin(bolus.amount)
//ValueWithUnit.Gram(mealLinkLoaded.carbs.toInt())
) )
disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id)) disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id))
.subscribe( .subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } }, { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) } { aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) }
) )
}) }
} ml.carbs?.let { carb ->
}
binding.bolusRemove.paintFlags = binding.bolusRemove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.carbsRemove.setOnClickListener { ml ->
val carb = (ml.tag as MealLink?)?.carbs ?: return@setOnClickListener
activity?.let { activity ->
val text = rh.gs(R.string.carbs) + ": " +
rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carb.amount.toInt()) + "\n" +
rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(carb.timestamp)
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable {
uel.log( uel.log(
Action.CARBS_REMOVED, Sources.Treatments, Action.CARBS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(carb.timestamp), ValueWithUnit.Timestamp(carb.timestamp),
@ -398,11 +495,12 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } }, { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) } { aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
) )
}) }
} }
} removeActionMode?.finish()
binding.carbsRemove.paintFlags = binding.carbsRemove.paintFlags or Paint.UNDERLINE_TEXT_FLAG })
} }
} else
removeActionMode?.finish()
} }
} }

View file

@ -1,10 +1,11 @@
package info.nightscout.androidaps.activities.fragments package info.nightscout.androidaps.activities.fragments
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 android.view.ActionMode
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
@ -57,61 +58,61 @@ class TreatmentsCareportalFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
private var _binding: TreatmentsCareportalFragmentBinding? = null private var _binding: TreatmentsCareportalFragmentBinding? = 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!!
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<TherapyEvent> = SparseArray()
private var showInvalidated = false
private var toolbar: Toolbar? = null
private var removeActionMode: ActionMode? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsCareportalFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsCareportalFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
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)
binding.refreshFromNightscout.setOnClickListener { }
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.refresheventsfromnightscout) + " ?", Runnable {
uel.log(Action.CAREPORTAL_NS_REFRESH, Sources.Treatments)
disposable += Completable.fromAction { repository.deleteAllTherapyEventsEntries() }
.subscribeOn(aapsSchedulers.io)
.subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) },
onComplete = { rxBus.send(EventTherapyEventChange()) }
)
rxBus.send(EventNSClientRestart())
})
}
}
binding.removeAndroidapsStartedEvents.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.careportal_removestartedevents), Runnable {
uel.log(Action.RESTART_EVENTS_REMOVED, Sources.Treatments)
repository.runTransactionForResult(InvalidateAAPSStartedTherapyEventTransaction(rh.gs(R.string.androidaps_start)))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) }
)
}, null)
}
}
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode() private fun refreshFromNightscout() {
if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE activity?.let { activity ->
binding.showInvalidated.setOnCheckedChangeListener { _, _ -> OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.refresheventsfromnightscout) + " ?", Runnable {
rxBus.send(EventTreatmentUpdateGui()) uel.log(Action.CAREPORTAL_NS_REFRESH, Sources.Treatments)
disposable += Completable.fromAction { repository.deleteAllTherapyEventsEntries() }
.subscribeOn(aapsSchedulers.io)
.subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) },
onComplete = { rxBus.send(EventTherapyEventChange()) }
)
rxBus.send(EventNSClientRestart())
})
}
}
private fun removeStartedEvents() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.careportal_removestartedevents), Runnable {
uel.log(Action.RESTART_EVENTS_REMOVED, Sources.Treatments)
disposable += repository.runTransactionForResult(InvalidateAAPSStartedTherapyEventTransaction(rh.gs(R.string.androidaps_start)))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) }
)
})
} }
} }
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
disposable += disposable +=
if (binding.showInvalidated.isChecked) if (showInvalidated)
repository repository
.getTherapyEventDataIncludingInvalidFromTime(now - millsToThePast, false) .getTherapyEventDataIncludingInvalidFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
@ -148,6 +149,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -170,42 +172,137 @@ class TreatmentsCareportalFragment : DaggerFragment() {
holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh) holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh)
holder.binding.note.text = therapyEvent.note holder.binding.note.text = therapyEvent.note
holder.binding.type.text = translator.translate(therapyEvent.type) holder.binding.type.text = translator.translate(therapyEvent.type)
holder.binding.remove.tag = therapyEvent holder.binding.cbRemove.visibility = (therapyEvent.isValid && removeActionMode != null).toVisibility()
if (removeActionMode != null) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, therapyEvent)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
}
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null
}
val nextTimestamp = if (therapyList.size != position + 1) therapyList[position + 1].timestamp else 0L val nextTimestamp = if (therapyList.size != position + 1) therapyList[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(therapyEvent.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDay(therapyEvent.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount(): Int { override fun getItemCount() = therapyList.size
return therapyList.size
}
inner class TherapyEventsViewHolder(view: View) : RecyclerView.ViewHolder(view) { inner class TherapyEventsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = TreatmentsCareportalItemBinding.bind(view) val binding = TreatmentsCareportalItemBinding.bind(view)
init {
binding.remove.setOnClickListener { v: View ->
val therapyEvent = v.tag as TherapyEvent
activity?.let { activity ->
val text = rh.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type) + "\n" +
rh.gs(R.string.notes_label) + ": " + (therapyEvent.note
?: "") + "\n" +
rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(therapyEvent.timestamp)
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable {
uel.log(Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note ,
ValueWithUnit.Timestamp(therapyEvent.timestamp),
ValueWithUnit.TherapyEventType(therapyEvent.type))
disposable += repository.runTransactionForResult(InvalidateTherapyEventTransaction(therapyEvent.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) }
)
}, null)
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
} }
} }
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_treatments_careportal, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_remove_started_events -> {
removeStartedEvents()
true
}
R.id.nav_refresh_ns -> {
refreshFromNightscout()
true
}
else -> false
}
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 therapyEvent = selectedItems.valueAt(0)
return rh.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type) + "\n" +
rh.gs(R.string.notes_label) + ": " + (therapyEvent.note ?: "") + "\n" +
rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(therapyEvent.timestamp)
}
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 { _, therapyEvent ->
uel.log(
Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note,
ValueWithUnit.Timestamp(therapyEvent.timestamp),
ValueWithUnit.TherapyEventType(therapyEvent.type)
)
disposable += repository.runTransactionForResult(InvalidateTherapyEventTransaction(therapyEvent.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) }
)
}
removeActionMode?.finish()
})
}
else
removeActionMode?.finish()
}
}

View file

@ -1,12 +1,12 @@
package info.nightscout.androidaps.activities.fragments package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.DialogInterface
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 android.view.ActionMode
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
@ -27,16 +27,17 @@ import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.activities.fragments.TreatmentsExtendedBolusesFragment.RecyclerViewAdapter.ExtendedBolusesViewHolder import info.nightscout.androidaps.activities.fragments.TreatmentsExtendedBolusesFragment.RecyclerViewAdapter.ExtendedBolusesViewHolder
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
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.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.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.LTag
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.plusAssign
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -60,23 +61,27 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
private var _binding: TreatmentsExtendedbolusFragmentBinding? = null private var _binding: TreatmentsExtendedbolusFragmentBinding? = 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?, private var selectedItems: SparseArray<ExtendedBolus> = SparseArray()
savedInstanceState: Bundle?): View = private var showInvalidated = false
private var removeActionMode: ActionMode? = null
private var toolbar: Toolbar? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setHasOptionsMenu(true)
toolbar = activity?.findViewById(R.id.toolbar)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
} }
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
if (binding.showInvalidated.isChecked) disposable += if (showInvalidated)
repository repository
.getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false) .getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
@ -109,6 +114,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -125,7 +131,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
holder.binding.ns.visibility = (extendedBolus.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ns.visibility = (extendedBolus.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.ph.visibility = (extendedBolus.interfaceIDs.pumpId != null).toVisibility() holder.binding.ph.visibility = (extendedBolus.interfaceIDs.pumpId != null).toVisibility()
holder.binding.invalid.visibility = extendedBolus.isValid.not().toVisibility() holder.binding.invalid.visibility = extendedBolus.isValid.not().toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(extendedBolus.timestamp, extendedBolusList[position-1].timestamp) val sameDayPrevious = position > 0 && dateUtil.isSameDay(extendedBolus.timestamp, extendedBolusList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(extendedBolus.timestamp) holder.binding.date.text = dateUtil.dateString(extendedBolus.timestamp)
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -143,41 +149,125 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iob) holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iob)
holder.binding.ratio.text = rh.gs(R.string.pump_basebasalrate, extendedBolus.rate) holder.binding.ratio.text = rh.gs(R.string.pump_basebasalrate, extendedBolus.rate)
if (iob.iob != 0.0) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor) if (iob.iob != 0.0) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor)
holder.binding.remove.tag = extendedBolus holder.binding.cbRemove.visibility = (extendedBolus.isValid && removeActionMode != null).toVisibility()
if (removeActionMode != null) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, extendedBolus)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
}
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null
}
val nextTimestamp = if (extendedBolusList.size != position + 1) extendedBolusList[position + 1].timestamp else 0L val nextTimestamp = if (extendedBolusList.size != position + 1) extendedBolusList[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(extendedBolus.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDay(extendedBolus.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount(): Int = extendedBolusList.size override fun getItemCount() = extendedBolusList.size
inner class ExtendedBolusesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class ExtendedBolusesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = TreatmentsExtendedbolusItemBinding.bind(itemView) val binding = TreatmentsExtendedbolusItemBinding.bind(itemView)
init {
binding.remove.setOnClickListener { v: View ->
val extendedBolus = v.tag as ExtendedBolus
context?.let { context ->
OKDialog.showConfirmation(context, rh.gs(R.string.removerecord),
"""
${rh.gs(R.string.extended_bolus)}
${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(extendedBolus.timestamp)}
""".trimIndent(), { _: DialogInterface, _: Int ->
uel.log(Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(extendedBolus.timestamp),
ValueWithUnit.Insulin(extendedBolus.amount),
ValueWithUnit.UnitPerHour(extendedBolus.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt()))
disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) })
}, null)
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
} }
} }
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_treatments_extended_bolus, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
true
}
else -> false
}
}
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 bolus = selectedItems.valueAt(0)
return rh.gs(R.string.extended_bolus) + "\n" +
"${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(bolus.timestamp)}"
}
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 { _, extendedBolus ->
uel.log(
Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(extendedBolus.timestamp),
ValueWithUnit.Insulin(extendedBolus.amount),
ValueWithUnit.UnitPerHour(extendedBolus.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt())
)
disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) })
}
removeActionMode?.finish()
})
}
else
removeActionMode?.finish()
}
}

View file

@ -2,9 +2,11 @@ package info.nightscout.androidaps.activities.fragments
import android.graphics.Paint 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 android.view.ActionMode
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
@ -61,50 +63,50 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
private var _binding: TreatmentsProfileswitchFragmentBinding? = null private var _binding: TreatmentsProfileswitchFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs() private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<ProfileSealed> = SparseArray()
private var showInvalidated = false
private var removeActionMode: ActionMode? = null
private var toolbar: Toolbar? = null
// This property is only valid between onCreateView and
// onDestroyView.
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 =
TreatmentsProfileswitchFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsProfileswitchFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
toolbar = activity?.findViewById(R.id.toolbar)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
}
binding.refreshFromNightscout.setOnClickListener { private fun refreshFromNightscout() {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") { OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") {
uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments) uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments)
disposable += disposable +=
Completable.fromAction { Completable.fromAction {
repository.deleteAllEffectiveProfileSwitches() repository.deleteAllEffectiveProfileSwitches()
repository.deleteAllProfileSwitches() repository.deleteAllProfileSwitches()
} }
.subscribeOn(aapsSchedulers.io) .subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribeBy( .subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) }, onError = { aapsLogger.error("Error removing entries", it) },
onComplete = { onComplete = {
rxBus.send(EventProfileSwitchChanged()) rxBus.send(EventProfileSwitchChanged())
rxBus.send(EventEffectiveProfileSwitchChanged(0L)) rxBus.send(EventEffectiveProfileSwitchChanged(0L))
rxBus.send(EventNewHistoryData(0, false)) rxBus.send(EventNewHistoryData(0, false))
} }
) )
rxBus.send(EventNSClientRestart()) rxBus.send(EventNSClientRestart())
}
} }
} }
if (!sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode()) binding.refreshFromNightscout.visibility = View.GONE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}
} }
private fun profileSwitchWithInvalid(now: Long) = repository private fun profileSwitchWithInvalid(now: Long) = repository
@ -126,23 +128,23 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
if (binding.showInvalidated.isChecked) disposable +=
disposable += profileSwitchWithInvalid(now) if (showInvalidated)
.zipWith(effectiveProfileSwitchWithInvalid(now)) { first, second -> first + second } profileSwitchWithInvalid(now)
.map { ml -> ml.sortedByDescending { it.timestamp } } .zipWith(effectiveProfileSwitchWithInvalid(now)) { first, second -> first + second }
.observeOn(aapsSchedulers.main) .map { ml -> ml.sortedByDescending { it.timestamp } }
.subscribe { list -> .observeOn(aapsSchedulers.main)
binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) .subscribe { list ->
} binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true)
else }
disposable += profileSwitches(now) else
.zipWith(effectiveProfileSwitches(now)) { first, second -> first + second } profileSwitches(now)
.map { ml -> ml.sortedByDescending { it.timestamp } } .zipWith(effectiveProfileSwitches(now)) { first, second -> first + second }
.observeOn(aapsSchedulers.main) .map { ml -> ml.sortedByDescending { it.timestamp } }
.subscribe { list -> .observeOn(aapsSchedulers.main)
binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) .subscribe { list ->
} binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true)
}
} }
@Synchronized @Synchronized
@ -168,6 +170,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -186,16 +189,27 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
holder.binding.date.text = dateUtil.dateString(profileSwitch.timestamp) holder.binding.date.text = dateUtil.dateString(profileSwitch.timestamp)
holder.binding.time.text = dateUtil.timeString(profileSwitch.timestamp) holder.binding.time.text = dateUtil.timeString(profileSwitch.timestamp)
holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(profileSwitch.duration ?: 0L).mins()) holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(profileSwitch.duration ?: 0L).mins())
holder.binding.name.text = if (profileSwitch is ProfileSealed.PS) profileSwitch.value.getCustomizedName() else if (profileSwitch is ProfileSealed.EPS) profileSwitch.value.originalCustomizedName else "" holder.binding.name.text = if (profileSwitch is ProfileSealed.PS) profileSwitch.value.getCustomizedName()
else if (profileSwitch is ProfileSealed.EPS) profileSwitch.value.originalCustomizedName else ""
if (profileSwitch.isInProgress(dateUtil)) holder.binding.date.setTextColor(rh.gc(R.color.colorActive)) if (profileSwitch.isInProgress(dateUtil)) holder.binding.date.setTextColor(rh.gc(R.color.colorActive))
else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor) else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor)
holder.binding.remove.tag = profileSwitch
holder.binding.clone.tag = profileSwitch holder.binding.clone.tag = profileSwitch
holder.binding.name.tag = profileSwitch holder.binding.name.tag = profileSwitch
holder.binding.date.tag = profileSwitch holder.binding.date.tag = profileSwitch
holder.binding.invalid.visibility = profileSwitch.isValid.not().toVisibility() holder.binding.invalid.visibility = profileSwitch.isValid.not().toVisibility()
holder.binding.duration.visibility = (profileSwitch.duration != 0L && profileSwitch.duration != null).toVisibility() holder.binding.duration.visibility = (profileSwitch.duration != 0L && profileSwitch.duration != null).toVisibility()
holder.binding.remove.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.cbRemove.visibility = (removeActionMode != null && profileSwitch is ProfileSealed.PS).toVisibility()
if (removeActionMode != null) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, profileSwitch)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
}
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null
}
holder.binding.clone.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.clone.visibility = (profileSwitch is ProfileSealed.PS).toVisibility()
holder.binding.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility()
holder.binding.root.setBackgroundColor(rh.gc(if (profileSwitch is ProfileSealed.PS) R.color.defaultbackground else R.color.list_delimiter)) holder.binding.root.setBackgroundColor(rh.gc(if (profileSwitch is ProfileSealed.PS) R.color.defaultbackground else R.color.list_delimiter))
@ -203,47 +217,39 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
holder.binding.delimiter.visibility = dateUtil.isSameDay(profileSwitch.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDay(profileSwitch.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount(): Int { override fun getItemCount() = profileSwitchList.size
return profileSwitchList.size
}
inner class ProfileSwitchViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class ProfileSwitchViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = TreatmentsProfileswitchItemBinding.bind(itemView) val binding = TreatmentsProfileswitchItemBinding.bind(itemView)
init { init {
binding.remove.setOnClickListener { view ->
val profileSwitch = view.tag as ProfileSealed.PS
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord),
rh.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName +
"\n" + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.timestamp), Runnable {
uel.log(Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName,
ValueWithUnit.Timestamp(profileSwitch.timestamp))
disposable += repository.runTransactionForResult(InvalidateProfileSwitchTransaction(profileSwitch.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) }
)
})
}
}
binding.clone.setOnClickListener { binding.clone.setOnClickListener {
activity?.let { activity -> activity?.let { activity ->
val profileSwitch = (it.tag as ProfileSealed.PS).value val profileSwitch = (it.tag as ProfileSealed.PS).value
val profileSealed = it.tag as ProfileSealed val profileSealed = it.tag as ProfileSealed
OKDialog.showConfirmation(activity, rh.gs(R.string.careportal_profileswitch), rh.gs(R.string.copytolocalprofile) + "\n" + profileSwitch.getCustomizedName() + "\n" + dateUtil.dateAndTimeString(profileSwitch.timestamp), Runnable { OKDialog.showConfirmation(
uel.log(Action.PROFILE_SWITCH_CLONED, Sources.Treatments, activity,
profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_"), rh.gs(R.string.careportal_profileswitch),
ValueWithUnit.Timestamp(profileSwitch.timestamp), rh.gs(R.string.copytolocalprofile) + "\n" + profileSwitch.getCustomizedName() + "\n" + dateUtil.dateAndTimeString(profileSwitch.timestamp),
ValueWithUnit.SimpleString(profileSwitch.profileName)) Runnable {
val nonCustomized = profileSealed.convertToNonCustomizedProfile(dateUtil) uel.log(
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(nonCustomized, profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_"))) Action.PROFILE_SWITCH_CLONED, Sources.Treatments,
rxBus.send(EventLocalProfileChanged()) profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_"),
}) ValueWithUnit.Timestamp(profileSwitch.timestamp),
ValueWithUnit.SimpleString(profileSwitch.profileName)
)
val nonCustomized = profileSealed.convertToNonCustomizedProfile(dateUtil)
localProfilePlugin.addProfile(
localProfilePlugin.copyFrom(
nonCustomized,
profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_")
)
)
rxBus.send(EventLocalProfileChanged())
})
} }
} }
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.clone.paintFlags = binding.clone.paintFlags or Paint.UNDERLINE_TEXT_FLAG binding.clone.paintFlags = binding.clone.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.name.setOnClickListener { binding.name.setOnClickListener {
ProfileViewerDialog().also { pvd -> ProfileViewerDialog().also { pvd ->
@ -266,4 +272,104 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
} }
} }
} }
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_treatments_profile_switch, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_refresh_ns -> {
refreshFromNightscout()
true
}
else -> false
}
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 profileSwitch = selectedItems.valueAt(0)
return rh.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName + "\n" + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.timestamp)
}
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 { _, profileSwitch ->
uel.log(
Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName,
ValueWithUnit.Timestamp(profileSwitch.timestamp)
)
disposable += repository.runTransactionForResult(InvalidateProfileSwitchTransaction(profileSwitch.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) }
)
}
removeActionMode?.finish()
})
}
else
removeActionMode?.finish()
}
}

View file

@ -1,12 +1,11 @@
package info.nightscout.androidaps.activities.fragments package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.DialogInterface
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
@ -30,6 +29,8 @@ import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.events.EventTreatmentUpdateGui import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder
import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged
import info.nightscout.androidaps.events.EventProfileSwitchChanged
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.T import info.nightscout.androidaps.utils.T
@ -40,6 +41,7 @@ import info.nightscout.androidaps.extensions.friendlyDescription
import info.nightscout.androidaps.extensions.highValueToUnitsToString import info.nightscout.androidaps.extensions.highValueToUnitsToString
import info.nightscout.androidaps.extensions.lowValueToUnitsToString import info.nightscout.androidaps.extensions.lowValueToUnitsToString
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
@ -65,57 +67,64 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
private var _binding: TreatmentsTemptargetFragmentBinding? = null private var _binding: TreatmentsTemptargetFragmentBinding? = 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!!
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<TemporaryTarget> = SparseArray()
private var showInvalidated = false
private var toolbar: Toolbar? = null
private var removeActionMode: ActionMode? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsTemptargetFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsTemptargetFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
toolbar = activity?.findViewById(R.id.toolbar)
setHasOptionsMenu(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.refreshFromNightscout.setOnClickListener { }
context?.let { context ->
OKDialog.showConfirmation(context, rh.gs(R.string.refresheventsfromnightscout) + " ?", { private fun refreshFromNightscout() {
uel.log(Action.TT_NS_REFRESH, Sources.Treatments) activity?.let { activity ->
disposable += Completable.fromAction { repository.deleteAllTempTargetEntries() } OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") {
uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments)
disposable +=
Completable.fromAction {
repository.deleteAllEffectiveProfileSwitches()
repository.deleteAllProfileSwitches()
}
.subscribeOn(aapsSchedulers.io) .subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribeBy( .subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) }, onError = { aapsLogger.error("Error removing entries", it) },
onComplete = { rxBus.send(EventTempTargetChange()) } onComplete = {
rxBus.send(EventProfileSwitchChanged())
rxBus.send(EventEffectiveProfileSwitchChanged(0L))
rxBus.send(EventNewHistoryData(0, false))
}
) )
rxBus.send(EventNSClientRestart())
rxBus.send(EventNSClientRestart())
})
} }
} }
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode()
if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.INVISIBLE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}
} }
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
if (binding.showInvalidated.isChecked) disposable +=
repository if (showInvalidated)
.getTemporaryTargetDataIncludingInvalidFromTime(now - millsToThePast, false) repository
.observeOn(aapsSchedulers.main) .getTemporaryTargetDataIncludingInvalidFromTime(now - millsToThePast, false)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } .observeOn(aapsSchedulers.main)
else .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
repository else
.getTemporaryTargetDataFromTime(now - millsToThePast, false) repository
.observeOn(aapsSchedulers.main) .getTemporaryTargetDataFromTime(now - millsToThePast, false)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } .observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
} }
@Synchronized @Synchronized
@ -145,6 +154,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -163,8 +173,19 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
val tempTarget = tempTargetList[position] val tempTarget = tempTargetList[position]
holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility() holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility()
holder.binding.remove.visibility = tempTarget.isValid.toVisibility() holder.binding.cbRemove.visibility = (tempTarget.isValid && removeActionMode != null).toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempTarget.timestamp, tempTargetList[position-1].timestamp) if (removeActionMode != null) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, tempTarget)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
}
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null
}
val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempTarget.timestamp, tempTargetList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(tempTarget.timestamp) holder.binding.date.text = dateUtil.dateString(tempTarget.timestamp)
holder.binding.time.text = dateUtil.timeRangeString(tempTarget.timestamp, tempTarget.end) holder.binding.time.text = dateUtil.timeRangeString(tempTarget.timestamp, tempTarget.end)
@ -177,43 +198,123 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
tempTarget.id == currentlyActiveTarget?.id -> rh.gc(R.color.colorActive) tempTarget.id == currentlyActiveTarget?.id -> rh.gc(R.color.colorActive)
tempTarget.timestamp > dateUtil.now() -> rh.gc(R.color.colorScheduled) tempTarget.timestamp > dateUtil.now() -> rh.gc(R.color.colorScheduled)
else -> holder.binding.reasonColon.currentTextColor else -> holder.binding.reasonColon.currentTextColor
}) }
holder.binding.remove.tag = tempTarget )
val nextTimestamp = if (tempTargetList.size != position + 1) tempTargetList[position + 1].timestamp else 0L val nextTimestamp = if (tempTargetList.size != position + 1) tempTargetList[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(tempTarget.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDay(tempTarget.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount(): Int = tempTargetList.size override fun getItemCount() = tempTargetList.size
inner class TempTargetsViewHolder(view: View) : RecyclerView.ViewHolder(view) { inner class TempTargetsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = TreatmentsTemptargetItemBinding.bind(view) val binding = TreatmentsTemptargetItemBinding.bind(view)
init {
binding.remove.setOnClickListener { v: View ->
val tempTarget = v.tag as TemporaryTarget
context?.let { context ->
OKDialog.showConfirmation(context, rh.gs(R.string.removerecord),
"""
${rh.gs(R.string.careportal_temporarytarget)}: ${tempTarget.friendlyDescription(profileFunction.getUnits(), rh)}
${dateUtil.dateAndTimeString(tempTarget.timestamp)}
""".trimIndent(),
{ _: DialogInterface?, _: Int ->
uel.log(Action.TT_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempTarget.timestamp),
ValueWithUnit.TherapyEventTTReason(tempTarget.reason),
ValueWithUnit.Mgdl(tempTarget.lowTarget),
ValueWithUnit.Mgdl(tempTarget.highTarget).takeIf { tempTarget.lowTarget != tempTarget.highTarget },
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tempTarget.duration).toInt()))
disposable += repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(tempTarget.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed temp target $tempTarget") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) })
}, null)
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
} }
} }
}
private fun removeSelected() {
if (selectedItems.size() > 0)
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable {
selectedItems.forEach { _, tempTarget ->
uel.log(
Action.TT_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempTarget.timestamp),
ValueWithUnit.TherapyEventTTReason(tempTarget.reason),
ValueWithUnit.Mgdl(tempTarget.lowTarget),
ValueWithUnit.Mgdl(tempTarget.highTarget).takeIf { tempTarget.lowTarget != tempTarget.highTarget },
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tempTarget.duration).toInt())
)
disposable += repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(tempTarget.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed temp target $tempTarget") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) })
}
removeActionMode?.finish()
})
}
else
removeActionMode?.finish()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_treatments_temp_target, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_refresh_ns -> {
refreshFromNightscout()
true
}
else -> false
}
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 tempTarget = selectedItems.valueAt(0)
return "${rh.gs(R.string.careportal_temporarytarget)}: ${tempTarget.friendlyDescription(profileFunction.getUnits(), rh)}\n" +
dateUtil.dateAndTimeString(tempTarget.timestamp)
}
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
}
}

View file

@ -1,11 +1,11 @@
package info.nightscout.androidaps.activities.fragments package info.nightscout.androidaps.activities.fragments
import android.content.DialogInterface
import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.util.Log
import android.view.View import android.util.SparseArray
import android.view.ViewGroup import android.view.*
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
@ -36,6 +36,7 @@ import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder import info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
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.T import info.nightscout.androidaps.utils.T
@ -64,18 +65,22 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
private var _binding: TreatmentsTempbasalsFragmentBinding? = null private var _binding: TreatmentsTempbasalsFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private val millsToThePast = T.days(30).msecs() private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<TemporaryBasal> = SparseArray()
// This property is only valid between onCreateView and private var showInvalidated = false
// onDestroyView. private var toolbar: Toolbar? = null
private val binding get() = _binding!! private var removeActionMode: ActionMode? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsTempbasalsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsTempbasalsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
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)
} }
@ -98,7 +103,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
disposable += disposable +=
if (activePlugin.activePump.isFakingTempsByExtendedBoluses) { if (activePlugin.activePump.isFakingTempsByExtendedBoluses) {
if (binding.showInvalidated.isChecked) if (showInvalidated)
tempBasalsWithInvalid(now) tempBasalsWithInvalid(now)
.zipWith(extendedBolusesWithInvalid(now)) { first, second -> first + second } .zipWith(extendedBolusesWithInvalid(now)) { first, second -> first + second }
.map { list -> list.filterNotNull() } .map { list -> list.filterNotNull() }
@ -113,7 +118,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
} else { } else {
if (binding.showInvalidated.isChecked) if (showInvalidated)
tempBasalsWithInvalid(now) tempBasalsWithInvalid(now)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
@ -150,6 +155,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -164,7 +170,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
holder.binding.ns.visibility = (tempBasal.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ns.visibility = (tempBasal.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = tempBasal.isValid.not().toVisibility() holder.binding.invalid.visibility = tempBasal.isValid.not().toVisibility()
holder.binding.ph.visibility = (tempBasal.interfaceIDs.pumpId != null).toVisibility() holder.binding.ph.visibility = (tempBasal.interfaceIDs.pumpId != null).toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempBasal.timestamp, tempBasalList[position-1].timestamp) val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempBasal.timestamp, tempBasalList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(tempBasal.timestamp) holder.binding.date.text = dateUtil.dateString(tempBasal.timestamp)
if (tempBasal.isInProgress) { if (tempBasal.isInProgress) {
@ -187,62 +193,147 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
holder.binding.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).toVisibility() holder.binding.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).toVisibility()
holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility() holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility()
if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor) if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor)
holder.binding.remove.tag = tempBasal holder.binding.cbRemove.visibility = (tempBasal.isValid && removeActionMode != null).toVisibility()
if (removeActionMode != null) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, tempBasal)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
}
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null
}
val nextTimestamp = if (tempBasalList.size != position + 1) tempBasalList[position + 1].timestamp else 0L val nextTimestamp = if (tempBasalList.size != position + 1) tempBasalList[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(tempBasal.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDay(tempBasal.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount(): Int = tempBasalList.size override fun getItemCount() = tempBasalList.size
inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = TreatmentsTempbasalsItemBinding.bind(itemView) val binding = TreatmentsTempbasalsItemBinding.bind(itemView)
init {
binding.remove.setOnClickListener { v: View ->
val tempBasal = v.tag as TemporaryBasal
var extendedBolus: ExtendedBolus? = null
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
if (isFakeExtended) {
val eb = repository.getExtendedBolusActiveAt(tempBasal.timestamp).blockingGet()
extendedBolus = if (eb is ValueWrapper.Existing) eb.value else null
}
val profile = profileFunction.getProfile(dateUtil.now())
?: return@setOnClickListener
context?.let {
OKDialog.showConfirmation(it, rh.gs(R.string.removerecord),
"""
${if (isFakeExtended) rh.gs(R.string.extended_bolus) else rh.gs(R.string.tempbasal_label)}: ${tempBasal.toStringFull(profile, dateUtil)}
${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(tempBasal.timestamp)}
""".trimIndent(),
{ _: DialogInterface?, _: Int ->
if (isFakeExtended && extendedBolus != null) {
uel.log(Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(extendedBolus.timestamp),
ValueWithUnit.Insulin(extendedBolus.amount),
ValueWithUnit.UnitPerHour(extendedBolus.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt()))
disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) })
} else if (!isFakeExtended) {
uel.log(Action.TEMP_BASAL_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempBasal.timestamp),
if (tempBasal.isAbsolute) ValueWithUnit.UnitPerHour(tempBasal.rate) else ValueWithUnit.Percent(tempBasal.rate.toInt()),
ValueWithUnit.Minute(T.msecs(tempBasal.duration).mins().toInt()))
disposable += repository.runTransactionForResult(InvalidateTemporaryBasalTransaction(tempBasal.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed temporary basal $tempBasal") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) })
}
}, null)
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
} }
} }
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_treatments_temp_basal, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
true
}
else -> false
}
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 tempBasal = selectedItems.valueAt(0)
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
val profile = profileFunction.getProfile(dateUtil.now())
if (profile != null)
return "${if (isFakeExtended) rh.gs(R.string.extended_bolus) else rh.gs(R.string.tempbasal_label)}: ${tempBasal.toStringFull(profile, dateUtil)}\n" +
"${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(tempBasal.timestamp)}"
}
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 {_, tempBasal ->
var extendedBolus: ExtendedBolus? = null
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
if (isFakeExtended) {
val eb = repository.getExtendedBolusActiveAt(tempBasal.timestamp).blockingGet()
extendedBolus = if (eb is ValueWrapper.Existing) eb.value else null
}
if (isFakeExtended && extendedBolus != null) {
uel.log(
Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(extendedBolus.timestamp),
ValueWithUnit.Insulin(extendedBolus.amount),
ValueWithUnit.UnitPerHour(extendedBolus.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt())
)
disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) })
} else if (!isFakeExtended) {
uel.log(
Action.TEMP_BASAL_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempBasal.timestamp),
if (tempBasal.isAbsolute) ValueWithUnit.UnitPerHour(tempBasal.rate) else ValueWithUnit.Percent(tempBasal.rate.toInt()),
ValueWithUnit.Minute(T.msecs(tempBasal.duration).mins().toInt())
)
disposable += repository.runTransactionForResult(InvalidateTemporaryBasalTransaction(tempBasal.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed temporary basal $tempBasal") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) })
}
}
removeActionMode?.finish()
})
}
else
removeActionMode?.finish()
}
}

View file

@ -1,9 +1,7 @@
package info.nightscout.androidaps.activities.fragments package info.nightscout.androidaps.activities.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.*
import android.view.View
import android.view.ViewGroup
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
@ -30,6 +28,7 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -51,11 +50,10 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
private val millsToThePastFiltered = T.days(30).msecs() private val millsToThePastFiltered = T.days(30).msecs()
private val millsToThePastUnFiltered = T.days(3).msecs() private val millsToThePastUnFiltered = T.days(3).msecs()
private var showLoop = false
private var _binding: TreatmentsUserEntryFragmentBinding? = null private var _binding: TreatmentsUserEntryFragmentBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// 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 =
@ -63,35 +61,33 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.ueExportToXml.setOnClickListener { }
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.ue_export_to_csv) + "?") { fun exportUserEnteries() {
uel.log(Action.EXPORT_CSV, Sources.Treatments) activity?.let { activity ->
importExportPrefs.exportUserEntriesCsv(activity) OKDialog.showConfirmation(activity, rh.gs(R.string.ue_export_to_csv) + "?") {
} uel.log(Action.EXPORT_CSV, Sources.Treatments)
importExportPrefs.exportUserEntriesCsv(activity)
} }
} }
binding.showLoop.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}
} }
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
if (binding.showLoop.isChecked) disposable +=
disposable.add( repository if (showLoop)
.getUserEntryDataFromTime(now - millsToThePastUnFiltered) repository
.observeOn(aapsSchedulers.main) .getUserEntryDataFromTime(now - millsToThePastUnFiltered)
.subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } .observeOn(aapsSchedulers.main)
) .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) }
else else
disposable.add( repository repository
.getUserEntryFilteredDataFromTime(now - millsToThePastFiltered) .getUserEntryFilteredDataFromTime(now - millsToThePastFiltered)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) }
)
} }
@Synchronized @Synchronized
@ -99,15 +95,15 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
super.onResume() super.onResume()
swapAdapter() swapAdapter()
disposable.add(rxBus disposable += rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable.add(rxBus disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) .toObservable(EventTreatmentUpdateGui::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
} }
@Synchronized @Synchronized
@ -132,7 +128,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) { override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) {
val current = entries[position] val current = entries[position]
val sameDayPrevious = position > 0 && dateUtil.isSameDay(current.timestamp, entries[position-1].timestamp) val sameDayPrevious = position > 0 && dateUtil.isSameDay(current.timestamp, entries[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(current.timestamp) holder.binding.date.text = dateUtil.dateString(current.timestamp)
holder.binding.time.text = dateUtil.timeStringWithSeconds(current.timestamp) holder.binding.time.text = dateUtil.timeStringWithSeconds(current.timestamp)
@ -152,7 +148,40 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
val binding = TreatmentsUserEntryItemBinding.bind(itemView) val binding = TreatmentsUserEntryItemBinding.bind(itemView)
} }
override fun getItemCount(): Int = entries.size override fun getItemCount() = entries.size
} }
} override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_treatments_user_entry, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_loop)?.isVisible = showLoop
menu.findItem(R.id.nav_show_loop)?.isVisible = !showLoop
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_show_loop -> {
showLoop = true
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_hide_loop -> {
showLoop = false
rxBus.send(EventTreatmentUpdateGui())
true
}
R.id.nav_export -> {
exportUserEnteries()
true
}
else -> false
}
}

View file

@ -1,54 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsBolusCarbsFragment"> tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsBolusCarbsFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<info.nightscout.androidaps.utils.ui.SingleClickButton
android:id="@+id/refresh_from_nightscout"
style="?android:attr/buttonStyle"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:drawableStart="@drawable/ic_refresh"
android:text="@string/nav_refreshtreatments" />
<info.nightscout.androidaps.utils.ui.SingleClickButton
android:id="@+id/delete_future_treatments"
style="?android:attr/buttonStyle"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:drawableStart="@drawable/ic_remove"
android:text="@string/deletefuturetreatments" />
<CheckBox
android:id="@+id/show_invalidated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:paddingEnd="5dp"
tools:ignore="RtlSymmetry" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/show_removed"
app:srcCompat="@drawable/ic_visibility" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview" android:id="@+id/recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -161,14 +161,12 @@
android:text="@string/invalid" android:text="@string/invalid"
android:textColor="@android:color/holo_red_light" /> android:textColor="@android:color/holo_red_light" />
<TextView <CheckBox
android:id="@+id/bolus_remove" android:id="@+id/cb_bolus_remove"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="5dp" android:contentDescription="@string/select_for_removal"
android:text="@string/remove_button" android:visibility="gone" />
android:textAlignment="viewEnd"
android:textColor="@android:color/holo_orange_light" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -257,13 +255,21 @@
<TextView <TextView
android:id="@+id/carbs_remove" android:id="@+id/carbs_remove"
android:visibility="gone"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:text="@string/remove_button" android:text="@string/remove_button"
android:textAlignment="viewEnd" android:textAlignment="viewEnd"
android:textColor="@android:color/holo_orange_light" /> android:textColor="@android:color/holo_orange_light" />
<CheckBox
android:id="@+id/cb_carbs_remove"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View file

@ -1,54 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsCareportalFragment"> tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsCareportalFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<info.nightscout.androidaps.utils.ui.SingleClickButton
android:id="@+id/refresh_from_nightscout"
style="?android:attr/buttonStyle"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:drawableStart="@drawable/ic_refresh"
android:text="@string/nav_refreshtreatments" />
<info.nightscout.androidaps.utils.ui.SingleClickButton
android:id="@+id/remove_androidaps_started_events"
style="?android:attr/buttonStyle"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:drawableStart="@drawable/ic_remove"
android:text="@string/careportal_removestartedevents" />
<CheckBox
android:id="@+id/show_invalidated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:paddingEnd="5dp"
tools:ignore="RtlSymmetry" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/show_removed"
app:srcCompat="@drawable/ic_visibility" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview" android:id="@+id/recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -112,14 +112,12 @@
android:text="Activity" android:text="Activity"
tools:ignore="HardcodedText,RtlSymmetry" /> tools:ignore="HardcodedText,RtlSymmetry" />
<TextView <CheckBox
android:id="@+id/remove" android:id="@+id/cb_remove"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="10dp" android:contentDescription="@string/select_for_removal"
android:text="@string/remove_button" android:visibility="gone" />
android:textAlignment="viewEnd"
android:textColor="@android:color/holo_orange_light" />
</LinearLayout> </LinearLayout>

View file

@ -1,34 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsExtendedBolusesFragment"> tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsExtendedBolusesFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/show_invalidated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:paddingEnd="5dp"
tools:ignore="RtlSymmetry" />
<ImageView
app:srcCompat="@drawable/ic_visibility"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/show_removed" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview" android:id="@+id/recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -153,14 +153,12 @@
android:text="@string/invalid" android:text="@string/invalid"
android:textColor="@android:color/holo_red_light" /> android:textColor="@android:color/holo_red_light" />
<TextView <CheckBox
android:id="@+id/remove" android:id="@+id/cb_remove"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="5dp" android:contentDescription="@string/select_for_removal"
android:text="@string/remove_button" android:visibility="gone" />
android:textAlignment="viewEnd"
android:textColor="@android:color/holo_orange_light" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -6,6 +6,13 @@
android:orientation="vertical" android:orientation="vertical"
tools:context="info.nightscout.androidaps.activities.TreatmentsActivity"> tools:context="info.nightscout.androidaps.activities.TreatmentsActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:titleTextColor="@android:color/white" />
<com.google.android.flexbox.FlexboxLayout <com.google.android.flexbox.FlexboxLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -1,44 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsProfileSwitchFragment"> tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsProfileSwitchFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<info.nightscout.androidaps.utils.ui.SingleClickButton
android:id="@+id/refresh_from_nightscout"
style="?android:attr/buttonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:drawableStart="@drawable/ic_refresh"
android:text="@string/refresheventsfromnightscout" />
<CheckBox
android:id="@+id/show_invalidated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:paddingEnd="5dp"
tools:ignore="RtlSymmetry" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/show_removed"
app:srcCompat="@drawable/ic_visibility" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview" android:id="@+id/recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -124,15 +124,12 @@
android:textAlignment="viewEnd" android:textAlignment="viewEnd"
android:textColor="@android:color/holo_blue_light" /> android:textColor="@android:color/holo_blue_light" />
<TextView <CheckBox
android:id="@+id/remove" android:id="@+id/cb_remove"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="10dp" android:contentDescription="@string/select_for_removal"
android:paddingEnd="5dp" android:visibility="gone" />
android:text="@string/remove_button"
android:textAlignment="viewEnd"
android:textColor="@android:color/holo_orange_light" />
</LinearLayout> </LinearLayout>

View file

@ -1,36 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment"> tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/show_invalidated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:paddingEnd="5dp"
tools:ignore="RtlSymmetry" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/show_removed"
app:srcCompat="@drawable/ic_visibility" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview" android:id="@+id/recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -172,15 +172,12 @@
android:text="@string/invalid" android:text="@string/invalid"
android:textColor="@android:color/holo_red_light" /> android:textColor="@android:color/holo_red_light" />
<TextView <CheckBox
android:id="@+id/remove" android:id="@+id/cb_remove"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="5dp" android:contentDescription="@string/select_for_removal"
android:visibility="gone" android:visibility="gone" />
android:text="@string/remove_button"
android:textAlignment="viewEnd"
android:textColor="@android:color/holo_orange_light" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -1,45 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment"> tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<info.nightscout.androidaps.utils.ui.SingleClickButton
android:id="@+id/refresh_from_nightscout"
style="?android:attr/buttonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:drawableStart="@drawable/ic_refresh"
android:text="@string/refresheventsfromnightscout"
tools:visibility="visible" />
<CheckBox
android:id="@+id/show_invalidated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:paddingEnd="5dp"
tools:ignore="RtlSymmetry" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/show_removed"
app:srcCompat="@drawable/ic_visibility" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview" android:id="@+id/recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -147,14 +147,12 @@
android:text="@string/invalid" android:text="@string/invalid"
android:textColor="@android:color/holo_red_light" /> android:textColor="@android:color/holo_red_light" />
<TextView <CheckBox
android:id="@+id/remove" android:id="@+id/cb_remove"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="10dp" android:contentDescription="@string/select_for_removal"
android:text="@string/remove_button" android:visibility="gone" />
android:textAlignment="viewEnd"
android:textColor="@android:color/holo_orange_light" />
</LinearLayout> </LinearLayout>

View file

@ -6,40 +6,6 @@
android:orientation="vertical" android:orientation="vertical"
tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsUserEntryFragment"> tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsUserEntryFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<info.nightscout.androidaps.utils.ui.SingleClickButton
android:id="@+id/ue_export_to_xml"
style="?android:attr/buttonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:drawableStart="@drawable/ic_header_export"
android:text="@string/ue_export_to_csv" />
<CheckBox
android:id="@+id/show_loop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:paddingEnd="5dp"
tools:ignore="RtlSymmetry" />
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/show_calculation"
app:srcCompat="@drawable/ic_loop_closed_white" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview" android:id="@+id/recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -0,0 +1,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/remove_selected"
android:icon="@drawable/ic_trash"
android:iconTint="@color/white"
android:title="@string/remove_selected_items"
app:showAsAction="always" />
</menu>

View file

@ -0,0 +1,25 @@
<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:title="@string/remove_items"
app:showAsAction="never" />
<item
android:id="@+id/nav_show_invalidated"
android:title="@string/show_invalidated"
app:showAsAction="never" />
<item
android:id="@+id/nav_hide_invalidated"
android:title="@string/hide_invalidated"
app:showAsAction="never" />
<item
android:id="@+id/nav_delete_future"
android:title="@string/delete_future_treatments"
app:showAsAction="never" />
<item
android:id="@+id/nav_refresh_ns"
android:title="@string/refresh_from_nightscout"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,25 @@
<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:title="@string/remove_items"
app:showAsAction="never" />
<item
android:id="@+id/nav_show_invalidated"
android:title="@string/show_invalidated"
app:showAsAction="never" />
<item
android:id="@+id/nav_hide_invalidated"
android:title="@string/hide_invalidated"
app:showAsAction="never" />
<item
android:id="@+id/nav_refresh_ns"
android:title="@string/refresh_from_nightscout"
app:showAsAction="never" />
<item
android:id="@+id/nav_remove_started_events"
android:title="@string/careportal_removestartedevents"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,17 @@
<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:title="@string/remove_items"
app:showAsAction="never" />
<item
android:id="@+id/nav_show_invalidated"
android:title="@string/show_invalidated"
app:showAsAction="never" />
<item
android:id="@+id/nav_hide_invalidated"
android:title="@string/hide_invalidated"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,17 @@
<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:title="@string/remove_items"
app:showAsAction="never" />
<item
android:id="@+id/nav_show_invalidated"
android:title="@string/show_invalidated"
app:showAsAction="never" />
<item
android:id="@+id/nav_hide_invalidated"
android:title="@string/hide_invalidated"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,17 @@
<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:title="@string/remove_items"
app:showAsAction="never" />
<item
android:id="@+id/nav_show_invalidated"
android:title="@string/show_invalidated"
app:showAsAction="never" />
<item
android:id="@+id/nav_hide_invalidated"
android:title="@string/hide_invalidated"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,21 @@
<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:title="@string/remove_items"
app:showAsAction="never" />
<item
android:id="@+id/nav_show_invalidated"
android:title="@string/show_invalidated"
app:showAsAction="never" />
<item
android:id="@+id/nav_hide_invalidated"
android:title="@string/hide_invalidated"
app:showAsAction="never" />
<item
android:id="@+id/nav_refresh_ns"
android:title="@string/refresh_from_nightscout"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,17 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/nav_show_loop"
android:title="@string/show_loop"
app:showAsAction="never" />
<item
android:id="@+id/nav_hide_loop"
android:title="@string/hide_loop"
app:showAsAction="never" />
<item
android:id="@+id/nav_export"
android:title="@string/ue_export_to_csv"
app:showAsAction="never" />
</menu>

View file

@ -827,6 +827,9 @@
<string name="sensitivity_raises_target_summary">When sensitivity is detected, raise the target glucose</string> <string name="sensitivity_raises_target_summary">When sensitivity is detected, raise the target glucose</string>
<string name="key_keep_screen_on" translatable="false">keep_screen_on</string> <string name="key_keep_screen_on" translatable="false">keep_screen_on</string>
<string name="careportal_removestartedevents">Clean AndroidAPS started</string> <string name="careportal_removestartedevents">Clean AndroidAPS started</string>
<string name="show_invalidated">Show invalidated</string>
<string name="hide_invalidated">Hide invalidated</string>
<string name="remove_items">Remove items</string>
<string name="storedsettingsfound">Stored settings found</string> <string name="storedsettingsfound">Stored settings found</string>
<string name="allow_hardware_pump_text">Attention: If you activate and connect to a hardware pump, AndroidAPS will copy the basal settings from the profile to the pump, overwriting the existing basal rate stored on the pump. Make sure you have the correct basal setting in AndroidAPS. If you are not sure or don\'t want to overwrite the basal settings on your pump, press cancel and repeat switching to the pump at a later time.</string> <string name="allow_hardware_pump_text">Attention: If you activate and connect to a hardware pump, AndroidAPS will copy the basal settings from the profile to the pump, overwriting the existing basal rate stored on the pump. Make sure you have the correct basal setting in AndroidAPS. If you are not sure or don\'t want to overwrite the basal settings on your pump, press cancel and repeat switching to the pump at a later time.</string>
<string name="error_adding_treatment_title">Treatment data incomplete</string> <string name="error_adding_treatment_title">Treatment data incomplete</string>
@ -1208,4 +1211,14 @@
<string name="wear_unknown_action_string">Unknown action command:</string> <string name="wear_unknown_action_string">Unknown action command:</string>
<string name="overview_editquickwizard_percentage">Percentage</string> <string name="overview_editquickwizard_percentage">Percentage</string>
<string name="app_default">Application default</string> <string name="app_default">Application default</string>
<string name="refresh_from_nightscout">Refresh from Nightscout</string>
<string name="remove_selected_items">Remove selected items</string>
<string name="select_for_removal">Select for removal</string>
<string name="profile_changes">Profile changes</string>
<string name="tempt_targets">Temp Targets</string>
<string name="carbs_and_bolus">Carbs and bolus</string>
<string name="confirm_remove_multiple_items">Are you sure you want to remove %1$d items</string>
<string name="hide_loop">Hide loop</string>
<string name="show_loop">Show loop</string>
<string name="count_selected">%1$d selected</string>
</resources> </resources>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
</vector>

View file

@ -3,6 +3,8 @@
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="windowActionModeOverlay">true</item>
<item name="actionModeCloseDrawable">@drawable/ic_close</item>
</style> </style>
</resources> </resources>

View file

@ -469,6 +469,7 @@
<string name="uel_stat_reset">STAT RESET</string> <string name="uel_stat_reset">STAT RESET</string>
<string name="uel_delete_logs">DELETE LOGS</string> <string name="uel_delete_logs">DELETE LOGS</string>
<string name="uel_delete_future_treatments">DELETE FUTURE TREATMENTS</string> <string name="uel_delete_future_treatments">DELETE FUTURE TREATMENTS</string>
<string name="delete_future_treatments">Delete future treatments</string>
<string name="uel_export_settings">EXPORT SETTINGS</string> <string name="uel_export_settings">EXPORT SETTINGS</string>
<string name="uel_import_settings">IMPORT SETTINGS</string> <string name="uel_import_settings">IMPORT SETTINGS</string>
<string name="uel_reset_databases">RESET DATABASES</string> <string name="uel_reset_databases">RESET DATABASES</string>