From 67e5fb8dc753067baf79520e523d206759dc516a Mon Sep 17 00:00:00 2001 From: Andries Smit Date: Tue, 22 Feb 2022 21:11:15 +0100 Subject: [PATCH 1/3] Add history treatments option menu --- .../activities/TreatmentsActivity.kt | 24 +- .../fragments/TreatmentsBolusCarbsFragment.kt | 360 +++++++++++------- .../fragments/TreatmentsCareportalFragment.kt | 224 +++++++---- .../TreatmentsExtendedBolusesFragment.kt | 162 ++++++-- .../TreatmentsProfileSwitchFragment.kt | 258 +++++++++---- .../fragments/TreatmentsTempTargetFragment.kt | 222 ++++++++--- .../TreatmentsTemporaryBasalsFragment.kt | 200 +++++++--- .../fragments/TreatmentsUserEntryFragment.kt | 95 +++-- .../treatments_bolus_carbs_fragment.xml | 44 --- .../layout/treatments_bolus_carbs_item.xml | 18 +- .../layout/treatments_careportal_fragment.xml | 44 --- .../res/layout/treatments_careportal_item.xml | 10 +- .../treatments_extendedbolus_fragment.xml | 24 -- .../layout/treatments_extendedbolus_item.xml | 10 +- .../main/res/layout/treatments_fragment.xml | 7 + .../treatments_profileswitch_fragment.xml | 34 -- .../layout/treatments_profileswitch_item.xml | 11 +- .../layout/treatments_tempbasals_fragment.xml | 26 -- .../res/layout/treatments_tempbasals_item.xml | 11 +- .../layout/treatments_temptarget_fragment.xml | 35 -- .../res/layout/treatments_temptarget_item.xml | 10 +- .../layout/treatments_user_entry_fragment.xml | 34 -- .../main/res/menu/menu_delete_selection.xml | 11 + .../res/menu/menu_treatments_carbs_bolus.xml | 25 ++ .../res/menu/menu_treatments_careportal.xml | 25 ++ .../menu/menu_treatments_extended_bolus.xml | 17 + .../menu/menu_treatments_profile_switch.xml | 17 + .../res/menu/menu_treatments_temp_basal.xml | 17 + .../res/menu/menu_treatments_temp_target.xml | 21 + .../res/menu/menu_treatments_user_entry.xml | 17 + app/src/main/res/values/strings.xml | 13 + core/src/main/res/drawable/ic_close.xml | 5 + core/src/main/res/values/layout.xml | 2 + core/src/main/res/values/strings.xml | 1 + 34 files changed, 1289 insertions(+), 745 deletions(-) create mode 100644 app/src/main/res/menu/menu_delete_selection.xml create mode 100644 app/src/main/res/menu/menu_treatments_carbs_bolus.xml create mode 100644 app/src/main/res/menu/menu_treatments_careportal.xml create mode 100644 app/src/main/res/menu/menu_treatments_extended_bolus.xml create mode 100644 app/src/main/res/menu/menu_treatments_profile_switch.xml create mode 100644 app/src/main/res/menu/menu_treatments_temp_basal.xml create mode 100644 app/src/main/res/menu/menu_treatments_temp_target.xml create mode 100644 app/src/main/res/menu/menu_treatments_user_entry.xml create mode 100644 core/src/main/res/drawable/ic_close.xml diff --git a/app/src/main/java/info/nightscout/androidaps/activities/TreatmentsActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/TreatmentsActivity.kt index 9a68aeaf5f..5290b48711 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/TreatmentsActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/TreatmentsActivity.kt @@ -1,13 +1,13 @@ package info.nightscout.androidaps.activities import android.os.Bundle +import android.view.MenuItem import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.fragments.* import info.nightscout.androidaps.databinding.TreatmentsFragmentBinding -import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.utils.buildHelper.BuildHelper import javax.inject.Inject @@ -23,40 +23,60 @@ class TreatmentsActivity : NoSplashAppCompatActivity() { super.onCreate(savedInstanceState) binding = TreatmentsFragmentBinding.inflate(layoutInflater) setContentView(binding.root) - //binding.tempBasals.visibility = buildHelper.isEngineeringMode().toVisibility() //binding.extendedBoluses.visibility = (buildHelper.isEngineeringMode() && !activePlugin.activePump.isFakingTempsByExtendedBoluses).toVisibility() binding.treatments.setOnClickListener { setFragment(TreatmentsBolusCarbsFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.carbs_and_bolus) } binding.extendedBoluses.setOnClickListener { setFragment(TreatmentsExtendedBolusesFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.extended_bolus) } binding.tempBasals.setOnClickListener { setFragment(TreatmentsTemporaryBasalsFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.tempbasal_label) } binding.tempTargets.setOnClickListener { setFragment(TreatmentsTempTargetFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.tempt_targets) } binding.profileSwitches.setOnClickListener { setFragment(TreatmentsProfileSwitchFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.profile_changes) } binding.careportal.setOnClickListener { setFragment(TreatmentsCareportalFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.careportal) } binding.userentry.setOnClickListener { setFragment(TreatmentsUserEntryFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.user_action) } setFragment(TreatmentsBolusCarbsFragment()) 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) { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt index 3ed805966e..dd359ef07d 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt @@ -3,9 +3,10 @@ package info.nightscout.androidaps.activities.fragments import android.annotation.SuppressLint import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* +import android.widget.CompoundButton +import android.view.ActionMode +import androidx.appcompat.widget.Toolbar import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -79,9 +80,14 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { private var _binding: TreatmentsBolusCarbsFragmentBinding? = 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 var selectedItems: MutableList = mutableListOf() + private var showInvalidated = false + private var removeActionMode: ActionMode? = null + + // val TAG = "TreatmentMenu" + private var toolbar: Toolbar? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root @@ -89,92 +95,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { @SuppressLint("CheckResult") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) + toolbar = activity?.findViewById(R.id.toolbar) binding.recyclerview.setHasFixedSize(true) 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 @@ -204,8 +128,8 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { fun swapAdapter() { val now = System.currentTimeMillis() - if (binding.showInvalidated.isChecked) - disposable += carbsMealLinksWithInvalid(now) + disposable += if (showInvalidated) + carbsMealLinksWithInvalid(now) .zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second } .zipWith(calcResultMealLinksWithInvalid(now)) { first, second -> first + second } .map { ml -> @@ -217,10 +141,9 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { .observeOn(aapsSchedulers.main) .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) - binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility() } else - disposable += carbsMealLinks(now) + carbsMealLinks(now) .zipWith(bolusMealLinks(now)) { first, second -> first + second } .zipWith(calcResultMealLinks(now)) { first, second -> first + second } .map { ml -> @@ -232,7 +155,6 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { .observeOn(aapsSchedulers.main) .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) - binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility() } } @@ -267,13 +189,14 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _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) : RecyclerView.Adapter() { + inner class RecyclerViewAdapter internal constructor(private var mealLinks: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): MealLinkLoadedViewHolder = MealLinkLoadedViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_bolus_carbs_item, viewGroup, false)) @@ -287,13 +210,13 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { holder.binding.date.text = dateUtil.dateString(timestamp(ml)) // 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 -> holder.binding.calcTime.text = dateUtil.timeString(bolusCalculatorResult.timestamp) } // 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 -> holder.binding.bolusTime.text = dateUtil.timeString(bolus.timestamp) holder.binding.insulin.text = rh.gs(R.string.formatinsulinunits, bolus.amount) @@ -321,7 +244,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { } } // 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 -> holder.binding.carbsTime.text = dateUtil.timeString(carbs.timestamp) holder.binding.carbs.text = rh.gs(R.string.format_carbs, carbs.amount.toInt()) @@ -331,18 +254,26 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility() } - holder.binding.bolusRemove.visibility = (ml.bolus?.isValid == true).toVisibility() - holder.binding.carbsRemove.visibility = (ml.carbs?.isValid == true).toVisibility() - holder.binding.bolusRemove.tag = ml - holder.binding.carbsRemove.tag = ml + val onChange = CompoundButton.OnCheckedChangeListener { _, value -> + if (value) { + selectedItems.add(ml) + } else { + selectedItems.remove(ml) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size) + } + holder.binding.cbBolusRemove.visibility = ((ml.bolus?.isValid == true) && (removeActionMode != null)).toVisibility() + holder.binding.cbBolusRemove.setOnCheckedChangeListener(onChange) + + holder.binding.cbCarbsRemove.visibility = (ml.carbs?.isValid == true && (removeActionMode != null)).toVisibility() + holder.binding.cbCarbsRemove.setOnCheckedChangeListener(onChange) + holder.binding.calculation.tag = ml val nextTimestamp = if (mealLinks.size != position + 1) timestamp(mealLinks[position + 1]) else 0L holder.binding.delimiter.visibility = dateUtil.isSameDay(timestamp(ml), nextTimestamp).toVisibility() } - override fun getItemCount(): Int { - return mealLinks.size - } + override fun getItemCount() = mealLinks.size inner class MealLinkLoadedViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) { @@ -359,35 +290,203 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { } } 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" + - rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(bolus.timestamp) - OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable { + } + } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_treatments_carbs_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 + 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 { + 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 + } + + 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 = mutableListOf() + 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() + mode.finish() + 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.first() + 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.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 { ml -> + ml.bolus?.let { bolus -> uel.log( Action.BOLUS_REMOVED, Sources.Treatments, ValueWithUnit.Timestamp(bolus.timestamp), ValueWithUnit.Insulin(bolus.amount) - //ValueWithUnit.Gram(mealLinkLoaded.carbs.toInt()) ) 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) } ) - }) - } - } - 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 { + } + ml.carbs?.let { carb -> uel.log( Action.CARBS_REMOVED, Sources.Treatments, ValueWithUnit.Timestamp(carb.timestamp), @@ -398,11 +497,12 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } }, { aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) } ) - }) + } } - } - binding.carbsRemove.paintFlags = binding.carbsRemove.paintFlags or Paint.UNDERLINE_TEXT_FLAG + selectedItems = mutableListOf() + }, Runnable { + selectedItems = mutableListOf() + }) } - } } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt index 0dca0e416f..59efc40861 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt @@ -1,10 +1,9 @@ package info.nightscout.androidaps.activities.fragments -import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* +import android.view.ActionMode +import androidx.appcompat.widget.Toolbar import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -60,11 +59,14 @@ class TreatmentsCareportalFragment : DaggerFragment() { private val disposable = CompositeDisposable() private val millsToThePast = T.days(30).msecs() + private var selectedItems: MutableList = mutableListOf() + private var showInvalidated = false + private var toolbar: Toolbar? = null + private var removeActionMode: ActionMode? = 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!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = @@ -72,46 +74,44 @@ class TreatmentsCareportalFragment : DaggerFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + toolbar = activity?.findViewById(R.id.toolbar) + setHasOptionsMenu(true) binding.recyclerview.setHasFixedSize(true) 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() - if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE - binding.showInvalidated.setOnCheckedChangeListener { _, _ -> - rxBus.send(EventTreatmentUpdateGui()) + private fun refreshFromNightscout() { + 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()) + }) + } + } + + 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() { val now = System.currentTimeMillis() disposable += - if (binding.showInvalidated.isChecked) + if (showInvalidated) repository .getTherapyEventDataIncludingInvalidFromTime(now - millsToThePast, false) .observeOn(aapsSchedulers.main) @@ -148,6 +148,7 @@ class TreatmentsCareportalFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -170,42 +171,135 @@ class TreatmentsCareportalFragment : DaggerFragment() { holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh) holder.binding.note.text = therapyEvent.note holder.binding.type.text = translator.translate(therapyEvent.type) - holder.binding.remove.tag = therapyEvent + holder.binding.cbRemove.visibility = (therapyEvent.isValid && (removeActionMode != null)).toVisibility() + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.add(therapyEvent) + } else { + selectedItems.remove(therapyEvent) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size) + } val nextTimestamp = if (therapyList.size != position + 1) therapyList[position + 1].timestamp else 0L holder.binding.delimiter.visibility = dateUtil.isSameDay(therapyEvent.timestamp, nextTimestamp).toVisibility() } - override fun getItemCount(): Int { - return therapyList.size - } + override fun getItemCount() = therapyList.size inner class TherapyEventsViewHolder(view: View) : RecyclerView.ViewHolder(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 - } } } -} \ No newline at end of file + + + 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 { + 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 + } + + 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 = mutableListOf() + 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() + mode.finish() + 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.first() + 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) } + ) + } + + }) + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt index bdd0e4f424..af1499bf76 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt @@ -1,12 +1,10 @@ package info.nightscout.androidaps.activities.fragments import android.annotation.SuppressLint -import android.content.DialogInterface -import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* +import android.view.ActionMode +import androidx.appcompat.widget.Toolbar import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -27,16 +25,17 @@ import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus 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.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.shared.logging.LTag import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign import java.util.concurrent.TimeUnit @@ -61,22 +60,28 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { 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!! - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View = + private var selectedItems: MutableList = mutableListOf() + private var showInvalidated = false + private var removeActionMode: ActionMode? = null + private var toolbar: Toolbar? = null + // val TAG = "TreatmentMenu" + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + setHasOptionsMenu(true) + toolbar = activity?.findViewById(R.id.toolbar) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) } fun swapAdapter() { val now = System.currentTimeMillis() - if (binding.showInvalidated.isChecked) + disposable += if (showInvalidated) repository .getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false) .observeOn(aapsSchedulers.main) @@ -109,6 +114,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -125,7 +131,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { holder.binding.ns.visibility = (extendedBolus.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ph.visibility = (extendedBolus.interfaceIDs.pumpId != null).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.text = dateUtil.dateString(extendedBolus.timestamp) @SuppressLint("SetTextI18n") @@ -143,41 +149,119 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iob) 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) - holder.binding.remove.tag = extendedBolus + holder.binding.cbRemove.visibility = (extendedBolus.isValid && (removeActionMode != null)).toVisibility() + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.add(extendedBolus) + } else { + selectedItems.remove(extendedBolus) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size) + } val nextTimestamp = if (extendedBolusList.size != position + 1) extendedBolusList[position + 1].timestamp else 0L 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) { 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 - } } } -} \ No newline at end of file + + 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 = mutableListOf() + 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() + mode.finish() + true + } + + else -> false + } + } + + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } + } + + private fun getConfirmationText(): String { + if (selectedItems.size == 1) { + return rh.gs(R.string.extended_bolus) + "\n" + + "${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(selectedItems.first().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) }) + } + }) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt index 91b46228be..3d2af3afd0 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt @@ -2,9 +2,9 @@ package info.nightscout.androidaps.activities.fragments import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* +import android.view.ActionMode +import androidx.appcompat.widget.Toolbar import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -65,9 +65,12 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { private val disposable = CompositeDisposable() private val millsToThePast = T.days(30).msecs() + private var selectedItems: MutableList = mutableListOf() + private var showInvalidated = false + private var removeActionMode: ActionMode? = null + private var toolbar: Toolbar? = null - // This property is only valid between onCreateView and - // onDestroyView. + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = @@ -75,36 +78,34 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) + toolbar = activity?.findViewById(R.id.toolbar) binding.recyclerview.setHasFixedSize(true) 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.deleteAllEffectiveProfileSwitches() - repository.deleteAllProfileSwitches() - } - .subscribeOn(aapsSchedulers.io) - .observeOn(aapsSchedulers.main) - .subscribeBy( - onError = { aapsLogger.error("Error removing entries", it) }, - onComplete = { - rxBus.send(EventProfileSwitchChanged()) - rxBus.send(EventEffectiveProfileSwitchChanged(0L)) - rxBus.send(EventNewHistoryData(0, false)) - } - ) - rxBus.send(EventNSClientRestart()) - } + 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.deleteAllEffectiveProfileSwitches() + repository.deleteAllProfileSwitches() + } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribeBy( + onError = { aapsLogger.error("Error removing entries", it) }, + onComplete = { + rxBus.send(EventProfileSwitchChanged()) + rxBus.send(EventEffectiveProfileSwitchChanged(0L)) + rxBus.send(EventNewHistoryData(0, false)) + } + ) + 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 @@ -126,23 +127,23 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { fun swapAdapter() { val now = System.currentTimeMillis() - if (binding.showInvalidated.isChecked) - disposable += profileSwitchWithInvalid(now) - .zipWith(effectiveProfileSwitchWithInvalid(now)) { first, second -> first + second } - .map { ml -> ml.sortedByDescending { it.timestamp } } - .observeOn(aapsSchedulers.main) - .subscribe { list -> - binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) - } - else - disposable += profileSwitches(now) - .zipWith(effectiveProfileSwitches(now)) { first, second -> first + second } - .map { ml -> ml.sortedByDescending { it.timestamp } } - .observeOn(aapsSchedulers.main) - .subscribe { list -> - binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) - } - + disposable += + if (showInvalidated) + profileSwitchWithInvalid(now) + .zipWith(effectiveProfileSwitchWithInvalid(now)) { first, second -> first + second } + .map { ml -> ml.sortedByDescending { it.timestamp } } + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) + } + else + profileSwitches(now) + .zipWith(effectiveProfileSwitches(now)) { first, second -> first + second } + .map { ml -> ml.sortedByDescending { it.timestamp } } + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) + } } @Synchronized @@ -168,6 +169,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -189,13 +191,20 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { 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)) else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor) - holder.binding.remove.tag = profileSwitch holder.binding.clone.tag = profileSwitch holder.binding.name.tag = profileSwitch holder.binding.date.tag = profileSwitch holder.binding.invalid.visibility = profileSwitch.isValid.not().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() + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.add(profileSwitch) + } else { + selectedItems.remove(profileSwitch) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size) + } holder.binding.clone.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)) @@ -203,47 +212,39 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { holder.binding.delimiter.visibility = dateUtil.isSameDay(profileSwitch.timestamp, nextTimestamp).toVisibility() } - override fun getItemCount(): Int { - return profileSwitchList.size - } + override fun getItemCount() = profileSwitchList.size inner class ProfileSwitchViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { val binding = TreatmentsProfileswitchItemBinding.bind(itemView) 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 { activity?.let { activity -> val profileSwitch = (it.tag as ProfileSealed.PS).value 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 { - uel.log(Action.PROFILE_SWITCH_CLONED, Sources.Treatments, - 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()) - }) + OKDialog.showConfirmation( + activity, + rh.gs(R.string.careportal_profileswitch), + rh.gs(R.string.copytolocalprofile) + "\n" + profileSwitch.getCustomizedName() + "\n" + dateUtil.dateAndTimeString(profileSwitch.timestamp), + Runnable { + uel.log( + Action.PROFILE_SWITCH_CLONED, Sources.Treatments, + 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.name.setOnClickListener { ProfileViewerDialog().also { pvd -> @@ -266,4 +267,103 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { } } } -} \ No newline at end of file + + 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 { + 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 + } + + 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 = mutableListOf() + 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() + mode.finish() + 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.first() + 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) } + ) + } + }) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt index c408fd4299..479f0008de 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt @@ -1,12 +1,9 @@ package info.nightscout.androidaps.activities.fragments import android.annotation.SuppressLint -import android.content.DialogInterface -import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* +import androidx.appcompat.widget.Toolbar import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -30,6 +27,8 @@ import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.events.EventTreatmentUpdateGui 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.FabricPrivacy import info.nightscout.androidaps.utils.T @@ -40,6 +39,7 @@ import info.nightscout.androidaps.extensions.friendlyDescription import info.nightscout.androidaps.extensions.highValueToUnitsToString import info.nightscout.androidaps.extensions.lowValueToUnitsToString 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.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP @@ -68,11 +68,14 @@ class TreatmentsTempTargetFragment : DaggerFragment() { private val disposable = CompositeDisposable() private val millsToThePast = T.days(30).msecs() + private var selectedItems: MutableList = mutableListOf() + private var showInvalidated = false + private var toolbar: Toolbar? = null + private var removeActionMode: ActionMode? = 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!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = @@ -80,42 +83,48 @@ class TreatmentsTempTargetFragment : DaggerFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding.recyclerview.setHasFixedSize(true) + toolbar = activity?.findViewById(R.id.toolbar) + setHasOptionsMenu(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.refreshFromNightscout.setOnClickListener { - context?.let { context -> - OKDialog.showConfirmation(context, rh.gs(R.string.refresheventsfromnightscout) + " ?", { - uel.log(Action.TT_NS_REFRESH, Sources.Treatments) - disposable += Completable.fromAction { repository.deleteAllTempTargetEntries() } + } + + 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.deleteAllEffectiveProfileSwitches() + repository.deleteAllProfileSwitches() + } .subscribeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.main) .subscribeBy( 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() { val now = System.currentTimeMillis() - if (binding.showInvalidated.isChecked) - repository - .getTemporaryTargetDataIncludingInvalidFromTime(now - millsToThePast, false) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } - else - repository - .getTemporaryTargetDataFromTime(now - millsToThePast, false) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + disposable += + if (showInvalidated) + repository + .getTemporaryTargetDataIncludingInvalidFromTime(now - millsToThePast, false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + else + repository + .getTemporaryTargetDataFromTime(now - millsToThePast, false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } } @Synchronized @@ -145,6 +154,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -163,8 +173,16 @@ class TreatmentsTempTargetFragment : DaggerFragment() { val tempTarget = tempTargetList[position] holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility() - holder.binding.remove.visibility = tempTarget.isValid.toVisibility() - val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempTarget.timestamp, tempTargetList[position-1].timestamp) + holder.binding.cbRemove.visibility = (tempTarget.isValid && (removeActionMode != null)).toVisibility() + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.add(tempTarget) + } else { + selectedItems.remove(tempTarget) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size) + } + val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempTarget.timestamp, tempTargetList[position - 1].timestamp) holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.text = dateUtil.dateString(tempTarget.timestamp) holder.binding.time.text = dateUtil.timeRangeString(tempTarget.timestamp, tempTarget.end) @@ -177,43 +195,123 @@ class TreatmentsTempTargetFragment : DaggerFragment() { tempTarget.id == currentlyActiveTarget?.id -> rh.gc(R.color.colorActive) tempTarget.timestamp > dateUtil.now() -> rh.gc(R.color.colorScheduled) else -> holder.binding.reasonColon.currentTextColor - }) - holder.binding.remove.tag = tempTarget + } + ) val nextTimestamp = if (tempTargetList.size != position + 1) tempTargetList[position + 1].timestamp else 0L 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) { 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 - } } } -} \ No newline at end of file + + private fun removeSelected() { + // TODO check if item should not be delete val profile = profileFunction.getProfile(dateUtil.now()) == null + 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) }) + } + }) + } + } + + 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 { + 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 + } + + 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 = mutableListOf() + 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() + mode.finish() + 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.first() + 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) + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt index 6d4c536236..4e166fc46f 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt @@ -1,11 +1,8 @@ package info.nightscout.androidaps.activities.fragments -import android.content.DialogInterface -import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* +import androidx.appcompat.widget.Toolbar import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -36,6 +33,7 @@ import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus 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.FabricPrivacy import info.nightscout.androidaps.utils.T @@ -66,9 +64,12 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { private var _binding: TreatmentsTempbasalsFragmentBinding? = null private val millsToThePast = T.days(30).msecs() + private var selectedItems: MutableList = mutableListOf() + private var showInvalidated = false + private var toolbar: Toolbar? = null + private var removeActionMode: ActionMode? = null - // This property is only valid between onCreateView and - // onDestroyView. + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = @@ -76,6 +77,8 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + toolbar = activity?.findViewById(R.id.toolbar) + setHasOptionsMenu(true) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) } @@ -98,7 +101,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { val now = System.currentTimeMillis() disposable += if (activePlugin.activePump.isFakingTempsByExtendedBoluses) { - if (binding.showInvalidated.isChecked) + if (showInvalidated) tempBasalsWithInvalid(now) .zipWith(extendedBolusesWithInvalid(now)) { first, second -> first + second } .map { list -> list.filterNotNull() } @@ -113,7 +116,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { .observeOn(aapsSchedulers.main) .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } } else { - if (binding.showInvalidated.isChecked) + if (showInvalidated) tempBasalsWithInvalid(now) .observeOn(aapsSchedulers.main) .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } @@ -150,6 +153,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -164,7 +168,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { holder.binding.ns.visibility = (tempBasal.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = tempBasal.isValid.not().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.text = dateUtil.dateString(tempBasal.timestamp) if (tempBasal.isInProgress) { @@ -187,62 +191,144 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { holder.binding.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).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) - holder.binding.remove.tag = tempBasal - + holder.binding.cbRemove.visibility = (tempBasal.isValid && (removeActionMode != null)).toVisibility() + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.add(tempBasal) + } else { + selectedItems.remove(tempBasal) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size) + } val nextTimestamp = if (tempBasalList.size != position + 1) tempBasalList[position + 1].timestamp else 0L 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) { 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 - } } } -} \ No newline at end of file + + 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 { + 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 = mutableListOf() + 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() + mode.finish() + 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.first() + 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() { + // TODO check if item should not be delete val profile = profileFunction.getProfile(dateUtil.now()) == null + 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) }) + } + } + }) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt index 4d3620052b..13a1b3de09 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt @@ -1,9 +1,7 @@ package info.nightscout.androidaps.activities.fragments import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView 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.rx.AapsSchedulers import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -51,7 +50,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() { private val millsToThePastFiltered = T.days(30).msecs() private val millsToThePastUnFiltered = T.days(3).msecs() - + private var showLoop = false private var _binding: TreatmentsUserEntryFragmentBinding? = null // This property is only valid between onCreateView and @@ -63,35 +62,33 @@ class TreatmentsUserEntryFragment : DaggerFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.ueExportToXml.setOnClickListener { - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.ue_export_to_csv) + "?") { - uel.log(Action.EXPORT_CSV, Sources.Treatments) - importExportPrefs.exportUserEntriesCsv(activity) - } + } + + fun exportUserEnteries() { + activity?.let { 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() { val now = System.currentTimeMillis() - if (binding.showLoop.isChecked) - disposable.add( repository - .getUserEntryDataFromTime(now - millsToThePastUnFiltered) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } - ) - else - disposable.add( repository - .getUserEntryFilteredDataFromTime(now - millsToThePastFiltered) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } - ) + disposable += + if (showLoop) + repository + .getUserEntryDataFromTime(now - millsToThePastUnFiltered) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } + else + repository + .getUserEntryFilteredDataFromTime(now - millsToThePastFiltered) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } } @Synchronized @@ -99,15 +96,15 @@ class TreatmentsUserEntryFragment : DaggerFragment() { super.onResume() swapAdapter() - disposable.add(rxBus + disposable += rxBus .toObservable(EventPreferenceChange::class.java) .observeOn(aapsSchedulers.io) - .subscribe({ swapAdapter() }, fabricPrivacy::logException)) - disposable.add(rxBus + .subscribe({ swapAdapter() }, fabricPrivacy::logException) + disposable += rxBus .toObservable(EventTreatmentUpdateGui::class.java) .observeOn(aapsSchedulers.io) .debounce(1L, TimeUnit.SECONDS) - .subscribe({ swapAdapter() }, fabricPrivacy::logException)) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) } @Synchronized @@ -132,7 +129,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() { override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) { 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.text = dateUtil.dateString(current.timestamp) holder.binding.time.text = dateUtil.timeStringWithSeconds(current.timestamp) @@ -152,7 +149,41 @@ class TreatmentsUserEntryFragment : DaggerFragment() { val binding = TreatmentsUserEntryItemBinding.bind(itemView) } - override fun getItemCount(): Int = entries.size + override fun getItemCount() = entries.size } -} \ No newline at end of file + 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 { + return 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 + } + } +} diff --git a/app/src/main/res/layout/treatments_bolus_carbs_fragment.xml b/app/src/main/res/layout/treatments_bolus_carbs_fragment.xml index 1012d218e3..388a6a9976 100644 --- a/app/src/main/res/layout/treatments_bolus_carbs_fragment.xml +++ b/app/src/main/res/layout/treatments_bolus_carbs_fragment.xml @@ -1,54 +1,10 @@ - - - - - - - - - - - - - + android:contentDescription="@string/select_for_removal" + android:visibility="gone" /> + + - - - - - - - - - - - - - + android:contentDescription="@string/select_for_removal" + android:visibility="gone" /> diff --git a/app/src/main/res/layout/treatments_extendedbolus_fragment.xml b/app/src/main/res/layout/treatments_extendedbolus_fragment.xml index 8434b39e86..945a861bb1 100644 --- a/app/src/main/res/layout/treatments_extendedbolus_fragment.xml +++ b/app/src/main/res/layout/treatments_extendedbolus_fragment.xml @@ -1,34 +1,10 @@ - - - - - - - - - + android:contentDescription="@string/select_for_removal" + android:visibility="gone" /> + - - - - - - - - - - - + android:contentDescription="@string/select_for_removal" + android:visibility="gone" /> diff --git a/app/src/main/res/layout/treatments_tempbasals_fragment.xml b/app/src/main/res/layout/treatments_tempbasals_fragment.xml index b7dba96815..9ef25cff3e 100644 --- a/app/src/main/res/layout/treatments_tempbasals_fragment.xml +++ b/app/src/main/res/layout/treatments_tempbasals_fragment.xml @@ -1,36 +1,10 @@ - - - - - - - - - - - + android:contentDescription="@string/select_for_removal" + android:visibility="gone" /> - - - - - - - - - - - + android:contentDescription="@string/select_for_removal" + android:visibility="gone" /> diff --git a/app/src/main/res/layout/treatments_user_entry_fragment.xml b/app/src/main/res/layout/treatments_user_entry_fragment.xml index 2244b94f6d..8479bbfde9 100644 --- a/app/src/main/res/layout/treatments_user_entry_fragment.xml +++ b/app/src/main/res/layout/treatments_user_entry_fragment.xml @@ -6,40 +6,6 @@ android:orientation="vertical" tools:context="info.nightscout.androidaps.activities.fragments.TreatmentsUserEntryFragment"> - - - - - - - - - - - + + + + diff --git a/app/src/main/res/menu/menu_treatments_carbs_bolus.xml b/app/src/main/res/menu/menu_treatments_carbs_bolus.xml new file mode 100644 index 0000000000..c45d9562d3 --- /dev/null +++ b/app/src/main/res/menu/menu_treatments_carbs_bolus.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/app/src/main/res/menu/menu_treatments_careportal.xml b/app/src/main/res/menu/menu_treatments_careportal.xml new file mode 100644 index 0000000000..7a90a0f10a --- /dev/null +++ b/app/src/main/res/menu/menu_treatments_careportal.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/app/src/main/res/menu/menu_treatments_extended_bolus.xml b/app/src/main/res/menu/menu_treatments_extended_bolus.xml new file mode 100644 index 0000000000..2822a53819 --- /dev/null +++ b/app/src/main/res/menu/menu_treatments_extended_bolus.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/app/src/main/res/menu/menu_treatments_profile_switch.xml b/app/src/main/res/menu/menu_treatments_profile_switch.xml new file mode 100644 index 0000000000..2822a53819 --- /dev/null +++ b/app/src/main/res/menu/menu_treatments_profile_switch.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/app/src/main/res/menu/menu_treatments_temp_basal.xml b/app/src/main/res/menu/menu_treatments_temp_basal.xml new file mode 100644 index 0000000000..2822a53819 --- /dev/null +++ b/app/src/main/res/menu/menu_treatments_temp_basal.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/app/src/main/res/menu/menu_treatments_temp_target.xml b/app/src/main/res/menu/menu_treatments_temp_target.xml new file mode 100644 index 0000000000..751c678f12 --- /dev/null +++ b/app/src/main/res/menu/menu_treatments_temp_target.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/app/src/main/res/menu/menu_treatments_user_entry.xml b/app/src/main/res/menu/menu_treatments_user_entry.xml new file mode 100644 index 0000000000..cab552390e --- /dev/null +++ b/app/src/main/res/menu/menu_treatments_user_entry.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa3550bae6..018baa8b9c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -826,6 +826,9 @@ When sensitivity is detected, raise the target glucose keep_screen_on Clean AndroidAPS started + Show invalidated + Hide invalidated + Remove items Stored settings found 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. Treatment data incomplete @@ -1207,4 +1210,14 @@ Unknown action command: Percentage Application default + Refresh from Nightscout + Remove selected items + Select for removal + Profile changes + Temp Targets + Carbs and bolus + Are you sure you want to remove %1$d items + Hide loop + Show loop + %1$d selected diff --git a/core/src/main/res/drawable/ic_close.xml b/core/src/main/res/drawable/ic_close.xml new file mode 100644 index 0000000000..70db409b33 --- /dev/null +++ b/core/src/main/res/drawable/ic_close.xml @@ -0,0 +1,5 @@ + + + diff --git a/core/src/main/res/values/layout.xml b/core/src/main/res/values/layout.xml index 5629fe5956..045d08e4e1 100644 --- a/core/src/main/res/values/layout.xml +++ b/core/src/main/res/values/layout.xml @@ -3,6 +3,8 @@ @color/colorPrimary @color/colorPrimaryDark @color/colorAccent + true + @drawable/ic_close \ No newline at end of file diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 76428aa88f..4d6b4b1157 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -469,6 +469,7 @@ STAT RESET DELETE LOGS DELETE FUTURE TREATMENTS + Delete future treatments EXPORT SETTINGS IMPORT SETTINGS RESET DATABASES From 967d8bb9d30ee9fad1a835aad7f203916f4f21f0 Mon Sep 17 00:00:00 2001 From: Andries Smit Date: Wed, 23 Feb 2022 08:51:10 +0100 Subject: [PATCH 2/3] chore: treatments menu code cleanup --- .../fragments/TreatmentsBolusCarbsFragment.kt | 76 +++++++++---------- .../fragments/TreatmentsCareportalFragment.kt | 8 +- .../TreatmentsExtendedBolusesFragment.kt | 3 +- .../TreatmentsProfileSwitchFragment.kt | 8 +- .../fragments/TreatmentsTempTargetFragment.kt | 8 +- .../TreatmentsTemporaryBasalsFragment.kt | 7 +- .../fragments/TreatmentsUserEntryFragment.kt | 8 +- core/src/main/res/drawable/ic_close.xml | 13 +++- 8 files changed, 61 insertions(+), 70 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt index dd359ef07d..5840cc1a35 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt @@ -86,7 +86,6 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { private var showInvalidated = false private var removeActionMode: ActionMode? = null - // val TAG = "TreatmentMenu" private var toolbar: Toolbar? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = @@ -128,34 +127,35 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { fun swapAdapter() { val now = System.currentTimeMillis() - disposable += if (showInvalidated) - carbsMealLinksWithInvalid(now) - .zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second } - .zipWith(calcResultMealLinksWithInvalid(now)) { first, second -> first + second } - .map { ml -> - ml.sortedByDescending { - it.carbs?.timestamp ?: it.bolus?.timestamp - ?: it.bolusCalculatorResult?.timestamp + disposable += + if (showInvalidated) + carbsMealLinksWithInvalid(now) + .zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second } + .zipWith(calcResultMealLinksWithInvalid(now)) { first, second -> first + second } + .map { ml -> + ml.sortedByDescending { + it.carbs?.timestamp ?: it.bolus?.timestamp + ?: it.bolusCalculatorResult?.timestamp + } } - } - .observeOn(aapsSchedulers.main) - .subscribe { list -> - binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) - } - else - carbsMealLinks(now) - .zipWith(bolusMealLinks(now)) { first, second -> first + second } - .zipWith(calcResultMealLinks(now)) { first, second -> first + second } - .map { ml -> - ml.sortedByDescending { - it.carbs?.timestamp ?: it.bolus?.timestamp - ?: it.bolusCalculatorResult?.timestamp + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) + } + else + carbsMealLinks(now) + .zipWith(bolusMealLinks(now)) { first, second -> first + second } + .zipWith(calcResultMealLinks(now)) { first, second -> first + second } + .map { ml -> + ml.sortedByDescending { + 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) - } } @@ -262,10 +262,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { } removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size) } - holder.binding.cbBolusRemove.visibility = ((ml.bolus?.isValid == true) && (removeActionMode != null)).toVisibility() + holder.binding.cbBolusRemove.visibility = ((ml.bolus?.isValid == true) && removeActionMode != null).toVisibility() holder.binding.cbBolusRemove.setOnCheckedChangeListener(onChange) - holder.binding.cbCarbsRemove.visibility = (ml.carbs?.isValid == true && (removeActionMode != null)).toVisibility() + holder.binding.cbCarbsRemove.visibility = (ml.carbs?.isValid == true && removeActionMode != null).toVisibility() holder.binding.cbCarbsRemove.setOnCheckedChangeListener(onChange) holder.binding.calculation.tag = ml @@ -310,8 +310,8 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { return super.onPrepareOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { R.id.nav_remove_items -> { removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) true @@ -335,15 +335,14 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { } R.id.nav_refresh_ns -> { - refreshFromNightScout() + refreshFromNightscout() true } else -> false } - } - private fun refreshFromNightScout() { + private fun refreshFromNightscout() { activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") { uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments) @@ -457,13 +456,11 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { val mealLink = selectedItems.first() val bolus = mealLink.bolus if (bolus != null) - return rh.gs(R.string.configbuilder_insulin) + ": " + - rh.gs(R.string.formatinsulinunits, bolus.amount) + "\n" + + 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.carbs) + ": " + rh.gs(R.string.format_carbs, carbs.amount.toInt()) + "\n" + + 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) @@ -499,9 +496,6 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { ) } } - selectedItems = mutableListOf() - }, Runnable { - selectedItems = mutableListOf() }) } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt index 59efc40861..b2230d9caa 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt @@ -171,7 +171,7 @@ class TreatmentsCareportalFragment : DaggerFragment() { holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh) holder.binding.note.text = therapyEvent.note holder.binding.type.text = translator.translate(therapyEvent.type) - holder.binding.cbRemove.visibility = (therapyEvent.isValid && (removeActionMode != null)).toVisibility() + holder.binding.cbRemove.visibility = (therapyEvent.isValid && removeActionMode != null).toVisibility() holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> if (value) { selectedItems.add(therapyEvent) @@ -193,7 +193,6 @@ class TreatmentsCareportalFragment : DaggerFragment() { } - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.menu_treatments_careportal, menu) super.onCreateOptionsMenu(menu, inflater) @@ -208,8 +207,8 @@ class TreatmentsCareportalFragment : DaggerFragment() { return super.onPrepareOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { R.id.nav_remove_items -> { removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) true @@ -239,7 +238,6 @@ class TreatmentsCareportalFragment : DaggerFragment() { else -> false } - } inner class RemoveActionModeCallback : ActionMode.Callback { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt index af1499bf76..c4ac1aff5b 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt @@ -67,7 +67,6 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { private var showInvalidated = false private var removeActionMode: ActionMode? = null private var toolbar: Toolbar? = null - // val TAG = "TreatmentMenu" override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root @@ -149,7 +148,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iob) 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) - holder.binding.cbRemove.visibility = (extendedBolus.isValid && (removeActionMode != null)).toVisibility() + holder.binding.cbRemove.visibility = (extendedBolus.isValid && removeActionMode != null).toVisibility() holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> if (value) { selectedItems.add(extendedBolus) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt index 3d2af3afd0..38073e13ad 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt @@ -188,7 +188,8 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { holder.binding.date.text = dateUtil.dateString(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.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)) else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor) holder.binding.clone.tag = profileSwitch @@ -282,8 +283,8 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { return super.onPrepareOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { R.id.nav_remove_items -> { removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) true @@ -308,7 +309,6 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { else -> false } - } inner class RemoveActionModeCallback : ActionMode.Callback { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt index 479f0008de..846c1122e0 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt @@ -173,7 +173,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() { val tempTarget = tempTargetList[position] holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility() - holder.binding.cbRemove.visibility = (tempTarget.isValid && (removeActionMode != null)).toVisibility() + holder.binding.cbRemove.visibility = (tempTarget.isValid && removeActionMode != null).toVisibility() holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> if (value) { selectedItems.add(tempTarget) @@ -211,7 +211,6 @@ class TreatmentsTempTargetFragment : DaggerFragment() { } private fun removeSelected() { - // TODO check if item should not be delete val profile = profileFunction.getProfile(dateUtil.now()) == null if (selectedItems.size > 0) activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { @@ -247,8 +246,8 @@ class TreatmentsTempTargetFragment : DaggerFragment() { return super.onPrepareOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { R.id.nav_remove_items -> { removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) true @@ -273,7 +272,6 @@ class TreatmentsTempTargetFragment : DaggerFragment() { else -> false } - } inner class RemoveActionModeCallback : ActionMode.Callback { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt index 4e166fc46f..b24f062004 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt @@ -191,7 +191,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { holder.binding.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).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) - holder.binding.cbRemove.visibility = (tempBasal.isValid && (removeActionMode != null)).toVisibility() + holder.binding.cbRemove.visibility = (tempBasal.isValid && removeActionMode != null).toVisibility() holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> if (value) { selectedItems.add(tempBasal) @@ -226,8 +226,8 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { return super.onPrepareOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { R.id.nav_remove_items -> { removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) true @@ -247,7 +247,6 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { else -> false } - } inner class RemoveActionModeCallback : ActionMode.Callback { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt index 13a1b3de09..071054d79c 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt @@ -53,8 +53,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() { private var showLoop = false private var _binding: TreatmentsUserEntryFragmentBinding? = null - // This property is only valid between onCreateView and - // onDestroyView. + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = @@ -164,8 +163,8 @@ class TreatmentsUserEntryFragment : DaggerFragment() { return super.onPrepareOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { R.id.nav_show_loop -> { showLoop = true rxBus.send(EventTreatmentUpdateGui()) @@ -185,5 +184,4 @@ class TreatmentsUserEntryFragment : DaggerFragment() { else -> false } - } } diff --git a/core/src/main/res/drawable/ic_close.xml b/core/src/main/res/drawable/ic_close.xml index 70db409b33..49ac6f1398 100644 --- a/core/src/main/res/drawable/ic_close.xml +++ b/core/src/main/res/drawable/ic_close.xml @@ -1,5 +1,10 @@ - - + + From cb3bb583196f1c139903daee42c20523d79a12ba Mon Sep 17 00:00:00 2001 From: Andries Smit Date: Wed, 23 Feb 2022 10:11:14 +0100 Subject: [PATCH 3/3] fix: treatments persist selection on scrolling --- .../fragments/TreatmentsBolusCarbsFragment.kt | 62 ++++++++++--------- .../fragments/TreatmentsCareportalFragment.kt | 47 +++++++------- .../TreatmentsExtendedBolusesFragment.kt | 39 +++++++----- .../TreatmentsProfileSwitchFragment.kt | 42 +++++++------ .../fragments/TreatmentsTempTargetFragment.kt | 47 +++++++------- .../TreatmentsTemporaryBasalsFragment.kt | 44 +++++++------ 6 files changed, 157 insertions(+), 124 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt index 5840cc1a35..1323b23113 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt @@ -3,10 +3,13 @@ package info.nightscout.androidaps.activities.fragments import android.annotation.SuppressLint import android.graphics.Paint import android.os.Bundle +import android.util.Log +import android.util.SparseArray 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.RecyclerView import dagger.android.support.DaggerFragment @@ -68,6 +71,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { @Inject lateinit var repository: AppRepository @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( val bolus: Bolus? = null, val carbs: Carbs? = null, @@ -75,17 +82,11 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { ) private val disposable = CompositeDisposable() - private val millsToThePast = T.days(30).msecs() - private var _binding: TreatmentsBolusCarbsFragmentBinding? = null - - // This property is only valid between onCreateView and onDestroyView. - private val binding get() = _binding!! - private var selectedItems: MutableList = mutableListOf() + private var selectedItems: SparseArray = SparseArray() private var showInvalidated = false private var removeActionMode: ActionMode? = null - private var toolbar: Toolbar? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = @@ -253,21 +254,22 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { holder.binding.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility() holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility() } - - val onChange = CompoundButton.OnCheckedChangeListener { _, value -> - if (value) { - selectedItems.add(ml) - } else { - selectedItems.remove(ml) - } - removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size) - } - holder.binding.cbBolusRemove.visibility = ((ml.bolus?.isValid == true) && removeActionMode != null).toVisibility() - holder.binding.cbBolusRemove.setOnCheckedChangeListener(onChange) - + holder.binding.cbBolusRemove.visibility = (ml.bolus?.isValid == true && removeActionMode != null).toVisibility() holder.binding.cbCarbsRemove.visibility = (ml.carbs?.isValid == true && removeActionMode != null).toVisibility() - holder.binding.cbCarbsRemove.setOnCheckedChangeListener(onChange) - + if (removeActionMode != null) { + val onChange = CompoundButton.OnCheckedChangeListener { _, value -> + 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 val nextTimestamp = if (mealLinks.size != position + 1) timestamp(mealLinks[position + 1]) else 0L holder.binding.delimiter.visibility = dateUtil.isSameDay(timestamp(ml), nextTimestamp).toVisibility() @@ -425,8 +427,8 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems = mutableListOf() - mode.title = rh.gs(R.string.count_selected, selectedItems.size) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) binding.recyclerview.adapter?.notifyDataSetChanged() return true } @@ -437,7 +439,6 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { return when (item.itemId) { R.id.remove_selected -> { removeSelected() - mode.finish() true } @@ -452,8 +453,8 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { } private fun getConfirmationText(): String { - if (selectedItems.size == 1) { - val mealLink = selectedItems.first() + 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" + @@ -463,14 +464,14 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { 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) + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } fun removeSelected() { - if (selectedItems.size > 0) + if (selectedItems.size() > 0) activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { - selectedItems.forEach { ml -> + selectedItems.forEach {key, ml -> ml.bolus?.let { bolus -> uel.log( Action.BOLUS_REMOVED, Sources.Treatments, @@ -496,7 +497,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { ) } } + removeActionMode?.finish() }) } + else + removeActionMode?.finish() } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt index b2230d9caa..d2069a504b 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt @@ -1,9 +1,11 @@ package info.nightscout.androidaps.activities.fragments import android.os.Bundle +import android.util.SparseArray import android.view.* import android.view.ActionMode import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -56,18 +58,17 @@ class TreatmentsCareportalFragment : DaggerFragment() { @Inject lateinit var repository: AppRepository @Inject lateinit var uel: UserEntryLogger - private val disposable = CompositeDisposable() + private var _binding: TreatmentsCareportalFragmentBinding? = null + // This property is only valid between onCreateView and onDestroyView. + private val binding get() = _binding!! + private val disposable = CompositeDisposable() private val millsToThePast = T.days(30).msecs() - private var selectedItems: MutableList = mutableListOf() + private var selectedItems: SparseArray = SparseArray() private var showInvalidated = false private var toolbar: Toolbar? = null private var removeActionMode: ActionMode? = null - private var _binding: TreatmentsCareportalFragmentBinding? = 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 = TreatmentsCareportalFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root @@ -172,13 +173,16 @@ class TreatmentsCareportalFragment : DaggerFragment() { holder.binding.note.text = therapyEvent.note holder.binding.type.text = translator.translate(therapyEvent.type) holder.binding.cbRemove.visibility = (therapyEvent.isValid && removeActionMode != null).toVisibility() - holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> - if (value) { - selectedItems.add(therapyEvent) - } else { - selectedItems.remove(therapyEvent) + 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()) } - 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 holder.binding.delimiter.visibility = dateUtil.isSameDay(therapyEvent.timestamp, nextTimestamp).toVisibility() @@ -243,8 +247,8 @@ class TreatmentsCareportalFragment : DaggerFragment() { override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems = mutableListOf() - mode.title = rh.gs(R.string.count_selected, selectedItems.size) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) binding.recyclerview.adapter?.notifyDataSetChanged() return true } @@ -255,7 +259,6 @@ class TreatmentsCareportalFragment : DaggerFragment() { return when (item.itemId) { R.id.remove_selected -> { removeSelected() - mode.finish() true } @@ -270,20 +273,20 @@ class TreatmentsCareportalFragment : DaggerFragment() { } private fun getConfirmationText(): String { - if (selectedItems.size == 1) { - val therapyEvent = selectedItems.first() + 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) + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } private fun removeSelected() { - if (selectedItems.size > 0) + if (selectedItems.size() > 0) activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { - selectedItems.forEach { therapyEvent -> + selectedItems.forEach { _, therapyEvent -> uel.log( Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note, ValueWithUnit.Timestamp(therapyEvent.timestamp), @@ -295,9 +298,11 @@ class TreatmentsCareportalFragment : DaggerFragment() { { aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) } ) } - + removeActionMode?.finish() }) } + else + removeActionMode?.finish() } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt index c4ac1aff5b..57e766dc54 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt @@ -2,9 +2,11 @@ package info.nightscout.androidaps.activities.fragments import android.annotation.SuppressLint import android.os.Bundle +import android.util.SparseArray import android.view.* import android.view.ActionMode import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -59,11 +61,10 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { @Inject lateinit var repository: AppRepository private var _binding: TreatmentsExtendedbolusFragmentBinding? = null - // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! - private var selectedItems: MutableList = mutableListOf() + private var selectedItems: SparseArray = SparseArray() private var showInvalidated = false private var removeActionMode: ActionMode? = null private var toolbar: Toolbar? = null @@ -149,13 +150,16 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { 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) holder.binding.cbRemove.visibility = (extendedBolus.isValid && removeActionMode != null).toVisibility() - holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> - if (value) { - selectedItems.add(extendedBolus) - } else { - selectedItems.remove(extendedBolus) + 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()) } - 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 holder.binding.delimiter.visibility = dateUtil.isSameDay(extendedBolus.timestamp, nextTimestamp).toVisibility() @@ -209,8 +213,8 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems = mutableListOf() - mode.title = rh.gs(R.string.count_selected, selectedItems.size) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) binding.recyclerview.adapter?.notifyDataSetChanged() return true } @@ -221,7 +225,6 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { return when (item.itemId) { R.id.remove_selected -> { removeSelected() - mode.finish() true } @@ -236,18 +239,19 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { } private fun getConfirmationText(): String { - if (selectedItems.size == 1) { + if (selectedItems.size() == 1) { + val bolus = selectedItems.valueAt(0) return rh.gs(R.string.extended_bolus) + "\n" + - "${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(selectedItems.first().timestamp)}" + "${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(bolus.timestamp)}" } - return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size) + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } private fun removeSelected() { - if (selectedItems.size > 0) + if (selectedItems.size() > 0) activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { - selectedItems.forEach { extendedBolus -> + selectedItems.forEach { _, extendedBolus -> uel.log( Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments, ValueWithUnit.Timestamp(extendedBolus.timestamp), @@ -260,7 +264,10 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { { aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") }, { aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) }) } + removeActionMode?.finish() }) } + else + removeActionMode?.finish() } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt index 38073e13ad..2922ead097 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt @@ -2,9 +2,11 @@ package info.nightscout.androidaps.activities.fragments import android.graphics.Paint import android.os.Bundle +import android.util.SparseArray import android.view.* import android.view.ActionMode import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -61,17 +63,16 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { @Inject lateinit var uel: UserEntryLogger 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 millsToThePast = T.days(30).msecs() - private var selectedItems: MutableList = mutableListOf() + private var selectedItems: SparseArray = 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 = TreatmentsProfileswitchFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root @@ -198,13 +199,16 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { holder.binding.invalid.visibility = profileSwitch.isValid.not().toVisibility() holder.binding.duration.visibility = (profileSwitch.duration != 0L && profileSwitch.duration != null).toVisibility() holder.binding.cbRemove.visibility = (removeActionMode != null && profileSwitch is ProfileSealed.PS).toVisibility() - holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> - if (value) { - selectedItems.add(profileSwitch) - } else { - selectedItems.remove(profileSwitch) + 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()) } - 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.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() @@ -314,8 +318,8 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems = mutableListOf() - mode.title = rh.gs(R.string.count_selected, selectedItems.size) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) binding.recyclerview.adapter?.notifyDataSetChanged() return true } @@ -326,7 +330,6 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { return when (item.itemId) { R.id.remove_selected -> { removeSelected() - mode.finish() true } @@ -341,18 +344,18 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { } private fun getConfirmationText(): String { - if (selectedItems.size == 1) { - val profileSwitch = selectedItems.first() + 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) + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } private fun removeSelected() { - if (selectedItems.size > 0) + if (selectedItems.size() > 0) activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { - selectedItems.forEach { profileSwitch -> + selectedItems.forEach { _, profileSwitch -> uel.log( Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName, ValueWithUnit.Timestamp(profileSwitch.timestamp) @@ -363,7 +366,10 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { { aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) } ) } + removeActionMode?.finish() }) } + else + removeActionMode?.finish() } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt index 846c1122e0..2144feb13e 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt @@ -2,8 +2,10 @@ package info.nightscout.androidaps.activities.fragments import android.annotation.SuppressLint import android.os.Bundle +import android.util.SparseArray import android.view.* import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -65,19 +67,17 @@ class TreatmentsTempTargetFragment : DaggerFragment() { @Inject lateinit var uel: UserEntryLogger @Inject lateinit var repository: AppRepository - private val disposable = CompositeDisposable() + private var _binding: TreatmentsTemptargetFragmentBinding? = null + // This property is only valid between onCreateView and onDestroyView. + private val binding get() = _binding!! + private val disposable = CompositeDisposable() private val millsToThePast = T.days(30).msecs() - private var selectedItems: MutableList = mutableListOf() + private var selectedItems: SparseArray = SparseArray() private var showInvalidated = false private var toolbar: Toolbar? = null private var removeActionMode: ActionMode? = null - private var _binding: TreatmentsTemptargetFragmentBinding? = 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 = TreatmentsTemptargetFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root @@ -174,13 +174,16 @@ class TreatmentsTempTargetFragment : DaggerFragment() { holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility() holder.binding.cbRemove.visibility = (tempTarget.isValid && removeActionMode != null).toVisibility() - holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> - if (value) { - selectedItems.add(tempTarget) - } else { - selectedItems.remove(tempTarget) + 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()) } - 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() @@ -211,10 +214,10 @@ class TreatmentsTempTargetFragment : DaggerFragment() { } private fun removeSelected() { - if (selectedItems.size > 0) + if (selectedItems.size() > 0) activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { - selectedItems.forEach { tempTarget -> + selectedItems.forEach { _, tempTarget -> uel.log( Action.TT_REMOVED, Sources.Treatments, ValueWithUnit.Timestamp(tempTarget.timestamp), @@ -228,8 +231,11 @@ class TreatmentsTempTargetFragment : DaggerFragment() { { 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) { @@ -277,8 +283,8 @@ class TreatmentsTempTargetFragment : DaggerFragment() { override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems = mutableListOf() - mode.title = rh.gs(R.string.count_selected, selectedItems.size) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) binding.recyclerview.adapter?.notifyDataSetChanged() return true } @@ -289,7 +295,6 @@ class TreatmentsTempTargetFragment : DaggerFragment() { return when (item.itemId) { R.id.remove_selected -> { removeSelected() - mode.finish() true } @@ -304,12 +309,12 @@ class TreatmentsTempTargetFragment : DaggerFragment() { } private fun getConfirmationText(): String { - if (selectedItems.size == 1) { - val tempTarget = selectedItems.first() + 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) + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt index b24f062004..babd7f64c4 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt @@ -1,8 +1,11 @@ package info.nightscout.androidaps.activities.fragments import android.os.Bundle +import android.util.Log +import android.util.SparseArray import android.view.* import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -62,16 +65,15 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { @Inject lateinit var repository: AppRepository 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 var selectedItems: MutableList = mutableListOf() + private var selectedItems: SparseArray = SparseArray() private var showInvalidated = false private var toolbar: Toolbar? = null private var removeActionMode: ActionMode? = 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 = TreatmentsTempbasalsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root @@ -192,13 +194,16 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { 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) holder.binding.cbRemove.visibility = (tempBasal.isValid && removeActionMode != null).toVisibility() - holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> - if (value) { - selectedItems.add(tempBasal) - } else { - selectedItems.remove(tempBasal) + 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()) } - 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 holder.binding.delimiter.visibility = dateUtil.isSameDay(tempBasal.timestamp, nextTimestamp).toVisibility() @@ -252,8 +257,8 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems = mutableListOf() - mode.title = rh.gs(R.string.count_selected, selectedItems.size) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) binding.recyclerview.adapter?.notifyDataSetChanged() return true } @@ -264,7 +269,6 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { return when (item.itemId) { R.id.remove_selected -> { removeSelected() - mode.finish() true } @@ -279,23 +283,22 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { } private fun getConfirmationText(): String { - if (selectedItems.size == 1) { - val tempBasal = selectedItems.first() + 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) + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } private fun removeSelected() { - // TODO check if item should not be delete val profile = profileFunction.getProfile(dateUtil.now()) == null - if (selectedItems.size > 0) + if (selectedItems.size() > 0) activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { - selectedItems.forEach { tempBasal -> + selectedItems.forEach {_, tempBasal -> var extendedBolus: ExtendedBolus? = null val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED if (isFakeExtended) { @@ -327,7 +330,10 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { { aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) }) } } + removeActionMode?.finish() }) } + else + removeActionMode?.finish() } }