diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.kt b/app/src/main/java/info/nightscout/androidaps/MainApp.kt index 781ab707c3..ef74b5c6f2 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.kt @@ -6,6 +6,7 @@ import android.content.IntentFilter import android.net.ConnectivityManager import android.net.wifi.WifiManager import android.os.Build +import androidx.lifecycle.ProcessLifecycleOwner import com.uber.rxdogtag.RxDogTag import dagger.android.AndroidInjector import dagger.android.DaggerApplication @@ -35,6 +36,7 @@ import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver import info.nightscout.androidaps.services.AlarmSoundServiceHelper import info.nightscout.androidaps.utils.ActivityMonitor import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.ProcessLifecycleListener import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.locale.LocaleHelper import info.nightscout.androidaps.utils.protection.PasswordCheck @@ -70,6 +72,7 @@ class MainApp : DaggerApplication() { @Inject lateinit var passwordCheck: PasswordCheck @Inject lateinit var alarmSoundServiceHelper: AlarmSoundServiceHelper @Inject lateinit var notificationStore: NotificationStore + @Inject lateinit var processLifecycleListener: ProcessLifecycleListener override fun onCreate() { super.onCreate() @@ -77,6 +80,7 @@ class MainApp : DaggerApplication() { RxDogTag.install() setRxErrorHandler() LocaleHelper.update(this) + ProcessLifecycleOwner.get().lifecycle.addObserver(processLifecycleListener) var gitRemote: String? = BuildConfig.REMOTE var commitHash: String? = BuildConfig.HEAD diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt index 60835562dd..6c69d5eae5 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt @@ -27,6 +27,7 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProv import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign @@ -50,6 +51,7 @@ class CarbsDialog : DialogFragmentWithDate() { @Inject lateinit var bolusTimer: BolusTimer @Inject lateinit var commandQueue: CommandQueue @Inject lateinit var repository: AppRepository + @Inject lateinit var protectionCheck: ProtectionCheck companion object { @@ -377,4 +379,17 @@ class CarbsDialog : DialogFragmentWithDate() { } return true } -} \ No newline at end of file + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt index 7205a36939..7455336aaa 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt @@ -22,7 +22,10 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.shared.SafeParse import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.extensions.formatColor +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.shared.logging.LTag import java.text.DecimalFormat import java.util.* import javax.inject.Inject @@ -36,6 +39,7 @@ class ExtendedBolusDialog : DialogFragmentWithDate() { @Inject lateinit var commandQueue: CommandQueue @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var protectionCheck: ProtectionCheck private var _binding: DialogExtendedbolusBinding? = null @@ -106,4 +110,17 @@ class ExtendedBolusDialog : DialogFragmentWithDate() { } return true } -} \ No newline at end of file + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt index 54b222ce2c..14158a101a 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt @@ -28,6 +28,8 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.shared.SafeParse import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.extensions.formatColor +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign @@ -44,6 +46,7 @@ class FillDialog : DialogFragmentWithDate() { @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var uel: UserEntryLogger @Inject lateinit var repository: AppRepository + @Inject lateinit var protectionCheck: ProtectionCheck private val disposable = CompositeDisposable() @@ -196,4 +199,17 @@ class FillDialog : DialogFragmentWithDate() { } }) } + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt index 21eb81ebce..46924a1fdc 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt @@ -28,6 +28,7 @@ import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.extensions.toSignedString +import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.shared.SafeParse import io.reactivex.rxjava3.disposables.CompositeDisposable @@ -52,6 +53,7 @@ class InsulinDialog : DialogFragmentWithDate() { @Inject lateinit var config: Config @Inject lateinit var bolusTimer: BolusTimer @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var protectionCheck: ProtectionCheck companion object { @@ -255,4 +257,17 @@ class InsulinDialog : DialogFragmentWithDate() { } return true } -} \ No newline at end of file + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt index 8e67a0ed6e..109a433afa 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt @@ -38,6 +38,7 @@ 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.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.shared.sharedPreferences.SP import io.reactivex.rxjava3.disposables.CompositeDisposable @@ -62,6 +63,7 @@ class LoopDialog : DaggerDialogFragment() { @Inject lateinit var dateUtil: DateUtil @Inject lateinit var repository: AppRepository @Inject lateinit var objectivePlugin: ObjectivesPlugin + @Inject lateinit var protectionCheck: ProtectionCheck private var showOkCancel: Boolean = true private var _binding: DialogLoopBinding? = null @@ -437,4 +439,17 @@ class LoopDialog : DaggerDialogFragment() { aapsLogger.debug(e.localizedMessage ?: e.toString()) } } + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt index 70ed31b8bc..04c6167f12 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.dialogs +import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -30,7 +31,9 @@ import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign @@ -51,6 +54,8 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { @Inject lateinit var hardLimits: HardLimits @Inject lateinit var rxBus: RxBus @Inject lateinit var defaultValueHelper: DefaultValueHelper + @Inject lateinit var ctx: Context + @Inject lateinit var protectionCheck: ProtectionCheck private var profileIndex: Int? = null @@ -245,4 +250,17 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { } return true } + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt index d458055b9d..c634c92f2f 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt @@ -20,7 +20,10 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.shared.SafeParse import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.extensions.formatColor +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.shared.logging.LTag import java.text.DecimalFormat import java.util.* import javax.inject.Inject @@ -35,6 +38,7 @@ class TempBasalDialog : DialogFragmentWithDate() { @Inject lateinit var commandQueue: CommandQueue @Inject lateinit var ctx: Context @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var protectionCheck: ProtectionCheck private var isPercentPump = true @@ -141,4 +145,17 @@ class TempBasalDialog : DialogFragmentWithDate() { } return true } -} \ No newline at end of file + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt index 16d41ef689..c282bffd1c 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.dialogs +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -26,7 +27,9 @@ import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign @@ -43,6 +46,8 @@ class TempTargetDialog : DialogFragmentWithDate() { @Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var uel: UserEntryLogger @Inject lateinit var repository: AppRepository + @Inject lateinit var ctx: Context + @Inject lateinit var protectionCheck: ProtectionCheck private lateinit var reasonList: List @@ -218,4 +223,17 @@ class TempTargetDialog : DialogFragmentWithDate() { } return true } + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt index 0e8a509517..bf82dcb38a 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt @@ -30,6 +30,7 @@ import info.nightscout.shared.SafeParse import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.extensions.formatColor +import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign @@ -48,6 +49,7 @@ class TreatmentDialog : DialogFragmentWithDate() { @Inject lateinit var config: Config @Inject lateinit var uel: UserEntryLogger @Inject lateinit var repository: AppRepository + @Inject lateinit var protectionCheck: ProtectionCheck private val disposable = CompositeDisposable() @@ -201,4 +203,17 @@ class TreatmentDialog : DialogFragmentWithDate() { } return true } -} \ No newline at end of file + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt index 36c8c90a1a..852dbab3f2 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt @@ -29,6 +29,7 @@ import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.valueToUnits import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP @@ -55,6 +56,7 @@ class WizardDialog : DaggerDialogFragment() { @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var repository: AppRepository @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var protectionCheck: ProtectionCheck private var wizard: BolusWizard? = null private var calculatedPercentage = 100.0 @@ -497,4 +499,17 @@ class WizardDialog : DaggerDialogFragment() { aapsLogger.debug(e.localizedMessage ?: "") } } + + override fun onResume() { + super.onResume() + activity?.let { activity -> + val cancelFail = { + aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}") + ToastUtils.showToastInUiThread(ctx, R.string.dialog_cancled) + dismiss() + } + + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, {}, cancelFail, fail = cancelFail) + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt index 54c21c929e..b42626de8d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout +import android.widget.TableRow import android.widget.TextView import androidx.core.content.ContextCompat import dagger.android.support.DaggerFragment @@ -108,6 +109,7 @@ class ActionsFragment : DaggerFragment() { private var insulinLevelLabel: TextView? = null private var pbLevelLabel: TextView? = null private var cannulaOrPatch: TextView? = null + private var batteryLayout: TableRow? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -143,7 +145,6 @@ class ActionsFragment : DaggerFragment() { historyBrowser = view.findViewById(R.id.actions_historybrowser) tddStats = view.findViewById(R.id.actions_tddstats) pumpBatteryChange = view.findViewById(R.id.actions_pumpbatterychange) - cannulaAge = view.findViewById(R.id.cannula_age) insulinAge = view.findViewById(R.id.insulin_age) reservoirLevel = view.findViewById(R.id.reservoir_level) @@ -155,6 +156,7 @@ class ActionsFragment : DaggerFragment() { insulinLevelLabel = view.findViewById(R.id.insulin_level_label) pbLevelLabel = view.findViewById(R.id.pb_level_label) cannulaOrPatch = view.findViewById(R.id.cannula_or_patch) + batteryLayout = view.findViewById(R.id.battery_layout) profileSwitch?.setOnClickListener { activity?.let { activity -> @@ -327,18 +329,14 @@ class ActionsFragment : DaggerFragment() { val activeBgSource = activePlugin.activeBgSource historyBrowser?.visibility = (profile != null).toVisibility() fill?.visibility = (pump.pumpDescription.isRefillingCapable && pump.isInitialized() && !pump.isSuspended()).toVisibility() - if (pump is DiaconnG8Plugin) { - pumpBatteryChange?.visibility = (pump.pumpDescription.isBatteryReplaceable && !pump.isBatteryChangeLoggingEnabled()).toVisibility() - } else { - pumpBatteryChange?.visibility = - (pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)).toVisibility() - } + pumpBatteryChange?.visibility = (pump.pumpDescription.isBatteryReplaceable || pump.isBatteryChangeLoggingEnabled()).toVisibility() tempTarget?.visibility = (profile != null && !loop.isDisconnected).toVisibility() tddStats?.visibility = pump.pumpDescription.supportsTDDs.toVisibility() - - cannulaOrPatch?.text = if (pump.pumpDescription.isPatchPump) rh.gs(R.string.patch_pump) else rh.gs(R.string.cannula) - val imageResource = if (pump.pumpDescription.isPatchPump) R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula + val isPatchPump = pump.pumpDescription.isPatchPump + cannulaOrPatch?.text = if (isPatchPump) rh.gs(R.string.patch_pump) else rh.gs(R.string.cannula) + val imageResource = if (isPatchPump) R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula cannulaOrPatch?.setCompoundDrawablesWithIntrinsicBounds(imageResource, 0, 0, 0) + batteryLayout?.visibility = (!isPatchPump || pump.pumpDescription.useHardwareLink).toVisibility() if (!config.NSCLIENT) { statusLightHandler.updateStatusLights(cannulaAge, insulinAge, reservoirLevel, sensorAge, sensorLevel, pbAge, batteryLevel) @@ -391,4 +389,4 @@ class ActionsFragment : DaggerFragment() { for (customButton in pumpCustomButtons) buttonsLayout?.removeView(customButton) pumpCustomButtons.clear() } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index a3b4f11b15..d2c0546d7f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -34,6 +34,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.databinding.OverviewFragmentBinding +import info.nightscout.androidaps.diaconn.DiaconnG8Plugin import info.nightscout.androidaps.dialogs.* import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.EventInitializationChanged @@ -61,6 +62,7 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin import info.nightscout.androidaps.plugins.source.DexcomPlugin import info.nightscout.androidaps.plugins.source.XdripPlugin import info.nightscout.androidaps.skins.SkinProvider @@ -866,13 +868,19 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList fun updateTime(from: String) { binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now()) // Status lights - val isPatchPump = activePlugin.activePump.pumpDescription.isPatchPump + val pump = activePlugin.activePump + val isPatchPump = pump.pumpDescription.isPatchPump binding.statusLightsLayout.apply { cannulaOrPatch.setImageResource(if (isPatchPump) R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula) cannulaOrPatch.contentDescription = rh.gs(if (isPatchPump) R.string.statuslights_patch_pump_age else R.string.statuslights_cannula_age) cannulaOrPatch.scaleX = if (isPatchPump) 1.4f else 2f cannulaOrPatch.scaleY = cannulaOrPatch.scaleX insulinAge.visibility = isPatchPump.not().toVisibility() + batteryLayout.visibility = (!isPatchPump || pump.pumpDescription.useHardwareLink).toVisibility() + pbAge.visibility = (pump.pumpDescription.isBatteryReplaceable || pump.isBatteryChangeLoggingEnabled()).toVisibility() + val useBatteryLevel = (pump.model() == PumpType.OMNIPOD_EROS && pump is OmnipodErosPumpPlugin) + || (pump.model() != PumpType.ACCU_CHEK_COMBO && pump.model() != PumpType.OMNIPOD_DASH) + batteryLevel.visibility = useBatteryLevel.toVisibility() statusLights.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility() } statusLightHandler.updateStatusLights( diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt index ba73fab429..254901adc5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt @@ -1,7 +1,6 @@ package info.nightscout.androidaps.plugins.general.overview import android.graphics.Color -import android.view.View import android.widget.TextView import androidx.annotation.StringRes import info.nightscout.androidaps.R @@ -42,7 +41,7 @@ class StatusLightHandler @Inject constructor( handleAge(careportal_cannula_age, TherapyEvent.Type.CANNULA_CHANGE, R.string.key_statuslights_cage_warning, 48.0, R.string.key_statuslights_cage_critical, 72.0) handleAge(careportal_insulin_age, TherapyEvent.Type.INSULIN_CHANGE, R.string.key_statuslights_iage_warning, 72.0, R.string.key_statuslights_iage_critical, 144.0) handleAge(careportal_sensor_age, TherapyEvent.Type.SENSOR_CHANGE, R.string.key_statuslights_sage_warning, 216.0, R.string.key_statuslights_sage_critical, 240.0) - if (pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)) { + if (pump.pumpDescription.isBatteryReplaceable || pump.isBatteryChangeLoggingEnabled()) { handleAge(careportal_pb_age, TherapyEvent.Type.PUMP_BATTERY_CHANGE, R.string.key_statuslights_bage_warning, 216.0, R.string.key_statuslights_bage_critical, 240.0) } if (!config.NSCLIENT) { @@ -58,16 +57,16 @@ class StatusLightHandler @Inject constructor( } if (!config.NSCLIENT) { - if (pump.model() == PumpType.OMNIPOD_DASH) { - // Omnipod Dash does not report its battery level + // The Omnipod Eros does not report its battery level. However, some RileyLink alternatives do. + // Depending on the user's configuration, we will either show the battery level reported by the RileyLink or "n/a" + // Pump instance check is needed because at startup, the pump can still be VirtualPumpPlugin and that will cause a crash + val erosBatteryLinkAvailable = pump.model() == PumpType.OMNIPOD_EROS && pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel + + if (pump.model().supportBatteryLevel || erosBatteryLinkAvailable) { + handleLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%") + } else { careportal_battery_level?.text = rh.gs(R.string.notavailable) careportal_battery_level?.setTextColor(Color.WHITE) - } else if (pump.model() == PumpType.OMNIPOD_EROS && pump is OmnipodErosPumpPlugin) { // instance of check is needed because at startup, pump can still be VirtualPumpPlugin and that will cause a crash because of the class cast below - // The Omnipod Eros does not report its battery level. However, some RileyLink alternatives do. - // Depending on the user's configuration, we will either show the battery level reported by the RileyLink or "n/a" - handleOmnipodErosBatteryLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%", pump.isUseRileyLinkBatteryLevel) - } else if (pump.model() != PumpType.ACCU_CHEK_COMBO) { - handleLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%") } } } @@ -104,13 +103,4 @@ class StatusLightHandler @Inject constructor( } } - @Suppress("SameParameterValue") - private fun handleOmnipodErosBatteryLevel(view: TextView?, criticalSetting: Int, criticalDefaultValue: Double, warnSetting: Int, warnDefaultValue: Double, level: Double, units: String, useRileyLinkBatteryLevel: Boolean) { - if (useRileyLinkBatteryLevel) { - handleLevel(view, criticalSetting, criticalDefaultValue, warnSetting, warnDefaultValue, level, units) - } else { - view?.text = rh.gs(R.string.notavailable) - view?.setTextColor(Color.WHITE) - } - } -} \ No newline at end of file +} 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 c3e3fd1b9f..c19899c75a 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 @@ -2,15 +2,9 @@ package info.nightscout.androidaps.plugins.general.overview.activities import android.annotation.SuppressLint import android.os.Bundle -import android.util.Log -import android.view.LayoutInflater -import android.view.MenuItem -import android.view.MotionEvent -import android.view.View -import android.view.ViewGroup -import android.widget.Button -import android.widget.ImageView -import android.widget.TextView +import android.util.SparseArray +import android.view.* +import androidx.core.util.forEach import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG @@ -23,11 +17,14 @@ import androidx.recyclerview.widget.RecyclerView import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult import info.nightscout.androidaps.databinding.OverviewQuickwizardlistActivityBinding +import info.nightscout.androidaps.databinding.OverviewQuickwizardlistItemBinding +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog import info.nightscout.androidaps.plugins.general.overview.events.EventQuickWizardChange import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.androidaps.utils.wizard.QuickWizardEntry @@ -46,17 +43,16 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() { @Inject lateinit var sp: SP private var disposable: CompositeDisposable = CompositeDisposable() + private var selectedItems: SparseArray = SparseArray() + private var removeActionMode: ActionMode? = null + private var sortActionMode: ActionMode? = null private lateinit var binding: OverviewQuickwizardlistActivityBinding private val itemTouchHelper by lazy { val simpleItemTouchCallback = object : ItemTouchHelper.SimpleCallback(UP or DOWN or START or END, 0) { - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { + override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { val adapter = recyclerView.adapter as RecyclerViewAdapter val from = viewHolder.layoutPosition val to = target.layoutPosition @@ -66,12 +62,10 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() { return true } - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { - } + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { super.onSelectedChanged(viewHolder, actionState) - if (actionState == ACTION_STATE_DRAG) { viewHolder?.itemView?.alpha = 0.5f } @@ -79,11 +73,8 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() { override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { super.clearView(recyclerView, viewHolder) - viewHolder.itemView.alpha = 1.0f - - val adapter = recyclerView.adapter as RecyclerViewAdapter - adapter.onDrop() + (recyclerView.adapter as RecyclerViewAdapter).onDrop() } } @@ -96,82 +87,84 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() { private inner class RecyclerViewAdapter(var fragmentManager: FragmentManager) : RecyclerView.Adapter() { - @SuppressLint("ClickableViewAccessibility") + private inner class QuickWizardEntryViewHolder(val binding: OverviewQuickwizardlistItemBinding, val fragmentManager: FragmentManager) : RecyclerView.ViewHolder(binding.root) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuickWizardEntryViewHolder { - val itemView = LayoutInflater.from(parent.context).inflate(R.layout.overview_quickwizardlist_item, parent, false) - val viewHolder = QuickWizardEntryViewHolder(itemView, fragmentManager) - - viewHolder.handleView.setOnTouchListener { _, event -> - if (event.actionMasked == MotionEvent.ACTION_DOWN) { - startDragging(viewHolder) - } - return@setOnTouchListener true - } - - return viewHolder + val binding = OverviewQuickwizardlistItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return QuickWizardEntryViewHolder(binding, fragmentManager) } + @SuppressLint("ClickableViewAccessibility") override fun onBindViewHolder(holder: QuickWizardEntryViewHolder, position: Int) { - holder.from.text = dateUtil.timeString(quickWizard[position].validFromDate()) - holder.to.text = dateUtil.timeString(quickWizard[position].validToDate()) - val wearControl = sp.getBoolean(R.string.key_wear_control, false) - - if (wearControl) { - holder.handleView.visibility = View.VISIBLE + val entry = quickWizard[position] + holder.binding.from.text = dateUtil.timeString(entry.validFromDate()) + holder.binding.to.text = dateUtil.timeString(entry.validToDate()) + holder.binding.buttonText.text = entry.buttonText() + holder.binding.carbs.text = rh.gs(R.string.format_carbs, entry.carbs()) + if (entry.device() == QuickWizardEntry.DEVICE_ALL) { + holder.binding.device.visibility = View.GONE } else { - holder.handleView.visibility = View.GONE - } - if (quickWizard[position].device() == QuickWizardEntry.DEVICE_ALL) { - holder.device.visibility = View.GONE - } else { - holder.device.visibility = View.VISIBLE - holder.device.setImageResource( + holder.binding.device.visibility = View.VISIBLE + holder.binding.device.setImageResource( when (quickWizard[position].device()) { QuickWizardEntry.DEVICE_WATCH -> R.drawable.ic_watch else -> R.drawable.ic_smartphone } ) } - holder.buttonText.text = quickWizard[position].buttonText() - holder.carbs.text = rh.gs(R.string.format_carbs, quickWizard[position].carbs()) + + if (sortActionMode != null && removeActionMode != null) { + holder.binding.cardview.setOnClickListener { + val manager = fragmentManager + val editQuickWizardDialog = EditQuickWizardDialog() + val bundle = Bundle() + bundle.putInt("position", position) + editQuickWizardDialog.arguments = bundle + editQuickWizardDialog.show(manager, "EditQuickWizardDialog") + } + } + + fun updateSelection(selected: Boolean) { + if (selected) { + selectedItems.put(position, entry) + } else { + selectedItems.remove(position) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + } + + holder.binding.cardview.setOnTouchListener { _, event -> + if (event.actionMasked == MotionEvent.ACTION_UP && sortActionMode == null && removeActionMode == null) { + val manager = fragmentManager + val editQuickWizardDialog = EditQuickWizardDialog() + val bundle = Bundle() + bundle.putInt("position", position) + editQuickWizardDialog.arguments = bundle + editQuickWizardDialog.show(manager, "EditQuickWizardDialog") + } + if (event.actionMasked == MotionEvent.ACTION_DOWN && sortActionMode != null) { + startDragging(holder) + } + if (event.actionMasked == MotionEvent.ACTION_UP && removeActionMode != null) { + holder.binding.cbRemove.toggle() + updateSelection(holder.binding.cbRemove.isChecked) + } + return@setOnTouchListener true + } + holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> updateSelection(value) } + holder.binding.handleView.visibility = (sortActionMode != null).toVisibility() + holder.binding.cbRemove.visibility = (removeActionMode != null).toVisibility() + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) } override fun getItemCount(): Int = quickWizard.size() - private inner class QuickWizardEntryViewHolder(itemView: View, var fragmentManager: FragmentManager) : RecyclerView.ViewHolder(itemView) { - - val buttonText: TextView = itemView.findViewById(R.id.overview_quickwizard_item_buttonText) - val carbs: TextView = itemView.findViewById(R.id.overview_quickwizard_item_carbs) - val from: TextView = itemView.findViewById(R.id.overview_quickwizard_item_from) - val handleView: ImageView = itemView.findViewById(R.id.handleView) - val device: ImageView = itemView.findViewById(R.id.overview_quickwizard_item_device) - val to: TextView = itemView.findViewById(R.id.overview_quickwizard_item_to) - private val editButton: Button = itemView.findViewById(R.id.overview_quickwizard_item_edit_button) - private val removeButton: Button = itemView.findViewById(R.id.overview_quickwizard_item_remove_button) - - init { - editButton.setOnClickListener { - val manager = fragmentManager - val editQuickWizardDialog = EditQuickWizardDialog() - val bundle = Bundle() - bundle.putInt("position", bindingAdapterPosition) - editQuickWizardDialog.arguments = bundle - editQuickWizardDialog.show(manager, "EditQuickWizardDialog") - } - removeButton.setOnClickListener { - quickWizard.remove(bindingAdapterPosition) - rxBus.send(EventQuickWizardChange()) - } - } - } - fun moveItem(from: Int, to: Int) { - Log.i("QuickWizard", "moveItem") quickWizard.move(from, to) } fun onDrop() { - Log.i("QuickWizard", "onDrop") rxBus.send(EventQuickWizardChange()) } } @@ -213,13 +206,107 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() { super.onPause() } + private fun removeSelected() { + if (selectedItems.size() > 0) + OKDialog.showConfirmation(this, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { + selectedItems.forEach { _, item -> + quickWizard.remove(item.position) + rxBus.send(EventQuickWizardChange()) + } + removeActionMode?.finish() + }) + else + removeActionMode?.finish() + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + val inflater = menuInflater + inflater.inflate(R.menu.menu_quickwizard, menu) + return super.onCreateOptionsMenu(menu) + } + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { - android.R.id.home -> { + android.R.id.home -> { finish() true } - else -> false + R.id.nav_remove_items -> { + removeActionMode = startActionMode(RemoveActionModeCallback()) + true + } + + R.id.nav_sort_items -> { + sortActionMode = startActionMode(SortActionModeCallback()) + true + } + + else -> false } + + inner class RemoveActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) + binding.recyclerview.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.remove_selected -> { + removeSelected() + true + } + + else -> false + } + } + + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } + } + + inner class SortActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.title = rh.gs(R.string.sort_label) + 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?) { + sortActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } + } + + private fun getConfirmationText(): String { + if (selectedItems.size() == 1) { + val entry = selectedItems.valueAt(0) + return "${rh.gs(R.string.remove_button)} ${entry.buttonText()} ${rh.gs(R.string.format_carbs, entry.carbs())}\n" + + "${dateUtil.timeString(entry.validFromDate())} - ${dateUtil.timeString(entry.validToDate())}" + } + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) + } } 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 d285db5f0a..c56b34c032 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 @@ -1,10 +1,10 @@ package info.nightscout.androidaps.plugins.source -import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +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 @@ -54,11 +54,12 @@ class BGSourceFragment : DaggerFragment() { private val disposable = CompositeDisposable() private val millsToThePast = T.hours(36).msecs() + private var selectedItems: SparseArray = SparseArray() + private var toolbar: Toolbar? = null + private var removeActionMode: ActionMode? = null private var _binding: BgsourceFragmentBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = @@ -66,6 +67,8 @@ class BGSourceFragment : 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) @@ -94,17 +97,45 @@ class BGSourceFragment : DaggerFragment() { @Synchronized override fun onPause() { + removeActionMode?.finish() disposable.clear() super.onPause() } + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.menu_bgsource, menu) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + // Only show when tab bg source is shown + menu.findItem(R.id.nav_remove_items)?.isVisible = isResumed + super.onPrepareOptionsMenu(menu) + } + @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.finish() binding.recyclerview.adapter = null // avoid leaks _binding = null } + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.nav_remove_items -> { + if (toolbar != null) { + removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) // in overview + } else { + removeActionMode = activity?.startActionMode(RemoveActionModeCallback()) // in Single FragmentActivity + } + true + } + + else -> false + } + } + inner class RecyclerViewAdapter internal constructor(private var glucoseValues: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): GlucoseValuesViewHolder { @@ -116,19 +147,43 @@ class BGSourceFragment : DaggerFragment() { val glucoseValue = glucoseValues[position] holder.binding.ns.visibility = (glucoseValue.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = (!glucoseValue.isValid).toVisibility() - val sameDayPrevious = position > 0 && dateUtil.isSameDay(glucoseValue.timestamp, glucoseValues[position-1].timestamp) + val sameDayPrevious = position > 0 && dateUtil.isSameDay(glucoseValue.timestamp, glucoseValues[position - 1].timestamp) holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.text = dateUtil.dateString(glucoseValue.timestamp) holder.binding.time.text = dateUtil.timeString(glucoseValue.timestamp) holder.binding.value.text = glucoseValue.valueToUnitsString(profileFunction.getUnits()) holder.binding.direction.setImageResource(glucoseValue.trendArrow.directionToIcon()) - holder.binding.remove.tag = glucoseValue if (position > 0) { val previous = glucoseValues[position - 1] val diff = previous.timestamp - glucoseValue.timestamp if (diff < T.secs(20).msecs()) holder.binding.root.setBackgroundColor(rh.gc(R.color.errorAlertBackground)) } + fun updateSelection(selected: Boolean) { + if (selected) { + selectedItems.put(position, glucoseValue) + } else { + selectedItems.remove(position) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + } + holder.binding.root.setOnLongClickListener { + if (removeActionMode == null) { + removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) + } + holder.binding.cbRemove.toggle() + updateSelection(holder.binding.cbRemove.isChecked) + true + } + holder.binding.root.setOnClickListener { + if (removeActionMode != null) { + holder.binding.cbRemove.toggle() + updateSelection(holder.binding.cbRemove.isChecked) + } + } + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> updateSelection(value) } + holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + holder.binding.cbRemove.visibility = (removeActionMode != null).toVisibility() } override fun getItemCount(): Int = glucoseValues.size @@ -136,38 +191,76 @@ class BGSourceFragment : DaggerFragment() { inner class GlucoseValuesViewHolder(view: View) : RecyclerView.ViewHolder(view) { val binding = BgsourceItemBinding.bind(view) - - init { - binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG - binding.remove.setOnClickListener { v: View -> - val glucoseValue = v.tag as GlucoseValue - activity?.let { activity -> - val text = dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits()) - OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable { - val source = when ((activePlugin.activeBgSource as PluginBase).pluginDescription.pluginName) { - R.string.dexcom_app_patched -> Sources.Dexcom - R.string.eversense -> Sources.Eversense - R.string.Glimp -> Sources.Glimp - R.string.MM640g -> Sources.MM640g - R.string.nsclientbg -> Sources.NSClientSource - R.string.poctech -> Sources.PocTech - R.string.tomato -> Sources.Tomato - R.string.glunovo -> Sources.Glunovo - R.string.xdrip -> Sources.Xdrip - else -> Sources.Unknown - } - uel.log( - Action.BG_REMOVED, source, - ValueWithUnit.Timestamp(glucoseValue.timestamp) - ) - repository.runTransactionForResult(InvalidateGlucoseValueTransaction(glucoseValue.id)) - .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating BG value", it) } - .blockingGet() - .also { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bg $it") } } - }) - } - } - } } } + + inner class RemoveActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) + binding.recyclerview.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.remove_selected -> { + removeSelected() + true + } + + else -> false + } + } + + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } + } + + private fun getConfirmationText(): String { + if (selectedItems.size() == 1) { + val glucoseValue = selectedItems.valueAt(0) + return dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits()) + } + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) + } + + private fun removeSelected() { + if (selectedItems.size() > 0) + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { + selectedItems.forEach { _, glucoseValue -> + val source = when ((activePlugin.activeBgSource as PluginBase).pluginDescription.pluginName) { + R.string.dexcom_app_patched -> Sources.Dexcom + R.string.eversense -> Sources.Eversense + R.string.Glimp -> Sources.Glimp + R.string.MM640g -> Sources.MM640g + R.string.nsclientbg -> Sources.NSClientSource + R.string.poctech -> Sources.PocTech + R.string.tomato -> Sources.Tomato + R.string.glunovo -> Sources.Glunovo + R.string.xdrip -> Sources.Xdrip + else -> Sources.Unknown + } + uel.log( + Action.BG_REMOVED, source, + ValueWithUnit.Timestamp(glucoseValue.timestamp) + ) + repository.runTransactionForResult(InvalidateGlucoseValueTransaction(glucoseValue.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating BG value", it) } + .blockingGet() + .also { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bg $it") } } + } + removeActionMode?.finish() + }) + } + else + removeActionMode?.finish() + } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/ProcessLifecycleListener.kt b/app/src/main/java/info/nightscout/androidaps/utils/ProcessLifecycleListener.kt new file mode 100644 index 0000000000..e1bc3a719b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/ProcessLifecycleListener.kt @@ -0,0 +1,13 @@ +package info.nightscout.androidaps.utils + +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import info.nightscout.androidaps.utils.protection.ProtectionCheck +import javax.inject.Inject + +class ProcessLifecycleListener @Inject constructor(private val protectionCheck: ProtectionCheck) : DefaultLifecycleObserver { + + override fun onPause(owner: LifecycleOwner) { + protectionCheck.resetAuthorization() + } +} diff --git a/app/src/main/res/layout/bgsource_item.xml b/app/src/main/res/layout/bgsource_item.xml index a7d9edc675..764a3cdda1 100644 --- a/app/src/main/res/layout/bgsource_item.xml +++ b/app/src/main/res/layout/bgsource_item.xml @@ -17,7 +17,7 @@ android:id="@+id/date" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/list_delimiter" + android:background="?android:attr/dividerHorizontal" android:gravity="center" android:text="1.1.2000" android:textAppearance="?android:attr/textAppearanceMedium" /> @@ -68,18 +68,19 @@ android:text="@string/invalid" android:textColor="@android:color/holo_red_light" /> - + + diff --git a/app/src/main/res/layout/careportal_stats_fragment.xml b/app/src/main/res/layout/careportal_stats_fragment.xml index 24e86b19f1..0e82a5345e 100644 --- a/app/src/main/res/layout/careportal_stats_fragment.xml +++ b/app/src/main/res/layout/careportal_stats_fragment.xml @@ -41,7 +41,6 @@ android:textColor="@android:color/white" android:textSize="14sp" /> - + android:background="?android:attr/dividerHorizontal" /> @@ -96,7 +95,6 @@ android:textSize="14sp" app:drawableStartCompat="@drawable/ic_cp_age_insulin" /> - + android:background="?android:attr/dividerHorizontal" /> @@ -226,11 +224,12 @@ android:layout_marginRight="20dp" android:layout_marginBottom="2dp" android:layout_span="5" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> @@ -246,7 +245,6 @@ android:textSize="14sp" app:drawableStartCompat="@drawable/ic_cp_age_battery" /> - - - - - - - diff --git a/app/src/main/res/layout/careportal_stats_fragment_lowres.xml b/app/src/main/res/layout/careportal_stats_fragment_lowres.xml deleted file mode 100644 index 7cd144893c..0000000000 --- a/app/src/main/res/layout/careportal_stats_fragment_lowres.xml +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_loop.xml b/app/src/main/res/layout/dialog_loop.xml index 1a138333cf..1b23c97aff 100644 --- a/app/src/main/res/layout/dialog_loop.xml +++ b/app/src/main/res/layout/dialog_loop.xml @@ -160,7 +160,7 @@ android:layout_marginStart="5dp" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> @@ -270,7 +270,7 @@ android:layout_marginStart="5dp" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> @@ -393,7 +393,7 @@ android:layout_marginStart="5dp" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> diff --git a/app/src/main/res/layout/loop_fragment.xml b/app/src/main/res/layout/loop_fragment.xml index 180523cd01..5f3fdffff1 100644 --- a/app/src/main/res/layout/loop_fragment.xml +++ b/app/src/main/res/layout/loop_fragment.xml @@ -61,7 +61,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="20dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> diff --git a/app/src/main/res/layout/openapsama_fragment.xml b/app/src/main/res/layout/openapsama_fragment.xml index 0baf5f1193..1da988ac91 100644 --- a/app/src/main/res/layout/openapsama_fragment.xml +++ b/app/src/main/res/layout/openapsama_fragment.xml @@ -68,7 +68,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="20dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> diff --git a/app/src/main/res/layout/overview_quickwizardlist_activity.xml b/app/src/main/res/layout/overview_quickwizardlist_activity.xml index a95c6d9516..31139e0bca 100644 --- a/app/src/main/res/layout/overview_quickwizardlist_activity.xml +++ b/app/src/main/res/layout/overview_quickwizardlist_activity.xml @@ -1,25 +1,27 @@ - - - + android:layout_height="wrap_content" /> - + + + diff --git a/app/src/main/res/layout/overview_quickwizardlist_item.xml b/app/src/main/res/layout/overview_quickwizardlist_item.xml index 5cf0f538d1..dc729b4109 100644 --- a/app/src/main/res/layout/overview_quickwizardlist_item.xml +++ b/app/src/main/res/layout/overview_quickwizardlist_item.xml @@ -2,7 +2,7 @@ @@ -90,6 +89,13 @@ android:scaleType="fitStart" card_view:srcCompat="@drawable/ic_reorder_gray_24dp" /> + + + - - - - - - - - diff --git a/app/src/main/res/layout/overview_statuslights_layout.xml b/app/src/main/res/layout/overview_statuslights_layout.xml index 75a71faf3b..ab2402c373 100644 --- a/app/src/main/res/layout/overview_statuslights_layout.xml +++ b/app/src/main/res/layout/overview_statuslights_layout.xml @@ -94,6 +94,7 @@ + android:background="?android:attr/dividerHorizontal" /> diff --git a/app/src/main/res/layout/treatments_careportal_item.xml b/app/src/main/res/layout/treatments_careportal_item.xml index e7be19cd34..da86b56722 100644 --- a/app/src/main/res/layout/treatments_careportal_item.xml +++ b/app/src/main/res/layout/treatments_careportal_item.xml @@ -20,7 +20,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" + android:background="?android:attr/dividerHorizontal" android:gravity="center" android:text="1.1.2000" android:textAppearance="?android:attr/textAppearanceMedium" @@ -129,7 +129,7 @@ android:layout_marginTop="5dp" android:layout_marginEnd="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> diff --git a/app/src/main/res/layout/treatments_extendedbolus_item.xml b/app/src/main/res/layout/treatments_extendedbolus_item.xml index b5afb8d8e2..8b381c97e4 100644 --- a/app/src/main/res/layout/treatments_extendedbolus_item.xml +++ b/app/src/main/res/layout/treatments_extendedbolus_item.xml @@ -20,7 +20,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" + android:background="?android:attr/dividerHorizontal" android:gravity="center" android:text="1.1.2000" android:textAppearance="?android:attr/textAppearanceMedium" @@ -176,7 +176,7 @@ android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:layout_marginTop="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> diff --git a/app/src/main/res/layout/treatments_profileswitch_item.xml b/app/src/main/res/layout/treatments_profileswitch_item.xml index be53148103..2243728dfe 100644 --- a/app/src/main/res/layout/treatments_profileswitch_item.xml +++ b/app/src/main/res/layout/treatments_profileswitch_item.xml @@ -20,7 +20,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" + android:background="?android:attr/dividerHorizontal" android:gravity="center" android:text="1.1.2000" android:textAppearance="?android:attr/textAppearanceMedium" @@ -141,7 +141,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> diff --git a/app/src/main/res/layout/treatments_tempbasals_item.xml b/app/src/main/res/layout/treatments_tempbasals_item.xml index f35cef125b..4a89a943fb 100644 --- a/app/src/main/res/layout/treatments_tempbasals_item.xml +++ b/app/src/main/res/layout/treatments_tempbasals_item.xml @@ -20,7 +20,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" + android:background="?android:attr/dividerHorizontal" android:gravity="center" android:text="1.1.2000" android:textAppearance="?android:attr/textAppearanceMedium" @@ -195,7 +195,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> diff --git a/app/src/main/res/layout/treatments_temptarget_item.xml b/app/src/main/res/layout/treatments_temptarget_item.xml index d9aa226a60..c1c5c9bdb0 100644 --- a/app/src/main/res/layout/treatments_temptarget_item.xml +++ b/app/src/main/res/layout/treatments_temptarget_item.xml @@ -20,7 +20,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" + android:background="?android:attr/dividerHorizontal" android:gravity="center" android:text="1.1.2000" android:textAppearance="?android:attr/textAppearanceMedium" @@ -164,7 +164,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> diff --git a/app/src/main/res/layout/treatments_user_entry_item.xml b/app/src/main/res/layout/treatments_user_entry_item.xml index bdc8adb604..d16db3746d 100644 --- a/app/src/main/res/layout/treatments_user_entry_item.xml +++ b/app/src/main/res/layout/treatments_user_entry_item.xml @@ -15,7 +15,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" + android:background="?android:attr/dividerHorizontal" android:gravity="center" android:text="1.1.2000" android:textAppearance="?android:attr/textAppearanceMedium" @@ -93,7 +93,7 @@ android:layout_marginTop="5dp" android:layout_marginEnd="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" + android:background="?android:attr/dividerHorizontal" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/notes" /> diff --git a/app/src/main/res/layout/virtualpump_fragment.xml b/app/src/main/res/layout/virtualpump_fragment.xml index 5183491b93..082f4abeae 100644 --- a/app/src/main/res/layout/virtualpump_fragment.xml +++ b/app/src/main/res/layout/virtualpump_fragment.xml @@ -65,7 +65,7 @@ android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_marginTop="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + + + + diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index a739f077e2..42e55843f0 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -2,45 +2,57 @@ xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" tools:context=".MainActivity"> + + diff --git a/app/src/main/res/menu/menu_quickwizard.xml b/app/src/main/res/menu/menu_quickwizard.xml new file mode 100644 index 0000000000..7a3e86905d --- /dev/null +++ b/app/src/main/res/menu/menu_quickwizard.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 07bf55f635..a74713b29f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -827,6 +827,7 @@ Show invalidated Hide invalidated Remove items + Sort 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 @@ -1139,6 +1140,7 @@ Errors Slow down uploads BG data status + Remove BG readings cannula age patch pump age Patch pump @@ -1222,4 +1224,6 @@ Hide loop Show loop %1$d selected + Sort + Dialog canceled diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index 6b43facf2d..3ae0cb84dc 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/app/src/main/res/xml/pref_general.xml @@ -1,6 +1,7 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:validate="http://schemas.android.com/apk/res-auto"> + android:title="@string/patient_name" /> + android:title="@string/master_password" /> + + - \ No newline at end of file + diff --git a/automation/src/main/res/layout/automation_dialog_event.xml b/automation/src/main/res/layout/automation_dialog_event.xml index bcfdb70ef8..6fde17856f 100644 --- a/automation/src/main/res/layout/automation_dialog_event.xml +++ b/automation/src/main/res/layout/automation_dialog_event.xml @@ -148,7 +148,7 @@ android:layout_height="2dip" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> InterfaceIDs.PumpType.USER DIACONN_G8 -> InterfaceIDs.PumpType.DIACONN_G8 } -} \ No newline at end of file +} diff --git a/core/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt b/core/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt index 7edc13830e..28661c0fd0 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt @@ -2,16 +2,22 @@ package info.nightscout.androidaps.utils.protection import androidx.fragment.app.FragmentActivity import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.shared.sharedPreferences.SP +import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton @Singleton class ProtectionCheck @Inject constructor( val sp: SP, - val passwordCheck: PasswordCheck + val passwordCheck: PasswordCheck, + val dateUtil: DateUtil ) { + private var lastAuthorization = mutableListOf(0L, 0L, 0L) + private val timeout = TimeUnit.SECONDS.toMillis(sp.getInt(R.string.key_protection_timeout, 0).toLong()) + enum class Protection { PREFERENCES, APPLICATION, @@ -52,6 +58,9 @@ class ProtectionCheck @Inject constructor( R.string.bolus_pin) fun isLocked(protection: Protection): Boolean { + if (activeSession(protection)) { + return false + } return when (ProtectionType.values()[sp.getInt(protectionTypeResourceIDs[protection.ordinal], ProtectionType.NONE.ordinal)]) { ProtectionType.NONE -> false ProtectionType.BIOMETRIC -> true @@ -61,19 +70,38 @@ class ProtectionCheck @Inject constructor( } } - fun queryProtection(activity: FragmentActivity, protection: Protection, - ok: Runnable?, cancel: Runnable? = null, fail: Runnable? = null) { + fun resetAuthorization() { + lastAuthorization = mutableListOf(0L, 0L, 0L) + } + + private fun activeSession(protection: Protection): Boolean { + val last = lastAuthorization[protection.ordinal] + val diff = dateUtil.now() - last + return diff < timeout + } + + private fun onOk(protection: Protection) { + lastAuthorization[protection.ordinal] = dateUtil.now() + } + + fun queryProtection(activity: FragmentActivity, protection: Protection, ok: Runnable?, cancel: Runnable? = null, fail: Runnable? = null) { + if (activeSession(protection)) { + onOk(protection) + ok?.run() + return + } + when (ProtectionType.values()[sp.getInt(protectionTypeResourceIDs[protection.ordinal], ProtectionType.NONE.ordinal)]) { ProtectionType.NONE -> ok?.run() ProtectionType.BIOMETRIC -> - BiometricCheck.biometricPrompt(activity, titlePassResourceIDs[protection.ordinal], ok, cancel, fail, passwordCheck) + BiometricCheck.biometricPrompt(activity, titlePassResourceIDs[protection.ordinal], { onOk(protection); ok?.run() }, cancel, fail, passwordCheck) ProtectionType.MASTER_PASSWORD -> - passwordCheck.queryPassword(activity, R.string.master_password, R.string.key_master_password, { ok?.run() }, { cancel?.run() }, { fail?.run() }) + passwordCheck.queryPassword(activity, R.string.master_password, R.string.key_master_password, { onOk(protection); ok?.run() }, { cancel?.run() }, { fail?.run() }) ProtectionType.CUSTOM_PASSWORD -> - passwordCheck.queryPassword(activity, titlePassResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() }) + passwordCheck.queryPassword(activity, titlePassResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], { onOk(protection); ok?.run() }, { cancel?.run() }, { fail?.run() }) ProtectionType.CUSTOM_PIN -> - passwordCheck.queryPassword(activity, titlePinResourceIDs[protection.ordinal], pinsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() }, true) + passwordCheck.queryPassword(activity, titlePinResourceIDs[protection.ordinal], pinsResourceIDs[protection.ordinal], { onOk(protection); ok?.run() }, { cancel?.run() }, { fail?.run() }, true) } } } diff --git a/core/src/main/res/layout/dialog_profileviewer.xml b/core/src/main/res/layout/dialog_profileviewer.xml index 2689ade712..f33217816c 100644 --- a/core/src/main/res/layout/dialog_profileviewer.xml +++ b/core/src/main/res/layout/dialog_profileviewer.xml @@ -118,7 +118,7 @@ android:layout_marginTop="5dp" android:layout_marginRight="20dp" android:layout_marginBottom="5dp" - android:background="@color/list_delimiter" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> + android:background="?android:attr/dividerHorizontal" /> diff --git a/core/src/main/res/values/protection.xml b/core/src/main/res/values/protection.xml index 807106b726..9f6c0e279b 100644 --- a/core/src/main/res/values/protection.xml +++ b/core/src/main/res/values/protection.xml @@ -12,6 +12,8 @@ Application PIN Bolus password Bolus PIN + Password and PIN retention [s] + Time before the password or PIN should be entered Unlock settings Biometric Custom password @@ -43,4 +45,5 @@ settings_protection application_protection bolus_protection + protection_timeout diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml index b22248a881..0d5083feef 100644 --- a/core/src/main/res/values/styles.xml +++ b/core/src/main/res/values/styles.xml @@ -28,6 +28,8 @@ @color/notificationLow @color/notificationInfo @color/notificationAnnouncement + + @drawable/ic_close