From 7afa0beb0a57a0c8584db8ab5f718b2c54009030 Mon Sep 17 00:00:00 2001 From: jbr7rr <> Date: Sat, 11 Mar 2023 20:19:06 +0100 Subject: [PATCH] Prepare patch step WIP --- pump/medtrum/src/main/AndroidManifest.xml | 1 + .../nightscout/pump/medtrum/MedtrumPlugin.kt | 59 ++++---- .../nightscout/pump/medtrum/code/EventType.kt | 11 ++ .../nightscout/pump/medtrum/code/PatchStep.kt | 18 +++ .../pump/medtrum/di/MedtrumModule.kt | 28 +++- .../extension/AppCompatActivityExtension.kt | 19 +++ .../pump/medtrum/services/BLEComm.kt | 9 +- .../pump/medtrum/services/MedtrumService.kt | 85 ++++++++++-- .../pump/medtrum/ui/MedtrumActivity.kt | 97 +++++++++++++ .../medtrum/ui/MedtrumOverviewFragment.kt | 35 +++-- .../medtrum/ui/MedtrumPreparePatchFragment.kt | 42 ++++++ .../pump/medtrum/ui/MedtrumPrimeFragment.kt | 32 +++++ .../pump/medtrum/ui/event/SingleLiveEvent.kt | 29 ++++ .../pump/medtrum/ui/event/UIEvent.kt | 7 + .../ui/viewmodel/MedtrumOverviewViewModel.kt | 65 +++++++-- .../medtrum/ui/viewmodel/MedtrumViewModel.kt | 128 ++++++++++++++++++ .../src/main/res/layout/activity_medtrum.xml | 50 +++++++ ...ment.xml => fragment_medtrum_overview.xml} | 12 +- .../layout/fragment_medtrum_prepare_patch.xml | 66 +++++++++ .../res/layout/fragment_medtrum_prime.xml | 33 +++++ pump/medtrum/src/main/res/values/strings.xml | 12 +- 21 files changed, 750 insertions(+), 88 deletions(-) create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/EventType.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/PatchStep.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/extension/AppCompatActivityExtension.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumActivity.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumPreparePatchFragment.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumPrimeFragment.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/event/SingleLiveEvent.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/event/UIEvent.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumViewModel.kt create mode 100644 pump/medtrum/src/main/res/layout/activity_medtrum.xml rename pump/medtrum/src/main/res/layout/{medtrum_overview_fragment.xml => fragment_medtrum_overview.xml} (98%) create mode 100644 pump/medtrum/src/main/res/layout/fragment_medtrum_prepare_patch.xml create mode 100644 pump/medtrum/src/main/res/layout/fragment_medtrum_prime.xml diff --git a/pump/medtrum/src/main/AndroidManifest.xml b/pump/medtrum/src/main/AndroidManifest.xml index f589b49319..2e03434790 100644 --- a/pump/medtrum/src/main/AndroidManifest.xml +++ b/pump/medtrum/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + - if (event.isChanged(rh.gs(info.nightscout.pump.medtrum.R.string.key_snInput))) { - pumpSync.connectNewPump() - changePump() - } - }, fabricPrivacy::logException) - changePump() } override fun onStop() { @@ -122,15 +111,8 @@ class MedtrumPlugin @Inject constructor( } } - fun changePump() { - aapsLogger.debug(LTag.PUMP, "changePump: called!") - try { - mDeviceSN = sp.getString(info.nightscout.pump.medtrum.R.string.key_snInput, " ").toLong(radix = 16) - } catch (e: NumberFormatException) { - aapsLogger.debug(LTag.PUMP, "changePump: Invalid input!") - } - // TODO: add medtrumPump.reset() - commandQueue.readStatus(rh.gs(info.nightscout.core.ui.R.string.device_changed), null) + fun getService(): MedtrumService? { + return medtrumService } override fun isInitialized(): Boolean { @@ -138,14 +120,17 @@ class MedtrumPlugin @Inject constructor( } override fun isSuspended(): Boolean { - return false + return true } override fun isBusy(): Boolean { - return false + return true + } + + override fun isConnected(): Boolean { + return if (!isInitialized()) true else medtrumService?.isConnected ?: true // This is a workaround to prevent AAPS to trigger connects when we are initializing } - override fun isConnected(): Boolean = medtrumService?.isConnected ?: false override fun isConnecting(): Boolean = medtrumService?.isConnecting ?: false override fun isHandshakeInProgress(): Boolean = false @@ -153,19 +138,23 @@ class MedtrumPlugin @Inject constructor( } override fun connect(reason: String) { - aapsLogger.debug(LTag.PUMP, "Medtrum connect - reason:$reason") - aapsLogger.debug(LTag.PUMP, "Medtrum connect - service::$medtrumService") - aapsLogger.debug(LTag.PUMP, "Medtrum connect - mDeviceSN:$mDeviceSN") - if (medtrumService != null && mDeviceSN != 0.toLong()) { - aapsLogger.debug(LTag.PUMP, "Medtrum connect - Attempt connection!") - val success = medtrumService?.connect(reason, mDeviceSN) ?: false - if (!success) ToastUtils.errorToast(context, info.nightscout.core.ui.R.string.ble_not_supported_or_not_paired) + if (isInitialized()) { + aapsLogger.debug(LTag.PUMP, "Medtrum connect - reason:$reason") + aapsLogger.debug(LTag.PUMP, "Medtrum connect - service::$medtrumService") + // aapsLogger.debug(LTag.PUMP, "Medtrum connect - mDeviceSN:$mDeviceSN") + if (medtrumService != null) { + aapsLogger.debug(LTag.PUMP, "Medtrum connect - Attempt connection!") + val success = medtrumService?.connect(reason) ?: false + if (!success) ToastUtils.errorToast(context, info.nightscout.core.ui.R.string.ble_not_supported_or_not_paired) + } } } override fun disconnect(reason: String) { - aapsLogger.debug(LTag.PUMP, "RS disconnect from: $reason") - medtrumService?.disconnect(reason) + if (isInitialized()) { + aapsLogger.debug(LTag.PUMP, "Medtrum disconnect from: $reason") + medtrumService?.disconnect(reason) + } } override fun stopConnecting() { @@ -173,11 +162,13 @@ class MedtrumPlugin @Inject constructor( } override fun getPumpStatus(reason: String) { - medtrumService?.readPumpStatus() + if (isInitialized()) { + medtrumService?.readPumpStatus() + } } override fun setNewBasalProfile(profile: Profile): PumpEnactResult { - return PumpEnactResult(injector) // TODO + return PumpEnactResult(injector).success(true).enacted(true) // TODO } override fun isThisProfileSet(profile: Profile): Boolean { diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/EventType.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/EventType.kt new file mode 100644 index 0000000000..32b4efb4ed --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/EventType.kt @@ -0,0 +1,11 @@ +package info.nightscout.pump.medtrum.code + +enum class EventType { + ACTIVATION_CLICKED, + DEACTIVATION_CLICKED, + INVALID_BASAL_RATE, + PROFILE_NOT_SET, + FINISH_ACTIVITY, + SHOW_DISCARD_DIALOG + ; +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/PatchStep.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/PatchStep.kt new file mode 100644 index 0000000000..3b2c973ae6 --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/PatchStep.kt @@ -0,0 +1,18 @@ +package info.nightscout.pump.medtrum.code + +enum class PatchStep { + SAFE_DEACTIVATION, + MANUALLY_TURNING_OFF_ALARM, + DISCARDED, + DISCARDED_FOR_CHANGE, + DISCARDED_FROM_ALARM, + PREPARE_PATCH, + PRIME, + ATTACH_INSERT_NEEDLE, + BASAL_SCHEDULE, + CHECK_CONNECTION, + CANCEL, + COMPLETE, + BACK_TO_HOME, + FINISH; +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumModule.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumModule.kt index 71af92437e..1666a4defe 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumModule.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumModule.kt @@ -7,17 +7,21 @@ import dagger.Module import dagger.Provides import dagger.android.ContributesAndroidInjector import dagger.multibindings.IntoMap +import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPreparePatchFragment +import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPrimeFragment import info.nightscout.pump.medtrum.services.MedtrumService +import info.nightscout.pump.medtrum.ui.MedtrumActivity import info.nightscout.pump.medtrum.ui.MedtrumOverviewFragment import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumOverviewViewModel +import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel import info.nightscout.pump.medtrum.ui.viewmodel.ViewModelFactory import info.nightscout.pump.medtrum.ui.viewmodel.ViewModelKey import javax.inject.Provider - @Module @Suppress("unused") abstract class MedtrumModule { + companion object { @Provides @@ -34,11 +38,29 @@ abstract class MedtrumModule { @ViewModelKey(MedtrumOverviewViewModel::class) internal abstract fun bindsMedtrumOverviewViewmodel(viewModel: MedtrumOverviewViewModel): ViewModel + @Binds + @IntoMap + @MedtrumPluginQualifier + @ViewModelKey(MedtrumViewModel::class) + internal abstract fun bindsMedtrumViewModel(viewModel: MedtrumViewModel): ViewModel + // FRAGMENTS @ContributesAndroidInjector abstract fun contributesMedtrumOverviewFragment(): MedtrumOverviewFragment + @FragmentScope + @ContributesAndroidInjector + internal abstract fun contributesPreparePatchFragment(): MedtrumPreparePatchFragment + + @FragmentScope + @ContributesAndroidInjector + internal abstract fun contributesPrimeFragment(): MedtrumPrimeFragment + + // ACTIVITIES + @ContributesAndroidInjector + abstract fun contributesMedtrumActivity(): MedtrumActivity + // SERVICE - @ContributesAndroidInjector - abstract fun contributesDanaRSService(): MedtrumService + @ContributesAndroidInjector + abstract fun contributesMedtrumService(): MedtrumService } \ No newline at end of file diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/extension/AppCompatActivityExtension.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/extension/AppCompatActivityExtension.kt new file mode 100644 index 0000000000..7345091cb4 --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/extension/AppCompatActivityExtension.kt @@ -0,0 +1,19 @@ +package info.nightscout.pump.medtrum.extension + +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction + +fun AppCompatActivity.replaceFragmentInActivity(fragment: Fragment, frameId: Int, addToBackStack: Boolean = false) { + supportFragmentManager.transact { + replace(frameId, fragment) + if (addToBackStack) addToBackStack(null) + } +} + +private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) { + beginTransaction().apply { + action() + }.commit() +} \ No newline at end of file diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt index b6ad902279..fb71c5c388 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt @@ -49,7 +49,9 @@ import javax.inject.Inject import javax.inject.Singleton interface BLECommCallback { + fun onBLEConnected() + fun onBLEDisconnected() fun onNotification(notification: ByteArray) fun onIndication(indication: ByteArray) fun onSendMessageError(reason: String) @@ -88,8 +90,8 @@ class BLEComm @Inject internal constructor( private val mBluetoothAdapter: BluetoothAdapter? get() = (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?)?.adapter private var mBluetoothGatt: BluetoothGatt? = null - var isConnected = false - var isConnecting = false + var isConnected = false // TODO: These may be removed have no function + var isConnecting = false// TODO: These may be removed have no function private var uartWrite: BluetoothGattCharacteristic? = null private var uartRead: BluetoothGattCharacteristic? = null @@ -179,7 +181,6 @@ class BLEComm @Inject internal constructor( } aapsLogger.debug(LTag.PUMPBTCOMM, "disconnect from: $from") mBluetoothGatt?.disconnect() - mBluetoothGatt = null } @Synchronized @@ -373,7 +374,7 @@ class BLEComm @Inject internal constructor( close() isConnected = false isConnecting = false - rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)) + mCallback?.onBLEDisconnected() aapsLogger.debug(LTag.PUMPBTCOMM, "Device was disconnected " + gatt.device.name) //Device was disconnected } } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt index aa9fd7e552..7ff73f7a35 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt @@ -32,6 +32,7 @@ import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventAppExit import info.nightscout.rx.events.EventInitializationChanged import info.nightscout.rx.events.EventOverviewBolusProgress +import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventProfileSwitchChanged import info.nightscout.rx.events.EventPumpStatusChanged import info.nightscout.rx.logging.AAPSLogger @@ -79,6 +80,8 @@ class MedtrumService : DaggerService(), BLECommCallback { private var mDeviceSN: Long = 0 private var currentState: State = IdleState() + var isConnected = false + var isConnecting = false // TODO: Stuff like this in a settings class? private var mLastDeviceTime: Long = 0 @@ -90,6 +93,16 @@ class MedtrumService : DaggerService(), BLECommCallback { .toObservable(EventAppExit::class.java) .observeOn(aapsSchedulers.io) .subscribe({ stopSelf() }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventPreferenceChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event -> + if (event.isChanged(rh.gs(info.nightscout.pump.medtrum.R.string.key_snInput))) { + pumpSync.connectNewPump() + changePump() + } + }, fabricPrivacy::logException) + changePump() } override fun onDestroy() { @@ -97,26 +110,27 @@ class MedtrumService : DaggerService(), BLECommCallback { super.onDestroy() } - val isConnected: Boolean - get() = bleComm.isConnected - - val isConnecting: Boolean - get() = bleComm.isConnecting - - fun connect(from: String, deviceSN: Long): Boolean { - // TODO Check we might want to replace this with start scan? - mDeviceSN = deviceSN - return bleComm.connect(from, deviceSN) + fun connect(from: String): Boolean { + aapsLogger.debug(LTag.PUMP, "connect: called!") + if (currentState is IdleState) { + isConnecting = true + isConnected = false + rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTING)) + return bleComm.connect(from, mDeviceSN) + } else { + aapsLogger.error(LTag.PUMPCOMM, "Connect attempt when in non Idle state from: " + from) + return false + } } fun stopConnecting() { // TODO proper way for this might need send commands etc - // bleComm.stopConnecting() + bleComm.stopConnecting() } fun disconnect(from: String) { // TODO proper way for this might need send commands etc - // bleComm.disconnect(from) + bleComm.disconnect(from) } fun readPumpStatus() { @@ -156,11 +170,30 @@ class MedtrumService : DaggerService(), BLECommCallback { return false } + fun changePump() { + aapsLogger.debug(LTag.PUMP, "changePump: called!") + try { + mDeviceSN = sp.getString(info.nightscout.pump.medtrum.R.string.key_snInput, " ").toLong(radix = 16) + } catch (e: NumberFormatException) { + aapsLogger.debug(LTag.PUMP, "changePump: Invalid input!") + } + // TODO: What do we do with active patch here? + when (currentState) { + // is IdleState -> connect("changePump") + // is ReadyState -> disconnect("changePump") + else -> null // TODO: What to do here? Abort stuff? + } + } + /** BLECommCallbacks */ override fun onBLEConnected() { currentState.onConnected() } + override fun onBLEDisconnected() { + currentState.onDisconnected() + } + override fun onNotification(notification: ByteArray) { aapsLogger.debug(LTag.PUMPCOMM, "<<<<< onNotification" + notification.contentToString()) // TODO @@ -207,14 +240,34 @@ class MedtrumService : DaggerService(), BLECommCallback { aapsLogger.debug(LTag.PUMPCOMM, "onIndicationr: " + this.toString() + "Should not be called here!") } - open fun onConnected() {} + open fun onConnected() { + aapsLogger.debug(LTag.PUMPCOMM, "onConnected") + } + + open fun onDisconnected() { + aapsLogger.debug(LTag.PUMPCOMM, "onDisconnected") + isConnecting = false + isConnected = false + rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)) + + // TODO: Check flow for this + toState(IdleState()) + } } private inner class IdleState : State() { + override fun onEnter() {} + override fun onConnected() { + super.onConnected() toState(AuthState()) } + + override fun onDisconnected() { + super.onDisconnected() + // TODO replace this by proper connecting state where we can retry + } } private inner class AuthState : State() { @@ -391,7 +444,11 @@ class MedtrumService : DaggerService(), BLECommCallback { override fun onEnter() { aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached ReadyState!") + // Now we are fully connected and authenticated and we can start sending commands. Let AAPS know + isConnecting = false + isConnected = true + rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED)) } // Just a placeholder, this state is reached when the patch is ready to receive commands (Bolus, temp basal and whatever) } -} \ No newline at end of file +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumActivity.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumActivity.kt new file mode 100644 index 0000000000..7b49049a6e --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumActivity.kt @@ -0,0 +1,97 @@ +package info.nightscout.pump.medtrum.ui + +import android.app.Dialog +import android.content.Context +import android.content.Intent +import android.media.MediaPlayer +import android.media.RingtoneManager +import android.os.Bundle +import android.view.MotionEvent +import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.ViewModelProvider +import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPreparePatchFragment +import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPrimeFragment +import info.nightscout.core.utils.extensions.safeGetSerializableExtra +import info.nightscout.pump.medtrum.R +import info.nightscout.pump.medtrum.code.PatchStep +import info.nightscout.pump.medtrum.databinding.ActivityMedtrumBinding +import info.nightscout.pump.medtrum.extension.replaceFragmentInActivity +import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel + +class MedtrumActivity : MedtrumBaseActivity() { + + override fun getLayoutId(): Int = R.layout.activity_medtrum + + override fun dispatchTouchEvent(event: MotionEvent): Boolean { + if (event.actionMasked == MotionEvent.ACTION_UP) { + // TODO + } + + return super.dispatchTouchEvent(event) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding.apply { + viewModel = ViewModelProvider(this@MedtrumActivity, viewModelFactory).get(MedtrumViewModel::class.java) + viewModel?.apply { + processIntent(intent) + + patchStep.observe(this@MedtrumActivity) { + when (it) { + PatchStep.PREPARE_PATCH -> setupViewFragment(MedtrumPreparePatchFragment.newInstance()) + PatchStep.PRIME -> setupViewFragment(MedtrumPrimeFragment.newInstance()) + PatchStep.CANCEL -> this@MedtrumActivity.finish() + else -> Unit + } + } + } + } + } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + processIntent(intent) + } + + private fun processIntent(intent: Intent?) { + binding.viewModel?.apply { + intent?.run { + val step = intent.safeGetSerializableExtra(EXTRA_START_PATCH_STEP, PatchStep::class.java) + initializePatchStep(step) + } + } + } + + override fun onDestroy() { + super.onDestroy() + // TODO + } + + override fun onBackPressed() { + binding.viewModel?.apply { + // TODO DEACTIVATION ? + } + } + + companion object { + + const val EXTRA_START_PATCH_STEP = "EXTRA_START_PATCH_FRAGMENT_UI" + const val EXTRA_START_FROM_MENU = "EXTRA_START_FROM_MENU" + + @JvmStatic + fun createIntentFromMenu(context: Context, patchStep: PatchStep): Intent { + return Intent(context, MedtrumActivity::class.java).apply { + putExtra(EXTRA_START_PATCH_STEP, patchStep) + putExtra(EXTRA_START_FROM_MENU, true) + } + } + + } + + private fun setupViewFragment(baseFragment: MedtrumBaseFragment<*>) { + replaceFragmentInActivity(baseFragment, R.id.framelayout_fragment, false) + } + +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumOverviewFragment.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumOverviewFragment.kt index a29b8c60ad..25aad5ce89 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumOverviewFragment.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumOverviewFragment.kt @@ -6,18 +6,18 @@ import android.view.View import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.lifecycle.ViewModelProvider -import info.nightscout.pump.medtrum.databinding.MedtrumOverviewFragmentBinding +import info.nightscout.pump.medtrum.databinding.FragmentMedtrumOverviewBinding import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumOverviewViewModel import info.nightscout.pump.medtrum.R +import info.nightscout.pump.medtrum.code.EventType +import info.nightscout.pump.medtrum.code.PatchStep import info.nightscout.rx.AapsSchedulers -import info.nightscout.rx.bus.RxBus import info.nightscout.rx.logging.AAPSLogger import io.reactivex.rxjava3.disposables.CompositeDisposable import javax.inject.Inject -class MedtrumOverviewFragment : MedtrumBaseFragment() { +class MedtrumOverviewFragment : MedtrumBaseFragment() { - @Inject lateinit var rxBus: RxBus @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsLogger: AAPSLogger private lateinit var resultLauncherForResume: ActivityResultLauncher @@ -25,7 +25,7 @@ class MedtrumOverviewFragment : MedtrumBaseFragment + when (evt.peekContent()) { + EventType.ACTIVATION_CLICKED -> requireContext().apply { startActivity(MedtrumActivity.createIntentFromMenu(this, PatchStep.PREPARE_PATCH)) } + else -> Unit + } } - } - resultLauncherForPause = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - when (it.resultCode) { - // TODO Handle events here, see eopatch eventhandler + resultLauncherForResume = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + when (it.resultCode) { + // TODO Handle events here, see eopatch eventhandler + } + } + + resultLauncherForPause = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + when (it.resultCode) { + // TODO Handle events here, see eopatch eventhandler + } } } } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumPreparePatchFragment.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumPreparePatchFragment.kt new file mode 100644 index 0000000000..650d103f6e --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumPreparePatchFragment.kt @@ -0,0 +1,42 @@ +package info.nightscout.androidaps.plugins.pump.eopatch.ui + +import android.os.Bundle +import android.view.View +import androidx.lifecycle.ViewModelProvider +import info.nightscout.pump.medtrum.R +import info.nightscout.pump.medtrum.databinding.FragmentMedtrumPreparePatchBinding +import info.nightscout.pump.medtrum.ui.MedtrumBaseFragment +import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import javax.inject.Inject + +class MedtrumPreparePatchFragment : MedtrumBaseFragment() { + + @Inject lateinit var aapsLogger: AAPSLogger + + companion object { + + fun newInstance(): MedtrumPreparePatchFragment = MedtrumPreparePatchFragment() + } + + override fun getLayoutId(): Int = R.layout.fragment_medtrum_prepare_patch + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + aapsLogger.debug(LTag.PUMP, "MedtrumPreparePatchFragment onViewCreated") + binding.apply { + viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(MedtrumViewModel::class.java) + viewModel?.apply { + setupStep.observe(viewLifecycleOwner) { + when (it) { + // TODO: Confirmation dialog + MedtrumViewModel.SetupStep.CONNECTED -> btnPositive.visibility = View.VISIBLE + else -> Unit + } + } + preparePatch() + } + } + } +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumPrimeFragment.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumPrimeFragment.kt new file mode 100644 index 0000000000..42f27d3105 --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumPrimeFragment.kt @@ -0,0 +1,32 @@ +package info.nightscout.androidaps.plugins.pump.eopatch.ui + +import android.os.Bundle +import android.view.View +import androidx.lifecycle.ViewModelProvider +import info.nightscout.pump.medtrum.R +import info.nightscout.pump.medtrum.databinding.FragmentMedtrumPrimeBinding +import info.nightscout.pump.medtrum.ui.MedtrumBaseFragment +import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import javax.inject.Inject + +class MedtrumPrimeFragment : MedtrumBaseFragment() { + + @Inject lateinit var aapsLogger: AAPSLogger + + companion object { + + fun newInstance(): MedtrumPrimeFragment = MedtrumPrimeFragment() + } + + override fun getLayoutId(): Int = R.layout.fragment_medtrum_prime + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.apply { + viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(MedtrumViewModel::class.java) + // TODO do stuff + } + } +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/event/SingleLiveEvent.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/event/SingleLiveEvent.kt new file mode 100644 index 0000000000..2d52d4d7cd --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/event/SingleLiveEvent.kt @@ -0,0 +1,29 @@ +package info.nightscout.pump.medtrum.ui.event + +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import androidx.annotation.MainThread +import java.util.concurrent.atomic.AtomicBoolean + +open class SingleLiveEvent : MutableLiveData() { + private val mPending = AtomicBoolean(false) + override fun observe(owner: LifecycleOwner, observer: Observer) { + super.observe(owner) { t -> + if (mPending.compareAndSet(true, false)) { + observer.onChanged(t) + } + } + } + + @MainThread + override fun setValue(t: T?) { + mPending.set(true) + super.setValue(t) + } + + @MainThread + fun call() { + value = null + } +} \ No newline at end of file diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/event/UIEvent.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/event/UIEvent.kt new file mode 100644 index 0000000000..e1f879799b --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/event/UIEvent.kt @@ -0,0 +1,7 @@ +package info.nightscout.pump.medtrum.ui.event + +open class UIEvent(private val content: T) { + var value: Any? = null + fun peekContent(): T = content +} + diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt index 0cce7e419c..d270275c03 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt @@ -1,32 +1,77 @@ package info.nightscout.pump.medtrum.ui.viewmodel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import info.nightscout.core.utils.fabric.FabricPrivacy +import info.nightscout.pump.medtrum.code.EventType import info.nightscout.pump.medtrum.ui.MedtrumBaseNavigator +import info.nightscout.pump.medtrum.ui.event.SingleLiveEvent +import info.nightscout.pump.medtrum.ui.event.UIEvent import info.nightscout.pump.medtrum.ui.viewmodel.BaseViewModel +import info.nightscout.interfaces.profile.ProfileFunction +import info.nightscout.rx.AapsSchedulers +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.events.EventPumpStatusChanged import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject class MedtrumOverviewViewModel @Inject constructor( - private val aapsLogger: AAPSLogger + private val aapsLogger: AAPSLogger, + private val rxBus: RxBus, + private val aapsSchedulers: AapsSchedulers, + private val fabricPrivacy: FabricPrivacy, + private val profileFunction: ProfileFunction, ) : BaseViewModel() { - val isPatchActivated : Boolean + private var disposable: CompositeDisposable = CompositeDisposable() + + private val _eventHandler = SingleLiveEvent>() + val eventHandler: LiveData> + get() = _eventHandler + + private val _bleStatus = SingleLiveEvent() + val bleStatus: LiveData + get() = _bleStatus + + // TODO make these livedata + val isPatchActivated: Boolean get() = false // TODO - val isPatchConnected: Boolean - get() = false // TODO - val bleStatus : String - get() ="" //TODO init { - // TODO + disposable += rxBus + .toObservable(EventPumpStatusChanged::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ + _bleStatus.value = when (it.status) { + EventPumpStatusChanged.Status.CONNECTING -> + "{fa-bluetooth-b spin} ${it.secondsElapsed}s" + + EventPumpStatusChanged.Status.CONNECTED -> + "{fa-bluetooth}" + + EventPumpStatusChanged.Status.DISCONNECTED -> + "{fa-bluetooth-b}" + + else -> + "" + } + }, fabricPrivacy::logException) } - fun onClickActivation(){ + fun onClickActivation() { aapsLogger.debug(LTag.PUMP, "Start Patch clicked!") - // TODO + val profile = profileFunction.getProfile() + if (profile == null) { + _eventHandler.postValue(UIEvent(EventType.PROFILE_NOT_SET)) + } else { + _eventHandler.postValue(UIEvent(EventType.ACTIVATION_CLICKED)) + } } - fun onClickDeactivation(){ + fun onClickDeactivation() { aapsLogger.debug(LTag.PUMP, "Stop Patch clicked!") // TODO } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumViewModel.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumViewModel.kt new file mode 100644 index 0000000000..328b42a89a --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumViewModel.kt @@ -0,0 +1,128 @@ +package info.nightscout.pump.medtrum.ui.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import info.nightscout.core.utils.fabric.FabricPrivacy +import info.nightscout.pump.medtrum.MedtrumPlugin +import info.nightscout.pump.medtrum.services.MedtrumService +import info.nightscout.pump.medtrum.code.EventType +import info.nightscout.pump.medtrum.code.PatchStep +import info.nightscout.pump.medtrum.ui.MedtrumBaseNavigator +import info.nightscout.pump.medtrum.ui.event.SingleLiveEvent +import info.nightscout.pump.medtrum.ui.event.UIEvent +import info.nightscout.shared.interfaces.ResourceHelper +import info.nightscout.rx.AapsSchedulers +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.events.EventPumpStatusChanged +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import info.nightscout.shared.sharedPreferences.SP +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import javax.inject.Inject + +class MedtrumViewModel @Inject constructor( + private val rh: ResourceHelper, + private val aapsLogger: AAPSLogger, + private val rxBus: RxBus, + private val aapsSchedulers: AapsSchedulers, + private val fabricPrivacy: FabricPrivacy, + private val medtrumPlugin: MedtrumPlugin, + private val sp: SP +) : BaseViewModel() { + + val patchStep = MutableLiveData() + + val title = "Activation" + + val medtrumService: MedtrumService? + get() = medtrumPlugin.getService() + + private var disposable: CompositeDisposable = CompositeDisposable() + + private val _eventHandler = SingleLiveEvent>() + val eventHandler: LiveData> + get() = _eventHandler + + private var mInitPatchStep: PatchStep? = null + + init { + disposable += rxBus + .toObservable(EventPumpStatusChanged::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ + when (it.status) { + EventPumpStatusChanged.Status.CONNECTING -> {} + + EventPumpStatusChanged.Status.CONNECTED + -> if (patchStep.value == PatchStep.PREPARE_PATCH) setupStep.postValue(SetupStep.CONNECTED) else { + } + + EventPumpStatusChanged.Status.DISCONNECTED -> {} + + else -> {} + } + }, fabricPrivacy::logException) + } + + fun moveStep(newPatchStep: PatchStep) { + val oldPatchStep = patchStep.value + + if (oldPatchStep != newPatchStep) { + when (newPatchStep) { + PatchStep.CANCEL -> { + if (medtrumService?.isConnected == true || medtrumService?.isConnecting == true) medtrumService?.disconnect("Cancel") else { + } + } + + else -> null + }?.let { + // TODO Update lifecycle + } + } + + prepareStep(newPatchStep) + + aapsLogger.info(LTag.PUMP, "moveStep: $oldPatchStep -> $newPatchStep") + } + + fun initializePatchStep(step: PatchStep?) { + mInitPatchStep = prepareStep(step) + } + + fun preparePatch() { + // TODO: Decide if we want to connect already when user is still filling, or if we want to wait after user is done filling + medtrumService?.connect("PreparePatch") + } + + private fun prepareStep(step: PatchStep?): PatchStep { + // TODO Title per screen :) And proper sync with patchstate + // (step ?: convertToPatchStep(patchConfig.lifecycleEvent.lifeCycle)).let { newStep -> + + (step ?: PatchStep.SAFE_DEACTIVATION).let { newStep -> + when (newStep) { + + else -> "" + }.let { + + } + + patchStep.postValue(newStep) + + return newStep + } + } + + enum class SetupStep { + CONNECTED, + PRIME_READY, + ACTIVATED + } + + val setupStep = MutableLiveData() + + private fun updateSetupStep(newSetupStep: SetupStep) { + aapsLogger.info(LTag.PUMP, "curSetupStep: ${setupStep.value}, newSetupStep: $newSetupStep") + setupStep.postValue(newSetupStep) + } +} diff --git a/pump/medtrum/src/main/res/layout/activity_medtrum.xml b/pump/medtrum/src/main/res/layout/activity_medtrum.xml new file mode 100644 index 0000000000..c7658e2ce9 --- /dev/null +++ b/pump/medtrum/src/main/res/layout/activity_medtrum.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pump/medtrum/src/main/res/layout/medtrum_overview_fragment.xml b/pump/medtrum/src/main/res/layout/fragment_medtrum_overview.xml similarity index 98% rename from pump/medtrum/src/main/res/layout/medtrum_overview_fragment.xml rename to pump/medtrum/src/main/res/layout/fragment_medtrum_overview.xml index 471d939b6d..b7b6cdedfe 100644 --- a/pump/medtrum/src/main/res/layout/medtrum_overview_fragment.xml +++ b/pump/medtrum/src/main/res/layout/fragment_medtrum_overview.xml @@ -3,9 +3,10 @@ xmlns:tools="http://schemas.android.com/tools"> + + type="info.nightscout.pump.medtrum.ui.viewmodel.MedtrumOverviewViewModel" /> +