Merge pull request #364 from bartsopers/dash

More Omnipod Dash preparation + some Eros fixes
This commit is contained in:
Milos Kozak 2021-02-22 17:33:13 +01:00 committed by GitHub
commit aaa1ecea8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
215 changed files with 5601 additions and 3193 deletions

View file

@ -186,6 +186,7 @@ dependencies {
implementation project(':insight')
implementation project(':rileylink')
implementation project(':medtronic')
implementation project(':omnipod-common')
implementation project(':omnipod-eros')
implementation project(':omnipod-dash')

View file

@ -633,8 +633,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.infoLayout.apsModeText.visibility = View.VISIBLE
}
pump.isSuspended() -> {
binding.infoLayout.apsMode.setImageResource(if (pump.pumpDescription.pumpType == PumpType.Insulet_Omnipod) {
pump.isSuspended() -> {
binding.infoLayout.apsMode.setImageResource(if (pump.model() == PumpType.Omnipod_Eros || pump.model() == PumpType.Omnipod_Dash) {
// For Omnipod, indicate the pump as disconnected when it's suspended.
// The only way to 'reconnect' it, is through the Omnipod tab
R.drawable.ic_loop_disconnected
@ -906,7 +906,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, now, useCobForScale, if (useCobForScale) 1.0 else 0.5)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, now, useDevForScale, 1.0, alignDevBgiScale)
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, now, useRatioForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, endTime, useBGIForScale, if(alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale)
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, endTime, useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, now, useDSForScale, 1.0)
// set manual x bounds to have nice steps

View file

@ -36,11 +36,11 @@ class StatusLightHandler @Inject constructor(
handleAge(careportal_cannula_age, CareportalEvent.SITECHANGE, R.string.key_statuslights_cage_warning, 48.0, R.string.key_statuslights_cage_critical, 72.0)
handleAge(careportal_insulin_age, CareportalEvent.INSULINCHANGE, R.string.key_statuslights_iage_warning, 72.0, R.string.key_statuslights_iage_critical, 144.0)
handleAge(careportal_sensor_age, CareportalEvent.SENSORCHANGE, R.string.key_statuslights_sage_warning, 216.0, R.string.key_statuslights_sage_critical, 240.0)
if (pump.pumpDescription.isBatteryReplaceable) {
if (pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)) {
handleAge(careportal_pb_age, CareportalEvent.PUMPBATTERYCHANGE, R.string.key_statuslights_bage_warning, 216.0, R.string.key_statuslights_bage_critical, 240.0)
}
if (!config.NSCLIENT) {
if (pump.model() == PumpType.Insulet_Omnipod) {
if (pump.model() == PumpType.Omnipod_Eros || pump.model() == PumpType.Omnipod_Dash) {
handleOmnipodReservoirLevel(careportal_reservoir_level, R.string.key_statuslights_res_critical, 10.0, R.string.key_statuslights_res_warning, 80.0, pump.reservoirLevel, "U")
} else {
handleLevel(careportal_reservoir_level, R.string.key_statuslights_res_critical, 10.0, R.string.key_statuslights_res_warning, 80.0, pump.reservoirLevel, "U")
@ -52,8 +52,14 @@ class StatusLightHandler @Inject constructor(
}
if (!config.NSCLIENT) {
if (pump.model() == PumpType.Insulet_Omnipod && 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
handleOmnipodBatteryLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%", pump.isUseRileyLinkBatteryLevel)
if (pump.model() == PumpType.Omnipod_Dash) {
// Omnipod Dash does not report its battery level
careportal_battery_level?.text = resourceHelper.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.AccuChekCombo) {
handleLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%")
}
@ -93,7 +99,7 @@ class StatusLightHandler @Inject constructor(
}
@Suppress("SameParameterValue")
private fun handleOmnipodBatteryLevel(view: TextView?, criticalSetting: Int, criticalDefaultValue: Double, warnSetting: Int, warnDefaultValue: Double, level: Double, units: String, useRileyLinkBatteryLevel: Boolean) {
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 file

@ -27,6 +27,7 @@ import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange
import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin
import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin
import info.nightscout.androidaps.setupwizard.elements.*
import info.nightscout.androidaps.setupwizard.events.EventSWUpdate
@ -284,14 +285,14 @@ class SWDefinition @Inject constructor(
.add(SWInfoText(injector)
.label(R.string.setupwizard_pump_pump_not_initialized)
.visibility { !isPumpInitialized() })
.add( // Omnipod only
.add( // Omnipod Eros only
SWInfoText(injector)
.label(R.string.setupwizard_pump_waiting_for_riley_link_connection)
.visibility {
val activePump = activePlugin.activePump
activePump is OmnipodErosPumpPlugin && !activePump.isRileyLinkReady
})
.add( // Omnipod only
.add( // Omnipod Eros only
SWEventListener(injector, EventRileyLinkDeviceStatusChange::class.java)
.label(R.string.setupwizard_pump_riley_link_status)
.visibility { activePlugin.activePump is OmnipodErosPumpPlugin })
@ -301,18 +302,21 @@ class SWDefinition @Inject constructor(
.visibility {
// Hide for Omnipod, because as we don't require a Pod to be paired in the setup wizard,
// Getting the status might not be possible
activePlugin.activePump !is OmnipodErosPumpPlugin
activePlugin.activePump !is OmnipodErosPumpPlugin && activePlugin.activePump !is OmnipodDashPumpPlugin
})
.add(SWEventListener(injector, EventPumpStatusChanged::class.java)
.visibility { activePlugin.activePump !is OmnipodErosPumpPlugin })
.visibility { activePlugin.activePump !is OmnipodErosPumpPlugin && activePlugin.activePump !is OmnipodDashPumpPlugin })
.validator { isPumpInitialized() }
private fun isPumpInitialized(): Boolean {
val activePump = activePlugin.activePump
// For Omnipod, consider the pump initialized when a RL has been configured successfully
// Users will be prompted to activate a Pod after completing the setup wizard.
return activePump.isInitialized() || (activePump is OmnipodErosPumpPlugin && activePump.isRileyLinkReady)
// For Omnipod, activating a Pod can be done after setup through the Omnipod fragment
// For the Eros model, consider the pump initialized when a RL has been configured successfully
// For Dash model, consider the pump setup without any extra conditions
return activePump.isInitialized()
|| (activePump is OmnipodErosPumpPlugin && activePump.isRileyLinkReady)
|| activePump is OmnipodDashPumpPlugin
}
private val screenAps = SWScreen(injector, R.string.configbuilder_aps)

View file

@ -42,6 +42,7 @@ dependencies {
api "io.reactivex.rxjava2:rxkotlin:$rxkotlin_version"
api "io.reactivex.rxjava2:rxandroid:$rxandroid_version"
api "org.apache.commons:commons-lang3:$commonslang3_version"
api "commons-codec:commons-codec:$commonscodec_version"
//CryptoUtil
api 'com.madgag.spongycastle:core:1.58.0.0'

View file

@ -98,12 +98,19 @@ public enum PumpType {
// Insulet
Insulet_Omnipod("Insulet Omnipod", ManufacturerType.Insulet, "Omnipod (Eros)", 0.05d, null, //
Omnipod_Eros("Omnipod", ManufacturerType.Insulet, "Eros", 0.05d, null, //
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
PumpTempBasalType.Absolute, //
new DoseSettings(0.05d, 30, 12 * 60, 0d, 30.0d), PumpCapability.BasalRate_Duration30minAllowed, //
0.05d, null, 0.05d, null, PumpCapability.OmnipodCapabilities, true),
Omnipod_Dash("Omnipod", ManufacturerType.Insulet, "Dash", 0.05d, null, //
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
PumpTempBasalType.Absolute, //
new DoseSettings(0.05d, 30, 12 * 60, 0d, 30.0d), PumpCapability.BasalRate_Duration30minAllowed, //
0.05d, null, 0.05d, null, PumpCapability.OmnipodCapabilities, false),
// Medtronic
Medtronic_512_712("Medtronic 512/712", ManufacturerType.Medtronic, "512/712", 0.1d, null, //
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //

View file

@ -19,6 +19,8 @@ files:
translation: /dana/src/main/res/values-%android_code%/strings.xml
- source: /medtronic/src/main/res/values/strings.xml
translation: /medtronic/src/main/res/values-%android_code%/strings.xml
- source: /omnipod-common/src/main/res/values/strings.xml
translation: /omnipod-common/src/main/res/values-%android_code%/strings.xml
- source: /omnipod-dash/src/main/res/values/strings.xml
translation: /omnipod-dash/src/main/res/values-%android_code%/strings.xml
- source: /omnipod-eros/src/main/res/values/strings.xml

1
omnipod-common/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,20 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.hiya.jacoco-android'
apply from: "${project.rootDir}/gradle/android_dependencies.gradle"
apply from: "${project.rootDir}/gradle/android_module_dependencies.gradle"
apply from: "${project.rootDir}/gradle/test_dependencies.gradle"
android {
defaultConfig {
versionCode 1
versionName "1.0"
}
}
dependencies {
implementation project(':core')
}

View file

21
omnipod-common/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="info.nightscout.androidaps.plugins.pump.omnipod.common">
<application></application>
</manifest>

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.pump.omnipod.eros.dagger
package info.nightscout.androidaps.plugins.pump.omnipod.common.dagger
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@ -15,7 +15,7 @@ annotation class OmnipodPluginQualifier
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
annotation class ViewModelKey(val value: KClass<out ViewModel>)
// TODO: These annotations and Factories could be used globally -> move to core or app

View file

@ -0,0 +1,75 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.dagger
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.action.InitializePodFragment
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.action.InsertCannulaFragment
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.info.AttachPodFragment
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.info.PodActivatedFragment
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.info.StartPodActivationFragment
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.action.DeactivatePodFragment
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.info.PodDeactivatedFragment
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.info.PodDiscardedFragment
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.info.StartPodDeactivationFragment
import javax.inject.Provider
@Module
abstract class OmnipodWizardModule {
companion object {
@Provides
@OmnipodPluginQualifier
fun providesViewModelFactory(@OmnipodPluginQualifier viewModels: MutableMap<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>): ViewModelProvider.Factory {
return ViewModelFactory(viewModels)
}
}
// #### FRAGMENTS ##############################################################################
// POD ACTIVATION
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesStartPodActivationFragment(): StartPodActivationFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesInitializeActionFragment(): InitializePodFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesAttachPodFragment(): AttachPodFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesInsertCannulaFragment(): InsertCannulaFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesPodActivatedFragment(): PodActivatedFragment
// POD DEACTIVATION
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesStartPodDeactivationFragment(): StartPodDeactivationFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesDeactivatePodFragment(): DeactivatePodFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesPodDeactivatedFragment(): PodDeactivatedFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesPodDiscardedFragment(): PodDiscardedFragment
}

View file

@ -0,0 +1,36 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.definition;
import info.nightscout.androidaps.plugins.pump.omnipod.common.R;
/**
* Created by andy on 4.8.2019
*/
public enum OmnipodCommandType {
INITIALIZE_POD(R.string.omnipod_common_cmd_initialize_pod), // First step of Pod activation
INSERT_CANNULA(R.string.omnipod_common_cmd_insert_cannula), // Second step of Pod activation
DEACTIVATE_POD(R.string.omnipod_common_cmd_deactivate_pod), //
SET_BASAL_PROFILE(R.string.omnipod_common_cmd_set_basal_schedule), //
SET_BOLUS(R.string.omnipod_common_cmd_set_bolus), //
CANCEL_BOLUS(R.string.omnipod_common_cmd_cancel_bolus), //
SET_TEMPORARY_BASAL(R.string.omnipod_common_cmd_set_tbr), //
CANCEL_TEMPORARY_BASAL(R.string.omnipod_common_cmd_cancel_tbr_by_driver), //
DISCARD_POD(R.string.omnipod_common_cmd_discard_pod), //
GET_POD_STATUS(R.string.omnipod_common_cmd_get_pod_status), //
SET_TIME(R.string.omnipod_common_cmd_set_time), //
CONFIGURE_ALERTS(R.string.omnipod_common_cmd_configure_alerts), //
ACKNOWLEDGE_ALERTS(R.string.omnipod_common_cmd_silence_alerts), //
READ_POD_PULSE_LOG(R.string.omnipod_common_cmd_read_pulse_log), //
SUSPEND_DELIVERY(R.string.omnipod_common_cmd_suspend_delivery),
RESUME_DELIVERY(R.string.omnipod_common_cmd_resume_delivery),
PLAY_TEST_BEEP(R.string.omnipod_common_cmd_play_test_beep);
private final int resourceId;
OmnipodCommandType(int resourceId) {
this.resourceId = resourceId;
}
public int getResourceId() {
return resourceId;
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command;
import org.jetbrains.annotations.NotNull;
import info.nightscout.androidaps.queue.commands.CustomCommand;
public final class CommandAcknowledgeAlerts implements CustomCommand {
@NotNull @Override public String getStatusDescription() {
return "ACKNOWLEDGE ALERTS";
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command;
import org.jetbrains.annotations.NotNull;
import info.nightscout.androidaps.queue.commands.CustomCommand;
public final class CommandDeactivatePod implements CustomCommand {
@NotNull @Override public String getStatusDescription() {
return "DEACTIVATE POD";
}
}

View file

@ -0,0 +1,21 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command;
import org.jetbrains.annotations.NotNull;
import info.nightscout.androidaps.queue.commands.CustomCommand;
public final class CommandHandleTimeChange implements CustomCommand {
private final boolean requestedByUser;
public CommandHandleTimeChange(boolean requestedByUser) {
this.requestedByUser = requestedByUser;
}
public boolean isRequestedByUser() {
return requestedByUser;
}
@NotNull @Override public String getStatusDescription() {
return "HANDLE TIME CHANGE";
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command;
import org.jetbrains.annotations.NotNull;
import info.nightscout.androidaps.queue.commands.CustomCommand;
public class CommandPlayTestBeep implements CustomCommand {
@NotNull @Override public String getStatusDescription() {
return "PLAY TEST BEEP";
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command;
import org.jetbrains.annotations.NotNull;
import info.nightscout.androidaps.queue.commands.CustomCommand;
public final class CommandResumeDelivery implements CustomCommand {
@NotNull @Override public String getStatusDescription() {
return "RESUME DELIVERY";
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command;
import org.jetbrains.annotations.NotNull;
import info.nightscout.androidaps.queue.commands.CustomCommand;
public final class CommandSuspendDelivery implements CustomCommand {
@NotNull @Override public String getStatusDescription() {
return "SUSPEND DELIVERY";
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command;
import org.jetbrains.annotations.NotNull;
import info.nightscout.androidaps.queue.commands.CustomCommand;
public final class CommandUpdateAlertConfiguration implements CustomCommand {
@NotNull @Override public String getStatusDescription() {
return "UPDATE ALERT CONFIGURATION";
}
}

View file

@ -0,0 +1,61 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation
import android.os.Bundle
import androidx.annotation.IdRes
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.activity.OmnipodWizardActivityBase
abstract class PodActivationWizardActivity : OmnipodWizardActivityBase() {
companion object {
const val KEY_TYPE = "wizardType"
const val KEY_START_DESTINATION = "startDestination"
}
enum class Type {
SHORT,
LONG
}
@IdRes
private var startDestination: Int = R.id.startPodActivationFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.omnipod_common_pod_activation_wizard_activity)
startDestination = savedInstanceState?.getInt(KEY_START_DESTINATION, R.id.startPodActivationFragment)
?: if (intent.getSerializableExtra(KEY_TYPE) as Type == Type.LONG) {
R.id.startPodActivationFragment
} else {
R.id.attachPodFragment
}
setStartDestination(startDestination)
}
private fun setStartDestination(@IdRes startDestination: Int) {
this.startDestination = startDestination
val navController = getNavController()
val navInflater = navController.navInflater
val graph = navInflater.inflate(R.navigation.omnipod_common_pod_activation_wizard_navigation_graph)
graph.startDestination = startDestination
navController.graph = graph
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(KEY_START_DESTINATION, startDestination)
}
override fun getTotalDefinedNumberOfSteps(): Int = 5
override fun getActualNumberOfSteps(): Int {
if (startDestination == R.id.attachPodFragment) {
return 3
}
return 5
}
}

View file

@ -0,0 +1,29 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.action
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InitializePodViewModel
import javax.inject.Inject
class InitializePodFragment : PodActivationActionFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: InitializePodViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@IdRes
override fun getNextPageActionId(): Int = R.id.action_initializePodFragment_to_attachPodFragment
override fun getIndex(): Int = 2
}

View file

@ -0,0 +1,29 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.action
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InsertCannulaViewModel
import javax.inject.Inject
class InsertCannulaFragment : PodActivationActionFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: InsertCannulaViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@IdRes
override fun getNextPageActionId(): Int = R.id.action_insertCannulaFragment_to_PodActivatedFragment
override fun getIndex(): Int = 4
}

View file

@ -0,0 +1,35 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.action
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Button
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.PodActivationActionViewModelBase
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment.ActionFragmentBase
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.PodDeactivationWizardActivity
abstract class PodActivationActionFragmentBase : ActionFragmentBase() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.omnipod_wizard_button_deactivate_pod).setOnClickListener {
activity?.let {
startActivity(Intent(it, PodDeactivationWizardActivity::class.java))
it.finish()
}
}
}
override fun onFailure() {
(viewModel as? PodActivationActionViewModelBase)?.let { viewModel ->
if (viewModel.isPodDeactivatable() and (viewModel.isPodInAlarm() or viewModel.isPodActivationTimeExceeded())) {
view?.let {
it.findViewById<Button>(R.id.omnipod_wizard_button_retry)?.visibility = View.GONE
it.findViewById<Button>(R.id.omnipod_wizard_button_deactivate_pod)?.visibility = View.VISIBLE
}
}
}
}
}

View file

@ -0,0 +1,50 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.info
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.annotation.IdRes
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.info.AttachPodViewModel
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment.InfoFragmentBase
import javax.inject.Inject
class AttachPodFragment : InfoFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: AttachPodViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@IdRes
override fun getNextPageActionId(): Int = R.id.action_attachPodFragment_to_insertCannulaFragment
override fun getIndex(): Int = 3
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.button_next).setOnClickListener {
context?.let {
AlertDialog.Builder(it)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(getString(getTitleId()))
.setMessage(getString(R.string.omnipod_common_pod_activation_wizard_attach_pod_confirm_insert_cannula_text))
.setPositiveButton(getString(R.string.omnipod_common_ok)) { _, _ -> findNavController().navigate(getNextPageActionId()) }
.setNegativeButton(getString(R.string.omnipod_common_cancel), null)
.show()
}
}
}
}

View file

@ -0,0 +1,29 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.info
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.info.PodActivatedViewModel
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment.InfoFragmentBase
import javax.inject.Inject
class PodActivatedFragment : InfoFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: PodActivatedViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@IdRes
override fun getNextPageActionId(): Int? = null
override fun getIndex(): Int = 5
}

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.info
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.info.StartPodActivationViewModel
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment.InfoFragmentBase
import javax.inject.Inject
class StartPodActivationFragment : InfoFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: StartPodActivationViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@IdRes
override fun getNextPageActionId(): Int = R.id.action_startPodActivationFragment_to_initializePodFragment
override fun getIndex(): Int = 1
}

View file

@ -0,0 +1,3 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action
abstract class InitializePodViewModel : PodActivationActionViewModelBase()

View file

@ -0,0 +1,3 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action
abstract class InsertCannulaViewModel : PodActivationActionViewModelBase()

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ActionViewModelBase
abstract class PodActivationActionViewModelBase : ActionViewModelBase() {
abstract fun isPodInAlarm(): Boolean
abstract fun isPodActivationTimeExceeded(): Boolean
abstract fun isPodDeactivatable(): Boolean
}

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.info
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ViewModelBase
abstract class AttachPodViewModel : ViewModelBase()

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.info
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ViewModelBase
abstract class PodActivatedViewModel : ViewModelBase()

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.info
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ViewModelBase
abstract class StartPodActivationViewModel : ViewModelBase()

View file

@ -1,16 +1,14 @@
package info.nightscout.androidaps.plugins.pump.omnipod.eros.ui.wizard.common.activity
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.activity
import androidx.appcompat.app.AlertDialog
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.plugins.pump.omnipod.eros.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
abstract class OmnipodWizardActivityBase : NoSplashAppCompatActivity() {
override fun onBackPressed() {
exitActivityAfterConfirmation()
}
override fun onBackPressed() = exitActivityAfterConfirmation()
fun exitActivityAfterConfirmation() {
if (getNavController().previousBackStackEntry == null) {
@ -18,10 +16,10 @@ abstract class OmnipodWizardActivityBase : NoSplashAppCompatActivity() {
} else {
AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(getString(R.string.omnipod_wizard_exit_confirmation_title))
.setMessage(getString(R.string.omnipod_wizard_exit_confirmation_text))
.setPositiveButton(getString(R.string.omnipod_yes)) { _, _ -> finish() }
.setNegativeButton(getString(R.string.omnipod_no), null)
.setTitle(getString(R.string.omnipod_common_wizard_exit_confirmation_title))
.setMessage(getString(R.string.omnipod_common_wizard_exit_confirmation_text))
.setPositiveButton(getString(R.string.omnipod_common_yes)) { _, _ -> finish() }
.setNegativeButton(getString(R.string.omnipod_common_no), null)
.show()
}
}

View file

@ -0,0 +1,74 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.LayoutRes
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ActionViewModelBase
import info.nightscout.androidaps.utils.extensions.toVisibility
abstract class ActionFragmentBase : WizardFragmentBase() {
val actionViewModel: ActionViewModelBase
get() = viewModel as ActionViewModelBase
@SuppressLint("CutPasteId")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.navButtonsLayout.buttonNext.isEnabled = false
view.findViewById<TextView>(R.id.omnipod_wizard_action_page_text).setText(getTextId())
view.findViewById<Button>(R.id.omnipod_wizard_button_retry).setOnClickListener { actionViewModel.executeAction() }
actionViewModel.isActionExecutingLiveData.observe(viewLifecycleOwner, { isExecuting ->
if (isExecuting) {
view.findViewById<TextView>(R.id.omnipod_wizard_action_error).visibility = View.GONE
view.findViewById<Button>(R.id.omnipod_wizard_button_deactivate_pod).visibility = View.GONE
view.findViewById<Button>(R.id.omnipod_wizard_button_discard_pod).visibility = View.GONE
view.findViewById<Button>(R.id.omnipod_wizard_button_retry).visibility = View.GONE
}
view.findViewById<ProgressBar>(R.id.omnipod_wizard_action_progress_indication).visibility = isExecuting.toVisibility()
view.findViewById<Button>(R.id.button_cancel).isEnabled = !isExecuting
})
actionViewModel.actionResultLiveData.observe(viewLifecycleOwner, { result ->
result?.let {
val isExecuting = isActionExecuting()
view.findViewById<Button>(R.id.button_next).isEnabled = result.success
view.findViewById<ImageView>(R.id.omnipod_wizard_action_success).visibility = result.success.toVisibility()
view.findViewById<TextView>(R.id.omnipod_wizard_action_error).visibility = (!isExecuting && !result.success).toVisibility()
view.findViewById<Button>(R.id.omnipod_wizard_button_retry).visibility = (!isExecuting && !result.success).toVisibility()
if (!result.success) {
view.findViewById<TextView>(R.id.omnipod_wizard_action_error).text = result.comment
onFailure()
}
}
})
if (savedInstanceState == null && !isActionExecuting()) {
actionViewModel.executeAction()
}
}
protected fun isActionExecuting() = actionViewModel.isActionExecutingLiveData.value!!
override fun onDestroyView() {
super.onDestroyView()
actionViewModel.isActionExecutingLiveData.removeObservers(viewLifecycleOwner)
actionViewModel.actionResultLiveData.removeObservers(viewLifecycleOwner)
}
abstract fun onFailure()
@LayoutRes
override fun getLayoutId(): Int = R.layout.omnipod_common_wizard_action_page_fragment
}

View file

@ -1,11 +1,10 @@
package info.nightscout.androidaps.plugins.pump.omnipod.eros.ui.wizard.common.fragment
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import info.nightscout.androidaps.plugins.pump.omnipod.eros.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
abstract class InfoFragmentBase : WizardFragmentBase() {
@ -15,11 +14,6 @@ abstract class InfoFragmentBase : WizardFragmentBase() {
view.findViewById<TextView>(R.id.omnipod_wizard_info_page_text).setText(getTextId())
}
@StringRes
abstract fun getTextId(): Int
@LayoutRes
override fun getLayoutId(): Int {
return R.layout.omnipod_wizard_info_page_fragment
}
override fun getLayoutId(): Int = R.layout.omnipod_common_wizard_info_page_fragment
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.pump.omnipod.eros.ui.wizard.common.fragment
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment
import android.content.res.ColorStateList
import android.os.Bundle
@ -10,28 +10,33 @@ import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import androidx.navigation.fragment.findNavController
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.plugins.pump.omnipod.eros.R
import info.nightscout.androidaps.plugins.pump.omnipod.eros.databinding.OmnipodWizardBaseFragmentBinding
import info.nightscout.androidaps.plugins.pump.omnipod.eros.ui.wizard.common.activity.OmnipodWizardActivityBase
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.databinding.OmnipodCommonWizardBaseFragmentBinding
import info.nightscout.androidaps.plugins.pump.omnipod.common.databinding.OmnipodCommonWizardProgressIndicationBinding
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.activity.OmnipodWizardActivityBase
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ViewModelBase
import kotlin.math.roundToInt
abstract class WizardFragmentBase : DaggerFragment() {
var _binding: OmnipodWizardBaseFragmentBinding? = null
protected lateinit var viewModel: ViewModelBase
// This property is only valid between onCreateView and
var _binding: OmnipodCommonWizardBaseFragmentBinding? = null
var _progressIndicationBinding: OmnipodCommonWizardProgressIndicationBinding? = null
// These properties are only valid between onCreateView and
// onDestroyView.
val binding get() = _binding!!
val progressIndicationBinding get() = _progressIndicationBinding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = OmnipodWizardBaseFragmentBinding.inflate(inflater, container, false)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
OmnipodCommonWizardBaseFragmentBinding.inflate(inflater, container, false).also {
_binding = it
_progressIndicationBinding = OmnipodCommonWizardProgressIndicationBinding.bind(it.root)
binding.fragmentContent.let {
it.layoutResource = getLayoutId()
it.inflate()
}
return binding.root
}
it.fragmentContent.layoutResource = getLayoutId()
it.fragmentContent.inflate()
}.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -41,7 +46,7 @@ abstract class WizardFragmentBase : DaggerFragment() {
val nextPage = getNextPageActionId()
if (nextPage == null) {
binding.navButtonsLayout.buttonNext.text = getString(R.string.omnipod_wizard_button_finish)
binding.navButtonsLayout.buttonNext.text = getString(R.string.omnipod_common_wizard_button_finish)
binding.navButtonsLayout.buttonNext.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.omnipod_wizard_finish_button, context?.theme))
}
@ -73,7 +78,7 @@ abstract class WizardFragmentBase : DaggerFragment() {
val currentFragment = getIndex() - (it.getTotalDefinedNumberOfSteps() - numberOfSteps)
val progressPercentage = (currentFragment / numberOfSteps.toDouble() * 100).roundToInt()
binding.progressIndicationLayout.progressIndication.progress = progressPercentage
progressIndicationBinding.progressIndication.progress = progressPercentage
}
}
@ -84,7 +89,9 @@ abstract class WizardFragmentBase : DaggerFragment() {
protected abstract fun getNextPageActionId(): Int?
@StringRes
protected abstract fun getTitleId(): Int
protected fun getTitleId(): Int = viewModel.getTitleId()
@StringRes protected fun getTextId(): Int = viewModel.getTextId()
protected abstract fun getIndex(): Int
}

View file

@ -1,15 +1,14 @@
package info.nightscout.androidaps.plugins.pump.omnipod.eros.ui.wizard.common.viewmodel
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import info.nightscout.androidaps.data.PumpEnactResult
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.SingleSubject
abstract class ActionViewModelBase : ViewModel() {
abstract class ActionViewModelBase : ViewModelBase() {
private val _isActionExecutingLiveData = MutableLiveData<Boolean>(false)
private val _isActionExecutingLiveData = MutableLiveData(false)
val isActionExecutingLiveData: LiveData<Boolean> = _isActionExecutingLiveData
private val _actionResultLiveData = MutableLiveData<PumpEnactResult?>(null)
@ -17,7 +16,7 @@ abstract class ActionViewModelBase : ViewModel() {
fun executeAction() {
_isActionExecutingLiveData.postValue(true)
SingleSubject.fromCallable<PumpEnactResult>(this::doExecuteAction)
SingleSubject.fromCallable(this::doExecuteAction)
.subscribeOn(Schedulers.io())
.doOnSuccess { result ->
_isActionExecutingLiveData.postValue(false)

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel
import androidx.annotation.StringRes
import androidx.lifecycle.ViewModel
abstract class ViewModelBase : ViewModel() {
@StringRes abstract fun getTitleId(): Int
@StringRes abstract fun getTextId(): Int
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation
import android.os.Bundle
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.activity.OmnipodWizardActivityBase
abstract class PodDeactivationWizardActivity : OmnipodWizardActivityBase() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.omnipod_common_pod_deactivation_wizard_activity)
}
override fun getTotalDefinedNumberOfSteps(): Int = 3
override fun getActualNumberOfSteps(): Int = 3
}

View file

@ -0,0 +1,58 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.action
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.annotation.IdRes
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment.ActionFragmentBase
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.viewmodel.action.DeactivatePodViewModel
import javax.inject.Inject
class DeactivatePodFragment : ActionFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var buttonDiscardPod: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: DeactivatePodViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.omnipod_wizard_button_discard_pod)?.setOnClickListener {
context?.let {
AlertDialog.Builder(it)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(getString(R.string.omnipod_common_pod_deactivation_wizard_discard_pod))
.setMessage(getString(R.string.omnipod_common_pod_deactivation_wizard_discard_pod_confirmation))
.setPositiveButton(getString(R.string.omnipod_common_yes)) { _, _ ->
(viewModel as DeactivatePodViewModel).discardPod()
findNavController().navigate(R.id.action_deactivatePodFragment_to_podDiscardedFragment)
}
.setNegativeButton(getString(R.string.omnipod_common_no), null)
.show()
}
}
}
override fun onFailure() {
buttonDiscardPod.visibility = View.VISIBLE
}
@IdRes
override fun getNextPageActionId(): Int = R.id.action_deactivatePodFragment_to_podDeactivatedFragment
override fun getIndex(): Int = 2
}

View file

@ -0,0 +1,29 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.info
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment.InfoFragmentBase
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.viewmodel.info.PodDeactivatedViewModel
import javax.inject.Inject
class PodDeactivatedFragment : InfoFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: PodDeactivatedViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@IdRes
override fun getNextPageActionId(): Int? = null
override fun getIndex(): Int = 3
}

View file

@ -0,0 +1,29 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.info
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment.InfoFragmentBase
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.viewmodel.info.PodDiscardedViewModel
import javax.inject.Inject
class PodDiscardedFragment : InfoFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: PodDiscardedViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@IdRes
override fun getNextPageActionId(): Int? = null
override fun getIndex(): Int = 3
}

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.info
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import info.nightscout.androidaps.plugins.pump.omnipod.common.R
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.fragment.InfoFragmentBase
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.viewmodel.info.StartPodDeactivationViewModel
import javax.inject.Inject
class StartPodDeactivationFragment : InfoFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: StartPodDeactivationViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@IdRes
override fun getNextPageActionId(): Int = R.id.action_startPodDeactivationFragment_to_deactivatePodFragment
override fun getIndex(): Int = 1
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.viewmodel.action
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ActionViewModelBase
abstract class DeactivatePodViewModel : ActionViewModelBase() {
abstract fun discardPod()
}

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.viewmodel.info
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ViewModelBase
abstract class PodDeactivatedViewModel : ViewModelBase()

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.viewmodel.info
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ViewModelBase
abstract class PodDiscardedViewModel : ViewModelBase()

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.viewmodel.info
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.common.viewmodel.ViewModelBase
abstract class StartPodDeactivationViewModel : ViewModelBase()

View file

@ -0,0 +1,68 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button_refresh_status"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawableTop="@drawable/ic_omnipod_overview_refresh_pod_status"
android:drawablePadding="@dimen/omnipod_icon_button_drawable_padding"
android:text="@string/refresh" />
<Button
android:id="@+id/button_pod_management"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawableTop="@drawable/ic_omnipod_overview_pod_management"
android:drawablePadding="@dimen/omnipod_icon_button_drawable_padding"
android:text="@string/omnipod_common_overview_button_pod_management" />
<Button
android:id="@+id/button_silence_alerts"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawableTop="@drawable/ic_omnipod_overview_silence_alerts"
android:drawablePadding="@dimen/omnipod_icon_button_drawable_padding"
android:text="@string/omnipod_common_overview_button_silence_alerts" />
<Button
android:id="@+id/button_set_time"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawableTop="@drawable/ic_omnipod_overview_set_time"
android:drawablePadding="@dimen/omnipod_icon_button_drawable_padding"
android:text="@string/omnipod_common_overview_button_set_time"
android:visibility="gone" />
<Button
android:id="@+id/button_resume_delivery"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawableTop="@drawable/ic_omnipod_overview_resume_delivery"
android:drawablePadding="@dimen/omnipod_icon_button_drawable_padding"
android:text="@string/omnipod_common_overview_button_resume_delivery"
android:visibility="gone" />
<Button
android:id="@+id/button_suspend_delivery"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawableTop="@drawable/ic_omnipod_overview_suspend_delivery"
android:drawablePadding="@dimen/omnipod_icon_button_drawable_padding"
android:text="@string/omnipod_common_overview_button_suspend_delivery"
android:visibility="gone" />
</merge>

View file

@ -0,0 +1,692 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Unique ID -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_pod_unique_id"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/pod_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<!-- Pod Lot -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_lot_number"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/pod_lot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<!-- Pod Tid -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_pod_sequence_number"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/pod_tid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<!-- Pod fw version -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_firmware_version"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/firmware_version"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<!-- Pod Time -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_time_on_pod"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/time_on_pod"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<!-- Pod Expires -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_pod_expiry_date"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/pod_expiry_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<!-- Pod Status -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_pod_status"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/pod_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text=""
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<TextView
android:id="@+id/queue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""
android:textAlignment="center" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_last_connection"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/last_connection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_last_bolus"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/last_bolus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_base_basal_rate"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/base_basal_rate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_temp_basal_rate"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/temp_basal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_reservoir"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/reservoir"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_total_delivered"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/total_delivered"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_errors"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/errors"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_common_overview_pod_active_alerts"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/pod_active_alerts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:paddingTop="10dp"
android:rotationX="180"
android:rotationY="180"
app:drawableTopCompat="@drawable/ic_pod" />
</merge>

View file

@ -11,5 +11,5 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/omnipod_pod_deactivation_wizard_navigation_graph" />
app:navGraph="@navigation/omnipod_common_pod_deactivation_wizard_navigation_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -56,7 +56,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@string/omnipod_wizard_button_deactivate_pod"
android:text="@string/omnipod_common_wizard_button_deactivate_pod"
android:visibility="gone" />
<Button
@ -64,7 +64,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@string/omnipod_wizard_button_discard_pod"
android:text="@string/omnipod_common_wizard_button_discard_pod"
android:visibility="gone" />
<Button
@ -72,7 +72,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@string/omnipod_wizard_button_retry"
android:text="@string/omnipod_common_wizard_button_retry"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>

View file

@ -5,9 +5,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/progress_indication_layout"
layout="@layout/omnipod_wizard_progress_indication" />
<include layout="@layout/omnipod_common_wizard_progress_indication" />
<TextView
android:id="@+id/fragment_title"
@ -30,5 +28,5 @@
<include
android:id="@+id/nav_buttons_layout"
layout="@layout/omnipod_wizard_nav_buttons" />
layout="@layout/omnipod_common_wizard_nav_buttons" />
</LinearLayout>

View file

@ -9,12 +9,12 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/omnipod_wizard_button_cancel" />
android:text="@string/omnipod_common_wizard_button_cancel" />
<Button
android:id="@+id/button_next"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/omnipod_wizard_button_next" />
android:text="@string/omnipod_common_wizard_button_next" />
</LinearLayout>

View file

@ -1,8 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ProgressBar
android:id="@+id/progress_indication"
@ -13,4 +10,4 @@
android:progress="0"
android:progressTint="@color/omnipod_wizard_progress_bar"
android:scaleY=".5" />
</LinearLayout>
</merge>

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:startDestination="@id/startPodActivationFragment">
<fragment
android:id="@+id/startPodActivationFragment"
android:name="info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.info.StartPodActivationFragment"
android:label="StartPodActivationFragment">
<action
android:id="@+id/action_startPodActivationFragment_to_initializePodFragment"
app:destination="@id/initializePodFragment" />
</fragment>
<fragment
android:id="@+id/initializePodFragment"
android:name="info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.action.InitializePodFragment"
android:label="InitializePodFragment">
<action
android:id="@+id/action_initializePodFragment_to_attachPodFragment"
app:destination="@id/attachPodFragment" />
</fragment>
<fragment
android:id="@+id/attachPodFragment"
android:name="info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.info.AttachPodFragment"
android:label="AttachPodFragment">
<action
android:id="@+id/action_attachPodFragment_to_insertCannulaFragment"
app:destination="@id/insertCannulaFragment" />
</fragment>
<fragment
android:id="@+id/insertCannulaFragment"
android:name="info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.action.InsertCannulaFragment"
android:label="InsertCannulaFragment">
<action
android:id="@+id/action_insertCannulaFragment_to_PodActivatedFragment"
app:destination="@id/PodActivatedFragment" />
</fragment>
<fragment
android:id="@+id/PodActivatedFragment"
android:name="info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.fragment.info.PodActivatedFragment"
android:label="PodActivatedFragment" />
</navigation>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:startDestination="@id/startPodDeactivationFragment">
<fragment
android:id="@+id/startPodDeactivationFragment"
android:name="info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.info.StartPodDeactivationFragment"
android:label="StartPodDeactivationFragment">
<action
android:id="@+id/action_startPodDeactivationFragment_to_deactivatePodFragment"
app:destination="@id/deactivatePodFragment" />
</fragment>
<fragment
android:id="@+id/deactivatePodFragment"
android:name="info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.action.DeactivatePodFragment"
android:label="DeactivatePodFragment">
<action
android:id="@+id/action_deactivatePodFragment_to_podDeactivatedFragment"
app:destination="@id/podDeactivatedFragment" />
<action
android:id="@+id/action_deactivatePodFragment_to_podDiscardedFragment"
app:destination="@id/podDiscardedFragment" />
</fragment>
<fragment
android:id="@+id/podDeactivatedFragment"
android:name="info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.info.PodDeactivatedFragment"
android:label="PodDeactivatedFragment" />
<fragment
android:id="@+id/podDiscardedFragment"
android:name="info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.deactivation.fragment.info.PodDiscardedFragment"
android:label="PodDiscardedFragment" />
</navigation>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Button icons - general -->
<color name="pod_icon_outline">#9E9E9E</color>
<!-- Omnipod Wizard -->
<color name="omnipod_wizard_progress_bar">#0099CC</color>
<color name="omnipod_wizard_finish_button">#ff669900</color>
<color name="icon_omnipod_wizard_success">#77DD77</color>
<!-- Omnipod tab - Button icons -->
<color name="ic_omnipod_overview_acknowledge_alerts">#FF4444</color>
<color name="ic_omnipod_overview_acknowledge_alerts_outline">@color/pod_icon_outline</color>
<color name="ic_omnipod_overview_pod_management">#4CAF50</color>
<color name="ic_omnipod_overview_pod_management_graph">@color/pod_icon_outline</color>
<color name="ic_omnipod_overview_pod_management_outline">@color/pod_icon_outline</color>
<color name="ic_omnipod_overview_refresh_pod_status">#42A5F5</color>
<color name="ic_omnipod_overview_refresh_pod_status_outline">@color/pod_icon_outline</color>
<color name="ic_omnipod_overview_resume_delivery">#67E86A</color>
<color name="ic_omnipod_overview_resume_delivery_outline">@color/pod_icon_outline</color>
<color name="ic_omnipod_overview_set_time">#B39DDB</color>
<color name="ic_omnipod_overview_set_time_outline">@color/pod_icon_outline</color>
<color name="ic_omnipod_overview_suspend_delivery">#ffbf00</color>
<color name="ic_omnipod_overview_suspend_delivery_outline">@color/pod_icon_outline</color>
<!-- Pod Management - Button icons -->
<color name="ic_pod_management_activate_pod">#67E86A</color>
<color name="ic_pod_management_activate_pod_outline">@color/pod_icon_outline</color>
<color name="ic_pod_management_deactivate_pod">#FF4444</color>
<color name="ic_pod_management_deactivate_pod_outline">@color/pod_icon_outline</color>
<color name="ic_pod_management_discard_pod">#85F803</color>
<color name="ic_pod_management_discard_pod_outline">@color/pod_icon_outline</color>
<color name="ic_pod_management_play_test_beep">#ffbf00</color>
<color name="ic_pod_management_play_test_beep_outline">@color/pod_icon_outline</color>
<color name="ic_pod_management_pulse_log">#4DD0E1</color>
<color name="ic_pod_management_pulse_log_outline">@color/pod_icon_outline</color>
<color name="ic_pod_management_pod_history">#FB8C00</color>
<color name="ic_pod_management_pod_history_outline">@color/pod_icon_outline</color>
</resources>

View file

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Omnipod - Keys -->
<string name="key_omnipod_common_bolus_beeps_enabled" translatable="false">AAPS.Omnipod.bolus_beeps_enabled</string>
<string name="key_omnipod_common_basal_beeps_enabled" translatable="false">AAPS.Omnipod.basal_beeps_enabled</string>
<string name="key_omnipod_common_smb_beeps_enabled" translatable="false">AAPS.Omnipod.smb_beeps_enabled</string>
<string name="key_omnipod_common_tbr_beeps_enabled" translatable="false">AAPS.Omnipod.tbr_beeps_enabled</string>
<string name="key_omnipod_common_suspend_delivery_button_enabled" translatable="false">AAPS.Omnipod.suspend_delivery_button_enabled</string>
<string name="key_omnipod_common_time_change_event_enabled" translatable="false">AAPS.Omnipod.time_change_enabled</string>
<string name="key_omnipod_common_expiration_reminder_enabled" translatable="false">AAPS.Omnipod.expiration_reminder_enabled</string>
<string name="key_omnipod_common_expiration_reminder_hours_before_shutdown" translatable="false">AAPS.Omnipod.expiration_reminder_hours_before_shutdown</string>
<string name="key_omnipod_common_low_reservoir_alert_enabled" translatable="false">AAPS.Omnipod.low_reservoir_alert_enabled</string>
<string name="key_omnipod_common_low_reservoir_alert_units" translatable="false">AAPS.Omnipod.low_reservoir_alert_units</string>
<string name="key_omnipod_common_automatically_silence_alerts_enabled" translatable="false">AAPS.Omnipod.automatically_acknowledge_alerts_enabled</string>
<!-- Omnipod - Pod Management -->
<string name="omnipod_common_pod_management_title">Pod Management</string>
<string name="omnipod_common_pod_management_heading_actions">Actions</string>
<string name="omnipod_common_pod_management_heading_tools">Tools</string>
<string name="omnipod_common_pod_management_button_activate_pod">Activate Pod</string>
<string name="omnipod_common_pod_management_button_deactivate_pod">Deactivate Pod</string>
<string name="omnipod_common_pod_management_button_discard_pod">Discard Pod</string>
<string name="omnipod_common_pod_management_discard_pod_confirmation">If you discard the Pod, you will not be able to communicate with it anymore. You should only do this when all communication with the Pod persistently fails. If you can still communicate with the Pod, please use the <b>Deactivate Pod</b> option.\n\nIf you wish to proceed, please make sure to remove the Pod from your body!</string>
<string name="omnipod_common_pod_management_button_play_test_beep">Play Test Beep</string>
<string name="omnipod_common_pod_management_button_playing_test_beep">Playing Test Beep…</string>
<string name="omnipod_common_pod_management_button_pod_history">Pod History</string>
<!-- Omnipod - Error -->
<string name="omnipod_common_error_pod_not_attached">No active Pod</string>
<string name="omnipod_common_error_set_basal_failed">Setting basal profile failed.</string>
<string name="omnipod_common_error_bolus_did_not_succeed">Bolus did not succeed.</string>
<string name="omnipod_common_error_failed_to_set_profile_empty_profile">Failed to set basal profile: received an empty profile. Make sure to activate your basal profile.</string>
<string name="omnipod_common_error_set_initial_basal_schedule_no_profile">No basal profile is active. Make sure to activate your basal profile.</string>
<string name="omnipod_common_error_unsupported_custom_command">Unsupported custom command: %1$s</string>
<string name="omnipod_common_error_failed_to_refresh_status">Failed to refresh status</string>
<string name="omnipod_common_error_failed_to_refresh_status_on_startup">Failed to refresh status on startup</string>
<string name="omnipod_common_error_failed_to_silence_alerts">Failed to silence alerts</string>
<string name="omnipod_common_error_failed_to_suspend_delivery">Failed to suspend delivery</string>
<string name="omnipod_common_error_failed_to_set_time">Failed to set time</string>
<string name="omnipod_common_error_failed_to_resume_delivery">Failed to resume delivery</string>
<string name="omnipod_common_error_failed_to_initialize_pod">Failed to initialize the Pod</string>
<string name="omnipod_common_error_failed_to_insert_cannula">Failed to insert cannula</string>
<string name="omnipod_common_error_pod_fault_activation_time_exceeded">The Pod\'s activation time has been exceeded. This Pod can no longer be activated.</string>
<string name="omnipod_common_error_failed_to_verify_activation_progress">Failed to verify activation progress. Please retry.</string>
<string name="omnipod_common_error_pod_suspended">Pod suspended</string>
<string name="omnipod_common_error_failed_to_play_test_beep">Failed to play test beep</string>
<string name="omnipod_common_error_time_out_of_sync">The time on the Pod is out of sync. Please update the time in the Omnipod tab.</string>
<string name="omnipod_common_error_unexpected_exception">An unexpected error occurred. Please report! (%1$s: %2$s).</string>
<!-- Omnipod - Confirmation -->
<string name="omnipod_common_confirmation">Confirmation</string>
<string name="omnipod_common_confirmation_time_or_timezone_change">Time and/or time zone changed on the Pod.</string>
<string name="omnipod_common_confirmation_expiration_alerts_updated">Alert configuration has been updated in the Pod.</string>
<string name="omnipod_common_confirmation_time_on_pod_updated">The time on the Pod has been updated.</string>
<string name="omnipod_common_confirmation_suspended_delivery">All insulin delivery has been suspended.</string>
<string name="omnipod_common_confirmation_silenced_alerts">Active alerts have been silenced.</string>
<string name="omnipod_common_confirmation_delivery_resumed">Insulin delivery has been resumed.</string>
<!-- Omnipod - Overview -->
<string name="omnipod_common_overview_button_set_time">Set time</string>
<string name="omnipod_common_overview_button_suspend_delivery">Suspend</string>
<string name="omnipod_common_overview_button_resume_delivery">Resume Delivery</string>
<string name="omnipod_common_overview_button_pod_management">Pod Mgmt</string>
<string name="omnipod_common_overview_button_silence_alerts">Silence Alerts</string>
<string name="omnipod_common_overview_pod_status">Pod Status</string>
<string name="omnipod_common_overview_total_delivered">Total Delivered</string>
<string name="omnipod_common_overview_total_delivered_value">%1$.2f U</string>
<string name="omnipod_common_overview_pod_unique_id">Unique ID</string>
<string name="omnipod_common_overview_lot_number">LOT Number</string>
<string name="omnipod_common_overview_pod_sequence_number">Sequence Number</string>
<string name="omnipod_common_overview_pod_expiry_date">Pod Expires</string>
<string name="omnipod_common_overview_last_connection">Last Connection</string>
<string name="omnipod_common_overview_last_bolus">Last Bolus</string>
<string name="omnipod_common_overview_temp_basal_rate">Temp Basal Rate</string>
<string name="omnipod_common_overview_base_basal_rate">Base Basal Rate</string>
<string name="omnipod_common_overview_reservoir">Reservoir</string>
<string name="omnipod_common_overview_pod_active_alerts">Active Pod Alerts</string>
<string name="omnipod_common_overview_firmware_version">Firmware Version</string>
<string name="omnipod_common_overview_time_on_pod">Time on Pod</string>
<string name="omnipod_common_overview_temp_basal_value">%1$.2fU/h @%2$s (%3$d/%4$d minutes)</string>
<string name="omnipod_common_overview_reservoir_value">%1$.2f U left</string>
<string name="omnipod_common_overview_reservoir_value_over50">Over 50 U left</string>
<string name="omnipod_common_overview_errors">Errors</string>
<string name="omnipod_common_overview_last_bolus_value" translatable="false">%1$.2f %2$s (%3$s)</string>
<!-- Omnipod - Wizard Base -->
<string name="omnipod_common_wizard_button_cancel">Cancel</string>
<string name="omnipod_common_wizard_button_finish">Finish</string>
<string name="omnipod_common_wizard_button_next">Next</string>
<string name="omnipod_common_wizard_button_retry">Retry</string>
<string name="omnipod_common_wizard_button_deactivate_pod">Deactivate Pod</string>
<string name="omnipod_common_wizard_button_discard_pod">Discard Pod</string>
<string name="omnipod_common_wizard_exit_confirmation_text">You haven\'t completed all steps yet. Are you sure you want to exit?</string>
<string name="omnipod_common_wizard_exit_confirmation_title">Exit</string>
<!-- Omnipod - Pod Activation Wizard -->
<string name="omnipod_common_pod_activation_wizard_start_pod_activation_title">Fill Pod</string>
<string name="omnipod_common_pod_activation_wizard_initialize_pod_title">Initialize Pod</string>
<string name="omnipod_common_pod_activation_wizard_attach_pod_title">Attach Pod</string>
<string name="omnipod_common_pod_activation_wizard_attach_pod_text">Prepare the infusion site. Remove the Pod\'s needle cap and adhesive backing and attach the Pod to the infusion site.\n\nIf the cannula sticks out, please press <b>Cancel</b> and discard your Pod.\n\nPress <b>Next</b> to insert the cannula and begin basal delivery.</string>
<string name="omnipod_common_pod_activation_wizard_attach_pod_confirm_insert_cannula_text">When you press <b>OK</b>, the cannula will be inserted. Make sure that you have attached the Pod to the infusion site.</string>
<string name="omnipod_common_pod_activation_wizard_insert_cannula_title">Insert Cannula</string>
<string name="omnipod_common_pod_activation_wizard_insert_cannula_text">Trying to set initial basal schedule and insert the cannula.\n\nWhen the cannula has successfully been inserted, you can press <b>Next</b>.</string>
<string name="omnipod_common_pod_activation_wizard_pod_activated_title">Pod Activated</string>
<string name="omnipod_common_pod_activation_wizard_pod_activated_text">The new Pod is now active.\n\nYour basal schedule has been programmed and the cannula has been inserted.\n\nPlease verify that the cannula has been inserted correctly and change your Pod if you think it has not.</string>
<!-- Omnipod - Pod Deactivation Wizard -->
<string name="omnipod_common_pod_deactivation_wizard_start_pod_deactivation_title">Deactivate Pod</string>
<string name="omnipod_common_pod_deactivation_wizard_start_pod_deactivation_text">Press <b>Next</b> to deactivate the Pod.\n\n<b>Note:</b> This will suspend all insulin delivery and deactivate the Pod.</string>
<string name="omnipod_common_pod_deactivation_wizard_deactivating_pod_title">Deactivating Pod</string>
<string name="omnipod_common_pod_deactivation_wizard_deactivating_pod_text">Deactivating the Pod.\n\nWhen deactivation has completed successfully, you can press <b>Next</b>.</string>
<string name="omnipod_common_pod_deactivation_wizard_pod_deactivated_title">Pod Deactivated</string>
<string name="omnipod_common_pod_deactivation_wizard_pod_deactivated_text">Your Pod has been deactivated.\n\nPlease remove the Pod from your body and recycle it.</string>
<string name="omnipod_common_pod_deactivation_wizard_pod_discarded_title">Pod Discarded</string>
<string name="omnipod_common_pod_deactivation_wizard_pod_discarded_text">The Pod state has been discarded. Insulin delivery has not been suspended because the Pod has not been properly deactivated!\n\nPlease remove the Pod from your body and recycle it.</string>
<string name="omnipod_common_pod_deactivation_wizard_discard_pod_confirmation">If you discard the Pod, you will not be able to communicate with it anymore. You should only do this when all communication with the Pod persistently fails. Are you sure you want to discard the Pod?</string>
<string name="omnipod_common_pod_deactivation_wizard_discard_pod">Discard Pod</string>
<!-- Omnipod - Preferences -->
<string name="omnipod_common_preferences_bolus_beeps_enabled">Bolus beeps enabled</string>
<string name="omnipod_common_preferences_basal_beeps_enabled">Basal beeps enabled</string>
<string name="omnipod_common_preferences_smb_beeps_enabled">SMB beeps enabled</string>
<string name="omnipod_common_preferences_tbr_beeps_enabled">TBR beeps enabled</string>
<string name="omnipod_common_preferences_suspend_delivery_button_enabled">Show Suspend Delivery button in Omnipod tab</string>
<string name="omnipod_common_preferences_time_change_enabled">DST/Time zone detection enabled</string>
<string name="omnipod_common_preferences_expiration_reminder_enabled">Expiration reminder enabled</string>
<string name="omnipod_common_preferences_expiration_reminder_hours_before_shutdown">Hours before shutdown</string>
<string name="omnipod_common_preferences_low_reservoir_alert_enabled">Low reservoir alert enabled</string>
<string name="omnipod_common_preferences_low_reservoir_alert_units">Number of units</string>
<string name="omnipod_common_preferences_automatically_silence_alerts">Automatically silence Pod alerts</string>
<string name="omnipod_common_preferences_category_other">Other</string>
<string name="omnipod_common_preferences_category_alerts">Alerts</string>
<string name="omnipod_common_preferences_category_confirmation_beeps">Confirmation Beeps</string>
<!-- Omnipod - Pod Status -->
<string name="omnipod_common_pod_status_no_active_pod">No Active Pod</string>
<string name="omnipod_common_pod_status_waiting_for_activation">Setup in progress (waiting for Pod activation)</string>
<string name="omnipod_common_pod_status_waiting_for_cannula_insertion">Setup in progress (waiting for cannula insertion)</string>
<string name="omnipod_common_pod_status_running">Running</string>
<string name="omnipod_common_pod_status_suspended">Suspended</string>
<string name="omnipod_common_pod_status_pod_fault">Pod Fault</string>
<string name="omnipod_common_pod_status_activation_time_exceeded">Activation time exceeded</string>
<string name="omnipod_common_pod_status_inactive">Inactive</string>
<string name="omnipod_common_pod_status_pod_fault_description">Pod Fault: %1$03d %2$s</string>
<!-- Omnipod - Commands -->
<string name="omnipod_common_cmd_deactivate_pod">Deactivate Pod</string>
<string name="omnipod_common_cmd_discard_pod">Discard Pod</string>
<string name="omnipod_common_cmd_set_bolus">Set Bolus</string>
<string name="omnipod_common_cmd_cancel_bolus">Cancel Bolus</string>
<string name="omnipod_common_cmd_set_tbr">Set Temporary Basal</string>
<string name="omnipod_common_cmd_cancel_tbr_by_driver">Cancel Temporary Basal (internally by driver)</string>
<string name="omnipod_common_cmd_cancel_tbr">Cancel Temporary Basal</string>
<string name="omnipod_common_cmd_set_basal_schedule">Set Basal Schedule</string>
<string name="omnipod_common_cmd_get_pod_status">Get Pod Status</string>
<string name="omnipod_common_cmd_get_pod_info">Get Pod Info</string>
<string name="omnipod_common_cmd_set_time">Set Time</string>
<string name="omnipod_common_cmd_configure_alerts">Configure Alerts</string>
<string name="omnipod_common_cmd_silence_alerts">Silence Alerts</string>
<string name="omnipod_common_cmd_suspend_delivery">Suspend Delivery</string>
<string name="omnipod_common_cmd_resume_delivery">Resume Delivery</string>
<string name="omnipod_common_cmd_unknown_entry">Unknown Entry</string>
<string name="omnipod_common_cmd_initialize_pod">Initialize Pod</string>
<string name="omnipod_common_cmd_insert_cannula">Insert Cannula</string>
<string name="omnipod_common_cmd_read_pulse_log">Read Pulse Log</string>
<string name="omnipod_common_cmd_set_fake_suspended_tbr">Set fake temporary basal because the Pod is suspended</string>
<string name="omnipod_common_cmd_cancel_fake_suspended_tbr">Cancel fake temporary basal that was created because the Pod was suspended</string>
<string name="omnipod_common_cmd_split_tbr">Split temporary basal because of uncertain failure in cancellation</string>
<string name="omnipod_common_cmd_beep_config">Beep Config</string>
<string name="omnipod_common_cmd_play_test_beep">Play Test Beep</string>
<!-- Omnipod - Alerts -->
<string name="omnipod_common_alert_finish_pairing_reminder">Finish pairing reminder</string>
<string name="omnipod_common_alert_finish_setup_reminder_reminder">Finish setup reminder</string>
<string name="omnipod_common_alert_expiration">Pod will expire soon</string>
<string name="omnipod_common_alert_expiration_advisory">Pod will expire soon</string>
<string name="omnipod_common_alert_shutdown_imminent">Shutdown is imminent</string>
<string name="omnipod_common_alert_low_reservoir">Low Reservoir</string>
<string name="omnipod_common_alert_unknown_alert">Unknown Alert</string>
<!-- Omnipod - Short status -->
<string name="omnipod_common_short_status_no_active_pod">No Active Pod</string>
<string name="omnipod_common_short_status_last_connection">LastConn: %1$d min ago</string>
<string name="omnipod_common_short_status_last_bolus">LastBolus: %1$s @ %2$s</string>
<string name="omnipod_common_short_status_temp_basal">Temp: %1$s</string>
<string name="omnipod_common_short_status_extended_bolus">Extended: %1$s</string>
<string name="omnipod_common_short_status_reservoir">Reserv: %1$sU</string>
<!-- Omnipod - Other -->
<string name="omnipod_common_yes">Yes</string>
<string name="omnipod_common_no">No</string>
<string name="omnipod_common_ok">OK</string>
<string name="omnipod_common_cancel">Cancel</string>
<string name="omnipod_common_warning">Warning</string>
<string name="omnipod_common_two_strings_concatenated_by_colon" translatable="false">%1$s: %2$s</string>
<!-- Omnipod - Times -->
<string name="omnipod_common_time_with_timezone" translatable="false">%1$s (%2$s)</string>
<string name="omnipod_common_moments_ago">Moments ago</string>
<string name="omnipod_common_less_than_a_minute_ago">Less than a minute ago</string>
<string name="omnipod_common_composite_time">%1$s and %2$s</string>
<string name="omnipod_common_time_ago">%1$s ago</string>
<plurals name="omnipod_common_minutes">
<item quantity="one">%1$d minute</item>
<item quantity="other">%1$d minutes</item>
</plurals>
<plurals name="omnipod_common_hours">
<item quantity="one">%1$d hour</item>
<item quantity="other">%1$d hours</item>
</plurals>
<plurals name="omnipod_common_days">
<item quantity="one">%1$d day</item>
<item quantity="other">%1$d days</item>
</plurals>
<plurals name="omnipod_common_pod_alerts">
<item quantity="one">Pod Alert: %1$s</item>
<item quantity="other">Pod Alerts: %1$s</item>
</plurals>
</resources>

View file

@ -16,4 +16,5 @@ android {
dependencies {
implementation project(':core')
implementation project(':omnipod-common')
}

View file

@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="info.nightscout.androidaps.plugins.pump.omnipod.dash">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="info.nightscout.androidaps.plugins.pump.omnipod.dash">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

View file

@ -1,5 +1,207 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash;
public class OmnipodDashPumpPlugin {
// TODO
import androidx.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.interfaces.PumpPluginBase;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.common.ManufacturerType;
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction;
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment;
import info.nightscout.androidaps.queue.commands.CustomCommand;
import info.nightscout.androidaps.utils.TimeChangeType;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
@Singleton
public class OmnipodDashPumpPlugin extends PumpPluginBase implements PumpInterface {
private static final PumpDescription PUMP_DESCRIPTION = new PumpDescription(PumpType.Omnipod_Dash);
private final AAPSLogger aapsLogger;
private final ResourceHelper resourceHelper;
private final CommandQueueProvider commandQueue;
@Inject
public OmnipodDashPumpPlugin(HasAndroidInjector injector, AAPSLogger aapsLogger, ResourceHelper resourceHelper, CommandQueueProvider commandQueue) {
super(new PluginDescription() //
.mainType(PluginType.PUMP) //
.fragmentClass(OmnipodDashOverviewFragment.class.getName()) //
.pluginIcon(R.drawable.ic_pod_128)
.pluginName(R.string.omnipod_dash_name) //
.shortName(R.string.omnipod_dash_name_short) //
.preferencesId(R.xml.omnipod_dash_preferences) //
.description(R.string.omnipod_dash_pump_description), injector, aapsLogger, resourceHelper, commandQueue);
this.aapsLogger = aapsLogger;
this.resourceHelper = resourceHelper;
this.commandQueue = commandQueue;
}
@Override public boolean isInitialized() {
return false;
}
@Override public boolean isSuspended() {
return false;
}
@Override public boolean isBusy() {
return false;
}
@Override public boolean isConnected() {
return false;
}
@Override public boolean isConnecting() {
return false;
}
@Override public boolean isHandshakeInProgress() {
return false;
}
@Override public void finishHandshaking() {
}
@Override public void connect(@NotNull String reason) {
}
@Override public void disconnect(@NotNull String reason) {
}
@Override public void stopConnecting() {
}
@Override public void getPumpStatus(@NotNull String reason) {
}
@NotNull @Override public PumpEnactResult setNewBasalProfile(@NotNull Profile profile) {
return null;
}
@Override public boolean isThisProfileSet(@NotNull Profile profile) {
return false;
}
@Override public long lastDataTime() {
return 0;
}
@Override public double getBaseBasalRate() {
return 0;
}
@Override public double getReservoirLevel() {
return 0;
}
@Override public int getBatteryLevel() {
return 0;
}
@NotNull @Override public PumpEnactResult deliverTreatment(@NotNull DetailedBolusInfo detailedBolusInfo) {
return null;
}
@Override public void stopBolusDelivering() {
}
@NotNull @Override public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NotNull Profile profile, boolean enforceNew) {
return null;
}
@NotNull @Override public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NotNull Profile profile, boolean enforceNew) {
return null;
}
@NotNull @Override public PumpEnactResult setExtendedBolus(double insulin, int durationInMinutes) {
return null;
}
@NotNull @Override public PumpEnactResult cancelTempBasal(boolean enforceNew) {
return null;
}
@NotNull @Override public PumpEnactResult cancelExtendedBolus() {
return null;
}
@NotNull @Override public JSONObject getJSONStatus(@NotNull Profile profile, @NotNull String profileName, @NotNull String version) {
return null;
}
@NotNull @Override public ManufacturerType manufacturer() {
return getPumpDescription().pumpType.getManufacturer();
}
@NotNull @Override public PumpType model() {
return getPumpDescription().pumpType;
}
@NotNull @Override public String serialNumber() {
return null;
}
@NotNull @Override public PumpDescription getPumpDescription() {
return PUMP_DESCRIPTION;
}
@NotNull @Override public String shortStatus(boolean veryShort) {
return null;
}
@Override public boolean isFakingTempsByExtendedBoluses() {
return false;
}
@NotNull @Override public PumpEnactResult loadTDDs() {
return null;
}
@Override public boolean canHandleDST() {
return false;
}
@Override
public List<CustomAction> getCustomActions() {
return Collections.emptyList();
}
@Override
public void executeCustomAction(@NotNull CustomActionType customActionType) {
aapsLogger.warn(LTag.PUMP, "Unsupported custom action: " + customActionType);
}
@Nullable @Override public PumpEnactResult executeCustomCommand(@NotNull CustomCommand customCommand) {
return null;
}
@Override public void timezoneOrDSTChanged(@NotNull TimeChangeType timeChangeType) {
}
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.dagger
import dagger.Module
@Module
@Suppress("unused")
abstract class OmnipodDashModule {
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod;
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver;
public class OmnipodDashManager {
public interface OmnipodDashManager {
}

View file

@ -0,0 +1,4 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm;
public interface OmnipodDashCommunicationManager {
}

View file

@ -1,84 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.CrcUtil;
abstract class CommandBase implements Command {
static final short HEADER_LENGTH = 6;
final CommandType commandType;
final int address;
final short sequenceNumber;
final boolean unknown;
CommandBase(CommandType commandType, int address, short sequenceNumber, boolean unknown) {
this.commandType = commandType;
this.address = address;
this.sequenceNumber = sequenceNumber;
this.unknown = unknown;
}
@Override public CommandType getCommandType() {
return commandType;
}
public int getAddress() {
return address;
}
public short getSequenceNumber() {
return sequenceNumber;
}
public boolean isUnknown() {
return unknown;
}
static byte[] formatCommand(byte[] command) {
List<Byte> temp = new ArrayList<>();
byte[] prefix = "S0.0=".getBytes(StandardCharsets.UTF_8);
for (byte b : prefix) {
temp.add(b);
}
byte[] length = ByteBuffer.allocate(2).putShort((short) command.length).array();
for (int i = 0; i < 2; i++) {
temp.add(length[i]);
}
// Append command
for (byte b : command) {
temp.add(b);
}
byte[] suffix = ",G0.0".getBytes(StandardCharsets.UTF_8);
for (byte b : suffix) {
temp.add(b);
}
byte[] out = new byte[((short) temp.size())];
for (int i2 = 0; i2 < temp.size(); i2++) {
out[i2] = temp.get(i2);
}
return out;
}
static byte[] appendCrc(byte[] command) {
return ByteBuffer.allocate(command.length + 2) //
.put(command) //
.putShort(CrcUtil.createCrc(command)) //
.array();
}
static byte[] encodeHeader(int address, short sequenceNumber, short length, boolean unknown) {
return ByteBuffer.allocate(6) //
.putInt(address) //
.putShort((short) (((sequenceNumber & 0x0f) << 10) | length | ((unknown ? 1 : 0) << 15))) //
.array();
}
}

View file

@ -2,29 +2,40 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
public class DeactivateCommand extends CommandBase {
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
public final class DeactivateCommand extends NonceEnabledCommand {
private static final short LENGTH = 6;
private static final byte BODY_LENGTH = 4;
DeactivateCommand(int address, short sequenceNumber, boolean unknown) {
super(CommandType.DEACTIVATE, address, sequenceNumber, unknown);
DeactivateCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, int nonce) {
super(CommandType.DEACTIVATE, uniqueId, sequenceNumber, multiCommandFlag, nonce);
}
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(address, sequenceNumber, LENGTH, unknown)) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(1229869870) // FIXME ?? was: byte array of int 777211465 converted to little endian
.putInt(nonce) //
.array());
}
@Override public String toString() {
return "DeactivateCommand{" +
"commandType=" + commandType +
", address=" + address +
"nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", unknown=" + unknown +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, DeactivateCommand> {
@Override protected final DeactivateCommand buildCommand() {
return new DeactivateCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce);
}
}
}

View file

@ -2,34 +2,41 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
public class GetVersionCommand extends CommandBase {
private static final int DEFAULT_ADDRESS = -1;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.HeaderEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.HeaderEnabledCommandBuilder;
public final class GetVersionCommand extends HeaderEnabledCommand {
public static final int DEFAULT_UNIQUE_ID = -1; // FIXME move
private static final short LENGTH = 6;
private static final byte BODY_LENGTH = 4;
public GetVersionCommand(short sequenceNumber, boolean unknown) {
this(DEFAULT_ADDRESS, sequenceNumber, unknown);
}
public GetVersionCommand(int address, short sequenceNumber, boolean unknown) {
super(CommandType.GET_VERSION, address, sequenceNumber, unknown);
GetVersionCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag) {
super(CommandType.GET_VERSION, uniqueId, sequenceNumber, multiCommandFlag);
}
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(address, sequenceNumber, LENGTH, unknown)) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(address) //
.putInt(uniqueId) //
.array());
}
@Override public String toString() {
return "GetVersionCommand{" +
"commandType=" + commandType +
", address=" + address +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", unknown=" + unknown +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends HeaderEnabledCommandBuilder<Builder, GetVersionCommand> {
@Override protected final GetVersionCommand buildCommand() {
return new GetVersionCommand(uniqueId, sequenceNumber, multiCommandFlag);
}
}
}

View file

@ -4,22 +4,25 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertConfiguration;
public class ProgramAlertsCommand extends CommandBase {
public final class ProgramAlertsCommand extends NonceEnabledCommand {
private final List<AlertConfiguration> alertConfigurations;
ProgramAlertsCommand(int address, short sequenceNumber, boolean unknown, List<AlertConfiguration> alertConfigurations) {
super(CommandType.PROGRAM_ALERTS, address, sequenceNumber, unknown);
ProgramAlertsCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, List<AlertConfiguration> alertConfigurations, int nonce) {
super(CommandType.PROGRAM_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce);
this.alertConfigurations = new ArrayList<>(alertConfigurations);
}
@Override public byte[] getEncoded() {
ByteBuffer byteBuffer = ByteBuffer.allocate(getLength() + HEADER_LENGTH) //
.put(encodeHeader(address, sequenceNumber, getLength(), unknown)) //
.put(encodeHeader(uniqueId, sequenceNumber, getLength(), multiCommandFlag)) //
.put(commandType.getValue()) //
.put(getBodyLength()) //
.putInt(1229869870); // FIXME ?? was: byte array of int 777211465 converted to little endian
.putInt(nonce);
for (AlertConfiguration configuration : alertConfigurations) {
byteBuffer.put(configuration.getEncoded());
}
@ -37,10 +40,27 @@ public class ProgramAlertsCommand extends CommandBase {
@Override public String toString() {
return "ProgramAlertsCommand{" +
"alertConfigurations=" + alertConfigurations +
", nonce=" + nonce +
", commandType=" + commandType +
", address=" + address +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", unknown=" + unknown +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramAlertsCommand> {
private List<AlertConfiguration> alertConfigurations;
public Builder setAlertConfigurations(List<AlertConfiguration> alertConfigurations) {
this.alertConfigurations = alertConfigurations;
return this;
}
@Override protected final ProgramAlertsCommand buildCommand() {
if (this.alertConfigurations == null) {
throw new IllegalArgumentException("alertConfigurations can not be null");
}
return new ProgramAlertsCommand(uniqueId, sequenceNumber, multiCommandFlag, alertConfigurations, nonce);
}
}
}

View file

@ -0,0 +1,132 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.HeaderEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.BasalInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.CurrentBasalInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.CurrentSlot;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ShortInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util.ProgramBasalUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder;
// Always preceded by 0x1a ProgramInsulinCommand
public final class ProgramBasalCommand extends HeaderEnabledCommand {
private final ProgramInsulinCommand interlockCommand;
private final List<BasalInsulinProgramElement> insulinProgramElements;
private final ProgramReminder programReminder;
private final byte currentInsulinProgramElementIndex;
private final short remainingTenthPulsesInCurrentInsulinProgramElement;
private final int delayUntilNextTenthPulseInUsec;
ProgramBasalCommand(ProgramInsulinCommand interlockCommand, int uniqueId, short sequenceNumber, boolean multiCommandFlag, List<BasalInsulinProgramElement> insulinProgramElements, ProgramReminder programReminder, byte currentInsulinProgramElementIndex, short remainingTenthPulsesInCurrentInsulinProgramElement, int delayUntilNextTenthPulseInUsec) {
super(CommandType.PROGRAM_BASAL, uniqueId, sequenceNumber, multiCommandFlag);
this.interlockCommand = interlockCommand;
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
this.programReminder = programReminder;
this.currentInsulinProgramElementIndex = currentInsulinProgramElementIndex;
this.remainingTenthPulsesInCurrentInsulinProgramElement = remainingTenthPulsesInCurrentInsulinProgramElement;
this.delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec;
}
short getLength() {
return (short) (insulinProgramElements.size() * 6 + 10);
}
byte getBodyLength() {
return (byte) (insulinProgramElements.size() * 6 + 8);
}
@Override public byte[] getEncoded() {
ByteBuffer buffer = ByteBuffer.allocate(getLength()) //
.put(getCommandType().getValue()) //
.put(getBodyLength()) //
.put(programReminder.getEncoded()) //
.put(currentInsulinProgramElementIndex) //
.putShort(remainingTenthPulsesInCurrentInsulinProgramElement) //
.putInt(delayUntilNextTenthPulseInUsec);
for (BasalInsulinProgramElement insulinProgramElement : insulinProgramElements) {
buffer.put(insulinProgramElement.getEncoded());
}
byte[] basalCommand = buffer.array();
byte[] interlockCommand = this.interlockCommand.getEncoded();
byte[] header = encodeHeader(uniqueId, sequenceNumber, (short) (basalCommand.length + interlockCommand.length), multiCommandFlag);
return appendCrc(ByteBuffer.allocate(basalCommand.length + interlockCommand.length + header.length) //
.put(header) //
.put(interlockCommand) //
.put(basalCommand) //
.array());
}
@Override public String toString() {
return "ProgramBasalCommand{" +
"interlockCommand=" + interlockCommand +
", insulinProgramElements=" + insulinProgramElements +
", programReminder=" + programReminder +
", currentInsulinProgramElementIndex=" + currentInsulinProgramElementIndex +
", remainingTenthPulsesInCurrentInsulinProgramElement=" + remainingTenthPulsesInCurrentInsulinProgramElement +
", delayUntilNextTenthPulseInUsec=" + delayUntilNextTenthPulseInUsec +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramBasalCommand> {
private BasalProgram basalProgram;
private ProgramReminder programReminder;
private Date currentTime;
public Builder setBasalProgram(BasalProgram basalProgram) {
this.basalProgram = basalProgram;
return this;
}
public Builder setProgramReminder(ProgramReminder programReminder) {
this.programReminder = programReminder;
return this;
}
public Builder setCurrentTime(Date currentTime) {
this.currentTime = currentTime;
return this;
}
@Override protected ProgramBasalCommand buildCommand() {
if (basalProgram == null) {
throw new IllegalArgumentException("basalProgram can not be null");
}
if (programReminder == null) {
throw new IllegalArgumentException("programReminder can not be null");
}
if (currentTime == null) {
throw new IllegalArgumentException("currentTime can not be null");
}
short[] pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram);
CurrentSlot currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime);
short checksum = ProgramBasalUtil.calculateChecksum(pulsesPerSlot, currentSlot);
List<BasalInsulinProgramElement> longInsulinProgramElements = ProgramBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram));
List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
CurrentBasalInsulinProgramElement currentBasalInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(longInsulinProgramElements, currentTime);
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce,
shortInsulinProgramElements, checksum, currentSlot.getIndex(), currentSlot.getEighthSecondsRemaining(),
currentSlot.getPulsesRemaining(), ProgramInsulinCommand.DeliveryType.BASAL);
return new ProgramBasalCommand(interlockCommand, uniqueId, sequenceNumber, multiCommandFlag,
longInsulinProgramElements, programReminder, currentBasalInsulinProgramElement.getIndex(),
currentBasalInsulinProgramElement.getRemainingTenthPulses(), currentBasalInsulinProgramElement.getDelayUntilNextTenthPulseInUsec());
}
}
}

View file

@ -0,0 +1,123 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.Collections;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.HeaderEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.BolusShortInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.MessageUtil;
// NOT SUPPORTED: extended bolus
public final class ProgramBolusCommand extends HeaderEnabledCommand {
private static final short LENGTH = 15;
private static final byte BODY_LENGTH = 13;
private final ProgramInsulinCommand interlockCommand;
private final ProgramReminder programReminder;
private final short numberOfTenthPulses;
private final int delayUntilFirstTenthPulseInUsec;
ProgramBolusCommand(ProgramInsulinCommand interlockCommand, int uniqueId, short sequenceNumber, boolean multiCommandFlag, ProgramReminder programReminder, short numberOfTenthPulses, int delayUntilFirstTenthPulseInUsec) {
super(CommandType.PROGRAM_BOLUS, uniqueId, sequenceNumber, multiCommandFlag);
this.interlockCommand = interlockCommand;
this.programReminder = programReminder;
this.numberOfTenthPulses = numberOfTenthPulses;
this.delayUntilFirstTenthPulseInUsec = delayUntilFirstTenthPulseInUsec;
}
@Override public byte[] getEncoded() {
byte[] bolusCommand = ByteBuffer.allocate(LENGTH) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.put(programReminder.getEncoded()) //
.putShort(numberOfTenthPulses) //
.putInt(delayUntilFirstTenthPulseInUsec) //
.putShort((short) 0) // Extended bolus pulses
.putInt(0) // Delay between tenth extended pulses in usec
.array();
byte[] interlockCommand = this.interlockCommand.getEncoded();
byte[] header = encodeHeader(uniqueId, sequenceNumber, (short) (bolusCommand.length + interlockCommand.length), multiCommandFlag);
return appendCrc(ByteBuffer.allocate(header.length + interlockCommand.length + bolusCommand.length) //
.put(header) //
.put(interlockCommand) //
.put(bolusCommand) //
.array());
}
@Override public String toString() {
return "ProgramBolusCommand{" +
"interlockCommand=" + interlockCommand +
", programReminder=" + programReminder +
", numberOfTenthPulses=" + numberOfTenthPulses +
", delayUntilFirstTenthPulseInUsec=" + delayUntilFirstTenthPulseInUsec +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramBolusCommand> {
private Double numberOfUnits;
private Byte delayBetweenPulsesInEighthSeconds;
private ProgramReminder programReminder;
public Builder setNumberOfUnits(double numberOfUnits) {
if (numberOfUnits <= 0.0D) {
throw new IllegalArgumentException("Number of units should be greater than zero");
}
if ((int) (numberOfUnits * 1000) % 50 != 0) {
throw new IllegalArgumentException("Number of units must be dividable by 0.05");
}
this.numberOfUnits = ((int) (numberOfUnits * 100)) / 100.0d;
return this;
}
public Builder setDelayBetweenPulsesInEighthSeconds(byte delayBetweenPulsesInEighthSeconds) {
this.delayBetweenPulsesInEighthSeconds = delayBetweenPulsesInEighthSeconds;
return this;
}
public Builder setProgramReminder(ProgramReminder programReminder) {
this.programReminder = programReminder;
return this;
}
@Override protected ProgramBolusCommand buildCommand() {
if (numberOfUnits == null) {
throw new IllegalArgumentException("numberOfUnits can not be null");
}
if (delayBetweenPulsesInEighthSeconds == null) {
throw new IllegalArgumentException("delayBetweenPulsesInEighthSeconds can not be null");
}
if (programReminder == null) {
throw new IllegalArgumentException("programReminder can not be null");
}
short numberOfPulses = (short) Math.round(numberOfUnits * 20);
short byte10And11 = (short) (numberOfPulses * delayBetweenPulsesInEighthSeconds);
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce,
Collections.singletonList(new BolusShortInsulinProgramElement(numberOfPulses)), calculateChecksum((byte) 0x01, byte10And11, numberOfPulses),
(byte) 0x01, byte10And11, (short) numberOfPulses, ProgramInsulinCommand.DeliveryType.BOLUS);
int delayUntilFirstTenthPulseInUsec = delayBetweenPulsesInEighthSeconds / 8 * 100_000;
return new ProgramBolusCommand(interlockCommand, uniqueId, sequenceNumber, multiCommandFlag, programReminder, (short) (numberOfPulses * 10), delayUntilFirstTenthPulseInUsec);
}
}
private static short calculateChecksum(byte numberOfSlots, short byte10And11, short numberOfPulses) {
return MessageUtil.calculateChecksum(ByteBuffer.allocate(7) //
.put(numberOfSlots) //
.putShort(byte10And11) //
.putShort(numberOfPulses) //
.putShort(numberOfPulses) //
.array());
}
}

View file

@ -0,0 +1,96 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ShortInsulinProgramElement;
// Always followed by one of: 0x13, 0x16, 0x17
final class ProgramInsulinCommand extends NonceEnabledCommand {
private final List<ShortInsulinProgramElement> insulinProgramElements;
private final short checksum;
private final byte byte9;
private final short byte10And11;
private final short byte12And13;
private final DeliveryType deliveryType;
ProgramInsulinCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, int nonce, List<ShortInsulinProgramElement> insulinProgramElements, short checksum, byte byte9, short byte10And11, short byte12And13, DeliveryType deliveryType) {
super(CommandType.PROGRAM_INSULIN, uniqueId, sequenceNumber, multiCommandFlag, nonce);
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
this.checksum = checksum;
this.byte9 = byte9;
this.byte10And11 = byte10And11;
this.byte12And13 = byte12And13;
this.deliveryType = deliveryType;
}
public short getLength() {
return (short) (insulinProgramElements.size() * 2 + 14);
}
public byte getBodyLength() {
return (byte) (insulinProgramElements.size() * 2 + 12);
}
@Override public byte[] getEncoded() {
ByteBuffer buffer = ByteBuffer.allocate(this.getLength()) //
.put(commandType.getValue()) //
.put(getBodyLength()) //
.putInt(nonce) //
.put(deliveryType.getValue()) //
.putShort(checksum) //
.put(byte9) // BASAL: currentSlot // BOLUS: number of ShortInsulinProgramElements
.putShort(byte10And11) // BASAL: remainingEighthSecondsInCurrentSlot // BOLUS: immediate pulses multiplied by delay between pulses in eighth seconds
.putShort(byte12And13); // BASAL: remainingPulsesInCurrentSlot // BOLUS: immediate pulses
for (ShortInsulinProgramElement element : insulinProgramElements) {
buffer.put(element.getEncoded());
}
return buffer.array();
}
enum DeliveryType {
BASAL((byte) 0x00),
TEMP_BASAL((byte) 0x01),
BOLUS((byte) 0x02);
private final byte value;
DeliveryType(byte value) {
this.value = value;
}
public byte getValue() {
return value;
}
}
public short calculateChecksum(byte[] bytes) {
short sum = 0;
for (byte b : bytes) {
sum += (short) (b & 0xff);
}
return sum;
}
@Override public String toString() {
return "ProgramInsulinCommand{" +
"insulinProgramElements=" + insulinProgramElements +
", checksum=" + checksum +
", byte9=" + byte9 +
", byte10And11=" + byte10And11 +
", byte12And13=" + byte12And13 +
", deliveryType=" + deliveryType +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
}

View file

@ -0,0 +1,123 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.HeaderEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.BasalInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ShortInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util.ProgramBasalUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util.ProgramTempBasalUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder;
// NOT SUPPORTED: percentage temp basal
public final class ProgramTempBasalCommand extends HeaderEnabledCommand {
private final ProgramInsulinCommand interlockCommand;
private final ProgramReminder programReminder;
private final List<BasalInsulinProgramElement> insulinProgramElements;
protected ProgramTempBasalCommand(ProgramInsulinCommand interlockCommand, int uniqueId, short sequenceNumber, boolean multiCommandFlag,
ProgramReminder programReminder, List<BasalInsulinProgramElement> insulinProgramElements) {
super(CommandType.PROGRAM_TEMP_BASAL, uniqueId, sequenceNumber, multiCommandFlag);
this.interlockCommand = interlockCommand;
this.programReminder = programReminder;
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
}
public byte getBodyLength() {
return (byte) (insulinProgramElements.size() * 6 + 8);
}
public short getLength() {
return (short) (getBodyLength() + 2);
}
@Override public byte[] getEncoded() {
BasalInsulinProgramElement firstProgramElement = insulinProgramElements.get(0);
short remainingTenthPulsesInFirstElement;
int delayUntilNextTenthPulseInUsec;
if (firstProgramElement.getTotalTenthPulses() == 0) {
remainingTenthPulsesInFirstElement = firstProgramElement.getNumberOfSlots();
delayUntilNextTenthPulseInUsec = ProgramBasalUtil.MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT;
} else {
remainingTenthPulsesInFirstElement = firstProgramElement.getTotalTenthPulses();
delayUntilNextTenthPulseInUsec = (int) ((long) firstProgramElement.getNumberOfSlots() * 1_800.0d / remainingTenthPulsesInFirstElement * 1_000_000);
}
ByteBuffer buffer = ByteBuffer.allocate(getLength()) //
.put(commandType.getValue()) //
.put(getBodyLength()) //
.put(programReminder.getEncoded()) //
.put((byte) 0x00) // Current slot index
.putShort(remainingTenthPulsesInFirstElement) //
.putInt(delayUntilNextTenthPulseInUsec);
for (BasalInsulinProgramElement element : insulinProgramElements) {
buffer.put(element.getEncoded());
}
byte[] tempBasalCommand = buffer.array();
byte[] interlockCommand = this.interlockCommand.getEncoded();
byte[] header = encodeHeader(uniqueId, sequenceNumber, (short) (tempBasalCommand.length + interlockCommand.length), multiCommandFlag);
return appendCrc(ByteBuffer.allocate(header.length + interlockCommand.length + tempBasalCommand.length) //
.put(header) //
.put(interlockCommand) //
.put(tempBasalCommand) //
.array());
}
public static class Builder extends NonceEnabledCommandBuilder<Builder, ProgramTempBasalCommand> {
private ProgramReminder programReminder;
private Double rateInUnitsPerHour;
private Short durationInMinutes;
public Builder setProgramReminder(ProgramReminder programReminder) {
this.programReminder = programReminder;
return this;
}
public Builder setRateInUnitsPerHour(double rateInUnitsPerHour) {
this.rateInUnitsPerHour = rateInUnitsPerHour;
return this;
}
public Builder setDurationInMinutes(short durationInMinutes) {
if (durationInMinutes % 30 != 0) {
throw new IllegalArgumentException("durationInMinutes must be dividable by 30");
}
this.durationInMinutes = durationInMinutes;
return this;
}
@Override protected ProgramTempBasalCommand buildCommand() {
if (programReminder == null) {
throw new IllegalArgumentException("programReminder can not be null");
}
if (rateInUnitsPerHour == null) {
throw new IllegalArgumentException("rateInUnitsPerHour can not be null");
}
if (durationInMinutes == null) {
throw new IllegalArgumentException("durationInMinutes can not be null");
}
byte durationInSlots = (byte) (durationInMinutes / 30);
short[] pulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToPulsesPerSlot(durationInSlots, rateInUnitsPerHour);
short[] tenthPulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToTenthPulsesPerSlot(durationInSlots, rateInUnitsPerHour);
List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramTempBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
List<BasalInsulinProgramElement> insulinProgramElements = ProgramTempBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot);
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce, shortInsulinProgramElements,
ProgramTempBasalUtil.calculateChecksum(durationInSlots, pulsesPerSlot[0], pulsesPerSlot), durationInSlots,
(short) 0x3840, pulsesPerSlot[0], ProgramInsulinCommand.DeliveryType.TEMP_BASAL);
return new ProgramTempBasalCommand(interlockCommand, uniqueId, sequenceNumber, multiCommandFlag, programReminder, insulinProgramElements);
}
}
}

View file

@ -4,8 +4,12 @@ import java.nio.ByteBuffer;
import java.util.Calendar;
import java.util.Date;
public class SetUniqueIdCommand extends CommandBase {
private static final int DEFAULT_ADDRESS = -1;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.HeaderEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.HeaderEnabledCommandBuilder;
public final class SetUniqueIdCommand extends HeaderEnabledCommand {
private static final int DEFAULT_UNIQUE_ID = -1;
private static final short LENGTH = 21;
private static final byte BODY_LENGTH = 19;
@ -13,8 +17,8 @@ public class SetUniqueIdCommand extends CommandBase {
private final int podSequenceNumber;
private final Date initializationTime;
SetUniqueIdCommand(int address, short sequenceNumber, int lotNumber, int podSequenceNumber, Date initializationTime, boolean unknown) {
super(CommandType.SET_UNIQUE_ID, address, sequenceNumber, unknown);
SetUniqueIdCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, int lotNumber, int podSequenceNumber, Date initializationTime) {
super(CommandType.SET_UNIQUE_ID, uniqueId, sequenceNumber, multiCommandFlag);
this.lotNumber = lotNumber;
this.podSequenceNumber = podSequenceNumber;
this.initializationTime = initializationTime;
@ -22,10 +26,10 @@ public class SetUniqueIdCommand extends CommandBase {
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(DEFAULT_ADDRESS, sequenceNumber, LENGTH, unknown)) //
.put(encodeHeader(DEFAULT_UNIQUE_ID, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(address) //
.putInt(uniqueId) //
.put((byte) 0x14) // FIXME ??
.put((byte) 0x04) // FIXME ??
.put(encodeInitializationTime(initializationTime)) //
@ -53,9 +57,43 @@ public class SetUniqueIdCommand extends CommandBase {
", podSequenceNumber=" + podSequenceNumber +
", initializationTime=" + initializationTime +
", commandType=" + commandType +
", address=" + address +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", unknown=" + unknown +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends HeaderEnabledCommandBuilder<Builder, SetUniqueIdCommand> {
private Integer lotNumber;
private Integer podSequenceNumber;
private Date initializationTime;
public Builder setLotNumber(int lotNumber) {
this.lotNumber = lotNumber;
return this;
}
public Builder setPodSequenceNumber(int podSequenceNumber) {
this.podSequenceNumber = podSequenceNumber;
return this;
}
public Builder setInitializationTime(Date initializationTime) {
this.initializationTime = initializationTime;
return this;
}
@Override protected final SetUniqueIdCommand buildCommand() {
if (lotNumber == null) {
throw new IllegalArgumentException("lotNumber can not be null");
}
if (podSequenceNumber == null) {
throw new IllegalArgumentException("podSequenceNumber can not be null");
}
if (initializationTime == null) {
throw new IllegalArgumentException("initializationTime can not be null");
}
return new SetUniqueIdCommand(uniqueId, sequenceNumber, multiCommandFlag, lotNumber, podSequenceNumber, initializationTime);
}
}
}

View file

@ -3,28 +3,44 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.BitSet;
public class SilenceAlertsCommand extends CommandBase {
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
public final class SilenceAlertsCommand extends NonceEnabledCommand {
private static final short LENGTH = (short) 7;
private static final byte BODY_LENGTH = (byte) 5;
private final SilenceAlertCommandParameters parameters;
public SilenceAlertsCommand(int address, short sequenceNumber, boolean unknown, SilenceAlertCommandParameters parameters) {
super(CommandType.SILENCE_ALERTS, address, sequenceNumber, unknown);
SilenceAlertsCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, SilenceAlertCommandParameters parameters, int nonce) {
super(CommandType.SILENCE_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce);
this.parameters = parameters;
}
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(address, sequenceNumber, LENGTH, unknown)) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(1229869870) // FIXME ?? was: byte array of int 777211465 converted to little endian
.putInt(nonce) //
.put(parameters.getEncoded()) //
.array());
}
public static final class SilenceAlertCommandParameters {
@Override public String toString() {
return "SilenceAlertsCommand{" +
"parameters=" + parameters +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
private static final class SilenceAlertCommandParameters implements Encodable {
private final boolean silenceAutoOffAlert;
private final boolean silenceMultiCommandAlert;
private final boolean silenceExpirationImminentAlert;
@ -34,7 +50,7 @@ public class SilenceAlertsCommand extends CommandBase {
private final boolean silenceSuspendEndedAlert;
private final boolean silencePodExpirationAlert;
public SilenceAlertCommandParameters(boolean silenceAutoOffAlert, boolean silenceMultiCommandAlert, boolean silenceExpirationImminentAlert, boolean silenceUserSetExpirationAlert, boolean silenceLowReservoirAlert, boolean silenceSuspendInProgressAlert, boolean silenceSuspendEndedAlert, boolean silencePodExpirationAlert) {
private SilenceAlertCommandParameters(boolean silenceAutoOffAlert, boolean silenceMultiCommandAlert, boolean silenceExpirationImminentAlert, boolean silenceUserSetExpirationAlert, boolean silenceLowReservoirAlert, boolean silenceSuspendInProgressAlert, boolean silenceSuspendEndedAlert, boolean silencePodExpirationAlert) {
this.silenceAutoOffAlert = silenceAutoOffAlert;
this.silenceMultiCommandAlert = silenceMultiCommandAlert;
this.silenceExpirationImminentAlert = silenceExpirationImminentAlert;
@ -45,6 +61,7 @@ public class SilenceAlertsCommand extends CommandBase {
this.silencePodExpirationAlert = silencePodExpirationAlert;
}
@Override
public byte[] getEncoded() {
BitSet bitSet = new BitSet(8);
bitSet.set(0, this.silenceAutoOffAlert);
@ -58,4 +75,59 @@ public class SilenceAlertsCommand extends CommandBase {
return bitSet.toByteArray();
}
}
public static class Builder extends NonceEnabledCommandBuilder<Builder, SilenceAlertsCommand> {
private boolean silenceAutoOffAlert;
private boolean silenceMultiCommandAlert;
private boolean silenceExpirationImminentAlert;
private boolean silenceUserSetExpirationAlert;
private boolean silenceLowReservoirAlert;
private boolean silenceSuspendInProgressAlert;
private boolean silenceSuspendEndedAlert;
private boolean silencePodExpirationAlert;
public Builder setSilenceAutoOffAlert(boolean silenceAutoOffAlert) {
this.silenceAutoOffAlert = silenceAutoOffAlert;
return this;
}
public Builder setSilenceMultiCommandAlert(boolean silenceMultiCommandAlert) {
this.silenceMultiCommandAlert = silenceMultiCommandAlert;
return this;
}
public Builder setSilenceExpirationImminentAlert(boolean silenceExpirationImminentAlert) {
this.silenceExpirationImminentAlert = silenceExpirationImminentAlert;
return this;
}
public Builder setSilenceUserSetExpirationAlert(boolean silenceUserSetExpirationAlert) {
this.silenceUserSetExpirationAlert = silenceUserSetExpirationAlert;
return this;
}
public Builder setSilenceLowReservoirAlert(boolean silenceLowReservoirAlert) {
this.silenceLowReservoirAlert = silenceLowReservoirAlert;
return this;
}
public Builder setSilenceSuspendInProgressAlert(boolean silenceSuspendInProgressAlert) {
this.silenceSuspendInProgressAlert = silenceSuspendInProgressAlert;
return this;
}
public Builder setSilenceSuspendEndedAlert(boolean silenceSuspendEndedAlert) {
this.silenceSuspendEndedAlert = silenceSuspendEndedAlert;
return this;
}
public Builder setSilencePodExpirationAlert(boolean silencePodExpirationAlert) {
this.silencePodExpirationAlert = silencePodExpirationAlert;
return this;
}
@Override protected final SilenceAlertsCommand buildCommand() {
return new SilenceAlertsCommand(uniqueId, sequenceNumber, multiCommandFlag, new SilenceAlertCommandParameters(silenceAutoOffAlert, silenceMultiCommandAlert, silenceExpirationImminentAlert, silenceUserSetExpirationAlert, silenceLowReservoirAlert, silenceSuspendInProgressAlert, silenceSuspendEndedAlert, silencePodExpirationAlert), nonce);
}
}
}

View file

@ -3,32 +3,48 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.BitSet;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
public class StopDeliveryCommand extends CommandBase {
public final class StopDeliveryCommand extends NonceEnabledCommand {
private static final short LENGTH = 7;
private static final byte BODY_LENGTH = 5;
private final DeliveryType deliveryType;
private final BeepType beepType;
public StopDeliveryCommand(int address, short sequenceNumber, boolean unknown, DeliveryType deliveryType, BeepType beepType) {
super(CommandType.STOP_DELIVERY, address, sequenceNumber, unknown);
StopDeliveryCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, DeliveryType deliveryType, BeepType beepType, int nonce) {
super(CommandType.STOP_DELIVERY, uniqueId, sequenceNumber, multiCommandFlag, nonce);
this.deliveryType = deliveryType;
this.beepType = beepType;
}
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(address, sequenceNumber, LENGTH, unknown)) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(1229869870) // FIXME ?? was: byte array of int 777211465 converted to little endian
.put((byte) ((beepType.getValue() << 4) | deliveryType.getEncoded())) //
.putInt(nonce) //
.put((byte) ((beepType.getValue() << 4) | deliveryType.getEncoded()[0])) //
.array());
}
public enum DeliveryType {
@Override public String toString() {
return "StopDeliveryCommand{" +
"deliveryType=" + deliveryType +
", beepType=" + beepType +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public enum DeliveryType implements Encodable {
BASAL(true, false, false),
TEMP_BASAL(false, true, false),
BOLUS(false, false, true),
@ -44,12 +60,38 @@ public class StopDeliveryCommand extends CommandBase {
this.bolus = bolus;
}
public byte getEncoded() {
@Override public byte[] getEncoded() {
BitSet bitSet = new BitSet(8);
bitSet.set(0, this.basal);
bitSet.set(1, this.tempBasal);
bitSet.set(2, this.bolus);
return bitSet.toByteArray()[0];
return bitSet.toByteArray();
}
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, StopDeliveryCommand> {
private DeliveryType deliveryType;
private BeepType beepType = BeepType.LONG_SINGLE_BEEP;
public Builder setDeliveryType(DeliveryType deliveryType) {
this.deliveryType = deliveryType;
return this;
}
public Builder setBeepType(BeepType beepType) {
this.beepType = beepType;
return this;
}
@Override protected final StopDeliveryCommand buildCommand() {
if (deliveryType == null) {
throw new IllegalArgumentException("deliveryType can not be null");
}
if (beepType == null) {
throw new IllegalArgumentException("beepType can not be null");
}
return new StopDeliveryCommand(uniqueId, sequenceNumber, multiCommandFlag, deliveryType, beepType, nonce);
}
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
public interface Command extends Encodable {
CommandType getCommandType();
}

View file

@ -1,15 +1,15 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base;
public enum CommandType {
SET_UNIQUE_ID((byte) 0x03),
GET_VERSION((byte) 0x07),
GET_STATUS((byte) 0x0e),
SILENCE_ALERTS((byte) 0x11),
PROGRAM_BASAL((byte) 0x13),
PROGRAM_TEMP_BASAL((byte) 0x16),
BOLUS((byte) 0x17),
PROGRAM_BASAL((byte) 0x13), // Always preceded by 0x1a
PROGRAM_TEMP_BASAL((byte) 0x16), // Always preceded by 0x1a
PROGRAM_BOLUS((byte) 0x17), // Always preceded by 0x1a
PROGRAM_ALERTS((byte) 0x19),
DELIVERY_INTERLOCK((byte) 0x1a),
PROGRAM_INSULIN((byte) 0x1a), // Always followed by one of: 0x13, 0x16, 0x17
DEACTIVATE((byte) 0x1c),
PROGRAM_BEEPS((byte) 0x1e),
STOP_DELIVERY((byte) 0x1f);

View file

@ -0,0 +1,40 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base;
import java.nio.ByteBuffer;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.MessageUtil;
public abstract class HeaderEnabledCommand implements Command {
protected static final short HEADER_LENGTH = 6;
protected final CommandType commandType;
protected final int uniqueId;
protected final short sequenceNumber;
protected final boolean multiCommandFlag;
protected HeaderEnabledCommand(CommandType commandType, int uniqueId, short sequenceNumber, boolean multiCommandFlag) {
this.commandType = commandType;
this.uniqueId = uniqueId;
this.sequenceNumber = sequenceNumber;
this.multiCommandFlag = multiCommandFlag;
}
@Override public CommandType getCommandType() {
return commandType;
}
protected static byte[] appendCrc(byte[] command) {
return ByteBuffer.allocate(command.length + 2) //
.put(command) //
.putShort(MessageUtil.createCrc(command)) //
.array();
}
protected static byte[] encodeHeader(int uniqueId, short sequenceNumber, short length, boolean multiCommandFlag) {
return ByteBuffer.allocate(6) //
.putInt(uniqueId) //
.putShort((short) (((sequenceNumber & 0x0f) << 10) | length | ((multiCommandFlag ? 1 : 0) << 15))) //
.array();
}
}

Some files were not shown because too many files have changed in this diff Show more