Merge pull request #1879 from andyrozman/pump_common
- extended pump-common package a little, so that in the future we can…
This commit is contained in:
commit
16506b8faf
34 changed files with 1547 additions and 36 deletions
|
@ -195,7 +195,11 @@ class MedtronicPumpPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCustomActions() {
|
override fun hasService(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartScheduledPumpActions() {
|
||||||
|
|
||||||
// check status every minute (if any status needs refresh we send readStatus command)
|
// check status every minute (if any status needs refresh we send readStatus command)
|
||||||
Thread {
|
Thread {
|
||||||
|
@ -674,7 +678,7 @@ class MedtronicPumpPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// if enforceNew===true current temp basal is canceled and new TBR set (duration is prolonged),
|
// if enforceNew===true current temp basal is canceled and new TBR set (duration is prolonged),
|
||||||
// if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed
|
// if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult {
|
override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult {
|
||||||
setRefreshButtonEnabled(false)
|
setRefreshButtonEnabled(false)
|
||||||
|
@ -744,7 +748,7 @@ class MedtronicPumpPlugin @Inject constructor(
|
||||||
PumpEnactResult(injector).success(false).enacted(false) //
|
PumpEnactResult(injector).success(false).enacted(false) //
|
||||||
.comment(R.string.medtronic_cmd_tbr_could_not_be_delivered)
|
.comment(R.string.medtronic_cmd_tbr_could_not_be_delivered)
|
||||||
} else {
|
} else {
|
||||||
medtronicPumpStatus.tempBasalStart = Date()
|
medtronicPumpStatus.tempBasalStart = System.currentTimeMillis()
|
||||||
medtronicPumpStatus.tempBasalAmount = absoluteRate
|
medtronicPumpStatus.tempBasalAmount = absoluteRate
|
||||||
medtronicPumpStatus.tempBasalLength = durationInMinutes
|
medtronicPumpStatus.tempBasalLength = durationInMinutes
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ class MedtronicPumpStatus @Inject constructor(private val rh: ResourceHelper,
|
||||||
get() {
|
get() {
|
||||||
if (tempBasalStart == null) return null
|
if (tempBasalStart == null) return null
|
||||||
if (tempBasalEnd == null) {
|
if (tempBasalEnd == null) {
|
||||||
val startTime = tempBasalStart!!.time
|
val startTime = tempBasalStart!!
|
||||||
tempBasalEnd = startTime + tempBasalLength!! * 60 * 1000
|
tempBasalEnd = startTime + tempBasalLength!! * 60 * 1000
|
||||||
}
|
}
|
||||||
if (System.currentTimeMillis() > tempBasalEnd!!) {
|
if (System.currentTimeMillis() > tempBasalEnd!!) {
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
<manifest>
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="info.nightscout.androidaps.plugins.pump.common">
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
|
<activity
|
||||||
|
android:name="info.nightscout.androidaps.plugins.pump.common.ui.PumpBLEConfigActivity"
|
||||||
|
android:theme="@style/AppTheme"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="info.nightscout.androidaps.plugins.pump.common.ui.PumpBLEConfigActivity" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name=".ui.PumpHistoryActivity" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import info.nightscout.androidaps.data.DetailedBolusInfo
|
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||||
import info.nightscout.androidaps.data.PumpEnactResult
|
import info.nightscout.androidaps.data.PumpEnactResult
|
||||||
|
@ -69,30 +70,41 @@ abstract class PumpPluginAbstract protected constructor(
|
||||||
protected var displayConnectionMessages = false
|
protected var displayConnectionMessages = false
|
||||||
|
|
||||||
var pumpType: PumpType = PumpType.GENERIC_AAPS
|
var pumpType: PumpType = PumpType.GENERIC_AAPS
|
||||||
|
get() = field
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
pumpDescription.fillFor(value)
|
pumpDescription.fillFor(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected var gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
|
||||||
|
|
||||||
abstract fun initPumpStatusData()
|
abstract fun initPumpStatusData()
|
||||||
|
|
||||||
|
open fun hasService(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
initPumpStatusData()
|
initPumpStatusData()
|
||||||
val intent = Intent(context, serviceClass)
|
if (hasService()) {
|
||||||
context.bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE)
|
val intent = Intent(context, serviceClass)
|
||||||
|
context.bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE)
|
||||||
|
disposable.add(rxBus
|
||||||
|
.toObservable(EventAppExit::class.java)
|
||||||
|
.observeOn(aapsSchedulers.io)
|
||||||
|
.subscribe({ _ -> context.unbindService(serviceConnection!!) }) { throwable: Throwable? -> fabricPrivacy.logException(throwable!!) }
|
||||||
|
)
|
||||||
|
}
|
||||||
serviceRunning = true
|
serviceRunning = true
|
||||||
disposable.add(rxBus
|
onStartScheduledPumpActions()
|
||||||
.toObservable(EventAppExit::class.java)
|
|
||||||
.observeOn(aapsSchedulers.io)
|
|
||||||
.subscribe({ context.unbindService(serviceConnection!!) }) { throwable: Throwable? -> fabricPrivacy.logException(throwable!!) }
|
|
||||||
)
|
|
||||||
onStartCustomActions()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
aapsLogger.debug(LTag.PUMP, deviceID() + " onStop()")
|
aapsLogger.debug(LTag.PUMP, model().model + " onStop()")
|
||||||
context.unbindService(serviceConnection!!)
|
if (hasService()) {
|
||||||
|
context.unbindService(serviceConnection!!)
|
||||||
|
}
|
||||||
serviceRunning = false
|
serviceRunning = false
|
||||||
disposable.clear()
|
disposable.clear()
|
||||||
super.onStop()
|
super.onStop()
|
||||||
|
@ -101,7 +113,7 @@ abstract class PumpPluginAbstract protected constructor(
|
||||||
/**
|
/**
|
||||||
* If we need to run any custom actions in onStart (triggering events, etc)
|
* If we need to run any custom actions in onStart (triggering events, etc)
|
||||||
*/
|
*/
|
||||||
abstract fun onStartCustomActions()
|
abstract fun onStartScheduledPumpActions()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service class (same one you did serviceConnection for)
|
* Service class (same one you did serviceConnection for)
|
||||||
|
@ -232,7 +244,7 @@ abstract class PumpPluginAbstract protected constructor(
|
||||||
val extended = JSONObject()
|
val extended = JSONObject()
|
||||||
try {
|
try {
|
||||||
battery.put("percent", pumpStatusData.batteryRemaining)
|
battery.put("percent", pumpStatusData.batteryRemaining)
|
||||||
status.put("status", pumpStatusData.pumpStatusType.status)
|
status.put("status", pumpStatusData.pumpRunningState.status)
|
||||||
extended.put("Version", version)
|
extended.put("Version", version)
|
||||||
try {
|
try {
|
||||||
extended.put("ActiveProfile", profileName)
|
extended.put("ActiveProfile", profileName)
|
||||||
|
@ -298,6 +310,7 @@ abstract class PumpPluginAbstract protected constructor(
|
||||||
// bolus needed, ask pump to deliver it
|
// bolus needed, ask pump to deliver it
|
||||||
deliverBolus(detailedBolusInfo)
|
deliverBolus(detailedBolusInfo)
|
||||||
} else {
|
} else {
|
||||||
|
detailedBolusInfo.timestamp = System.currentTimeMillis()
|
||||||
|
|
||||||
// no bolus required, carb only treatment
|
// no bolus required, carb only treatment
|
||||||
pumpSyncStorage.addCarbs(PumpDbEntryCarbs(detailedBolusInfo, this))
|
pumpSyncStorage.addCarbs(PumpDbEntryCarbs(detailedBolusInfo, this))
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.ble
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import dagger.android.DaggerBroadcastReceiver
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.events.EventPumpConnectionParametersChanged
|
||||||
|
import info.nightscout.androidaps.interfaces.ResourceHelper
|
||||||
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
|
import info.nightscout.shared.logging.LTag
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class BondStateReceiver(
|
||||||
|
@StringRes var deviceAddress: Int,
|
||||||
|
@StringRes var bondedFlag: Int,
|
||||||
|
var targetDevice: String,
|
||||||
|
var targetState: Int
|
||||||
|
) : DaggerBroadcastReceiver() {
|
||||||
|
|
||||||
|
@Inject lateinit var sp: SP
|
||||||
|
@Inject lateinit var context: Context
|
||||||
|
@Inject lateinit var rh: ResourceHelper
|
||||||
|
@Inject lateinit var aapsLogger: AAPSLogger
|
||||||
|
@Inject lateinit var rxBus: RxBus
|
||||||
|
|
||||||
|
var TAG = LTag.PUMPBTCOMM
|
||||||
|
var gson = Gson()
|
||||||
|
var applicationContext: Context? = null
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
super.onReceive(context, intent)
|
||||||
|
val action = intent.action
|
||||||
|
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
|
||||||
|
aapsLogger.info(TAG, "in onReceive: INTENT" + gson.toJson(intent))
|
||||||
|
if (device == null) {
|
||||||
|
aapsLogger.error(TAG, "onReceive. Device is null. Exiting.")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if (device.address != targetDevice) {
|
||||||
|
aapsLogger.error(TAG, "onReceive. Device is not the same as targetDevice. Exiting.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if action is valid
|
||||||
|
if (action == null) return
|
||||||
|
|
||||||
|
// Take action depending on new bond state
|
||||||
|
if (action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) {
|
||||||
|
val bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR)
|
||||||
|
val previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1)
|
||||||
|
aapsLogger.info(TAG, "in onReceive: bondState=$bondState, previousBondState=$previousBondState")
|
||||||
|
if (bondState == targetState) {
|
||||||
|
aapsLogger.info(TAG, "onReceive: found targeted state: $targetState")
|
||||||
|
val currentDeviceSettings = sp.getString(deviceAddress, "")
|
||||||
|
if (currentDeviceSettings.equals(targetDevice)) {
|
||||||
|
if (targetState == 12) {
|
||||||
|
sp.putBoolean(bondedFlag, true)
|
||||||
|
rxBus.send(EventPumpConnectionParametersChanged())
|
||||||
|
} else if (targetState == 10) {
|
||||||
|
sp.putBoolean(bondedFlag, false)
|
||||||
|
rxBus.send(EventPumpConnectionParametersChanged())
|
||||||
|
}
|
||||||
|
context.unregisterReceiver(this)
|
||||||
|
} else {
|
||||||
|
aapsLogger.error(TAG, "onReceive: Device stored in SP is not the same as target device, process interrupted")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
aapsLogger.info(TAG, "onReceive: currentBondState=$bondState, targetBondState=$targetState")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.common.data
|
package info.nightscout.androidaps.plugins.pump.common.data
|
||||||
|
|
||||||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpStatusType
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpRunningState
|
||||||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -33,12 +33,13 @@ abstract class PumpStatus(var pumpType: PumpType) {
|
||||||
var dailyTotalUnits: Double? = null
|
var dailyTotalUnits: Double? = null
|
||||||
var maxDailyTotalUnits: String? = null
|
var maxDailyTotalUnits: String? = null
|
||||||
var units: String? = null // Constants.MGDL or Constants.MMOL
|
var units: String? = null // Constants.MGDL or Constants.MMOL
|
||||||
var pumpStatusType = PumpStatusType.Running
|
var pumpRunningState = PumpRunningState.Running
|
||||||
var basalsByHour: DoubleArray? = null
|
var basalsByHour: DoubleArray? = null
|
||||||
var tempBasalStart: Date? = null
|
var tempBasalStart: Long? = null
|
||||||
var tempBasalAmount: Double? = 0.0
|
var tempBasalAmount: Double? = 0.0
|
||||||
var tempBasalLength: Int? = 0
|
var tempBasalLength: Int? = 0
|
||||||
var tempBasalEnd: Long? = null
|
var tempBasalEnd: Long? = null
|
||||||
|
var pumpTime: PumpTimeDifferenceDto? = null
|
||||||
|
|
||||||
abstract fun initSettings()
|
abstract fun initSettings()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.data
|
||||||
|
|
||||||
|
import org.joda.time.DateTime
|
||||||
|
import org.joda.time.Seconds
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 28/05/2021.
|
||||||
|
*/
|
||||||
|
class PumpTimeDifferenceDto constructor(var localDeviceTime: DateTime,
|
||||||
|
var pumpTime: DateTime) {
|
||||||
|
|
||||||
|
var timeDifference = 0
|
||||||
|
|
||||||
|
fun calculateDifference() {
|
||||||
|
val secondsBetween = Seconds.secondsBetween(localDeviceTime, pumpTime)
|
||||||
|
timeDifference = secondsBetween.seconds
|
||||||
|
|
||||||
|
// val diff = localDeviceTime - pumpTime
|
||||||
|
// timeDifference = (diff / 1000.0).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
calculateDifference()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.defs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 1/20/19.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum BasalProfileStatus {
|
||||||
|
|
||||||
|
NotInitialized, //
|
||||||
|
ProfileOK, //
|
||||||
|
ProfileChanged, //
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
|
@ -1,13 +1,25 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.common.defs
|
package info.nightscout.androidaps.plugins.pump.common.defs
|
||||||
|
|
||||||
enum class PumpDriverState {
|
import info.nightscout.androidaps.plugins.pump.common.R
|
||||||
|
|
||||||
NotInitialized,
|
// TODO there are 3 classes now, that do similar things, sort of, need to define exact rules: PumpDeviceState, PumpDriverState, PumpStatusState
|
||||||
Connecting,
|
|
||||||
Connected,
|
// TODO split this enum into 2
|
||||||
Initialized,
|
enum class PumpDriverState(var resourceId: Int) {
|
||||||
Ready, Busy,
|
|
||||||
Suspended;
|
NotInitialized(R.string.pump_status_not_initialized), // this state should be set only when driver is created
|
||||||
|
Connecting(R.string.connecting), //
|
||||||
|
Connected(R.string.connected), //
|
||||||
|
Initialized(R.string.pump_status_initialized), // this is weird state that probably won't be used, since its more driver centric that communication centric
|
||||||
|
EncryptCommunication(R.string.pump_status_encrypt), //
|
||||||
|
Ready(R.string.pump_status_ready),
|
||||||
|
Busy(R.string.pump_status_busy), //
|
||||||
|
Suspended(R.string.pump_status_suspended), //
|
||||||
|
Sleeping(R.string.pump_status_sleeping),
|
||||||
|
ExecutingCommand(R.string.pump_status_executing_command),
|
||||||
|
Disconnecting(R.string.disconnecting),
|
||||||
|
Disconnected(R.string.disconnected),
|
||||||
|
ErrorCommunicatingWithPump(R.string.pump_status_error_comm);
|
||||||
|
|
||||||
fun isConnected(): Boolean = this == Connected || this == Initialized || this == Busy || this == Suspended
|
fun isConnected(): Boolean = this == Connected || this == Initialized || this == Busy || this == Suspended
|
||||||
fun isInitialized(): Boolean = this == Initialized || this == Busy || this == Suspended
|
fun isInitialized(): Boolean = this == Initialized || this == Busy || this == Suspended
|
||||||
|
|
|
@ -2,7 +2,7 @@ package info.nightscout.androidaps.plugins.pump.common.defs
|
||||||
|
|
||||||
import info.nightscout.androidaps.plugins.pump.common.R
|
import info.nightscout.androidaps.plugins.pump.common.R
|
||||||
import info.nightscout.androidaps.interfaces.ResourceHelper
|
import info.nightscout.androidaps.interfaces.ResourceHelper
|
||||||
import java.util.*
|
import kotlin.streams.toList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes
|
* This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes
|
||||||
|
@ -11,9 +11,10 @@ import java.util.*
|
||||||
*
|
*
|
||||||
* Author: Andy {andy.rozman@gmail.com}
|
* Author: Andy {andy.rozman@gmail.com}
|
||||||
*/
|
*/
|
||||||
enum class PumpHistoryEntryGroup(val resourceId: Int) {
|
enum class PumpHistoryEntryGroup(val resourceId: Int, val pumpTypeGroupConfig: PumpTypeGroupConfig = PumpTypeGroupConfig.All) {
|
||||||
|
|
||||||
All(R.string.history_group_all),
|
All(R.string.history_group_all),
|
||||||
|
Base(R.string.history_group_base),
|
||||||
Bolus(R.string.history_group_bolus),
|
Bolus(R.string.history_group_bolus),
|
||||||
Basal(R.string.history_group_basal),
|
Basal(R.string.history_group_basal),
|
||||||
Prime(R.string.history_group_prime),
|
Prime(R.string.history_group_prime),
|
||||||
|
@ -22,7 +23,14 @@ enum class PumpHistoryEntryGroup(val resourceId: Int) {
|
||||||
Glucose(R.string.history_group_glucose),
|
Glucose(R.string.history_group_glucose),
|
||||||
Notification(R.string.history_group_notification),
|
Notification(R.string.history_group_notification),
|
||||||
Statistic(R.string.history_group_statistic),
|
Statistic(R.string.history_group_statistic),
|
||||||
Unknown(R.string.history_group_unknown);
|
Other(R.string.history_group_other),
|
||||||
|
Unknown(R.string.history_group_unknown),
|
||||||
|
|
||||||
|
// Ypso
|
||||||
|
EventsOnly(R.string.history_group_events),
|
||||||
|
EventsNoStat(R.string.history_group_events_no_stat)
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
var translated: String? = null
|
var translated: String? = null
|
||||||
private set
|
private set
|
||||||
|
@ -33,9 +41,10 @@ enum class PumpHistoryEntryGroup(val resourceId: Int) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private var translatedList: MutableList<PumpHistoryEntryGroup>? = null
|
@JvmStatic private var translatedList: MutableList<PumpHistoryEntryGroup>? = null
|
||||||
|
|
||||||
private fun doTranslation(rh: ResourceHelper) {
|
fun doTranslation(rh: ResourceHelper) {
|
||||||
|
if (translatedList != null) return
|
||||||
translatedList = ArrayList()
|
translatedList = ArrayList()
|
||||||
for (pumpHistoryEntryGroup in values()) {
|
for (pumpHistoryEntryGroup in values()) {
|
||||||
pumpHistoryEntryGroup.translated = rh.gs(pumpHistoryEntryGroup.resourceId)
|
pumpHistoryEntryGroup.translated = rh.gs(pumpHistoryEntryGroup.resourceId)
|
||||||
|
@ -43,9 +52,27 @@ enum class PumpHistoryEntryGroup(val resourceId: Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME this is just for Java compatibility reasons (can be removed when all drivers using it are in Kotlin - OmnipodEros still in java)
|
||||||
fun getTranslatedList(rh: ResourceHelper): List<PumpHistoryEntryGroup> {
|
fun getTranslatedList(rh: ResourceHelper): List<PumpHistoryEntryGroup> {
|
||||||
|
return getTranslatedList(rh, PumpTypeGroupConfig.All)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTranslatedList(rh: ResourceHelper, pumpTypeGroupConfig: PumpTypeGroupConfig = PumpTypeGroupConfig.All): List<PumpHistoryEntryGroup> {
|
||||||
if (translatedList == null) doTranslation(rh)
|
if (translatedList == null) doTranslation(rh)
|
||||||
return translatedList!!
|
|
||||||
|
val outList: List<PumpHistoryEntryGroup>
|
||||||
|
|
||||||
|
if (pumpTypeGroupConfig == PumpTypeGroupConfig.All) {
|
||||||
|
outList = translatedList!!.stream()
|
||||||
|
.filter { pre -> pre.pumpTypeGroupConfig == PumpTypeGroupConfig.All }
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
outList = translatedList!!.stream()
|
||||||
|
.filter { pre -> (pre.pumpTypeGroupConfig == PumpTypeGroupConfig.All || pre.pumpTypeGroupConfig == pumpTypeGroupConfig) }
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return outList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.defs
|
||||||
|
|
||||||
|
// TODO there are 3 classes now, that do similar things, sort of, need to define exact rules: PumpDeviceState, PumpDriverState, PumpStatusState
|
||||||
|
|
||||||
|
enum class PumpRunningState(val status: String) {
|
||||||
|
|
||||||
|
Running("normal"),
|
||||||
|
Suspended("suspended");
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.defs
|
||||||
|
|
||||||
|
enum class PumpTypeGroupConfig {
|
||||||
|
All,
|
||||||
|
Medtronic,
|
||||||
|
OmnipodEros,
|
||||||
|
YpsoPump
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.defs
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
enum class PumpUpdateFragmentType {
|
||||||
|
None,
|
||||||
|
PumpStatus,
|
||||||
|
DriverStatus,
|
||||||
|
Queue,
|
||||||
|
Bolus,
|
||||||
|
TBR,
|
||||||
|
ProfileChange,
|
||||||
|
TBRCount,
|
||||||
|
BolusCount,
|
||||||
|
TreatmentValues(Arrays.asList(Bolus, TBR, TBRCount, BolusCount, ProfileChange)), // Last Bolus, TBR, Profile Change, TBR Count, Bolus Count
|
||||||
|
Full,
|
||||||
|
Configuration, // Firmware, Errors
|
||||||
|
Battery,
|
||||||
|
Reservoir,
|
||||||
|
OtherValues(Arrays.asList(Battery, Reservoir)), // Battery, Reservoir
|
||||||
|
Custom_1,
|
||||||
|
Custom_2,
|
||||||
|
Custom_3,
|
||||||
|
Custom_4,
|
||||||
|
Custom_5,
|
||||||
|
Custom_6,
|
||||||
|
Custom_7,
|
||||||
|
Custom_8
|
||||||
|
;
|
||||||
|
|
||||||
|
final var children: List<PumpUpdateFragmentType>? = null
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(children: List<PumpUpdateFragmentType>) {
|
||||||
|
this.children = children;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isOptionIncluded(type: PumpUpdateFragmentType): Boolean {
|
||||||
|
if (this == type)
|
||||||
|
return true
|
||||||
|
else if (this.children != null && this.children!!.contains(type))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.driver
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.driver.history.PumpHistoryDataProvider
|
||||||
|
|
||||||
|
interface PumpDriverConfiguration {
|
||||||
|
|
||||||
|
fun getPumpBLESelector(): PumpBLESelector
|
||||||
|
|
||||||
|
fun getPumpHistoryDataProvider(): PumpHistoryDataProvider
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.driver
|
||||||
|
|
||||||
|
interface PumpDriverConfigurationCapable {
|
||||||
|
|
||||||
|
fun getPumpDriverConfiguration(): PumpDriverConfiguration
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.driver
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice
|
||||||
|
import android.bluetooth.le.ScanFilter
|
||||||
|
import android.bluetooth.le.ScanSettings
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
interface PumpBLESelector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on resume
|
||||||
|
*/
|
||||||
|
fun onResume()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on destory
|
||||||
|
*/
|
||||||
|
fun onDestroy()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when device is being removed (it can be empty if you don't need to do any special action, but if you
|
||||||
|
* have to unbound (for example), then this is method where to call it. For unbounding removeBond is available
|
||||||
|
*/
|
||||||
|
fun removeDevice(device: BluetoothDevice)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup method after device was removed
|
||||||
|
*/
|
||||||
|
fun cleanupAfterDeviceRemoved()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* operations when scan failed
|
||||||
|
*/
|
||||||
|
fun onScanFailed(context: Context, errorCode: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* operations when scan starts
|
||||||
|
*/
|
||||||
|
fun onStartLeDeviceScan(context: Context)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* operations when scan stops
|
||||||
|
*/
|
||||||
|
fun onStopLeDeviceScan(context: Context)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* operations when scan was stopped manualy (press on button)
|
||||||
|
*/
|
||||||
|
fun onManualStopLeDeviceScan(context: Context)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* operations when on non manual stop of scan (on timeout)
|
||||||
|
*/
|
||||||
|
fun onNonManualStopLeDeviceScan(context: Context)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get Scan Filters
|
||||||
|
*/
|
||||||
|
fun getScanFilters(): List<ScanFilter>?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get Scan Settings
|
||||||
|
*/
|
||||||
|
fun getScanSettings(): ScanSettings?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter device on search (for cases where we can't do it with Scan Filters
|
||||||
|
*/
|
||||||
|
fun filterDevice(device: BluetoothDevice): BluetoothDevice?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* operations when device selected
|
||||||
|
*/
|
||||||
|
fun onDeviceSelected(bluetoothDevice: BluetoothDevice, bleAddress: String, deviceName: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If pump has no name, this name will be used
|
||||||
|
*/
|
||||||
|
fun getUnknownPumpName(): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get Address of Currently selected pump, empty string if none
|
||||||
|
*/
|
||||||
|
fun currentlySelectedPumpAddress(): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get Name of Currently selected pump, getUnknownPumpName() string if none
|
||||||
|
*/
|
||||||
|
fun currentlySelectedPumpName(): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Translation Text
|
||||||
|
*/
|
||||||
|
fun getText(key: PumpBLESelectorText): String
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PumpBLESelectorText {
|
||||||
|
SCAN_TITLE,
|
||||||
|
SELECTED_PUMP_TITLE,
|
||||||
|
REMOVE_TITLE,
|
||||||
|
REMOVE_TEXT,
|
||||||
|
NO_SELECTED_PUMP,
|
||||||
|
PUMP_CONFIGURATION
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.driver.ble
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice
|
||||||
|
import android.bluetooth.le.ScanFilter
|
||||||
|
import android.bluetooth.le.ScanSettings
|
||||||
|
import android.content.Context
|
||||||
|
import android.widget.Toast
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.R
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.driver.PumpBLESelector
|
||||||
|
import info.nightscout.androidaps.interfaces.ResourceHelper
|
||||||
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
|
import info.nightscout.shared.logging.LTag
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
|
||||||
|
abstract class PumpBLESelectorAbstract constructor(
|
||||||
|
var resourceHelper: ResourceHelper,
|
||||||
|
var aapsLogger: AAPSLogger,
|
||||||
|
var sp: SP,
|
||||||
|
var rxBus: RxBus,
|
||||||
|
var context: Context
|
||||||
|
) : PumpBLESelector {
|
||||||
|
|
||||||
|
protected val TAG = LTag.PUMPBTCOMM
|
||||||
|
|
||||||
|
override fun getScanSettings(): ScanSettings? {
|
||||||
|
return ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getScanFilters(): MutableList<ScanFilter>? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun filterDevice(device: BluetoothDevice): BluetoothDevice? {
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeDevice(device: BluetoothDevice) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cleanupAfterDeviceRemoved() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onManualStopLeDeviceScan(context: Context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNonManualStopLeDeviceScan(context: Context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//fun onDeviceSelected(bluetoothDevice: BluetoothDevice, bleAddress: String, deviceName: String)
|
||||||
|
|
||||||
|
override fun onScanFailed(context: Context, errorCode: Int) {
|
||||||
|
Toast.makeText(
|
||||||
|
context, resourceHelper.gs(R.string.ble_config_scan_error, errorCode),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartLeDeviceScan(context: Context) {
|
||||||
|
Toast.makeText(context, R.string.ble_config_scan_scanning, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStopLeDeviceScan(context: Context) {
|
||||||
|
Toast.makeText(context, R.string.ble_config_scan_finished, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun removeBond(bluetoothDevice: BluetoothDevice): Boolean {
|
||||||
|
return try {
|
||||||
|
val method = bluetoothDevice.javaClass.getMethod("removeBond")
|
||||||
|
val resultObject = method.invoke(bluetoothDevice)
|
||||||
|
if (resultObject == null) {
|
||||||
|
aapsLogger.error(TAG, "ERROR: result object is null")
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
val result = resultObject as Boolean
|
||||||
|
if (result) {
|
||||||
|
aapsLogger.info(TAG, "Successfully removed bond")
|
||||||
|
} else {
|
||||||
|
aapsLogger.warn(TAG, "Bond was not removed")
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
aapsLogger.error(TAG, "ERROR: could not remove bond")
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun getBondingStatusDescription(state: Int): String {
|
||||||
|
return if (state == 10) {
|
||||||
|
"BOND_NONE"
|
||||||
|
} else if (state == 11) {
|
||||||
|
"BOND_BONDING"
|
||||||
|
} else if (state == 12) {
|
||||||
|
"BOND_BONDED"
|
||||||
|
} else {
|
||||||
|
"UNKNOWN BOND STATUS ($state)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.driver.history
|
||||||
|
|
||||||
|
interface PumpDataConverter {
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.driver.history
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.R
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup
|
||||||
|
|
||||||
|
interface PumpHistoryDataProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Data, specified with PumpHistoryPeriod
|
||||||
|
*/
|
||||||
|
fun getData(period: PumpHistoryPeriod): List<PumpHistoryEntry>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Initial Period
|
||||||
|
*/
|
||||||
|
fun getInitialPeriod(): PumpHistoryPeriod
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get InitialData
|
||||||
|
*/
|
||||||
|
fun getInitialData(): List<PumpHistoryEntry>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Allowed Pump History Groups (for specific pump)
|
||||||
|
*/
|
||||||
|
fun getAllowedPumpHistoryGroups(): List<PumpHistoryEntryGroup>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Spinner Width in pixels (same as specifying 150dp)
|
||||||
|
*/
|
||||||
|
fun getSpinnerWidthInPixels(): Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Translation Text
|
||||||
|
*/
|
||||||
|
fun getText(key: PumpHistoryText): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For filtering of items
|
||||||
|
*/
|
||||||
|
fun isItemInSelection(itemGroup: PumpHistoryEntryGroup, targetGroup: PumpHistoryEntryGroup): Boolean
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PumpHistoryPeriod constructor(
|
||||||
|
@StringRes var stringId: Int,
|
||||||
|
var isHours: Boolean = false
|
||||||
|
) {
|
||||||
|
|
||||||
|
TODAY(R.string.time_today),
|
||||||
|
LAST_HOUR(R.string.time_last_hour, true),
|
||||||
|
LAST_3_HOURS(R.string.time_last_3_hours, true),
|
||||||
|
LAST_6_HOURS(R.string.time_last_6_hours, true),
|
||||||
|
LAST_12_HOURS(R.string.time_last_12_hours, true),
|
||||||
|
LAST_2_DAYS(R.string.time_last_2_days),
|
||||||
|
LAST_4_DAYS(R.string.time_last_4_days),
|
||||||
|
LAST_WEEK(R.string.time_last_week),
|
||||||
|
LAST_MONTH(R.string.time_last_month),
|
||||||
|
ALL(R.string.history_group_all)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PumpHistoryText {
|
||||||
|
|
||||||
|
PUMP_HISTORY,
|
||||||
|
|
||||||
|
// OLD ONES
|
||||||
|
SCAN_TITLE,
|
||||||
|
SELECTED_PUMP_TITLE,
|
||||||
|
REMOVE_TITLE,
|
||||||
|
REMOVE_TEXT,
|
||||||
|
NO_SELECTED_PUMP,
|
||||||
|
PUMP_CONFIGURATION
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.driver.history
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
abstract class PumpHistoryDataProviderAbstract : PumpHistoryDataProvider {
|
||||||
|
|
||||||
|
override fun getInitialData(): List<PumpHistoryEntry> {
|
||||||
|
return getData(getInitialPeriod());
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSpinnerWidthInPixels(): Int {
|
||||||
|
return 150
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun getStartingTimeForData(period: PumpHistoryPeriod): Long {
|
||||||
|
val gregorianCalendar = GregorianCalendar()
|
||||||
|
|
||||||
|
if (!period.isHours) {
|
||||||
|
gregorianCalendar.set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
gregorianCalendar.set(Calendar.MINUTE, 0)
|
||||||
|
gregorianCalendar.set(Calendar.SECOND, 0)
|
||||||
|
gregorianCalendar.set(Calendar.MILLISECOND, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (period) {
|
||||||
|
PumpHistoryPeriod.TODAY -> return gregorianCalendar.timeInMillis
|
||||||
|
PumpHistoryPeriod.ALL -> return 0L
|
||||||
|
PumpHistoryPeriod.LAST_2_DAYS -> gregorianCalendar.add(Calendar.DAY_OF_MONTH, -1)
|
||||||
|
PumpHistoryPeriod.LAST_4_DAYS -> gregorianCalendar.add(Calendar.DAY_OF_MONTH, -3)
|
||||||
|
PumpHistoryPeriod.LAST_WEEK -> gregorianCalendar.add(Calendar.WEEK_OF_YEAR, -1)
|
||||||
|
PumpHistoryPeriod.LAST_MONTH -> gregorianCalendar.add(Calendar.MONTH, -1)
|
||||||
|
PumpHistoryPeriod.LAST_HOUR -> gregorianCalendar.add(Calendar.HOUR_OF_DAY, -1)
|
||||||
|
PumpHistoryPeriod.LAST_3_HOURS -> gregorianCalendar.add(Calendar.HOUR_OF_DAY, -3)
|
||||||
|
PumpHistoryPeriod.LAST_6_HOURS -> gregorianCalendar.add(Calendar.HOUR_OF_DAY, -6)
|
||||||
|
PumpHistoryPeriod.LAST_12_HOURS -> gregorianCalendar.add(Calendar.HOUR_OF_DAY, -12)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gregorianCalendar.timeInMillis
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isItemInSelection(itemGroup: PumpHistoryEntryGroup, targetGroup: PumpHistoryEntryGroup): Boolean {
|
||||||
|
return itemGroup === targetGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.driver.history
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup
|
||||||
|
import info.nightscout.androidaps.interfaces.ResourceHelper
|
||||||
|
|
||||||
|
interface PumpHistoryEntry {
|
||||||
|
|
||||||
|
fun prepareEntryData(resourceHelper: ResourceHelper, pumpDataConverter: PumpDataConverter)
|
||||||
|
|
||||||
|
fun getEntryDateTime(): String
|
||||||
|
|
||||||
|
fun getEntryType(): String
|
||||||
|
|
||||||
|
fun getEntryValue(): String
|
||||||
|
|
||||||
|
fun getEntryTypeGroup(): PumpHistoryEntryGroup
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
|
||||||
|
class EventBondChanged(
|
||||||
|
var connectionAddress: String,
|
||||||
|
var bondStatus: Boolean
|
||||||
|
) : Event()
|
|
@ -0,0 +1,8 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
|
||||||
|
class EventPumpChanged(var serialNumber: String,
|
||||||
|
var connectionAddress: String,
|
||||||
|
var parameters: MutableMap<String, Any>? = null) : Event() {
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
|
||||||
|
class EventPumpConnectionParametersChanged : Event() {
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpUpdateFragmentType
|
||||||
|
|
||||||
|
class EventPumpFragmentValuesChanged : Event {
|
||||||
|
|
||||||
|
var updateType: PumpUpdateFragmentType = PumpUpdateFragmentType.None
|
||||||
|
|
||||||
|
constructor(updateType: PumpUpdateFragmentType) {
|
||||||
|
this.updateType = updateType
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,338 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.ui
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.bluetooth.BluetoothAdapter
|
||||||
|
import android.bluetooth.BluetoothDevice
|
||||||
|
import android.bluetooth.BluetoothManager
|
||||||
|
import android.bluetooth.le.BluetoothLeScanner
|
||||||
|
import android.bluetooth.le.ScanCallback
|
||||||
|
import android.bluetooth.le.ScanFilter
|
||||||
|
import android.bluetooth.le.ScanResult
|
||||||
|
import android.bluetooth.le.ScanSettings
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.HandlerThread
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.AdapterView
|
||||||
|
import android.widget.AdapterView.OnItemClickListener
|
||||||
|
import android.widget.BaseAdapter
|
||||||
|
import android.widget.TextView
|
||||||
|
import dagger.android.support.DaggerAppCompatActivity
|
||||||
|
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.R
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.ble.BlePreCheck
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.databinding.PumpBleConfigActivityBinding
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.driver.PumpBLESelector
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.driver.PumpBLESelectorText
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.driver.PumpDriverConfigurationCapable
|
||||||
|
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||||
|
import info.nightscout.androidaps.interfaces.ResourceHelper
|
||||||
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
|
import info.nightscout.shared.logging.LTag
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
class PumpBLEConfigActivity : DaggerAppCompatActivity() {
|
||||||
|
|
||||||
|
@Inject lateinit var resourceHelper: ResourceHelper
|
||||||
|
@Inject lateinit var activePlugin: ActivePlugin
|
||||||
|
@Inject lateinit var sp: SP
|
||||||
|
@Inject lateinit var blePreCheck: BlePreCheck
|
||||||
|
@Inject lateinit var context: Context
|
||||||
|
@Inject lateinit var aapsLogger: AAPSLogger
|
||||||
|
@Inject lateinit var rxBus: RxBus
|
||||||
|
|
||||||
|
private lateinit var binding: PumpBleConfigActivityBinding
|
||||||
|
private lateinit var bleSelector: PumpBLESelector
|
||||||
|
|
||||||
|
private var settings: ScanSettings? = null
|
||||||
|
private var filters: List<ScanFilter>? = null
|
||||||
|
private var bleScanner: BluetoothLeScanner? = null
|
||||||
|
private var deviceListAdapter = LeDeviceListAdapter()
|
||||||
|
private val handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
|
||||||
|
private val bluetoothAdapter: BluetoothAdapter? get() = (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?)?.adapter
|
||||||
|
var scanning = false
|
||||||
|
private val devicesMap: MutableMap<String, BluetoothDevice> = HashMap()
|
||||||
|
|
||||||
|
private val stopScanAfterTimeoutRunnable = Runnable {
|
||||||
|
if (scanning) {
|
||||||
|
stopLeDeviceScan(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = PumpBleConfigActivityBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
if (!blePreCheck.prerequisitesCheck(this)) {
|
||||||
|
aapsLogger.error(TAG, "prerequisitesCheck failed.")
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
val activePump = activePlugin.activePump
|
||||||
|
|
||||||
|
if (activePump is PumpDriverConfigurationCapable) {
|
||||||
|
bleSelector = activePump.getPumpDriverConfiguration().getPumpBLESelector()
|
||||||
|
} else {
|
||||||
|
throw RuntimeException("PumpBLEConfigActivity can be used only with PumpDriverConfigurationCapable pump driver.")
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.pumpBleConfigCurrentlySelectedText.text = bleSelector.getText(PumpBLESelectorText.SELECTED_PUMP_TITLE)
|
||||||
|
binding.pumpBleConfigScanTitle.text = bleSelector.getText(PumpBLESelectorText.SCAN_TITLE)
|
||||||
|
|
||||||
|
title = bleSelector.getText(PumpBLESelectorText.PUMP_CONFIGURATION)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
|
binding.pumpBleConfigScanDeviceList.adapter = deviceListAdapter
|
||||||
|
binding.pumpBleConfigScanDeviceList.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, view: View, _: Int, _: Long ->
|
||||||
|
// stop scanning if still active
|
||||||
|
if (scanning) {
|
||||||
|
stopLeDeviceScan(true)
|
||||||
|
}
|
||||||
|
val bleAddress = (view.findViewById<View>(R.id.pump_ble_config_scan_item_device_address) as TextView).text.toString()
|
||||||
|
val deviceName = (view.findViewById<View>(R.id.pump_ble_config_scan_item_device_name) as TextView).text.toString()
|
||||||
|
|
||||||
|
if (devicesMap.containsKey(bleAddress)) {
|
||||||
|
aapsLogger.debug(TAG, "Device FOUND in deviceMap: $bleAddress")
|
||||||
|
val bluetoothDevice = devicesMap[bleAddress]
|
||||||
|
bleSelector.onDeviceSelected(bluetoothDevice!!, bleAddress, deviceName)
|
||||||
|
} else {
|
||||||
|
aapsLogger.debug(TAG, "Device NOT found in deviceMap: $bleAddress")
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
binding.pumpBleConfigScanStart.setOnClickListener { startLeDeviceScan() }
|
||||||
|
binding.pumpBleConfigButtonScanStop.setOnClickListener {
|
||||||
|
if (scanning) {
|
||||||
|
stopLeDeviceScan(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.pumpBleConfigButtonRemove.setOnClickListener {
|
||||||
|
OKDialog.showConfirmation(
|
||||||
|
this@PumpBLEConfigActivity,
|
||||||
|
bleSelector.getText(PumpBLESelectorText.REMOVE_TITLE),
|
||||||
|
bleSelector.getText(PumpBLESelectorText.REMOVE_TEXT),
|
||||||
|
Runnable {
|
||||||
|
val deviceAddress: String = binding.pumpBleConfigCurrentlySelectedPumpAddress.text.toString()
|
||||||
|
aapsLogger.debug(TAG, "Removing device as selected: $deviceAddress")
|
||||||
|
if (devicesMap.containsKey(deviceAddress)) {
|
||||||
|
val bluetoothDevice = devicesMap[deviceAddress]
|
||||||
|
aapsLogger.debug(TAG, "Device can be detected near, so trying to remove bond if possible.")
|
||||||
|
bleSelector.removeDevice(bluetoothDevice!!)
|
||||||
|
} else {
|
||||||
|
val remoteDevice = bluetoothAdapter!!.getRemoteDevice(deviceAddress)
|
||||||
|
if (remoteDevice != null) {
|
||||||
|
bleSelector.removeDevice(remoteDevice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bleSelector.cleanupAfterDeviceRemoved()
|
||||||
|
updateCurrentlySelectedBTDevice()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCurrentlySelectedBTDevice() {
|
||||||
|
val address = bleSelector.currentlySelectedPumpAddress()
|
||||||
|
if (StringUtils.isEmpty(address)) {
|
||||||
|
binding.pumpBleConfigCurrentlySelectedPumpName.text = bleSelector.getText(PumpBLESelectorText.NO_SELECTED_PUMP)
|
||||||
|
binding.pumpBleConfigCurrentlySelectedPumpAddress.visibility = View.GONE
|
||||||
|
binding.pumpBleConfigButtonRemove.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.pumpBleConfigCurrentlySelectedPumpAddress.visibility = View.VISIBLE
|
||||||
|
binding.pumpBleConfigButtonRemove.visibility = View.VISIBLE
|
||||||
|
binding.pumpBleConfigCurrentlySelectedPumpName.text = bleSelector.currentlySelectedPumpName()
|
||||||
|
binding.pumpBleConfigCurrentlySelectedPumpAddress.text = address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean =
|
||||||
|
when (item.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
finish()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
bleSelector.onResume()
|
||||||
|
prepareForScanning()
|
||||||
|
updateCurrentlySelectedBTDevice()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
if (scanning) {
|
||||||
|
stopLeDeviceScan(false)
|
||||||
|
}
|
||||||
|
bleSelector.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepareForScanning() {
|
||||||
|
bleScanner = bluetoothAdapter!!.bluetoothLeScanner
|
||||||
|
settings = bleSelector.getScanSettings()
|
||||||
|
filters = bleSelector.getScanFilters()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val bleScanCallback: ScanCallback = object : ScanCallback() {
|
||||||
|
|
||||||
|
override fun onScanResult(callbackType: Int, scanRecord: ScanResult) {
|
||||||
|
aapsLogger.debug(TAG, scanRecord.toString())
|
||||||
|
runOnUiThread { if (addDevice(scanRecord)) deviceListAdapter.notifyDataSetChanged() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBatchScanResults(results: List<ScanResult>) {
|
||||||
|
runOnUiThread {
|
||||||
|
var added = false
|
||||||
|
for (result in results) {
|
||||||
|
aapsLogger.debug(TAG, "SCAN: " + result.advertisingSid + " name=" + result.device.address)
|
||||||
|
if (addDevice(result)) added = true
|
||||||
|
}
|
||||||
|
if (added)
|
||||||
|
deviceListAdapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addDevice(result: ScanResult): Boolean {
|
||||||
|
var device = result.device
|
||||||
|
|
||||||
|
device = bleSelector.filterDevice(device)
|
||||||
|
|
||||||
|
if (device == null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceListAdapter.addDevice(result)
|
||||||
|
if (!devicesMap.containsKey(device.address)) {
|
||||||
|
devicesMap[device.address] = device
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onScanFailed(errorCode: Int) {
|
||||||
|
aapsLogger.error(TAG, "Scan Failed - Error Code: $errorCode")
|
||||||
|
bleSelector.onScanFailed(this@PumpBLEConfigActivity, errorCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startLeDeviceScan() {
|
||||||
|
if (bleScanner == null) {
|
||||||
|
aapsLogger.error(LTag.PUMPBTCOMM, "startLeDeviceScan failed: bleScanner is null")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
deviceListAdapter.clear()
|
||||||
|
deviceListAdapter.notifyDataSetChanged()
|
||||||
|
handler.postDelayed(stopScanAfterTimeoutRunnable, SCAN_PERIOD_MILLIS)
|
||||||
|
runOnUiThread {
|
||||||
|
binding.pumpBleConfigScanStart.isEnabled = false
|
||||||
|
binding.pumpBleConfigButtonScanStop.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
scanning = true
|
||||||
|
bleScanner!!.startScan(filters, settings, bleScanCallback)
|
||||||
|
aapsLogger.debug(LTag.PUMPBTCOMM, "startLeDeviceScan: Scanning Start")
|
||||||
|
bleSelector.onStartLeDeviceScan(this@PumpBLEConfigActivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopLeDeviceScan(manualStop: Boolean) {
|
||||||
|
if (scanning) {
|
||||||
|
scanning = false
|
||||||
|
bleScanner!!.stopScan(bleScanCallback)
|
||||||
|
aapsLogger.debug(LTag.PUMPBTCOMM, "stopLeDeviceScan: Scanning Stop")
|
||||||
|
bleSelector.onStopLeDeviceScan(this@PumpBLEConfigActivity)
|
||||||
|
handler.removeCallbacks(stopScanAfterTimeoutRunnable)
|
||||||
|
}
|
||||||
|
if (manualStop) {
|
||||||
|
bleSelector.onManualStopLeDeviceScan(this@PumpBLEConfigActivity)
|
||||||
|
} else {
|
||||||
|
bleSelector.onNonManualStopLeDeviceScan(this@PumpBLEConfigActivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
runOnUiThread {
|
||||||
|
binding.pumpBleConfigScanStart.isEnabled = true
|
||||||
|
binding.pumpBleConfigButtonScanStop.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class LeDeviceListAdapter : BaseAdapter() {
|
||||||
|
|
||||||
|
private var devicesList: ArrayList<BluetoothDevice> = arrayListOf()
|
||||||
|
private var devicesMap: MutableMap<BluetoothDevice, Int> = mutableMapOf()
|
||||||
|
|
||||||
|
fun addDevice(result: ScanResult) {
|
||||||
|
if (!devicesList.contains(result.device)) {
|
||||||
|
devicesList.add(result.device)
|
||||||
|
}
|
||||||
|
devicesMap[result.device] = result.rssi
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
devicesList.clear()
|
||||||
|
devicesMap.clear()
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCount(): Int {
|
||||||
|
val c = devicesList.size
|
||||||
|
aapsLogger.info(TAG, "D: count=$c")
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(i: Int): Any = devicesList[i]
|
||||||
|
override fun getItemId(i: Int): Long = i.toLong()
|
||||||
|
|
||||||
|
override fun getView(i: Int, convertView: View?, viewGroup: ViewGroup?): View {
|
||||||
|
var v = convertView
|
||||||
|
val holder: ViewHolder
|
||||||
|
if (v == null) {
|
||||||
|
v = View.inflate(applicationContext, R.layout.pump_ble_config_scan_item, null)
|
||||||
|
holder = ViewHolder()
|
||||||
|
holder.deviceAddress = v.findViewById(R.id.pump_ble_config_scan_item_device_address)
|
||||||
|
holder.deviceName = v.findViewById(R.id.pump_ble_config_scan_item_device_name)
|
||||||
|
v.tag = holder
|
||||||
|
} else {
|
||||||
|
// reuse view if already exists
|
||||||
|
holder = v.tag as ViewHolder
|
||||||
|
}
|
||||||
|
|
||||||
|
val device = devicesList[i]
|
||||||
|
var deviceName = device.name
|
||||||
|
if (StringUtils.isBlank(deviceName)) {
|
||||||
|
deviceName = bleSelector.getUnknownPumpName()
|
||||||
|
}
|
||||||
|
deviceName += " [" + devicesMap[device] + "]"
|
||||||
|
val currentlySelectedAddress = bleSelector.currentlySelectedPumpAddress() // TODO
|
||||||
|
if (currentlySelectedAddress == device.address) {
|
||||||
|
deviceName += " (" + resources.getString(R.string.ble_config_scan_selected) + ")"
|
||||||
|
}
|
||||||
|
holder.deviceName!!.text = deviceName
|
||||||
|
holder.deviceAddress!!.text = device.address
|
||||||
|
return v!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ViewHolder {
|
||||||
|
var deviceName: TextView? = null
|
||||||
|
var deviceAddress: TextView? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = LTag.PUMPBTCOMM
|
||||||
|
private const val SCAN_PERIOD_MILLIS: Long = 15000
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,217 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.AdapterView
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import dagger.android.support.DaggerAppCompatActivity
|
||||||
|
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.R
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.databinding.PumpHistoryActivityBinding
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.driver.PumpDriverConfigurationCapable
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.driver.history.PumpHistoryDataProvider
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.driver.history.PumpHistoryEntry
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.driver.history.PumpHistoryText
|
||||||
|
import info.nightscout.androidaps.interfaces.ResourceHelper
|
||||||
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
|
import info.nightscout.shared.logging.LTag
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class PumpHistoryActivity : DaggerAppCompatActivity() {
|
||||||
|
|
||||||
|
@Inject lateinit var resourceHelper: ResourceHelper
|
||||||
|
@Inject lateinit var aapsLogger: AAPSLogger
|
||||||
|
@Inject lateinit var activePlugin: ActivePlugin
|
||||||
|
@Inject lateinit var context: Context
|
||||||
|
|
||||||
|
var filteredHistoryList: MutableList<PumpHistoryEntry> = mutableListOf()
|
||||||
|
var typeListFull: List<TypeList>? = null
|
||||||
|
var fullList: MutableList<PumpHistoryEntry> = mutableListOf()
|
||||||
|
|
||||||
|
private lateinit var historyDataProvider: PumpHistoryDataProvider
|
||||||
|
private lateinit var binding: PumpHistoryActivityBinding
|
||||||
|
|
||||||
|
var manualChange = false
|
||||||
|
|
||||||
|
lateinit var recyclerViewAdapter: RecyclerViewAdapter
|
||||||
|
|
||||||
|
private fun prepareData() {
|
||||||
|
|
||||||
|
val allData = historyDataProvider.getInitialData()
|
||||||
|
|
||||||
|
aapsLogger.info(LTag.PUMP, "Loaded ${allData.size} items from database. [initialSize=${historyDataProvider.getInitialPeriod()}]")
|
||||||
|
|
||||||
|
this.fullList.addAll(allData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterHistory(group: PumpHistoryEntryGroup) {
|
||||||
|
filteredHistoryList.clear()
|
||||||
|
|
||||||
|
if (group === PumpHistoryEntryGroup.All) {
|
||||||
|
filteredHistoryList.addAll(fullList)
|
||||||
|
} else {
|
||||||
|
for (pumpHistoryEntry in fullList) {
|
||||||
|
if (historyDataProvider.isItemInSelection(pumpHistoryEntry.getEntryTypeGroup(), group)) {
|
||||||
|
filteredHistoryList.add(pumpHistoryEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aapsLogger.info(LTag.PUMP, "Filtered list ${filteredHistoryList.size} items (group ${group}), from full list (${fullList.size}).")
|
||||||
|
|
||||||
|
recyclerViewAdapter.setHistoryListInternal(filteredHistoryList)
|
||||||
|
recyclerViewAdapter.notifyDataSetChanged()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
//filterHistory(selectedGroup)
|
||||||
|
//setHistoryTypeSpinner()
|
||||||
|
//aapsLogger.info(LTag.PUMP, "onResume")
|
||||||
|
//binding.pumpHistoryRoot.requestLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setHistoryTypeSpinner() {
|
||||||
|
manualChange = true
|
||||||
|
for (i in typeListFull!!.indices) {
|
||||||
|
if (typeListFull!![i].entryGroup === selectedGroup) {
|
||||||
|
binding.pumpHistoryType.setSelection(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SystemClock.sleep(200)
|
||||||
|
manualChange = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = PumpHistoryActivityBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
val activePump = activePlugin.activePump
|
||||||
|
|
||||||
|
if (activePump is PumpDriverConfigurationCapable) {
|
||||||
|
historyDataProvider = activePump.getPumpDriverConfiguration().getPumpHistoryDataProvider()
|
||||||
|
} else {
|
||||||
|
throw RuntimeException("PumpHistoryActivity can be used only with PumpDriverConfigurationCapable pump driver.")
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareData()
|
||||||
|
|
||||||
|
binding.pumpHistoryRecyclerView.setHasFixedSize(true)
|
||||||
|
binding.pumpHistoryRecyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
|
recyclerViewAdapter = RecyclerViewAdapter(filteredHistoryList)
|
||||||
|
binding.pumpHistoryRecyclerView.adapter = recyclerViewAdapter
|
||||||
|
binding.pumpHistoryStatus.visibility = View.GONE
|
||||||
|
typeListFull = getTypeList(historyDataProvider.getAllowedPumpHistoryGroups())
|
||||||
|
val spinnerAdapter = ArrayAdapter(this, R.layout.spinner_centered, typeListFull!!)
|
||||||
|
|
||||||
|
binding.pumpHistoryText.text = historyDataProvider.getText(PumpHistoryText.PUMP_HISTORY)
|
||||||
|
|
||||||
|
binding.pumpHistoryType.adapter = spinnerAdapter
|
||||||
|
binding.pumpHistoryType.getLayoutParams().width = fromDpToSize(historyDataProvider.getSpinnerWidthInPixels())
|
||||||
|
binding.pumpHistoryType.requestLayout();
|
||||||
|
binding.pumpHistoryType.setOnItemSelectedListener(object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
|
||||||
|
if (manualChange) return
|
||||||
|
val selected = binding.pumpHistoryType.getSelectedItem() as TypeList
|
||||||
|
showingType = selected
|
||||||
|
selectedGroup = selected.entryGroup
|
||||||
|
filterHistory(selectedGroup)
|
||||||
|
val selectedText = parent!!.getChildAt(0) as TextView
|
||||||
|
selectedText.textSize = 15.0f // FIXME hack for selected item, also concerns pump_type marginTop
|
||||||
|
|
||||||
|
binding.pumpHistoryTop.requestLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
|
if (manualChange) return
|
||||||
|
filterHistory(PumpHistoryEntryGroup.All)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
binding.pumpHistoryTypeText.requestLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTypeList(list: List<PumpHistoryEntryGroup>?): List<TypeList> {
|
||||||
|
val typeList = ArrayList<TypeList>()
|
||||||
|
for (pumpHistoryEntryGroup in list!!) {
|
||||||
|
typeList.add(TypeList(pumpHistoryEntryGroup))
|
||||||
|
}
|
||||||
|
return typeList
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromDpToSize(dpSize: Int): Int {
|
||||||
|
val scale = context.resources.displayMetrics.density
|
||||||
|
val pixelsFl = ((dpSize * scale) + 0.5f)
|
||||||
|
return pixelsFl.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypeList internal constructor(var entryGroup: PumpHistoryEntryGroup) {
|
||||||
|
|
||||||
|
var name: String
|
||||||
|
override fun toString(): String {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
name = entryGroup.translated!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecyclerViewAdapter internal constructor(
|
||||||
|
var historyList: List<PumpHistoryEntry>
|
||||||
|
) : RecyclerView.Adapter<RecyclerViewAdapter.HistoryViewHolder>() {
|
||||||
|
|
||||||
|
fun setHistoryListInternal(historyList: List<PumpHistoryEntry>) {
|
||||||
|
this.historyList = historyList
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): HistoryViewHolder {
|
||||||
|
val v = LayoutInflater.from(viewGroup.context).inflate(
|
||||||
|
R.layout.pump_history_item, //
|
||||||
|
viewGroup, false
|
||||||
|
)
|
||||||
|
return HistoryViewHolder(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) {
|
||||||
|
val record = historyList[position]
|
||||||
|
holder.timeView.text = record.getEntryDateTime()
|
||||||
|
holder.typeView.text = record.getEntryType()
|
||||||
|
holder.valueView.text = record.getEntryValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return historyList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
class HistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
|
var timeView: TextView
|
||||||
|
var typeView: TextView
|
||||||
|
var valueView: TextView
|
||||||
|
|
||||||
|
init {
|
||||||
|
timeView = itemView.findViewById(R.id.pump_history_time)
|
||||||
|
typeView = itemView.findViewById(R.id.pump_history_source)
|
||||||
|
valueView = itemView.findViewById(R.id.pump_history_description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var showingType: TypeList? = null
|
||||||
|
var selectedGroup = PumpHistoryEntryGroup.All
|
||||||
|
}
|
||||||
|
}
|
82
pump-common/src/main/res/layout/pump_ble_config_activity.xml
Executable file
82
pump-common/src/main/res/layout/pump_ble_config_activity.xml
Executable file
|
@ -0,0 +1,82 @@
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="5dp">
|
||||||
|
|
||||||
|
<!-- Currently selected Pump -->
|
||||||
|
<TextView
|
||||||
|
style="@style/TextAppearance.AppCompat.Title"
|
||||||
|
android:id="@+id/pump_ble_config_currently_selected_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_ble_config_currently_selected_pump_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_ble_config_currently_selected_pump_address"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/pump_ble_config_button_remove"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/ble_config_remove"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Scan -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextAppearance.AppCompat.Title"
|
||||||
|
android:id="@+id/pump_ble_config_scan_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/pump_ble_config_scan_start"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/ble_config_button_scan_start" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/pump_ble_config_button_scan_stop"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/ble_config_scan_stop"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Scan results -->
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/pump_ble_config_scan_device_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
24
pump-common/src/main/res/layout/pump_ble_config_scan_item.xml
Executable file
24
pump-common/src/main/res/layout/pump_ble_config_scan_item.xml
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_ble_config_scan_item_device_name"
|
||||||
|
style="@style/TextAppearance.AppCompat.Medium.Inverse"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_ble_config_scan_item_device_address"
|
||||||
|
style="@style/TextAppearance.AppCompat.Medium.Inverse"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
67
pump-common/src/main/res/layout/pump_history_activity.xml
Executable file
67
pump-common/src/main/res/layout/pump_history_activity.xml
Executable file
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/pump_history_root"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:background="?attr/activity_title_backgroundColor"
|
||||||
|
tools:context=".ui.PumpHistoryActivity">
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/pump_history_top"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_history_type_text"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="1dp"
|
||||||
|
android:layout_marginTop="7dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/pump_history_type"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:theme="@style/YourSpinnerItemStyle"
|
||||||
|
android:id="@+id/pump_history_type"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="5dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_history_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="right"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/pillborder"
|
||||||
|
android:gravity="center_horizontal" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_history_status"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/pump_history_top"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:gravity="center_horizontal" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/pump_history_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:layout_below="@+id/pump_history_status" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
32
pump-common/src/main/res/layout/pump_history_item.xml
Executable file
32
pump-common/src/main/res/layout/pump_history_item.xml
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_history_time"
|
||||||
|
android:layout_width="70dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_history_source"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pump_history_description"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -6,7 +6,10 @@
|
||||||
<string name="pump_operation_not_yet_supported_by_pump">Operation not YET supported by pump.</string>
|
<string name="pump_operation_not_yet_supported_by_pump">Operation not YET supported by pump.</string>
|
||||||
<string name="common_resultok">OK</string>
|
<string name="common_resultok">OK</string>
|
||||||
|
|
||||||
<!-- PumoCommon - Pump Status -->
|
<string name="pump_serial_number">Pump Serial Number</string>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- PumpCommon - Pump Status -->
|
||||||
<string name="pump_status_never_contacted">Never contacted</string>
|
<string name="pump_status_never_contacted">Never contacted</string>
|
||||||
<string name="pump_status_waking_up">Waking up</string>
|
<string name="pump_status_waking_up">Waking up</string>
|
||||||
<string name="pump_status_error_comm">Error with communication</string>
|
<string name="pump_status_error_comm">Error with communication</string>
|
||||||
|
@ -15,6 +18,13 @@
|
||||||
<string name="pump_status_invalid_config">Invalid configuration</string>
|
<string name="pump_status_invalid_config">Invalid configuration</string>
|
||||||
<string name="pump_status_active">Active</string>
|
<string name="pump_status_active">Active</string>
|
||||||
<string name="pump_status_sleeping">Sleeping</string>
|
<string name="pump_status_sleeping">Sleeping</string>
|
||||||
|
<string name="pump_status_not_initialized">Not initialized</string>
|
||||||
|
<string name="pump_status_initialized">Initialized</string>
|
||||||
|
<string name="pump_status_encrypt">Encrypting communication</string>
|
||||||
|
<string name="pump_status_ready">Ready</string>
|
||||||
|
<string name="pump_status_busy">Busy</string>
|
||||||
|
<string name="pump_status_suspended">Suspended</string>
|
||||||
|
<string name="pump_status_executing_command">Executing Command</string>
|
||||||
|
|
||||||
<!-- PumpCommon - History Group -->
|
<!-- PumpCommon - History Group -->
|
||||||
<string name="history_group_basal">Basals</string>
|
<string name="history_group_basal">Basals</string>
|
||||||
|
@ -27,5 +37,67 @@
|
||||||
<string name="history_group_prime">Prime</string>
|
<string name="history_group_prime">Prime</string>
|
||||||
<string name="history_group_alarm">Alarms</string>
|
<string name="history_group_alarm">Alarms</string>
|
||||||
<string name="history_group_glucose">Glucose</string>
|
<string name="history_group_glucose">Glucose</string>
|
||||||
|
<string name="history_group_base">Base</string>
|
||||||
|
<string name="history_group_other">Other</string>
|
||||||
|
<string name="history_group_events">All Events</string>
|
||||||
|
<string name="history_group_events_no_stat">Events (no Stat)</string>
|
||||||
|
|
||||||
|
<!-- Time -->
|
||||||
|
<string name="time_today">Today</string>
|
||||||
|
<string name="time_last_hour">Last Hour</string>
|
||||||
|
<string name="time_last_3_hours">Last 3 hours</string>
|
||||||
|
<string name="time_last_6_hours">Last 6 hours</string>
|
||||||
|
<string name="time_last_12_hours">Last 12 hours</string>
|
||||||
|
<string name="time_last_2_days">Last 2 days</string>
|
||||||
|
<string name="time_last_4_days">Last 4 days</string>
|
||||||
|
<string name="time_last_week">Last week</string>
|
||||||
|
<string name="time_last_month">Last month</string>
|
||||||
|
|
||||||
|
<!-- BLE Config -->
|
||||||
|
<string name="ble_config_button_scan_start">Scan</string>
|
||||||
|
<string name="ble_config_scan_stop">Stop</string>
|
||||||
|
<string name="ble_config_scan_selected">Selected</string>
|
||||||
|
<string name="ble_config_scan_scanning">Scanning</string>
|
||||||
|
<string name="ble_config_scan_finished">Scanning finished</string>
|
||||||
|
<string name="ble_config_scan_error">Scanning error: %1$d</string>
|
||||||
|
<string name="ble_config_connected_never">Never</string>
|
||||||
|
<string name="ble_config_remove">Remove</string>
|
||||||
|
|
||||||
|
<!-- BLE Errors -->
|
||||||
|
<string name="ble_error_bt_disabled">Bluetooth disabled</string>
|
||||||
|
<string name="ble_error_no_bt_adapter">No Bluetooth Adapter</string>
|
||||||
|
<string name="ble_error_configured_pump_not_found">Configured Pump Not Found</string>
|
||||||
|
<string name="ble_error_pump_unreachable">Pump unreachable</string>
|
||||||
|
<string name="ble_error_failed_to_conn_to_ble_device">Failed To Connect To BLE Device</string>
|
||||||
|
<string name="ble_error_encryption_failed">Encryption Failed</string>
|
||||||
|
<string name="ble_error_pump_found_unbonded">Found not Bonded Pump</string>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Pump Error -->
|
||||||
|
<string name="pump_settings_error_basal_profiles_not_enabled">Basal profiles/patterns setting is not enabled on pump. Enable it on the pump.</string>
|
||||||
|
<string name="pump_settings_error_incorrect_basal_profile_selected">Basal profile set on pump is incorrect (must be %s).</string>
|
||||||
|
<string name="pump_settings_error_wrong_tbr_type_set">Wrong TBR type set on pump (must be %s).</string>
|
||||||
|
<string name="pump_settings_error_wrong_max_bolus_set">Wrong Max Bolus set on Pump (must be %1$.2f).</string>
|
||||||
|
<string name="pump_settings_error_wrong_max_basal_set">Wrong Max Basal set on Pump (must be %1$.2f).</string>
|
||||||
|
|
||||||
|
<!-- Pump History -->
|
||||||
|
<string name="pump_history_type">Type:</string>
|
||||||
|
|
||||||
|
|
||||||
|
<plurals name="duration_days">
|
||||||
|
<item quantity="one">%1$d day</item>
|
||||||
|
<item quantity="other">%1$d days</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="duration_hours">
|
||||||
|
<item quantity="one">%1$d hour</item>
|
||||||
|
<item quantity="other">%1$d hours</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="hoursago">
|
||||||
|
<item quantity="one">%1$d hour ago</item>
|
||||||
|
<item quantity="other">%1$d hours ago</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="daysago">
|
||||||
|
<item quantity="one">%1$d day ago</item>
|
||||||
|
<item quantity="other">%1$d days ago</item>
|
||||||
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
18
pump-common/src/main/res/values/styles.xml
Normal file
18
pump-common/src/main/res/values/styles.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||||
|
<item name="android:spinnerItemStyle">@style/YourSpinnerItemStyle</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="YourSpinnerItemStyle" parent="Widget.AppCompat.TextView.SpinnerItem">
|
||||||
|
<item name="android:textColor">@android:color/white</item>
|
||||||
|
<item name="android:textSize">15sp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="mySpinnerItemStyle" parent="ThemeOverlay.AppCompat.Dark">
|
||||||
|
<item name="android:textSize">15sp</item>
|
||||||
|
<item name="android:textColor">@color/white</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue