From f4a65097e7688d52f2eb496fb2eb408d03b3d1d3 Mon Sep 17 00:00:00 2001 From: Andries Smit Date: Mon, 28 Mar 2022 17:48:02 +0200 Subject: [PATCH] chore: treatment use shared actionModeHelper --- .../fragments/TreatmentsBolusCarbsFragment.kt | 170 ++++++++--------- .../fragments/TreatmentsCareportalFragment.kt | 138 ++++++-------- .../TreatmentsExtendedBolusesFragment.kt | 143 +++++++-------- .../TreatmentsProfileSwitchFragment.kt | 127 ++++++------- .../fragments/TreatmentsTempTargetFragment.kt | 159 +++++++--------- .../TreatmentsTemporaryBasalsFragment.kt | 173 ++++++++---------- .../fragments/TreatmentsUserEntryFragment.kt | 2 - .../activities/QuickWizardListActivity.kt | 6 +- .../plugins/source/BGSourceFragment.kt | 6 +- app/src/main/res/drawable/ic_visibility.xml | 4 +- .../main/res/drawable/ic_visibility_off.xml | 9 + .../layout/treatments_bolus_carbs_item.xml | 24 +-- .../res/layout/treatments_careportal_item.xml | 3 +- .../layout/treatments_extendedbolus_item.xml | 3 +- .../layout/treatments_profileswitch_item.xml | 3 +- .../res/layout/treatments_tempbasals_item.xml | 3 +- .../res/layout/treatments_temptarget_item.xml | 3 +- .../res/menu/menu_treatments_carbs_bolus.xml | 13 +- .../res/menu/menu_treatments_careportal.xml | 13 +- .../menu/menu_treatments_extended_bolus.xml | 11 +- .../menu/menu_treatments_profile_switch.xml | 11 +- .../res/menu/menu_treatments_temp_basal.xml | 11 +- .../res/menu/menu_treatments_temp_target.xml | 12 +- .../res/menu/menu_treatments_user_entry.xml | 2 + app/src/main/res/values/strings.xml | 2 + .../general/automation/AutomationFragment.kt | 8 +- .../{ActionHelper.kt => ActionModeHelper.kt} | 2 +- 27 files changed, 482 insertions(+), 579 deletions(-) create mode 100644 app/src/main/res/drawable/ic_visibility_off.xml rename core/src/main/java/info/nightscout/androidaps/utils/{ActionHelper.kt => ActionModeHelper.kt} (95%) 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 c4a23113bf..be2743e40e 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 @@ -5,9 +5,6 @@ import android.graphics.Paint import android.os.Bundle 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 @@ -38,9 +35,11 @@ import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData +import info.nightscout.androidaps.utils.ActionModeHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.resources.ResourceHelper @@ -71,8 +70,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { @Inject lateinit var activePlugin: ActivePlugin private var _binding: TreatmentsBolusCarbsFragmentBinding? = null + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! + private var menu: Menu? = null class MealLink( val bolus: Bolus? = null, @@ -81,21 +82,22 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { ) private val disposable = CompositeDisposable() + private lateinit var actionHelper: ActionModeHelper private val millsToThePast = T.days(30).msecs() - private var selectedItems: SparseArray = SparseArray() + // 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 = TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root - @SuppressLint("CheckResult") + @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + actionHelper = ActionModeHelper(rh, activity) + actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() } + actionHelper.setOnRemoveHandler { removeSelected(it) } setHasOptionsMenu(true) - toolbar = activity?.findViewById(R.id.toolbar) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) } @@ -127,7 +129,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { fun swapAdapter() { val now = System.currentTimeMillis() - disposable += + disposable += if (showInvalidated) carbsMealLinksWithInvalid(now) .zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second } @@ -183,13 +185,13 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { @Synchronized override fun onPause() { super.onPause() + actionHelper.finish() disposable.clear() } @Synchronized override fun onDestroyView() { super.onDestroyView() - removeActionMode?.finish() binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -242,6 +244,17 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { Bolus.Type.NORMAL -> rh.gs(R.string.mealbolus) Bolus.Type.PRIMING -> rh.gs(R.string.prime) } + holder.binding.cbBolusRemove.visibility = (ml.bolus.isValid && actionHelper.isRemoving).toVisibility() + if (actionHelper.isRemoving) { + holder.binding.cbBolusRemove.setOnCheckedChangeListener { _, value -> + actionHelper.updateSelection(position, ml, value) + } + holder.binding.root.setOnClickListener { + holder.binding.cbBolusRemove.toggle() + actionHelper.updateSelection(position, ml, holder.binding.cbBolusRemove.isChecked) + } + holder.binding.cbBolusRemove.isChecked = actionHelper.isSelected(position) + } } // Carbs holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || showInvalidated)).toVisibility() @@ -252,23 +265,19 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { holder.binding.carbsNs.visibility = (carbs.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility() holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility() - } - holder.binding.cbBolusRemove.visibility = (ml.bolus?.isValid == true && removeActionMode != null).toVisibility() - holder.binding.cbCarbsRemove.visibility = (ml.carbs?.isValid == true && removeActionMode != null).toVisibility() - if (removeActionMode != null) { - val onChange = CompoundButton.OnCheckedChangeListener { _, value -> - if (value) { - selectedItems.put(position, ml) - } else { - selectedItems.remove(position) + holder.binding.cbCarbsRemove.visibility = (ml.carbs.isValid && actionHelper.isRemoving).toVisibility() + if (actionHelper.isRemoving) { + holder.binding.cbCarbsRemove.setOnCheckedChangeListener { _, value -> + actionHelper.updateSelection(position, ml, value) } - removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + holder.binding.root.setOnClickListener { + holder.binding.cbBolusRemove.toggle() + actionHelper.updateSelection(position, ml, holder.binding.cbBolusRemove.isChecked) + } + holder.binding.cbCarbsRemove.isChecked = actionHelper.isSelected(position) } - 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.isSameDayGroup(timestamp(ml), nextTimestamp).toVisibility() @@ -296,13 +305,18 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + this.menu = menu inflater.inflate(R.menu.menu_treatments_carbs_bolus, menu) super.onCreateOptionsMenu(menu, inflater) } + private fun updateMenuVisibility() { + menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + } + override fun onPrepareOptionsMenu(menu: Menu) { - menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated - menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + updateMenuVisibility() 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 @@ -313,19 +327,20 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { - R.id.nav_remove_items -> { - removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) - true - } + R.id.nav_remove_items -> actionHelper.startRemove() R.id.nav_show_invalidated -> { showInvalidated = true + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } R.id.nav_hide_invalidated -> { showInvalidated = false + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } @@ -422,36 +437,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { } } - inner class RemoveActionModeCallback : ActionMode.Callback { - - override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { - mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems.clear() - mode.title = rh.gs(R.string.count_selected, selectedItems.size()) - binding.recyclerview.adapter?.notifyDataSetChanged() - return true - } - - override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - return when (item.itemId) { - R.id.remove_selected -> { - removeSelected() - true - } - - else -> false - } - } - - override fun onDestroyActionMode(mode: ActionMode?) { - removeActionMode = null - binding.recyclerview.adapter?.notifyDataSetChanged() - } - } - - private fun getConfirmationText(): String { + private fun getConfirmationText(selectedItems: SparseArray): String { if (selectedItems.size() == 1) { val mealLink = selectedItems.valueAt(0) val bolus = mealLink.bolus @@ -466,40 +452,38 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { 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) + private fun removeSelected(selectedItems: SparseArray) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable { + selectedItems.forEach { _, ml -> + ml.bolus?.let { bolus -> + uel.log( + Action.BOLUS_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(bolus.timestamp), + ValueWithUnit.Insulin(bolus.amount) + ) + 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.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) } - ) - } - ml.carbs?.let { carb -> - uel.log( - Action.CARBS_REMOVED, Sources.Treatments, - ValueWithUnit.Timestamp(carb.timestamp), - ValueWithUnit.Gram(carb.amount.toInt()) - ) - 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) } - ) - } } - removeActionMode?.finish() - }) - } - else - removeActionMode?.finish() + ml.carbs?.let { carb -> + uel.log( + Action.CARBS_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(carb.timestamp), + ValueWithUnit.Gram(carb.amount.toInt()) + ) + 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) } + ) + } + } + actionHelper.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 0f7fb29725..32732b7ea9 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 @@ -3,39 +3,34 @@ 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 import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InvalidateAAPSStartedTherapyEventTransaction import info.nightscout.androidaps.database.transactions.InvalidateTherapyEventTransaction import info.nightscout.androidaps.databinding.TreatmentsCareportalFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsCareportalItemBinding import info.nightscout.androidaps.events.EventTherapyEventChange -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag +import info.nightscout.androidaps.events.EventTreatmentUpdateGui +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.logging.UserEntryLogger 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.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.T -import info.nightscout.androidaps.utils.Translator +import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.disposables.CompositeDisposable @@ -59,23 +54,23 @@ class TreatmentsCareportalFragment : DaggerFragment() { @Inject lateinit var uel: UserEntryLogger private var _binding: TreatmentsCareportalFragmentBinding? = null + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! - + private var menu: Menu? = null private val disposable = CompositeDisposable() private val millsToThePast = T.days(30).msecs() - private var selectedItems: SparseArray = SparseArray() + private lateinit var actionHelper: ActionModeHelper private var showInvalidated = false - private var toolbar: Toolbar? = null - private var removeActionMode: ActionMode? = null - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsCareportalFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - toolbar = activity?.findViewById(R.id.toolbar) + actionHelper = ActionModeHelper(rh, activity) + actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() } + actionHelper.setOnRemoveHandler { removeSelected(it) } setHasOptionsMenu(true) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) @@ -143,13 +138,13 @@ class TreatmentsCareportalFragment : DaggerFragment() { @Synchronized override fun onPause() { super.onPause() + actionHelper.finish() disposable.clear() } @Synchronized override fun onDestroyView() { super.onDestroyView() - removeActionMode?.finish() binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -172,18 +167,15 @@ 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() - if (removeActionMode != null) { - holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> - if (value) { - selectedItems.put(position, therapyEvent) - } else { - selectedItems.remove(position) - } - removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) - } - holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + holder.binding.cbRemove.visibility = (therapyEvent.isValid && actionHelper.isRemoving).toVisibility() + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + actionHelper.updateSelection(position, therapyEvent, value) } + holder.binding.root.setOnClickListener { + holder.binding.cbRemove.toggle() + actionHelper.updateSelection(position, therapyEvent, holder.binding.cbRemove.isChecked) + } + holder.binding.cbRemove.isChecked = actionHelper.isSelected(position) val nextTimestamp = if (therapyList.size != position + 1) therapyList[position + 1].timestamp else 0L holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(therapyEvent.timestamp, nextTimestamp).toVisibility() } @@ -198,34 +190,40 @@ class TreatmentsCareportalFragment : DaggerFragment() { } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + this.menu = menu 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 + updateMenuVisibility() 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) } + private fun updateMenuVisibility() { + menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + } + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { - R.id.nav_remove_items -> { - removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) - true - } + R.id.nav_remove_items -> actionHelper.startRemove() R.id.nav_show_invalidated -> { showInvalidated = true + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } R.id.nav_hide_invalidated -> { showInvalidated = false + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } @@ -243,36 +241,7 @@ class TreatmentsCareportalFragment : DaggerFragment() { else -> false } - inner class RemoveActionModeCallback : ActionMode.Callback { - - override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { - mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems.clear() - mode.title = rh.gs(R.string.count_selected, selectedItems.size()) - binding.recyclerview.adapter?.notifyDataSetChanged() - return true - } - - override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - return when (item.itemId) { - R.id.remove_selected -> { - removeSelected() - true - } - - else -> false - } - } - - override fun onDestroyActionMode(mode: ActionMode?) { - removeActionMode = null - binding.recyclerview.adapter?.notifyDataSetChanged() - } - } - - private fun getConfirmationText(): String { + private fun getConfirmationText(selectedItems: SparseArray): String { if (selectedItems.size() == 1) { val therapyEvent = selectedItems.valueAt(0) return rh.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type) + "\n" + @@ -282,27 +251,24 @@ class TreatmentsCareportalFragment : DaggerFragment() { 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) + private fun removeSelected(selectedItems: SparseArray) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), 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) } ) - disposable += repository.runTransactionForResult(InvalidateTherapyEventTransaction(therapyEvent.id)) - .subscribe( - { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) } - ) - } - removeActionMode?.finish() - }) - } - else - removeActionMode?.finish() + } + actionHelper.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 a9ccda548c..a8fe54b1a6 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,15 +1,15 @@ 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 import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.fragments.TreatmentsExtendedBolusesFragment.RecyclerViewAdapter.ExtendedBolusesViewHolder import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.ExtendedBolus import info.nightscout.androidaps.database.entities.UserEntry.Action @@ -20,25 +20,26 @@ import info.nightscout.androidaps.database.transactions.InvalidateExtendedBolusT import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusItemBinding import info.nightscout.androidaps.events.EventExtendedBolusChange +import info.nightscout.androidaps.events.EventTreatmentUpdateGui import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.isInProgress 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.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.ActionModeHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.ToastUtils 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.AAPSLogger +import info.nightscout.shared.logging.LTag import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign -import info.nightscout.shared.logging.LTag import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -60,20 +61,23 @@ 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: SparseArray = SparseArray() + private var menu: Menu? = null + private lateinit var actionHelper: ActionModeHelper private var showInvalidated = false - private var removeActionMode: ActionMode? = null - private var toolbar: Toolbar? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root + @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + actionHelper = ActionModeHelper(rh, activity) + actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() } + actionHelper.setOnRemoveHandler { removeSelected(it) } setHasOptionsMenu(true) - toolbar = activity?.findViewById(R.id.toolbar) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) } @@ -96,24 +100,28 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { override fun onResume() { super.onResume() swapAdapter() - disposable += rxBus .toObservable(EventExtendedBolusChange::class.java) .observeOn(aapsSchedulers.io) .debounce(1L, TimeUnit.SECONDS) .subscribe({ swapAdapter() }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above + .observeOn(aapsSchedulers.io) + .debounce(1L, TimeUnit.SECONDS) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) } @Synchronized override fun onPause() { super.onPause() + actionHelper.finish() disposable.clear() } @Synchronized override fun onDestroyView() { super.onDestroyView() - removeActionMode?.finish() binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -147,17 +155,16 @@ 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() - if (removeActionMode != null) { + holder.binding.cbRemove.visibility = (extendedBolus.isValid && actionHelper.isRemoving).toVisibility() + if (actionHelper.isRemoving) { 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()) + actionHelper.updateSelection(position, extendedBolus, value) } - holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + holder.binding.root.setOnClickListener { + holder.binding.cbRemove.toggle() + actionHelper.updateSelection(position, extendedBolus, holder.binding.cbRemove.isChecked) + } + holder.binding.cbRemove.isChecked = actionHelper.isSelected(position) } val nextTimestamp = if (extendedBolusList.size != position + 1) extendedBolusList[position + 1].timestamp else 0L holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(extendedBolus.timestamp, nextTimestamp).toVisibility() @@ -173,32 +180,37 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + this.menu = menu 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 + private fun updateMenuVisibility() { + menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + } + override fun onPrepareOptionsMenu(menu: Menu) { + updateMenuVisibility() 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_remove_items -> actionHelper.startRemove() R.id.nav_show_invalidated -> { showInvalidated = true + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } R.id.nav_hide_invalidated -> { showInvalidated = false + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } @@ -207,36 +219,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { } } - inner class RemoveActionModeCallback : ActionMode.Callback { - - override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { - mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems.clear() - mode.title = rh.gs(R.string.count_selected, selectedItems.size()) - binding.recyclerview.adapter?.notifyDataSetChanged() - return true - } - - override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - return when (item.itemId) { - R.id.remove_selected -> { - removeSelected() - true - } - - else -> false - } - } - - override fun onDestroyActionMode(mode: ActionMode?) { - removeActionMode = null - binding.recyclerview.adapter?.notifyDataSetChanged() - } - } - - private fun getConfirmationText(): String { + private fun getConfirmationText(selectedItems: SparseArray): String { if (selectedItems.size() == 1) { val bolus = selectedItems.valueAt(0) return rh.gs(R.string.extended_bolus) + "\n" + @@ -245,27 +228,25 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } - private fun removeSelected() { - if (selectedItems.size() > 0) - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { - selectedItems.forEach { _, extendedBolus -> - uel.log( - Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments, - ValueWithUnit.Timestamp(extendedBolus.timestamp), - ValueWithUnit.Insulin(extendedBolus.amount), - ValueWithUnit.UnitPerHour(extendedBolus.rate), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt()) - ) - disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id)) - .subscribe( - { aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) }) - } - removeActionMode?.finish() - }) - } - else - removeActionMode?.finish() + private fun removeSelected(selectedItems: SparseArray) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), 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) }) + } + actionHelper.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 7fa2063e64..8b5cf3fa3d 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 @@ -1,11 +1,10 @@ package info.nightscout.androidaps.activities.fragments +import android.annotation.SuppressLint 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 @@ -32,9 +31,11 @@ import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientR import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged +import info.nightscout.androidaps.utils.ActionModeHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.resources.ResourceHelper @@ -46,6 +47,7 @@ import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.subscribeBy +import java.util.concurrent.TimeUnit import javax.inject.Inject class TreatmentsProfileSwitchFragment : DaggerFragment() { @@ -63,24 +65,25 @@ 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 var menu: Menu? = null + private lateinit var actionHelper: ActionModeHelper private val disposable = CompositeDisposable() private val millsToThePast = T.days(30).msecs() - 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 = TreatmentsProfileswitchFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root + @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + actionHelper = ActionModeHelper(rh, activity) + actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() } + actionHelper.setOnRemoveHandler { removeSelected(it) } setHasOptionsMenu(true) - toolbar = activity?.findViewById(R.id.toolbar) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) } @@ -159,18 +162,23 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { .toObservable(EventEffectiveProfileSwitchChanged::class.java) .observeOn(aapsSchedulers.main) .subscribe({ swapAdapter() }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above + .observeOn(aapsSchedulers.io) + .debounce(1L, TimeUnit.SECONDS) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) } @Synchronized override fun onPause() { super.onPause() + actionHelper.finish() disposable.clear() } @Synchronized override fun onDestroyView() { super.onDestroyView() - removeActionMode?.finish() binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -198,17 +206,16 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { 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.cbRemove.visibility = (removeActionMode != null && profileSwitch is ProfileSealed.PS).toVisibility() - if (removeActionMode != null) { + holder.binding.cbRemove.visibility = (actionHelper.isRemoving && profileSwitch is ProfileSealed.PS).toVisibility() + if (actionHelper.isRemoving) { 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()) + actionHelper.updateSelection(position, profileSwitch, value) } - holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + holder.binding.root.setOnClickListener { + holder.binding.cbRemove.toggle() + actionHelper.updateSelection(position, profileSwitch, holder.binding.cbRemove.isChecked) + } + holder.binding.cbRemove.isChecked = actionHelper.isSelected(position) } holder.binding.clone.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() @@ -273,13 +280,18 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + this.menu = menu inflater.inflate(R.menu.menu_treatments_profile_switch, menu) super.onCreateOptionsMenu(menu, inflater) } + private fun updateMenuVisibility() { + menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + } + override fun onPrepareOptionsMenu(menu: Menu) { - menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated - menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + updateMenuVisibility() val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode() menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly @@ -288,19 +300,20 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { - R.id.nav_remove_items -> { - removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) - true - } + R.id.nav_remove_items -> actionHelper.startRemove() R.id.nav_show_invalidated -> { showInvalidated = true + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } R.id.nav_hide_invalidated -> { showInvalidated = false + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } @@ -313,36 +326,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { else -> false } - inner class RemoveActionModeCallback : ActionMode.Callback { - - override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { - mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems.clear() - mode.title = rh.gs(R.string.count_selected, selectedItems.size()) - binding.recyclerview.adapter?.notifyDataSetChanged() - return true - } - - override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - return when (item.itemId) { - R.id.remove_selected -> { - removeSelected() - true - } - - else -> false - } - } - - override fun onDestroyActionMode(mode: ActionMode?) { - removeActionMode = null - binding.recyclerview.adapter?.notifyDataSetChanged() - } - } - - private fun getConfirmationText(): String { + private fun getConfirmationText(selectedItems: SparseArray): String { if (selectedItems.size() == 1) { val profileSwitch = selectedItems.valueAt(0) return rh.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName + "\n" + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.timestamp) @@ -350,25 +334,22 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { 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) + private fun removeSelected(selectedItems: SparseArray) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), 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) } ) - disposable += repository.runTransactionForResult(InvalidateProfileSwitchTransaction(profileSwitch.id)) - .subscribe( - { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") } }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) } - ) - } - removeActionMode?.finish() - }) - } - else - removeActionMode?.finish() + } + actionHelper.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 f022fae27d..22d66c1bde 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 @@ -4,46 +4,42 @@ 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 import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper -import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.transactions.InvalidateTemporaryTargetTransaction import info.nightscout.androidaps.databinding.TreatmentsTemptargetFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsTemptargetItemBinding -import info.nightscout.androidaps.events.EventTempTargetChange -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.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 -import info.nightscout.androidaps.utils.Translator -import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.buildHelper.BuildHelper +import info.nightscout.androidaps.events.EventTempTargetChange +import info.nightscout.androidaps.events.EventTreatmentUpdateGui 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.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData +import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.disposables.CompositeDisposable @@ -68,22 +64,24 @@ class TreatmentsTempTargetFragment : DaggerFragment() { @Inject lateinit var repository: AppRepository private var _binding: TreatmentsTemptargetFragmentBinding? = null + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! - + private var menu: Menu? = null + private lateinit var actionHelper: ActionModeHelper private val disposable = CompositeDisposable() private val millsToThePast = T.days(30).msecs() - private var selectedItems: SparseArray = SparseArray() private var showInvalidated = false - private var toolbar: Toolbar? = null - private var removeActionMode: ActionMode? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsTemptargetFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root + @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding.recyclerview.setHasFixedSize(true) - toolbar = activity?.findViewById(R.id.toolbar) + actionHelper = ActionModeHelper(rh, activity) + actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() } + actionHelper.setOnRemoveHandler { removeSelected(it) } setHasOptionsMenu(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) } @@ -131,13 +129,11 @@ class TreatmentsTempTargetFragment : DaggerFragment() { override fun onResume() { super.onResume() swapAdapter() - disposable += rxBus .toObservable(EventTempTargetChange::class.java) .observeOn(aapsSchedulers.io) .debounce(1L, TimeUnit.SECONDS) .subscribe({ swapAdapter() }, fabricPrivacy::logException) - disposable += rxBus .toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above .observeOn(aapsSchedulers.io) @@ -148,13 +144,13 @@ class TreatmentsTempTargetFragment : DaggerFragment() { @Synchronized override fun onPause() { super.onPause() + actionHelper.finish() disposable.clear() } @Synchronized override fun onDestroyView() { super.onDestroyView() - removeActionMode?.finish() binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -173,17 +169,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.cbRemove.visibility = (tempTarget.isValid && removeActionMode != null).toVisibility() - if (removeActionMode != null) { + holder.binding.cbRemove.visibility = (tempTarget.isValid && actionHelper.isRemoving).toVisibility() + if (actionHelper.isRemoving) { 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()) + actionHelper.updateSelection(position, tempTarget, value) } - holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + holder.binding.root.setOnClickListener { + holder.binding.cbRemove.toggle() + actionHelper.updateSelection(position, tempTarget, holder.binding.cbRemove.isChecked) + } + holder.binding.cbRemove.isChecked = actionHelper.isSelected(position) } val newDay = position == 0 || !dateUtil.isSameDayGroup(tempTarget.timestamp, tempTargetList[position - 1].timestamp) holder.binding.date.visibility = newDay.toVisibility() @@ -213,39 +208,19 @@ class TreatmentsTempTargetFragment : DaggerFragment() { } } - private fun removeSelected() { - if (selectedItems.size() > 0) - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { - selectedItems.forEach { _, tempTarget -> - uel.log( - Action.TT_REMOVED, Sources.Treatments, - ValueWithUnit.Timestamp(tempTarget.timestamp), - ValueWithUnit.TherapyEventTTReason(tempTarget.reason), - ValueWithUnit.Mgdl(tempTarget.lowTarget), - ValueWithUnit.Mgdl(tempTarget.highTarget).takeIf { tempTarget.lowTarget != tempTarget.highTarget }, - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tempTarget.duration).toInt()) - ) - disposable += repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(tempTarget.id)) - .subscribe( - { aapsLogger.debug(LTag.DATABASE, "Removed temp target $tempTarget") }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) }) - } - removeActionMode?.finish() - }) - } - else - removeActionMode?.finish() - } - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + this.menu = menu inflater.inflate(R.menu.menu_treatments_temp_target, menu) super.onCreateOptionsMenu(menu, inflater) } + private fun updateMenuVisibility() { + menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + } + override fun onPrepareOptionsMenu(menu: Menu) { - menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated - menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + updateMenuVisibility() val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode() menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly @@ -254,19 +229,20 @@ class TreatmentsTempTargetFragment : DaggerFragment() { override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { - R.id.nav_remove_items -> { - removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) - true - } + R.id.nav_remove_items -> actionHelper.startRemove() R.id.nav_show_invalidated -> { showInvalidated = true + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } R.id.nav_hide_invalidated -> { showInvalidated = false + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } @@ -279,36 +255,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() { else -> false } - inner class RemoveActionModeCallback : ActionMode.Callback { - - override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { - mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems.clear() - mode.title = rh.gs(R.string.count_selected, selectedItems.size()) - binding.recyclerview.adapter?.notifyDataSetChanged() - return true - } - - override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - return when (item.itemId) { - R.id.remove_selected -> { - removeSelected() - true - } - - else -> false - } - } - - override fun onDestroyActionMode(mode: ActionMode?) { - removeActionMode = null - binding.recyclerview.adapter?.notifyDataSetChanged() - } - } - - private fun getConfirmationText(): String { + private fun getConfirmationText(selectedItems: SparseArray): String { if (selectedItems.size() == 1) { val tempTarget = selectedItems.valueAt(0) return "${rh.gs(R.string.careportal_temporarytarget)}: ${tempTarget.friendlyDescription(profileFunction.getUnits(), rh)}\n" + @@ -317,4 +264,26 @@ class TreatmentsTempTargetFragment : DaggerFragment() { return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } + private fun removeSelected(selectedItems: SparseArray) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), 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) }) + } + actionHelper.finish() + }) + } + } + } 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 b1ac871b5a..fc8b44943a 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,14 +1,15 @@ 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 import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper @@ -24,24 +25,25 @@ import info.nightscout.androidaps.databinding.TreatmentsTempbasalsFragmentBindin import info.nightscout.androidaps.databinding.TreatmentsTempbasalsItemBinding import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventTempBasalChange +import info.nightscout.androidaps.events.EventTreatmentUpdateGui import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.extensions.toTemporaryBasal 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.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder -import info.nightscout.androidaps.events.EventTreatmentUpdateGui +import info.nightscout.androidaps.utils.ActionModeHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.ToastUtils 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.AAPSLogger +import info.nightscout.shared.logging.LTag import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import java.util.concurrent.TimeUnit @@ -64,21 +66,23 @@ 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 var menu: Menu? = null + private lateinit var actionHelper: ActionModeHelper private val millsToThePast = T.days(30).msecs() - private var selectedItems: SparseArray = SparseArray() private var showInvalidated = false - private var toolbar: Toolbar? = null - private var removeActionMode: ActionMode? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsTempbasalsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root + @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - toolbar = activity?.findViewById(R.id.toolbar) + actionHelper = ActionModeHelper(rh, activity) + actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() } + actionHelper.setOnRemoveHandler { removeSelected(it) } setHasOptionsMenu(true) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) @@ -133,28 +137,31 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { override fun onResume() { super.onResume() swapAdapter() - disposable += rxBus .toObservable(EventTempBasalChange::class.java) .observeOn(aapsSchedulers.main) .subscribe({ swapAdapter() }, fabricPrivacy::logException) - disposable += rxBus .toObservable(EventAutosensCalculationFinished::class.java) .observeOn(aapsSchedulers.main) .subscribe({ swapAdapter() }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above + .observeOn(aapsSchedulers.io) + .debounce(1L, TimeUnit.SECONDS) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) } @Synchronized override fun onPause() { super.onPause() + actionHelper.finish() disposable.clear() } @Synchronized override fun onDestroyView() { super.onDestroyView() - removeActionMode?.finish() binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -194,17 +201,16 @@ 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() - if (removeActionMode != null) { + holder.binding.cbRemove.visibility = (tempBasal.isValid && actionHelper.isRemoving).toVisibility() + if (actionHelper.isRemoving) { 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()) + actionHelper.updateSelection(position, tempBasal, value) } - holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + holder.binding.root.setOnClickListener { + holder.binding.cbRemove.toggle() + actionHelper.updateSelection(position, tempBasal, holder.binding.cbRemove.isChecked) + } + holder.binding.cbRemove.isChecked = actionHelper.isSelected(position) } val nextTimestamp = if (tempBasalList.size != position + 1) tempBasalList[position + 1].timestamp else 0L holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(tempBasal.timestamp, nextTimestamp).toVisibility() @@ -221,32 +227,38 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + this.menu = menu inflater.inflate(R.menu.menu_treatments_temp_basal, menu) super.onCreateOptionsMenu(menu, inflater) } + private fun updateMenuVisibility() { + menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + } + override fun onPrepareOptionsMenu(menu: Menu) { - menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated - menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + updateMenuVisibility() return super.onPrepareOptionsMenu(menu) } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { - R.id.nav_remove_items -> { - removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) - true - } + R.id.nav_remove_items -> actionHelper.startRemove() R.id.nav_show_invalidated -> { showInvalidated = true + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } R.id.nav_hide_invalidated -> { showInvalidated = false + updateMenuVisibility() + ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) rxBus.send(EventTreatmentUpdateGui()) true } @@ -254,36 +266,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { else -> false } - inner class RemoveActionModeCallback : ActionMode.Callback { - - override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { - mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) - selectedItems.clear() - mode.title = rh.gs(R.string.count_selected, selectedItems.size()) - binding.recyclerview.adapter?.notifyDataSetChanged() - return true - } - - override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - return when (item.itemId) { - R.id.remove_selected -> { - removeSelected() - true - } - - else -> false - } - } - - override fun onDestroyActionMode(mode: ActionMode?) { - removeActionMode = null - binding.recyclerview.adapter?.notifyDataSetChanged() - } - } - - private fun getConfirmationText(): String { + private fun getConfirmationText(selectedItems: SparseArray): String { if (selectedItems.size() == 1) { val tempBasal = selectedItems.valueAt(0) val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED @@ -295,46 +278,44 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } - private fun removeSelected() { + private fun removeSelected(selectedItems: SparseArray) { 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) }) - } + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), 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 } - removeActionMode?.finish() - }) - } - else - removeActionMode?.finish() + 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) }) + } + } + actionHelper.finish() + }) + } } } 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 71cb5c1084..56b6c14ef7 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 @@ -47,7 +47,6 @@ class TreatmentsUserEntryFragment : DaggerFragment() { @Inject lateinit var userEntryPresentationHelper: UserEntryPresentationHelper private val disposable = CompositeDisposable() - private val millsToThePastFiltered = T.days(30).msecs() private val millsToThePastUnFiltered = T.days(3).msecs() private var showLoop = false @@ -94,7 +93,6 @@ class TreatmentsUserEntryFragment : DaggerFragment() { override fun onResume() { super.onResume() swapAdapter() - disposable += rxBus .toObservable(EventPreferenceChange::class.java) .observeOn(aapsSchedulers.io) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt index a932e65685..128f6c6613 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt @@ -20,7 +20,7 @@ import info.nightscout.androidaps.utils.dragHelpers.OnStartDragListener import info.nightscout.androidaps.utils.dragHelpers.SimpleItemTouchHelperCallback import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog import info.nightscout.androidaps.plugins.general.overview.events.EventQuickWizardChange -import info.nightscout.androidaps.utils.ActionHelper +import info.nightscout.androidaps.utils.ActionModeHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.alertDialogs.OKDialog @@ -42,7 +42,7 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult(), OnStartDrag @Inject lateinit var sp: SP private var disposable: CompositeDisposable = CompositeDisposable() - private lateinit var actionHelper: ActionHelper + private lateinit var actionHelper: ActionModeHelper private val itemTouchHelper = ItemTouchHelper(SimpleItemTouchHelperCallback()) private lateinit var binding: OverviewQuickwizardlistActivityBinding @@ -121,7 +121,7 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult(), OnStartDrag binding = OverviewQuickwizardlistActivityBinding.inflate(layoutInflater) setContentView(binding.root) - actionHelper = ActionHelper(rh, this) + actionHelper = ActionModeHelper(rh, this) actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() } actionHelper.setOnRemoveHandler { removeSelected(it) } actionHelper.enableSort = true diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt index f3e06897c4..c4249955ed 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt @@ -25,7 +25,7 @@ import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus -import info.nightscout.androidaps.utils.ActionHelper +import info.nightscout.androidaps.utils.ActionModeHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T @@ -54,7 +54,7 @@ class BGSourceFragment : DaggerFragment() { private val disposable = CompositeDisposable() private val millsToThePast = T.hours(36).msecs() - private lateinit var actionHelper: ActionHelper + private lateinit var actionHelper: ActionModeHelper private var _binding: BgsourceFragmentBinding? = null // This property is only valid between onCreateView and onDestroyView. @@ -65,7 +65,7 @@ class BGSourceFragment : DaggerFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - actionHelper = ActionHelper(rh, activity) + actionHelper = ActionModeHelper(rh, activity) actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() } actionHelper.setOnRemoveHandler { removeSelected(it) } setHasOptionsMenu(actionHelper.inMenu) diff --git a/app/src/main/res/drawable/ic_visibility.xml b/app/src/main/res/drawable/ic_visibility.xml index de67f3ee84..76c9b61954 100644 --- a/app/src/main/res/drawable/ic_visibility.xml +++ b/app/src/main/res/drawable/ic_visibility.xml @@ -5,8 +5,8 @@ android:viewportHeight="24"> + android:fillColor="?attr/colorControlNormal"/> + android:fillColor="?attr/colorControlNormal"/> diff --git a/app/src/main/res/drawable/ic_visibility_off.xml b/app/src/main/res/drawable/ic_visibility_off.xml new file mode 100644 index 0000000000..00ecc3cdb3 --- /dev/null +++ b/app/src/main/res/drawable/ic_visibility_off.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/treatments_bolus_carbs_item.xml b/app/src/main/res/layout/treatments_bolus_carbs_item.xml index 137c5f2b77..0ce268ef9b 100644 --- a/app/src/main/res/layout/treatments_bolus_carbs_item.xml +++ b/app/src/main/res/layout/treatments_bolus_carbs_item.xml @@ -164,8 +164,9 @@ - - + android:id="@+id/cb_carbs_remove" + android:layout_width="wrap_content" + android:layout_height="19dp" + android:contentDescription="@string/select_for_removal" + android:minWidth="0dp" + android:visibility="gone" /> diff --git a/app/src/main/res/layout/treatments_extendedbolus_item.xml b/app/src/main/res/layout/treatments_extendedbolus_item.xml index ed771d1cca..ffc0d10fd5 100644 --- a/app/src/main/res/layout/treatments_extendedbolus_item.xml +++ b/app/src/main/res/layout/treatments_extendedbolus_item.xml @@ -156,8 +156,9 @@ diff --git a/app/src/main/res/layout/treatments_tempbasals_item.xml b/app/src/main/res/layout/treatments_tempbasals_item.xml index cbbb4d57ab..9788110bb7 100644 --- a/app/src/main/res/layout/treatments_tempbasals_item.xml +++ b/app/src/main/res/layout/treatments_tempbasals_item.xml @@ -175,8 +175,9 @@ diff --git a/app/src/main/res/menu/menu_treatments_carbs_bolus.xml b/app/src/main/res/menu/menu_treatments_carbs_bolus.xml index c45d9562d3..c31f9e5295 100644 --- a/app/src/main/res/menu/menu_treatments_carbs_bolus.xml +++ b/app/src/main/res/menu/menu_treatments_carbs_bolus.xml @@ -3,20 +3,27 @@ + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> + + + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> + + + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/menu/menu_treatments_profile_switch.xml b/app/src/main/res/menu/menu_treatments_profile_switch.xml index 2822a53819..8623848e54 100644 --- a/app/src/main/res/menu/menu_treatments_profile_switch.xml +++ b/app/src/main/res/menu/menu_treatments_profile_switch.xml @@ -3,15 +3,20 @@ + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/menu/menu_treatments_temp_basal.xml b/app/src/main/res/menu/menu_treatments_temp_basal.xml index 2822a53819..8623848e54 100644 --- a/app/src/main/res/menu/menu_treatments_temp_basal.xml +++ b/app/src/main/res/menu/menu_treatments_temp_basal.xml @@ -3,15 +3,20 @@ + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/menu/menu_treatments_temp_target.xml b/app/src/main/res/menu/menu_treatments_temp_target.xml index 751c678f12..b861d41178 100644 --- a/app/src/main/res/menu/menu_treatments_temp_target.xml +++ b/app/src/main/res/menu/menu_treatments_temp_target.xml @@ -3,16 +3,22 @@ + app:showAsAction="ifRoom" /> + + app:showAsAction="ifRoom" /> + + android:icon="@drawable/ic_visibility_off" + app:showAsAction="ifRoom" /> + + + Unknown action command: Percentage Application default + Show invalidated / removed records + Hide invalidated / removed records Select profile to edit Refresh from Nightscout Remove selected items diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt index 194a85f228..15d72f6682 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt @@ -3,8 +3,6 @@ package info.nightscout.androidaps.plugins.general.automation import android.annotation.SuppressLint import android.content.Context import android.os.Bundle -import android.os.Handler -import android.os.Looper import android.text.method.ScrollingMovementMethod import android.util.SparseArray import android.view.* @@ -29,7 +27,7 @@ import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDi import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector -import info.nightscout.androidaps.utils.ActionHelper +import info.nightscout.androidaps.utils.ActionModeHelper import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.alertDialogs.OKDialog @@ -54,7 +52,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { private var disposable: CompositeDisposable = CompositeDisposable() private lateinit var eventListAdapter: EventListAdapter - private lateinit var actionHelper: ActionHelper + private lateinit var actionHelper: ActionModeHelper private val itemTouchHelper = ItemTouchHelper(SimpleItemTouchHelperCallback()) private var _binding: AutomationFragmentBinding? = null @@ -63,7 +61,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = AutomationFragmentBinding.inflate(inflater, container, false) - actionHelper = ActionHelper(rh, activity) + actionHelper = ActionModeHelper(rh, activity) actionHelper.setUpdateListHandler { binding.eventListView.adapter?.notifyDataSetChanged() } actionHelper.setOnRemoveHandler { removeSelected(it) } actionHelper.enableSort = true diff --git a/core/src/main/java/info/nightscout/androidaps/utils/ActionHelper.kt b/core/src/main/java/info/nightscout/androidaps/utils/ActionModeHelper.kt similarity index 95% rename from core/src/main/java/info/nightscout/androidaps/utils/ActionHelper.kt rename to core/src/main/java/info/nightscout/androidaps/utils/ActionModeHelper.kt index 9fbe17692e..5a928bc8eb 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/ActionHelper.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/ActionModeHelper.kt @@ -10,7 +10,7 @@ import info.nightscout.androidaps.core.R import info.nightscout.androidaps.utils.resources.ResourceHelper -class ActionHelper(val rh: ResourceHelper, val activity: FragmentActivity?) { +class ActionModeHelper(val rh: ResourceHelper, val activity: FragmentActivity?) { var enableSort = false private var selectedItems: SparseArray = SparseArray()