Begin implementation of new combo plugin, called "combov2"

Signed-off-by: Carlos Rafael Giani <crg7475@mailbox.org>
This commit is contained in:
Carlos Rafael Giani 2022-02-23 17:58:08 +01:00
parent 643f26b7bf
commit 7d9925db47
23 changed files with 3935 additions and 1 deletions

View file

@ -189,6 +189,7 @@ dependencies {
implementation project(':implementation')
implementation project(':database')
implementation project(':pump:combo')
implementation project(':pump:combov2')
implementation project(':pump:dana')
implementation project(':pump:danars')
implementation project(':pump:danar')

View file

@ -43,6 +43,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin
import info.nightscout.androidaps.plugins.general.wear.WearPlugin
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
import info.nightscout.androidaps.plugins.pump.combov2.ComboV2Plugin
import info.nightscout.androidaps.plugins.pump.eopatch.EopatchPumpPlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
@ -92,6 +93,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
@Inject lateinit var danaRv2Plugin: DanaRv2Plugin
@Inject lateinit var danaRSPlugin: DanaRSPlugin
@Inject lateinit var comboPlugin: ComboPlugin
@Inject lateinit var combov2Plugin: ComboV2Plugin
@Inject lateinit var insulinOrefFreePeakPlugin: InsulinOrefFreePeakPlugin
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var localInsightPlugin: LocalInsightPlugin
@ -205,6 +207,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
addPreferencesFromResourceIfEnabled(danaRSPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(localInsightPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(comboPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(combov2Plugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(medtronicPumpPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(diaconnG8Plugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(eopatchPumpPlugin, rootKey, config.PUMPDRIVERS)

View file

@ -6,6 +6,7 @@ import dagger.android.AndroidInjectionModule
import dagger.android.AndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.combo.di.ComboModule
import info.nightscout.androidaps.combov2.di.ComboV2Module
import info.nightscout.androidaps.dana.di.DanaHistoryModule
import info.nightscout.androidaps.dana.di.DanaModule
import info.nightscout.androidaps.danar.di.DanaRModule
@ -61,6 +62,7 @@ import javax.inject.Singleton
// pumps
ComboModule::class,
ComboV2Module::class,
DanaHistoryModule::class,
DanaModule::class,
DanaRModule::class,

View file

@ -28,6 +28,7 @@ import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin
import info.nightscout.androidaps.plugins.general.wear.WearPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
import info.nightscout.androidaps.plugins.pump.combov2.ComboV2Plugin
import info.nightscout.androidaps.plugins.pump.eopatch.EopatchPumpPlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
@ -424,6 +425,12 @@ abstract class PluginsListModule {
@IntKey(500)
abstract fun bindThemeSwitcherPlugin(plugin: ThemeSwitcherPlugin): PluginBase
@Binds
@PumpDriver
@IntoMap
@IntKey(510)
abstract fun bindComboV2Plugin(plugin: ComboV2Plugin): PluginBase
@Qualifier
annotation class AllConfigs

View file

@ -45,8 +45,8 @@ buildscript {
play_services_wearable_version = '17.1.0'
play_services_location_version = '20.0.0'
kotlinx_coroutines_version = '1.6.3'
kotlinx_datetime_version = '0.3.2'
kotlinx_serialization_core_version = '1.3.2'
}
repositories {
google()

View file

@ -131,6 +131,8 @@ open class Notification {
const val IDENTIFICATION_NOT_SET = 77
const val PERMISSION_BT = 78
const val EOELOW_PATCH_ALERTS = 79
const val COMBO_PUMP_SUSPENDED = 80
const val COMBO_UNKNOWN_TBR = 81
const val USER_MESSAGE = 1000

40
pump/combov2/build.gradle Normal file
View file

@ -0,0 +1,40 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-allopen'
apply plugin: 'com.hiya.jacoco-android'
apply from: "${project.rootDir}/core/android_dependencies.gradle"
apply from: "${project.rootDir}/core/android_module_dependencies.gradle"
apply from: "${project.rootDir}/core/test_dependencies.gradle"
apply from: "${project.rootDir}/core/jacoco_global.gradle"
dependencies {
implementation project(':core')
implementation project(':shared')
implementation project(':libraries')
implementation(project(":pump:combov2:comboctl"))
implementation("org.jetbrains.kotlinx:kotlinx-datetime:$kotlinx_datetime_version")
// This is necessary to avoid errors like these which otherwise come up often at runtime:
// "WARNING: Failed to transform class kotlinx/datetime/TimeZone$Companion
// java.lang.NoClassDefFoundError: kotlinx/serialization/KSerializer"
//
// "Rejecting re-init on previously-failed class java.lang.Class<
// kotlinx.datetime.serializers.LocalDateTimeIso8601Serializer>:
// java.lang.NoClassDefFoundError: Failed resolution of: Lkotlinx/serialization/KSerializer"
//
// kotlinx-datetime higher than 0.2.0 depends on kotlinx-serialization, but that dependency
// is declared as "compileOnly". The runtime dependency on kotlinx-serialization is missing,
// causing this error. Solution is to add runtimeOnly here.
//
// Source: https://github.com/mockk/mockk/issues/685#issuecomment-907076353:
// TODO: Revisit this when upgrading kotlinx-datetime
runtimeOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlinx_serialization_core_version")
}
android {
namespace 'info.nightscout.androidaps.combov2'
buildFeatures {
dataBinding true
}
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="info.nightscout.androidaps.plugins.pump.combov2">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<application>
<activity
android:name="info.nightscout.androidaps.plugins.pump.combov2.activities.ComboV2PairingActivity"
android:label="@string/combov2_pair_with_pump_title"
android:theme="@style/AppTheme" />
</application>
</manifest>

View file

@ -0,0 +1,14 @@
package info.nightscout.androidaps.combov2.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.pump.combov2.ComboV2Fragment
import info.nightscout.androidaps.plugins.pump.combov2.activities.ComboV2PairingActivity
@Module
@Suppress("unused")
abstract class ComboV2ActivitiesModule {
@ContributesAndroidInjector abstract fun contributesComboV2PairingActivity(): ComboV2PairingActivity
@ContributesAndroidInjector abstract fun contributesComboV2Fragment(): ComboV2Fragment
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.combov2.di
import dagger.Module
@Module(includes = [
ComboV2ActivitiesModule::class
])
open class ComboV2Module

View file

@ -0,0 +1,40 @@
package info.nightscout.androidaps.plugins.pump.combov2
import android.util.Log
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.comboctl.base.LogLevel
import info.nightscout.comboctl.base.LoggerBackend as ComboCtlLoggerBackend
internal class AAPSComboCtlLogger(private val aapsLogger: AAPSLogger) : ComboCtlLoggerBackend {
override fun log(tag: String, level: LogLevel, throwable: Throwable?, message: String?) {
val ltag = with (tag) {
when {
startsWith("Bluetooth") || startsWith("AndroidBluetooth") -> LTag.PUMPBTCOMM
endsWith("IO") -> LTag.PUMPCOMM
else -> LTag.PUMP
}
}
val fullMessage = "[$tag]" +
(if (throwable != null) " (${throwable::class.qualifiedName}: \"${throwable.message}\")" else "") +
(if (message != null) " $message" else "")
val stackInfo = Throwable().stackTrace[1]
val className = stackInfo.className.substringAfterLast(".")
val methodName = stackInfo.methodName
val lineNumber = stackInfo.lineNumber
when (level) {
// Log verbose content directly with Android's logger to not let this
// end up in AndroidAPS log files, which otherwise would quickly become
// very big, since verbose logging produces a lot of material.
LogLevel.VERBOSE -> Log.v(tag, message, throwable)
LogLevel.DEBUG -> aapsLogger.debug(className, methodName, lineNumber, ltag, fullMessage)
LogLevel.INFO -> aapsLogger.info(className, methodName, lineNumber, ltag, fullMessage)
LogLevel.WARN -> aapsLogger.warn(className, methodName, lineNumber, ltag, fullMessage)
LogLevel.ERROR -> aapsLogger.error(className, methodName, lineNumber, ltag, fullMessage)
}
}
}

View file

@ -0,0 +1,303 @@
package info.nightscout.androidaps.plugins.pump.combov2
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.interfaces.CommandQueue
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.combov2.R
import info.nightscout.androidaps.combov2.databinding.Combov2FragmentBinding
import info.nightscout.comboctl.base.NullDisplayFrame
import info.nightscout.comboctl.main.Pump as ComboCtlPump
import info.nightscout.comboctl.base.Tbr as ComboCtlTbr
import info.nightscout.comboctl.parser.BatteryState
import info.nightscout.comboctl.parser.ReservoirState
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import java.util.Locale
import javax.inject.Inject
import kotlin.math.max
class ComboV2Fragment : DaggerFragment() {
@Inject lateinit var combov2Plugin: ComboV2Plugin
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var commandQueue: CommandQueue
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding: Combov2FragmentBinding = DataBindingUtil.inflate(
inflater, R.layout.combov2_fragment, container, false)
val view = binding.root
binding.combov2RefreshButton.setOnClickListener {
binding.combov2RefreshButton.isEnabled = false
commandQueue.readStatus(rh.gs(R.string.user_request), null)
}
viewLifecycleOwner.lifecycleScope.launch {
// Start all of these flows with repeatOnLifecycle()
// which will automatically cancel the flows when
// the lifecycle reaches the STOPPED stage and
// re-runs the lambda (as a suspended function)
// when the lifecycle reaches the STARTED stage.
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
combov2Plugin.pairedStateUIFlow
.onEach { isPaired ->
binding.combov2FragmentUnpairedUi.visibility = if (isPaired) View.GONE else View.VISIBLE
binding.combov2FragmentMainUi.visibility = if (isPaired) View.VISIBLE else View.GONE
}
.launchIn(this)
combov2Plugin.driverStateUIFlow
.onEach { connectionState ->
val text = when (connectionState) {
ComboV2Plugin.DriverState.NotInitialized -> rh.gs(R.string.combov2_not_initialized)
ComboV2Plugin.DriverState.Disconnected -> rh.gs(R.string.disconnected)
ComboV2Plugin.DriverState.Connecting -> rh.gs(R.string.connecting)
ComboV2Plugin.DriverState.CheckingPump -> rh.gs(R.string.combov2_checking_pump)
ComboV2Plugin.DriverState.Ready -> rh.gs(R.string.combov2_ready)
ComboV2Plugin.DriverState.Suspended -> rh.gs(R.string.combov2_suspended)
ComboV2Plugin.DriverState.Error -> rh.gs(R.string.error)
is ComboV2Plugin.DriverState.ExecutingCommand ->
when (val desc = connectionState.description) {
is ComboCtlPump.GettingBasalProfileCommandDesc ->
rh.gs(R.string.combov2_getting_basal_profile_cmddesc)
is ComboCtlPump.SettingBasalProfileCommandDesc ->
rh.gs(R.string.combov2_setting_basal_profile_cmddesc)
is ComboCtlPump.SettingTbrCommandDesc ->
if (desc.percentage != 100)
rh.gs(R.string.combov2_setting_tbr_cmddesc, desc.percentage, desc.durationInMinutes)
else
rh.gs(R.string.combov2_cancelling_tbr)
is ComboCtlPump.DeliveringBolusCommandDesc ->
rh.gs(R.string.combov2_delivering_bolus_cmddesc, desc.bolusAmount.cctlBolusToIU())
is ComboCtlPump.FetchingTDDHistoryCommandDesc ->
rh.gs(R.string.combov2_fetching_tdd_history_cmddesc)
is ComboCtlPump.UpdatingPumpDateTimeCommandDesc ->
rh.gs(R.string.combov2_updating_pump_datetime_cmddesc)
is ComboCtlPump.UpdatingPumpStatusCommandDesc ->
rh.gs(R.string.combov2_updating_pump_status_cmddesc)
else -> rh.gs(R.string.combov2_executing_command)
}
}
binding.combov2DriverState.text = text
binding.combov2RefreshButton.isEnabled = when (connectionState) {
ComboV2Plugin.DriverState.Disconnected,
ComboV2Plugin.DriverState.Suspended -> true
else -> false
}
binding.combov2DriverState.setTextColor(
when (connectionState) {
ComboV2Plugin.DriverState.Error -> Color.RED
ComboV2Plugin.DriverState.Suspended -> Color.YELLOW
else -> Color.WHITE
}
)
}
.launchIn(this)
combov2Plugin.lastConnectionTimestampUIFlow
.onEach { lastConnectionTimestamp ->
updateLastConnectionField(lastConnectionTimestamp, binding)
}
.launchIn(this)
// This "Activity" is not to be confused with Android's "Activity" class.
combov2Plugin.currentActivityUIFlow
.onEach { currentActivity ->
if (currentActivity.description.isEmpty()) {
binding.combov2CurrentActivityDesc.text = rh.gs(R.string.combov2_no_activity)
binding.combov2CurrentActivityProgress.progress = 0
} else {
binding.combov2CurrentActivityDesc.text = currentActivity.description
binding.combov2CurrentActivityProgress.progress = (currentActivity.overallProgress * 100.0).toInt()
}
}
.launchIn(this)
combov2Plugin.batteryStateUIFlow
.onEach { batteryState ->
when (batteryState) {
null -> binding.combov2Battery.text = ""
BatteryState.NO_BATTERY -> {
binding.combov2Battery.text = rh.gs(R.string.combov2_battery_empty_indicator)
binding.combov2Battery.setTextColor(Color.RED)
}
BatteryState.LOW_BATTERY -> {
binding.combov2Battery.text = rh.gs(R.string.combov2_battery_low_indicator)
binding.combov2Battery.setTextColor(Color.YELLOW)
}
BatteryState.FULL_BATTERY -> {
binding.combov2Battery.text = rh.gs(R.string.combov2_battery_full_indicator)
binding.combov2Battery.setTextColor(Color.WHITE)
}
}
}
.launchIn(this)
combov2Plugin.reservoirLevelUIFlow
.onEach { reservoirLevel ->
binding.combov2Reservoir.text = if (reservoirLevel != null)
"${reservoirLevel.availableUnits} ${rh.gs(R.string.insulin_unit_shortname)}"
else
""
binding.combov2Reservoir.setTextColor(
when (reservoirLevel?.state) {
null -> Color.WHITE
ReservoirState.EMPTY -> Color.RED
ReservoirState.LOW -> Color.YELLOW
ReservoirState.FULL -> Color.WHITE
}
)
}
.launchIn(this)
combov2Plugin.lastBolusUIFlow
.onEach { lastBolus ->
updateLastBolusField(lastBolus, binding)
}
.launchIn(this)
combov2Plugin.currentTbrUIFlow
.onEach { tbr ->
updateCurrentTbrField(tbr, binding)
}
.launchIn(this)
combov2Plugin.baseBasalRateUIFlow
.onEach { baseBasalRate ->
binding.combov2BaseBasalRate.text = if (baseBasalRate != null)
rh.gs(R.string.pump_basebasalrate, baseBasalRate)
else
""
}
.launchIn(this)
combov2Plugin.serialNumberUIFlow
.onEach { serialNumber ->
binding.combov2PumpId.text = serialNumber
}
.launchIn(this)
combov2Plugin.bluetoothAddressUIFlow
.onEach { bluetoothAddress ->
binding.combov2BluetoothAddress.text = bluetoothAddress.uppercase(Locale.ROOT)
}
.launchIn(this)
combov2Plugin.displayFrameUIFlow
.onEach { displayFrame ->
binding.combov2RtDisplayFrame.displayFrame = displayFrame ?: NullDisplayFrame
}
.launchIn(this)
launch {
while (true) {
delay(30 * 1000L) // Wait for 30 seconds
updateLastConnectionField(combov2Plugin.lastConnectionTimestampUIFlow.value, binding)
updateLastBolusField(combov2Plugin.lastBolusUIFlow.value, binding)
updateCurrentTbrField(combov2Plugin.currentTbrUIFlow.value, binding)
}
}
}
}
return view
}
private fun updateLastConnectionField(lastConnectionTimestamp: Long?, binding: Combov2FragmentBinding) {
val currentTimestamp = System.currentTimeMillis()
// If the last connection is >= 30 minutes ago,
// we display a different message, one that
// warns the user that a long time passed
when (val secondsPassed = lastConnectionTimestamp?.let { (currentTimestamp - it) / 1000 }) {
null ->
binding.combov2LastConnection.text = ""
in 0..60 -> {
binding.combov2LastConnection.text = rh.gs(R.string.combov2_less_than_one_minute_ago)
binding.combov2LastConnection.setTextColor(Color.WHITE)
}
in 60..(30 * 60) -> {
binding.combov2LastConnection.text = rh.gs(info.nightscout.androidaps.core.R.string.minago, secondsPassed / 60)
binding.combov2LastConnection.setTextColor(Color.WHITE)
}
else -> {
binding.combov2LastConnection.text = rh.gs(R.string.combov2_no_connection_for_n_mins, secondsPassed / 60)
binding.combov2LastConnection.setTextColor(Color.RED)
}
}
}
private fun updateLastBolusField(lastBolus: ComboCtlPump.LastBolus?, binding: Combov2FragmentBinding) {
val currentTimestamp = System.currentTimeMillis()
if (lastBolus == null) {
binding.combov2LastBolus.text = ""
return
}
// If the last bolus is >= 30 minutes ago,
// we display a different message, one that
// warns the user that a long time passed
val bolusAgoText = when (val secondsPassed = (currentTimestamp - lastBolus.timestamp.toEpochMilliseconds()) / 1000) {
in 60..(30 * 60) ->
rh.gs(R.string.combov2_less_than_one_minute_ago)
else ->
rh.gs(info.nightscout.androidaps.core.R.string.minago, secondsPassed / 60)
}
binding.combov2LastBolus.text =
rh.gs(
R.string.combov2_last_bolus,
lastBolus.bolusAmount.cctlBolusToIU(),
rh.gs(R.string.insulin_unit_shortname),
bolusAgoText
)
}
private fun updateCurrentTbrField(currentTbr: ComboCtlTbr?, binding: Combov2FragmentBinding) {
val currentTimestamp = System.currentTimeMillis()
if (currentTbr == null) {
binding.combov2CurrentTbr.text = ""
return
}
val remainingSeconds = max(
currentTbr.durationInMinutes * 60 - (currentTimestamp - currentTbr.timestamp.toEpochMilliseconds()) / 1000,
0
)
binding.combov2CurrentTbr.text =
if (remainingSeconds >= 60)
rh.gs(
R.string.combov2_current_tbr,
currentTbr.percentage,
remainingSeconds / 60
)
else
rh.gs(
R.string.combov2_current_tbr_less_than_1min,
currentTbr.percentage
)
}
}

View file

@ -0,0 +1,84 @@
package info.nightscout.androidaps.plugins.pump.combov2
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import info.nightscout.comboctl.base.DISPLAY_FRAME_HEIGHT
import info.nightscout.comboctl.base.DISPLAY_FRAME_WIDTH
import info.nightscout.comboctl.base.DisplayFrame
import info.nightscout.comboctl.base.NUM_DISPLAY_FRAME_PIXELS
import info.nightscout.comboctl.base.NullDisplayFrame
/**
* Custom [View] to show a Combo remote terminal display frame on the UI.
*
* The [DisplayFrame] is shown on the UI via [Canvas]. To do that, the frame
* is converted to a [Bitmap], and then that bitmap is rendered on the UI.
*
* Callers pass new frames to the view by setting the [displayFrame] property.
* The frame -> bitmap conversion happens on-demand, when [onDraw] is called.
* That way, if this view is not shown on the UI (because for example the
* associated fragment is not visible at the moment), no unnecessary conversions
* are performed, saving computational effort.
*
* The frame is drawn unsmoothed to better mimic the Combo's LCD.
*/
internal class ComboV2RTDisplayFrameView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private companion object {
const val BACKGROUND_SHADE = 0xB5
const val FOREGROUND_SHADE = 0x20
}
private val bitmap = Bitmap.createBitmap(DISPLAY_FRAME_WIDTH, DISPLAY_FRAME_HEIGHT, Bitmap.Config.ARGB_8888, false)
private val bitmapPixels = IntArray(NUM_DISPLAY_FRAME_PIXELS) { BACKGROUND_SHADE }
private val bitmapPaint = Paint().apply {
style = Paint.Style.FILL
// These are necessary to ensure nearest neighbor scaling.
isAntiAlias = false
isFilterBitmap = false
}
private val bitmapRect = Rect()
private var isNewDisplayFrame = true
var displayFrame = NullDisplayFrame
set(value) {
field = value
isNewDisplayFrame = true
// Necessary to inform Android that during
// the next UI update it should call onDraw().
invalidate()
}
override fun onDraw(canvas: Canvas) {
updateBitmap()
canvas.drawBitmap(bitmap, null, bitmapRect, bitmapPaint)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
bitmapRect.set(0, 0, w, h)
}
private fun updateBitmap() {
if (!isNewDisplayFrame)
return
for (pixelIdx in 0 until NUM_DISPLAY_FRAME_PIXELS) {
val srcPixel = if (displayFrame[pixelIdx]) FOREGROUND_SHADE else BACKGROUND_SHADE
bitmapPixels[pixelIdx] = Color.argb(0xFF, srcPixel, srcPixel, srcPixel)
}
bitmap.setPixels(bitmapPixels, 0, DISPLAY_FRAME_WIDTH, 0, 0, DISPLAY_FRAME_WIDTH, DISPLAY_FRAME_HEIGHT)
isNewDisplayFrame = false
}
}

View file

@ -0,0 +1,165 @@
package info.nightscout.androidaps.plugins.pump.combov2
import info.nightscout.comboctl.base.BluetoothAddress
import info.nightscout.comboctl.base.CurrentTbrState
import info.nightscout.comboctl.base.InvariantPumpData
import info.nightscout.comboctl.base.Nonce
import info.nightscout.comboctl.base.PumpStateStore
import info.nightscout.comboctl.base.Tbr
import info.nightscout.comboctl.base.toBluetoothAddress
import info.nightscout.comboctl.base.toCipher
import info.nightscout.comboctl.base.toNonce
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.sharedPreferences.SPDelegateInt
import info.nightscout.shared.sharedPreferences.SPDelegateLong
import info.nightscout.shared.sharedPreferences.SPDelegateString
import kotlinx.datetime.Instant
import kotlinx.datetime.UtcOffset
/**
* AndroidAPS [SP] based pump state store.
*
* This store is set up to contain a single paired pump. AndroidAPS is not
* designed to handle multiple pumps, so this simplification makes sense.
* This affects all accessors, which
*/
class SPPumpStateStore(private val sp: SP) : PumpStateStore {
private var btAddress: String
by SPDelegateString(sp, BT_ADDRESS_KEY, "")
// The nonce is updated with commit instead of apply to make sure
// is atomically written to storage synchronously, minimizing
// the likelihood that it could be lost due to app crashes etc.
// It is very important to not lose the nonce, hence that choice.
private var nonceString: String
by SPDelegateString(sp, NONCE_KEY, Nonce.nullNonce().toString(), commit = true)
private var cpCipherString: String
by SPDelegateString(sp, CP_CIPHER_KEY, "")
private var pcCipherString: String
by SPDelegateString(sp, PC_CIPHER_KEY, "")
private var keyResponseAddressInt: Int
by SPDelegateInt(sp, KEY_RESPONSE_ADDRESS_KEY, 0)
private var pumpID: String
by SPDelegateString(sp, PUMP_ID_KEY, "")
private var tbrTimestamp: Long
by SPDelegateLong(sp, TBR_TIMESTAMP_KEY, 0)
private var tbrPercentage: Int
by SPDelegateInt(sp, TBR_PERCENTAGE_KEY, 0)
private var tbrDuration: Int
by SPDelegateInt(sp, TBR_DURATION_KEY, 0)
private var tbrType: String
by SPDelegateString(sp, TBR_TYPE_KEY, "")
private var utcOffsetSeconds: Int
by SPDelegateInt(sp, UTC_OFFSET_KEY, 0)
override fun createPumpState(
pumpAddress: BluetoothAddress,
invariantPumpData: InvariantPumpData,
utcOffset: UtcOffset,
tbrState: CurrentTbrState
) {
// Write these values via edit() instead of using the delegates
// above to be able to write all of them with a single commit.
sp.edit(commit = true) {
putString(BT_ADDRESS_KEY, pumpAddress.toString().uppercase())
putString(CP_CIPHER_KEY, invariantPumpData.clientPumpCipher.toString())
putString(PC_CIPHER_KEY, invariantPumpData.pumpClientCipher.toString())
putInt(KEY_RESPONSE_ADDRESS_KEY, invariantPumpData.keyResponseAddress.toInt() and 0xFF)
putString(PUMP_ID_KEY, invariantPumpData.pumpID)
putLong(TBR_TIMESTAMP_KEY, if (tbrState is CurrentTbrState.TbrStarted) tbrState.tbr.timestamp.epochSeconds else -1)
putInt(TBR_PERCENTAGE_KEY, if (tbrState is CurrentTbrState.TbrStarted) tbrState.tbr.percentage else -1)
putInt(TBR_DURATION_KEY, if (tbrState is CurrentTbrState.TbrStarted) tbrState.tbr.durationInMinutes else -1)
putString(TBR_TYPE_KEY, if (tbrState is CurrentTbrState.TbrStarted) tbrState.tbr.type.stringId else "")
putInt(UTC_OFFSET_KEY, utcOffset.totalSeconds)
}
}
override fun deletePumpState(pumpAddress: BluetoothAddress): Boolean {
val hasState = sp.contains(NONCE_KEY)
sp.edit(commit = true) {
remove(BT_ADDRESS_KEY)
remove(NONCE_KEY)
remove(CP_CIPHER_KEY)
remove(PC_CIPHER_KEY)
remove(KEY_RESPONSE_ADDRESS_KEY)
remove(TBR_TIMESTAMP_KEY)
remove(TBR_PERCENTAGE_KEY)
remove(TBR_DURATION_KEY)
remove(TBR_TYPE_KEY)
remove(UTC_OFFSET_KEY)
}
return hasState
}
override fun hasPumpState(pumpAddress: BluetoothAddress) =
sp.contains(NONCE_KEY)
override fun getAvailablePumpStateAddresses() =
if (btAddress.isBlank()) setOf() else setOf(btAddress.toBluetoothAddress())
override fun getInvariantPumpData(pumpAddress: BluetoothAddress) = InvariantPumpData(
clientPumpCipher = cpCipherString.toCipher(),
pumpClientCipher = pcCipherString.toCipher(),
keyResponseAddress = keyResponseAddressInt.toByte(),
pumpID = pumpID
)
override fun getCurrentTxNonce(pumpAddress: BluetoothAddress) = nonceString.toNonce()
override fun setCurrentTxNonce(pumpAddress: BluetoothAddress, currentTxNonce: Nonce) {
nonceString = currentTxNonce.toString()
}
override fun getCurrentUtcOffset(pumpAddress: BluetoothAddress) =
UtcOffset(seconds = utcOffsetSeconds)
override fun setCurrentUtcOffset(pumpAddress: BluetoothAddress, utcOffset: UtcOffset) {
utcOffsetSeconds = utcOffset.totalSeconds
}
override fun getCurrentTbrState(pumpAddress: BluetoothAddress) =
if (tbrTimestamp >= 0)
CurrentTbrState.TbrStarted(Tbr(
timestamp = Instant.fromEpochSeconds(tbrTimestamp),
percentage = tbrPercentage,
durationInMinutes = tbrDuration,
type = Tbr.Type.fromStringId(tbrType)!!
))
else
CurrentTbrState.NoTbrOngoing
override fun setCurrentTbrState(pumpAddress: BluetoothAddress, currentTbrState: CurrentTbrState) {
when (currentTbrState) {
is CurrentTbrState.TbrStarted -> {
tbrTimestamp = currentTbrState.tbr.timestamp.epochSeconds
tbrPercentage = currentTbrState.tbr.percentage
tbrDuration = currentTbrState.tbr.durationInMinutes
tbrType = currentTbrState.tbr.type.stringId
}
else -> {
tbrTimestamp = -1
tbrPercentage = -1
tbrDuration = -1
tbrType = ""
}
}
}
companion object {
const val BT_ADDRESS_KEY = "combov2-bt-address-key"
const val NONCE_KEY = "combov2-nonce-key"
const val CP_CIPHER_KEY = "combov2-cp-cipher-key"
const val PC_CIPHER_KEY = "combov2-pc-cipher-key"
const val KEY_RESPONSE_ADDRESS_KEY = "combov2-key-response-address-key"
const val PUMP_ID_KEY = "combov2-pump-id-key"
const val TBR_TIMESTAMP_KEY = "combov2-tbr-timestamp"
const val TBR_PERCENTAGE_KEY = "combov2-tbr-percentage"
const val TBR_DURATION_KEY = "combov2-tbr-duration"
const val TBR_TYPE_KEY = "combov2-tbr-type"
const val UTC_OFFSET_KEY = "combov2-utc-offset"
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.plugins.pump.combov2
import info.nightscout.comboctl.main.BasalProfile
import info.nightscout.comboctl.main.NUM_COMBO_BASAL_PROFILE_FACTORS
import info.nightscout.androidaps.interfaces.Profile as AAPSProfile
// Utility extension functions for clearer conversion between
// ComboCtl units and AAPS units. ComboCtl uses integer-encoded
// decimals. For basal values, the last 3 digits of an integer
// make up the decimal, so for example, 1568 actually means
// 1.568 IU, and 419 means 0.419 IU etc. Similarly, for bolus
// values, the last digit makes up the decimal. 57 means 5.7 IU,
// 4 means 0.4 IU etc.
// To emphasize better that such a conversion is taking place,
// these extension functions are put in place.
internal fun Int.cctlBasalToIU() = this.toDouble() / 1000.0
internal fun Int.cctlBolusToIU() = this.toDouble() / 10.0
internal fun Double.iuToCctlBolus() = (this * 10.0).toInt()
fun AAPSProfile.toComboCtlBasalProfile(): BasalProfile {
val factors = IntRange(0, NUM_COMBO_BASAL_PROFILE_FACTORS - 1).map { hour ->
(this.getBasalTimeFromMidnight(hour * 60 * 60) * 1000.0).toInt()
}
return BasalProfile(factors)
}

View file

@ -0,0 +1,238 @@
package info.nightscout.androidaps.plugins.pump.combov2.activities
import android.app.Activity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.combov2.R
import info.nightscout.androidaps.combov2.databinding.Combov2PairingActivityBinding
import info.nightscout.androidaps.plugins.pump.combov2.ComboV2Plugin
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.shared.logging.LTag
import info.nightscout.comboctl.base.BasicProgressStage
import info.nightscout.comboctl.base.PairingPIN
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
class ComboV2PairingActivity : NoSplashAppCompatActivity() {
@Inject lateinit var combov2Plugin: ComboV2Plugin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Install an activity result caller for when the user presses
// "deny" or "reject" in the dialog that pops up when Android
// asks for permission to enable device discovery. In such a
// case, without this caller, the logic would continue to look
// for devices even though discovery isn't actually happening.
// With this caller, we can cancel pairing in this case instead.
val startPairingActivityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
when (result.resultCode) {
Activity.RESULT_CANCELED -> {
aapsLogger.info(LTag.PUMP, "User rejected discovery request; cancelling pairing")
combov2Plugin.cancelPairing()
}
else -> Unit
}
}
combov2Plugin.customDiscoveryActivityStartCallback = { intent ->
startPairingActivityLauncher.launch(intent)
}
val binding: Combov2PairingActivityBinding = DataBindingUtil.setContentView(
this, R.layout.combov2_pairing_activity)
binding.combov2PairingFinishedOk.setOnClickListener {
finish()
}
binding.combov2PairingAborted.setOnClickListener {
finish()
}
val pinFormatRegex = "(\\d{1,3})(\\d{1,3})?(\\d{1,4})?".toRegex()
val nonDigitsRemovalRegex = "\\D".toRegex()
val whitespaceRemovalRegex = "\\s".toRegex()
// Add a custom TextWatcher to format the PIN in the
// same format it is shown on the Combo LCD, which is:
//
// xxx xxx xxxx
binding.combov2PinEntryEdit.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
if (s == null)
return
val originalText = s.toString()
val trimmedText = originalText.trim().replace(nonDigitsRemovalRegex, "")
val digitGroupValues = pinFormatRegex.matchEntire(trimmedText)?.let { matchResult ->
// Get the digit groups. Skip the first group, which contains the entire original string.
if (matchResult.groupValues.isEmpty())
listOf()
else
matchResult.groupValues.subList(1, matchResult.groupValues.size)
} ?: listOf()
// Join the groups to a string with a whitespace in between to construct
// a correct PIN string (see the format above). Skip empty groups to
// not have a trailing whitespace.
val processedText = digitGroupValues.filter { it.isNotEmpty() }.joinToString(" ")
if (originalText != processedText) {
// Remove and add this listener to modify the text
// without causing an infinite loop (text is changed,
// listener is called, listener changes text).
binding.combov2PinEntryEdit.removeTextChangedListener(this)
// Shift the cursor position to skip the whitespaces.
val cursorPosition = when (val it = binding.combov2PinEntryEdit.selectionStart) {
4 -> 5
8 -> 9
else -> it
}
binding.combov2PinEntryEdit.setText(processedText)
binding.combov2PinEntryEdit.setSelection(cursorPosition)
binding.combov2PinEntryEdit.addTextChangedListener(this)
}
}
})
binding.combov2EnterPin.setOnClickListener {
// We need to skip whitespaces since the
// TextWatcher above inserts some.
val pinString = binding.combov2PinEntryEdit.text.replace(whitespaceRemovalRegex, "")
runBlocking {
val PIN = PairingPIN(pinString.map { it - '0' }.toIntArray())
combov2Plugin.providePairingPIN(PIN)
}
}
binding.combov2StartPairing.setOnClickListener {
combov2Plugin.startPairing()
}
binding.combov2CancelPairing.setOnClickListener {
OKDialog.showConfirmation(this, "Confirm pairing cancellation", "Do you really want to cancel pairing?", ok = Runnable {
combov2Plugin.cancelPairing()
})
}
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
combov2Plugin.getPairingProgressFlow()
.onEach { progressReport ->
val stage = progressReport.stage
binding.combov2PairingSectionInitial.visibility =
if (stage == BasicProgressStage.Idle) View.VISIBLE else View.GONE
binding.combov2PairingSectionFinished.visibility =
if (stage == BasicProgressStage.Finished) View.VISIBLE else View.GONE
binding.combov2PairingSectionAborted.visibility =
if (stage is BasicProgressStage.Aborted) View.VISIBLE else View.GONE
binding.combov2PairingSectionMain.visibility = when (stage) {
BasicProgressStage.Idle,
BasicProgressStage.Finished,
is BasicProgressStage.Aborted -> View.GONE
else -> View.VISIBLE
}
if (stage is BasicProgressStage.Aborted) {
binding.combov2PairingAbortedReasonText.text = when (stage) {
is BasicProgressStage.Cancelled -> rh.gs(R.string.combov2_pairing_cancelled)
is BasicProgressStage.Timeout -> rh.gs(R.string.combov2_pairing_combo_scan_timeout_reached)
is BasicProgressStage.Error -> rh.gs(R.string.combov2_pairing_failed_due_to_error, stage.cause.toString())
else -> rh.gs(R.string.combov2_pairing_aborted_unknown_reasons)
}
}
binding.combov2CurrentPairingStepDesc.text = when (val progStage = stage) {
BasicProgressStage.ScanningForPumpStage ->
rh.gs(R.string.combov2_scanning_for_pump)
is BasicProgressStage.EstablishingBtConnection -> {
rh.gs(
R.string.combov2_establishing_bt_connection,
progStage.currentAttemptNr
)
}
BasicProgressStage.PerformingConnectionHandshake ->
rh.gs(R.string.combov2_pairing_performing_handshake)
BasicProgressStage.ComboPairingKeyAndPinRequested ->
rh.gs(R.string.combov2_pairing_pump_requests_pin)
BasicProgressStage.ComboPairingFinishing ->
rh.gs(R.string.combov2_pairing_finishing)
else -> ""
}
if (stage == BasicProgressStage.ComboPairingKeyAndPinRequested) {
binding.combov2PinEntryUi.visibility = View.VISIBLE
} else
binding.combov2PinEntryUi.visibility = View.INVISIBLE
// Scanning for the pump can take a long time and happens at the
// beginning, so set the progress bar to indeterminate during that
// time to show _something_ to the user.
binding.combov2PairingProgressBar.isIndeterminate =
(stage == BasicProgressStage.ScanningForPumpStage)
binding.combov2PairingProgressBar.progress = (progressReport.overallProgress * 100).toInt()
}
.launchIn(this)
combov2Plugin.previousPairingAttemptFailedFlow
.onEach { previousAttemptFailed ->
binding.combov2PinFailureIndicator.visibility =
if (previousAttemptFailed) View.VISIBLE else View.INVISIBLE
}
.launchIn(this)
}
}
}
override fun onBackPressed() {
aapsLogger.info(LTag.PUMP, "User pressed the back button; cancelling any ongoing pairing")
combov2Plugin.cancelPairing()
super.onBackPressed()
}
override fun onDestroy() {
// Reset the pairing progress reported to allow for future pairing attempts.
// Do this only after pairing was finished or aborted. onDestroy() can be
// called in the middle of a pairing process, and we do not want to reset
// the progress reporter mid-pairing.
when (combov2Plugin.getPairingProgressFlow().value.stage) {
BasicProgressStage.Finished,
is BasicProgressStage.Aborted -> {
aapsLogger.debug(
LTag.PUMP,
"Resetting pairing progress reporter after pairing was finished/aborted"
)
combov2Plugin.resetPairingProgress()
}
else -> Unit
}
super.onDestroy()
}
}

View file

@ -0,0 +1,107 @@
<vector android:height="100dp" android:viewportHeight="300"
android:viewportWidth="600" android:width="200dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#464646" android:pathData="M488.195,157.927C458.285,58.052 466.785,-0.052 335.939,2.361L88.761,2.361C85.492,2.361 82.84,4.991 82.84,8.23L82.84,290.011C82.84,293.25 85.492,295.881 88.761,295.881C224.79,295.794 375.022,292.871 492.871,293.984C491.295,246.05 499.821,196.746 488.195,157.927Z"/>
<path android:fillAlpha="0.53" android:fillColor="#FF000000" android:pathData="M490.739,286.213l4.139,0l0,7.322l-4.139,0z"/>
<path android:fillColor="#464646" android:pathData="M322.836,2.36l108.727,0l0,13.274l-108.727,0z"/>
<path android:fillColor="#464646" android:pathData="M443.395,10.164C441.656,4.554 435.686,1.415 430.077,3.153L409.749,9.456C404.141,11.195 401,17.16 402.739,22.77L442.928,152.381C444.667,157.991 450.632,161.132 456.24,159.393L476.568,153.09C482.176,151.351 485.323,145.385 483.583,139.775L443.395,10.164Z"/>
<path android:fillColor="#464646" android:pathData="M483.813,149.316L477.072,122.488L484.009,125.023L488.728,143.803L483.813,149.316Z"/>
<path android:fillColor="#464646" android:pathData="M462.843,73.912L454.367,47.445L462.119,49.339L468.052,67.866L462.843,73.912Z"/>
<path android:fillColor="#FF000000" android:pathData="M447.001,23.469l-1.412,0.425l42.517,141.237l1.412,-0.425z"/>
<path android:fillColor="#FF000000" android:pathData="M82.838,19.618l309.745,0l0,1.229l-309.745,0z"/>
<path android:fillColor="#FF000000" android:pathData="M389.994,19.301L393.142,19.846C432.527,43.973 466.678,79.349 473.136,127.873C464.91,78.173 429.623,43.999 389.994,19.301Z"/>
<path android:fillColor="#464646" android:pathData="M82.628,3.577L74.877,3.577C67.434,3.577 61.392,9.62 61.392,17.063L61.392,285.37C61.392,291.171 66.101,295.881 71.902,295.881L82.628,295.881L82.628,3.577Z"/>
<path android:fillColor="#FF000000" android:pathData="M82.628,4.888l1.475,0l0,289.243l-1.475,0z"/>
<path android:fillAlpha="0.46" android:fillColor="#6B6B6B" android:pathData="M83.149,21.136l3.686,0l0,229.606l-3.686,0z"/>
<path android:fillColor="#464646" android:pathData="M538.606,209.214C538.606,202.737 533.2,197.48 526.544,197.48L490.353,197.48L491.251,294.196L526.544,291.577C533.2,291.577 538.606,286.318 538.606,279.841L538.606,209.214Z"/>
<path android:fillColor="#FF000000" android:pathData="M495.834,198.897C495.834,198.24 495.301,197.705 494.642,197.705L492.254,197.705C491.594,197.705 491.059,198.24 491.059,198.897L491.059,292.711C491.059,293.369 491.594,293.906 492.254,293.906L494.642,293.906C495.301,293.906 495.834,293.369 495.834,292.711L495.834,198.897Z"/>
<path android:fillColor="#545050" android:pathData="M536.077,203.484C536.077,202.147 534.991,201.061 533.655,201.061L504.182,201.061C502.845,201.061 501.759,202.147 501.759,203.484L501.759,208.331C501.759,209.668 502.845,210.754 504.182,210.754L533.655,210.754C534.991,210.754 536.077,209.668 536.077,208.331L536.077,203.484Z"/>
<path android:fillColor="#545050" android:pathData="M538.608,231.773C538.608,229.941 537.122,228.454 535.29,228.454L504.21,228.454C502.378,228.454 500.892,229.941 500.892,231.773L500.892,238.41C500.892,240.241 502.378,241.729 504.21,241.729L535.29,241.729C537.122,241.729 538.608,240.241 538.608,238.41L538.608,231.773Z"/>
<path android:fillColor="#545050" android:pathData="M537.764,264.485C537.764,262.857 536.441,261.535 534.814,261.535L502.784,261.535C501.158,261.535 499.835,262.857 499.835,264.485L499.835,270.385C499.835,272.013 501.158,273.335 502.784,273.335L534.814,273.335C536.441,273.335 537.764,272.013 537.764,270.385L537.764,264.485Z"/>
<path android:fillColor="#FF000000" android:pathData="M459.763,145.568C437.91,77.95 426.306,39.41 352.331,37.453C279.374,35.524 205.483,35.819 131.222,36.585C110.191,36.585 93.118,52.819 93.118,72.814C91.55,117.388 91.429,161.962 93.333,206.535C93.989,239.028 111.184,261.849 151.431,262.099C237.913,262.636 332.049,261.573 422.107,262.262C462.95,262.574 481.669,247.844 473.212,208.058L459.763,145.568Z"/>
<path android:fillColor="#CACACA" android:pathData="M454.941,151.462C434.56,88.634 430.075,44.303 350.464,42.249C280.002,40.643 207.689,40.589 136.393,41.307C116.203,41.307 99.811,56.533 99.811,75.286C98.021,117.701 97.352,159.481 99.811,201.896C101.829,236.749 116.977,255.499 158.302,255.333L406.507,254.706C455.731,256.278 475.142,251.85 466.576,202.533L454.941,151.462Z"/>
<path android:fillColor="#3C3C3C" android:pathData="M442.779,148.348C423.733,93.076 419.542,54.077 345.145,52.27C279.298,50.857 211.722,50.809 145.095,51.441C126.227,51.441 110.909,64.836 110.909,81.334L110.263,192.954C109.879,222.358 124.59,241.815 164.918,240.814L397.517,239.176C443.518,240.559 461.657,236.663 453.652,193.277L442.779,148.348Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M205.273,82.727L205.38,82.21C205.703,82.189 205.961,82.103 206.133,81.931C206.305,81.78 206.434,81.565 206.499,81.307C206.585,81.027 206.628,80.662 206.628,80.21C206.628,79.952 206.606,79.651 206.585,79.328L206.52,77.973L201.895,77.973L201.315,79.005C201.035,79.479 200.841,79.887 200.712,80.253C200.583,80.597 200.519,80.898 200.519,81.156C200.519,81.759 200.884,82.103 201.594,82.21L201.465,82.727L197.185,82.727L197.292,82.21C197.507,82.189 197.722,82.103 197.916,81.952C198.088,81.802 198.303,81.587 198.518,81.307C198.733,81.006 199.035,80.533 199.443,79.866L206.176,68.229L207.746,68.229L208.499,79.93C208.542,80.49 208.607,80.92 208.693,81.221C208.779,81.544 208.908,81.78 209.08,81.931C209.23,82.081 209.467,82.167 209.768,82.21L209.639,82.727L205.273,82.727ZM202.369,77.048L206.477,77.048L206.283,73.671C206.262,73.219 206.262,72.638 206.24,71.95C206.219,71.24 206.219,70.746 206.219,70.444L206.111,70.444L202.369,77.048Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M222.455,68.767L221.831,71.606L220.82,71.606C220.755,70.746 220.583,70.122 220.261,69.735C219.938,69.347 219.422,69.175 218.712,69.175C218.131,69.175 217.572,69.347 217.056,69.692C216.539,70.036 216.045,70.53 215.593,71.197C215.12,71.843 214.733,72.617 214.388,73.499C214.044,74.359 213.765,75.263 213.593,76.188C213.42,77.091 213.313,77.908 213.313,78.661C213.313,79.801 213.506,80.64 213.872,81.156C214.259,81.694 214.84,81.974 215.636,81.974C216.174,81.974 216.625,81.888 217.013,81.737C217.4,81.587 217.787,81.307 218.11,80.941C218.454,80.554 218.798,80.038 219.142,79.371L220.153,79.371L219.508,82.339C218.798,82.555 218.11,82.684 217.443,82.77C216.776,82.856 216.088,82.899 215.399,82.899C214.517,82.899 213.786,82.705 213.162,82.361C212.56,81.995 212.108,81.479 211.807,80.791C211.485,80.102 211.334,79.285 211.334,78.317C211.334,77.822 211.377,77.328 211.442,76.854C211.528,76.274 211.657,75.628 211.829,74.94C212.065,74.08 212.366,73.284 212.711,72.574C213.098,71.821 213.528,71.176 214.001,70.595C214.453,70.057 214.948,69.627 215.485,69.261C215.98,68.939 216.496,68.681 217.077,68.487C217.636,68.315 218.239,68.229 218.884,68.229C219.508,68.229 220.11,68.25 220.648,68.336C221.186,68.422 221.788,68.552 222.455,68.767Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M233.746,68.767L233.122,71.606L232.111,71.606C232.046,70.746 231.874,70.122 231.552,69.735C231.229,69.347 230.713,69.175 230.003,69.175C229.422,69.175 228.863,69.347 228.347,69.692C227.83,70.036 227.336,70.53 226.884,71.197C226.411,71.843 226.024,72.617 225.679,73.499C225.335,74.359 225.056,75.263 224.883,76.188C224.711,77.091 224.604,77.908 224.604,78.661C224.604,79.801 224.797,80.64 225.163,81.156C225.55,81.694 226.131,81.974 226.927,81.974C227.465,81.974 227.916,81.888 228.304,81.737C228.691,81.587 229.078,81.307 229.401,80.941C229.745,80.554 230.089,80.038 230.433,79.371L231.444,79.371L230.799,82.339C230.089,82.555 229.401,82.684 228.734,82.77C228.067,82.856 227.379,82.899 226.69,82.899C225.808,82.899 225.077,82.705 224.453,82.361C223.851,81.995 223.399,81.479 223.098,80.791C222.775,80.102 222.625,79.285 222.625,78.317C222.625,77.822 222.668,77.328 222.732,76.854C222.819,76.274 222.948,75.628 223.12,74.94C223.356,74.08 223.657,73.284 224.002,72.574C224.389,71.821 224.819,71.176 225.292,70.595C225.744,70.057 226.239,69.627 226.776,69.261C227.271,68.939 227.787,68.681 228.368,68.487C228.927,68.315 229.53,68.229 230.175,68.229C230.799,68.229 231.401,68.25 231.939,68.336C232.477,68.422 233.079,68.552 233.746,68.767Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M235.65,68.896L235.757,68.379L239.995,68.379L239.866,68.896C239.608,68.96 239.414,69.025 239.306,69.089C239.177,69.154 239.07,69.261 238.984,69.412C238.898,69.541 238.79,69.778 238.704,70.057C238.597,70.358 238.489,70.853 238.339,71.52L237.026,77.392C236.94,77.801 236.854,78.188 236.811,78.532C236.747,78.898 236.725,79.285 236.725,79.651C236.725,80.425 236.919,81.006 237.285,81.393C237.672,81.78 238.231,81.974 238.984,81.974C239.694,81.974 240.296,81.823 240.769,81.544C241.242,81.242 241.63,80.812 241.909,80.275C242.21,79.715 242.447,79.005 242.641,78.124L244.103,71.52C244.168,71.197 244.232,70.896 244.275,70.595C244.318,70.294 244.34,70.036 244.34,69.821C244.34,69.498 244.254,69.261 244.082,69.132C243.91,69.003 243.652,68.917 243.307,68.896L243.415,68.379L247.416,68.379L247.287,68.896C247.029,68.96 246.857,69.025 246.727,69.089C246.598,69.175 246.491,69.283 246.405,69.412C246.297,69.563 246.211,69.778 246.125,70.079C246.039,70.38 245.91,70.853 245.759,71.52L244.404,77.801C244.125,79.07 243.738,80.059 243.243,80.812C242.748,81.544 242.124,82.081 241.371,82.404C240.64,82.748 239.715,82.899 238.64,82.899C237.435,82.899 236.51,82.619 235.865,82.038C235.22,81.436 234.897,80.597 234.897,79.479C234.897,79.07 234.94,78.618 235.004,78.124C235.09,77.607 235.198,77.048 235.349,76.403L236.446,71.52C236.532,71.133 236.596,70.81 236.639,70.509C236.661,70.229 236.682,69.993 236.682,69.821C236.682,69.498 236.618,69.283 236.446,69.132C236.295,69.003 236.015,68.917 235.65,68.896Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M252.177,78.016L247.596,78.016L247.94,76.381L252.522,76.381L252.177,78.016Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M265.81,68.767L265.186,71.606L264.175,71.606C264.111,70.746 263.939,70.122 263.616,69.735C263.294,69.347 262.777,69.175 262.067,69.175C261.487,69.175 260.927,69.347 260.411,69.692C259.895,70.036 259.4,70.53 258.948,71.197C258.475,71.843 258.088,72.617 257.744,73.499C257.4,74.359 257.12,75.263 256.948,76.188C256.776,77.091 256.668,77.908 256.668,78.661C256.668,79.801 256.862,80.64 257.228,81.156C257.615,81.694 258.196,81.974 258.992,81.974C259.529,81.974 259.981,81.888 260.368,81.737C260.755,81.587 261.143,81.307 261.465,80.941C261.809,80.554 262.154,80.038 262.498,79.371L263.509,79.371L262.863,82.339C262.154,82.555 261.465,82.684 260.798,82.77C260.132,82.856 259.443,82.899 258.755,82.899C257.873,82.899 257.142,82.705 256.518,82.361C255.916,81.995 255.464,81.479 255.163,80.791C254.84,80.102 254.689,79.285 254.689,78.317C254.689,77.822 254.733,77.328 254.797,76.854C254.883,76.274 255.012,75.628 255.184,74.94C255.421,74.08 255.722,73.284 256.066,72.574C256.453,71.821 256.884,71.176 257.357,70.595C257.808,70.057 258.303,69.627 258.841,69.261C259.336,68.939 259.852,68.681 260.433,68.487C260.992,68.315 261.594,68.229 262.24,68.229C262.863,68.229 263.466,68.25 264.003,68.336C264.541,68.422 265.143,68.552 265.81,68.767Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M276.449,79.672C276.341,80.081 276.298,80.404 276.255,80.662C276.234,80.898 276.212,81.113 276.212,81.307C276.212,81.63 276.298,81.845 276.449,81.974C276.621,82.103 276.879,82.189 277.245,82.21L277.137,82.727L272.943,82.727L273.05,82.21C273.308,82.167 273.524,82.103 273.653,82.017C273.782,81.909 273.889,81.78 273.975,81.608C274.083,81.436 274.169,81.242 274.233,81.006C274.319,80.748 274.427,80.296 274.578,79.586L275.438,75.693L269.802,75.693L268.942,79.672C268.877,79.93 268.834,80.145 268.791,80.296C268.77,80.468 268.748,80.64 268.727,80.791C268.727,80.941 268.705,81.113 268.705,81.307C268.705,81.608 268.791,81.823 268.92,81.952C269.071,82.103 269.329,82.167 269.738,82.21L269.63,82.727L265.436,82.727L265.543,82.21C265.801,82.167 266.017,82.103 266.146,82.017C266.275,81.909 266.382,81.78 266.468,81.608C266.576,81.436 266.64,81.242 266.726,81.006C266.812,80.748 266.92,80.296 267.07,79.586L268.856,71.52C268.92,71.219 268.985,70.918 269.028,70.595C269.071,70.272 269.092,70.014 269.092,69.821C269.092,69.498 269.006,69.261 268.834,69.132C268.662,69.003 268.404,68.917 268.06,68.896L268.168,68.379L272.383,68.379L272.254,68.896C271.996,68.96 271.803,69.025 271.695,69.089C271.566,69.154 271.48,69.261 271.373,69.412C271.286,69.541 271.179,69.778 271.093,70.057C271.007,70.358 270.878,70.853 270.727,71.52L270.017,74.725L275.653,74.725L276.363,71.52C276.427,71.219 276.492,70.918 276.535,70.595C276.578,70.272 276.599,70.014 276.599,69.821C276.599,69.498 276.513,69.261 276.341,69.132C276.191,69.003 275.911,68.917 275.567,68.896L275.696,68.379L279.891,68.379L279.783,68.896C279.525,68.96 279.374,69.003 279.267,69.068C279.181,69.111 279.073,69.175 279.009,69.261C278.923,69.347 278.837,69.476 278.772,69.627C278.686,69.778 278.621,70.014 278.535,70.315C278.449,70.616 278.342,71.025 278.234,71.52L276.449,79.672Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M292.236,68.379L291.504,71.606L290.493,71.606C290.472,71.111 290.429,70.724 290.386,70.423C290.321,70.143 290.257,69.95 290.192,69.799C290.128,69.67 290.041,69.563 289.955,69.498C289.848,69.433 289.74,69.39 289.611,69.347C289.461,69.326 289.267,69.304 289.03,69.304L285.61,69.304L284.406,74.811L286.406,74.811C286.707,74.811 286.966,74.768 287.181,74.682C287.396,74.617 287.589,74.467 287.761,74.252C287.933,74.058 288.127,73.735 288.342,73.305L289.289,73.305L288.342,77.349L287.46,77.349C287.46,76.962 287.439,76.661 287.417,76.489C287.396,76.295 287.353,76.166 287.31,76.08C287.267,75.994 287.202,75.929 287.138,75.886C287.073,75.822 286.966,75.8 286.858,75.779C286.75,75.757 286.557,75.736 286.299,75.736L284.212,75.736L282.857,81.802L285.976,81.802C286.277,81.802 286.535,81.78 286.75,81.737C286.966,81.694 287.159,81.608 287.331,81.501C287.503,81.393 287.675,81.242 287.847,81.07C287.998,80.877 288.17,80.662 288.342,80.382C288.514,80.102 288.751,79.672 289.03,79.091L290.063,79.091L289.138,82.727L279.824,82.727L279.932,82.21C280.19,82.167 280.405,82.103 280.534,82.017C280.663,81.909 280.771,81.78 280.857,81.608C280.964,81.436 281.029,81.242 281.115,81.006C281.201,80.748 281.308,80.296 281.459,79.586L283.244,71.52C283.395,70.81 283.481,70.251 283.481,69.821C283.481,69.498 283.395,69.261 283.223,69.132C283.051,69.003 282.793,68.917 282.448,68.896L282.556,68.379L292.236,68.379Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M296.589,74.875L296.955,74.875C297.234,74.875 297.514,74.811 297.75,74.725C297.987,74.617 298.267,74.445 298.589,74.187C298.912,73.929 299.428,73.456 300.138,72.768C300.826,72.058 301.343,71.541 301.644,71.176C301.923,70.81 302.139,70.509 302.289,70.251C302.418,69.971 302.483,69.735 302.483,69.519C302.483,69.347 302.418,69.218 302.289,69.089C302.139,68.982 301.945,68.917 301.687,68.896L301.794,68.379L306.311,68.379L306.182,68.896C305.903,68.939 305.58,69.089 305.236,69.347C304.892,69.584 304.333,70.079 303.623,70.789L299.536,74.768L301.773,80.145C301.966,80.597 302.117,80.941 302.225,81.156C302.354,81.35 302.483,81.544 302.612,81.673C302.741,81.823 302.891,81.931 303.063,82.038C303.236,82.124 303.429,82.167 303.666,82.21L303.558,82.727L299.493,82.727L299.622,82.21C299.988,82.167 300.181,82.017 300.181,81.716C300.181,81.608 300.181,81.522 300.16,81.415C300.138,81.329 300.095,81.178 300.031,80.984C299.988,80.812 299.901,80.576 299.794,80.318L298.417,76.962C298.267,76.575 298.116,76.295 298.009,76.123C297.88,75.973 297.75,75.865 297.621,75.8C297.492,75.736 297.277,75.693 296.998,75.693L296.417,75.693L295.556,79.672C295.449,80.081 295.406,80.425 295.363,80.662C295.341,80.898 295.32,81.113 295.32,81.307C295.32,81.63 295.406,81.845 295.556,81.974C295.729,82.103 295.987,82.189 296.352,82.21L296.245,82.727L292.05,82.727L292.158,82.21C292.416,82.167 292.631,82.103 292.76,82.017C292.889,81.909 292.997,81.78 293.083,81.608C293.19,81.436 293.255,81.242 293.341,81.006C293.427,80.748 293.534,80.296 293.685,79.586L295.47,71.52C295.535,71.197 295.599,70.875 295.642,70.573C295.685,70.272 295.707,70.036 295.707,69.821C295.707,69.498 295.621,69.261 295.449,69.132C295.277,69.003 295.019,68.917 294.675,68.896L294.782,68.379L298.998,68.379L298.869,68.896C298.611,68.96 298.417,69.025 298.31,69.089C298.181,69.154 298.095,69.261 297.987,69.412C297.901,69.541 297.793,69.778 297.707,70.057C297.621,70.358 297.492,70.853 297.342,71.52L296.589,74.875Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M312.382,78.786C312.382,78.872 312.36,78.958 312.36,79.044C312.36,79.108 312.36,79.194 312.36,79.259C312.36,79.667 312.403,80.012 312.511,80.291C312.618,80.571 312.769,80.786 312.984,80.958C313.177,81.13 313.414,81.259 313.694,81.345C313.973,81.431 314.274,81.453 314.619,81.453C314.963,81.453 315.307,81.41 315.608,81.324C315.931,81.216 316.189,81.087 316.425,80.894C316.662,80.7 316.856,80.463 317.006,80.162C317.135,79.883 317.2,79.56 317.2,79.173C317.2,78.936 317.178,78.721 317.114,78.527C317.049,78.334 316.963,78.14 316.834,77.968C316.727,77.818 316.597,77.667 316.447,77.516C316.275,77.387 316.124,77.237 315.931,77.129C315.737,77 315.543,76.871 315.35,76.764C315.156,76.656 314.941,76.527 314.726,76.419C314.511,76.312 314.296,76.183 314.081,76.054C313.844,75.925 313.651,75.796 313.435,75.645C313.242,75.516 313.048,75.344 312.876,75.193C312.726,75.021 312.575,74.828 312.446,74.634C312.317,74.419 312.231,74.204 312.145,73.967C312.08,73.709 312.037,73.451 312.037,73.15C312.037,72.763 312.08,72.419 312.188,72.117C312.274,71.795 312.403,71.494 312.575,71.235C312.747,70.956 312.962,70.719 313.199,70.504C313.435,70.289 313.694,70.117 313.973,69.966C314.253,69.837 314.554,69.73 314.877,69.644C315.199,69.579 315.522,69.536 315.845,69.536C316.017,69.536 316.167,69.536 316.339,69.558C316.533,69.579 316.705,69.601 316.877,69.644C317.049,69.687 317.221,69.73 317.372,69.794C317.544,69.837 317.673,69.902 317.802,69.966C317.91,70.052 318.017,70.117 318.082,70.203C318.168,70.289 318.211,70.375 318.211,70.461C318.211,70.569 318.189,70.698 318.189,70.848C318.189,70.999 318.189,71.171 318.189,71.365C318.168,71.558 318.168,71.773 318.168,72.01C318.146,72.246 318.146,72.483 318.125,72.741L317.587,72.698C317.608,72.591 317.608,72.462 317.608,72.354C317.608,72.01 317.565,71.687 317.458,71.429C317.35,71.171 317.2,70.934 317.028,70.762C316.834,70.59 316.597,70.461 316.339,70.375C316.081,70.289 315.78,70.225 315.457,70.225C315.135,70.225 314.834,70.289 314.554,70.397C314.274,70.504 314.038,70.655 313.844,70.848C313.629,71.042 313.479,71.257 313.349,71.515C313.242,71.795 313.177,72.074 313.177,72.397C313.177,72.634 313.22,72.87 313.285,73.085C313.349,73.3 313.435,73.494 313.565,73.666C313.672,73.838 313.823,73.989 313.973,74.139C314.145,74.29 314.317,74.419 314.511,74.548C314.683,74.677 314.898,74.785 315.113,74.914C315.307,75.021 315.522,75.15 315.737,75.258C315.952,75.365 316.189,75.494 316.404,75.602C316.619,75.731 316.834,75.86 317.028,76.011C317.221,76.14 317.415,76.29 317.587,76.462C317.759,76.613 317.888,76.807 318.017,77C318.146,77.194 318.232,77.409 318.318,77.645C318.383,77.882 318.426,78.162 318.426,78.441C318.426,78.915 318.361,79.345 318.232,79.71C318.103,80.098 317.91,80.42 317.694,80.678C317.479,80.958 317.221,81.195 316.942,81.388C316.662,81.582 316.361,81.732 316.038,81.84C315.737,81.948 315.436,82.034 315.113,82.098C314.812,82.141 314.533,82.163 314.274,82.163C313.93,82.163 313.586,82.141 313.285,82.098C312.984,82.055 312.726,81.991 312.489,81.904C312.252,81.84 312.08,81.754 311.951,81.646C311.822,81.539 311.758,81.431 311.758,81.345C311.758,81.109 311.758,80.764 311.779,80.334C311.801,79.883 311.822,79.366 311.844,78.764L312.382,78.786Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M318.675,74.871C319.062,74.763 319.406,74.656 319.686,74.591C319.965,74.505 320.18,74.462 320.374,74.419C320.567,74.376 320.718,74.354 320.826,74.333C320.933,74.311 320.998,74.311 321.062,74.311C321.148,74.311 321.234,74.311 321.299,74.354C321.385,74.376 321.449,74.419 321.514,74.484C321.557,74.548 321.6,74.634 321.643,74.742C321.664,74.849 321.686,74.978 321.686,75.129C321.686,75.172 321.686,75.279 321.686,75.43C321.664,75.602 321.664,75.796 321.643,76.075C321.643,76.355 321.621,76.678 321.6,77.065C321.578,77.473 321.535,77.925 321.514,78.463L321.578,78.463C321.837,77.796 322.138,77.194 322.46,76.656C322.783,76.14 323.106,75.71 323.428,75.344C323.751,74.978 324.074,74.72 324.396,74.527C324.719,74.354 325.042,74.268 325.343,74.268C325.579,74.268 325.773,74.311 325.967,74.397C326.139,74.484 326.311,74.591 326.44,74.742C326.569,74.892 326.676,75.064 326.784,75.258C326.87,75.451 326.934,75.667 326.999,75.882C327.064,76.075 327.085,76.29 327.128,76.505C327.15,76.742 327.15,76.936 327.15,77.129C327.15,77.645 327.107,78.119 326.999,78.57C326.891,79.022 326.741,79.431 326.569,79.797C326.375,80.184 326.16,80.506 325.923,80.807C325.665,81.087 325.407,81.345 325.106,81.539C324.826,81.754 324.525,81.904 324.203,82.012C323.88,82.12 323.579,82.163 323.256,82.163C322.912,82.163 322.589,82.098 322.267,81.991C321.944,81.861 321.643,81.668 321.342,81.367C321.32,82.249 321.277,83.152 321.234,84.099C321.191,85.023 321.17,86.013 321.17,87.024L320.073,87.153L319.965,87.153C320.008,86.486 320.073,85.798 320.116,85.088C320.159,84.378 320.202,83.647 320.223,82.915C320.266,82.206 320.309,81.474 320.331,80.786C320.374,80.076 320.395,79.431 320.417,78.807C320.438,78.183 320.46,77.624 320.46,77.129C320.481,76.635 320.481,76.204 320.481,75.882C320.481,75.753 320.481,75.624 320.46,75.538C320.46,75.43 320.417,75.344 320.374,75.279C320.352,75.215 320.288,75.172 320.202,75.129C320.137,75.107 320.03,75.086 319.901,75.086C319.772,75.086 319.621,75.086 319.427,75.129C319.234,75.15 319.019,75.193 318.761,75.258L318.675,74.871ZM321.406,80.807C321.708,81.044 321.987,81.238 322.288,81.367C322.589,81.474 322.891,81.539 323.17,81.539C323.579,81.539 323.923,81.453 324.267,81.281C324.59,81.13 324.891,80.872 325.128,80.549C325.364,80.205 325.558,79.818 325.687,79.323C325.816,78.829 325.88,78.269 325.88,77.624C325.88,77.387 325.859,77.151 325.837,76.893C325.794,76.656 325.73,76.419 325.665,76.204C325.579,76.011 325.472,75.839 325.343,75.71C325.214,75.581 325.063,75.516 324.891,75.516C324.676,75.516 324.461,75.559 324.246,75.688C324.031,75.796 323.837,75.946 323.643,76.118C323.428,76.312 323.256,76.527 323.063,76.785C322.891,77.043 322.719,77.301 322.546,77.581C322.396,77.861 322.245,78.162 322.116,78.463C321.966,78.764 321.858,79.044 321.751,79.323C321.664,79.624 321.578,79.883 321.514,80.141C321.449,80.377 321.406,80.614 321.406,80.807Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M330.016,72.117C329.887,72.117 329.78,72.096 329.694,72.053C329.586,71.988 329.522,71.924 329.457,71.838C329.393,71.773 329.328,71.666 329.307,71.558C329.264,71.451 329.242,71.343 329.242,71.235C329.242,71.085 329.264,70.934 329.328,70.805C329.371,70.698 329.436,70.59 329.522,70.504C329.608,70.418 329.694,70.375 329.801,70.332C329.909,70.289 330.016,70.268 330.103,70.268C330.232,70.268 330.339,70.289 330.425,70.332C330.511,70.375 330.597,70.44 330.662,70.504C330.748,70.59 330.791,70.676 330.834,70.784C330.855,70.87 330.877,70.977 330.877,71.106C330.877,71.214 330.855,71.343 330.812,71.472C330.769,71.58 330.726,71.687 330.64,71.795C330.576,71.902 330.468,71.967 330.361,72.031C330.253,72.096 330.146,72.117 330.016,72.117ZM330.468,74.484C330.404,74.892 330.361,75.301 330.318,75.688C330.275,76.097 330.253,76.484 330.21,76.85C330.189,77.237 330.167,77.581 330.167,77.925C330.146,78.248 330.146,78.549 330.146,78.829C330.146,79.194 330.146,79.495 330.167,79.753C330.167,80.033 330.189,80.248 330.21,80.42C330.232,80.592 330.275,80.743 330.296,80.85C330.339,80.958 330.382,81.044 330.447,81.109C330.49,81.152 330.554,81.195 330.619,81.216C330.705,81.238 330.791,81.238 330.877,81.238C331.049,81.238 331.221,81.216 331.415,81.152C331.608,81.109 331.802,81.044 332.017,80.958L332.103,81.216C331.974,81.324 331.823,81.431 331.63,81.539C331.436,81.646 331.243,81.732 331.027,81.818C330.812,81.904 330.619,81.969 330.404,82.034C330.21,82.077 330.038,82.12 329.887,82.12C329.801,82.12 329.694,82.098 329.629,82.098C329.543,82.077 329.479,82.034 329.414,81.969C329.35,81.904 329.285,81.818 329.221,81.689C329.178,81.582 329.135,81.41 329.113,81.195C329.07,80.98 329.049,80.721 329.027,80.399C329.005,80.098 329.005,79.71 329.005,79.259C329.005,79.001 329.005,78.699 329.005,78.377C329.005,78.033 329.027,77.689 329.027,77.301C329.049,76.936 329.049,76.527 329.07,76.097C329.092,75.688 329.113,75.236 329.135,74.806C329.156,74.699 329.178,74.613 329.221,74.57C329.264,74.505 329.328,74.462 329.393,74.419C329.457,74.376 329.522,74.354 329.608,74.354C329.672,74.333 329.758,74.333 329.801,74.333C330.038,74.333 330.275,74.376 330.468,74.484Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M333.332,82.012L333.246,82.012C333.267,81.689 333.31,81.324 333.332,80.937C333.353,80.528 333.375,80.141 333.397,79.732C333.418,79.323 333.44,78.936 333.44,78.527C333.461,78.14 333.483,77.753 333.483,77.409C333.504,77.065 333.504,76.764 333.526,76.484C333.526,76.226 333.526,76.011 333.526,75.839C333.526,75.71 333.526,75.602 333.504,75.516C333.504,75.408 333.483,75.344 333.44,75.279C333.397,75.215 333.353,75.172 333.267,75.129C333.203,75.107 333.117,75.086 332.988,75.086C332.945,75.086 332.88,75.086 332.816,75.107C332.751,75.107 332.687,75.107 332.601,75.129C332.515,75.15 332.429,75.172 332.342,75.193C332.235,75.215 332.127,75.236 331.998,75.258L331.934,74.892C332.515,74.677 332.988,74.527 333.332,74.44C333.698,74.354 333.934,74.311 334.063,74.311C334.3,74.311 334.472,74.376 334.558,74.527C334.666,74.656 334.709,74.849 334.709,75.129C334.709,75.15 334.709,75.215 334.709,75.344C334.709,75.451 334.687,75.602 334.687,75.796C334.666,75.989 334.644,76.204 334.623,76.462C334.623,76.721 334.601,76.979 334.58,77.28C334.558,77.559 334.515,77.861 334.494,78.183C334.472,78.484 334.429,78.807 334.407,79.108L334.451,79.13C334.623,78.441 334.816,77.796 335.031,77.194C335.246,76.613 335.483,76.097 335.741,75.645C335.999,75.215 336.3,74.871 336.623,74.613C336.924,74.376 337.268,74.247 337.655,74.247C337.785,74.247 337.914,74.268 338.021,74.311C338.15,74.354 338.236,74.419 338.322,74.484C338.408,74.57 338.473,74.656 338.537,74.763C338.58,74.849 338.602,74.978 338.602,75.086C338.602,75.258 338.559,75.408 338.494,75.538C338.43,75.688 338.322,75.817 338.172,75.925L338.129,75.925C337.978,75.817 337.849,75.731 337.72,75.667C337.591,75.624 337.44,75.581 337.29,75.581C337.139,75.581 336.989,75.624 336.838,75.667C336.688,75.731 336.559,75.817 336.429,75.925C336.236,76.118 336.064,76.355 335.892,76.656C335.72,76.957 335.548,77.28 335.418,77.645C335.268,78.011 335.139,78.398 335.031,78.807C334.924,79.194 334.816,79.581 334.73,79.969C334.644,80.356 334.58,80.7 334.537,81.044C334.494,81.367 334.451,81.646 334.429,81.883L333.332,82.012Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M340.493,72.117C340.364,72.117 340.257,72.096 340.171,72.053C340.063,71.988 339.999,71.924 339.934,71.838C339.869,71.773 339.805,71.666 339.783,71.558C339.74,71.451 339.719,71.343 339.719,71.235C339.719,71.085 339.74,70.934 339.805,70.805C339.848,70.698 339.912,70.59 339.999,70.504C340.085,70.418 340.171,70.375 340.278,70.332C340.386,70.289 340.493,70.268 340.579,70.268C340.708,70.268 340.816,70.289 340.902,70.332C340.988,70.375 341.074,70.44 341.139,70.504C341.225,70.59 341.268,70.676 341.311,70.784C341.332,70.87 341.354,70.977 341.354,71.106C341.354,71.214 341.332,71.343 341.289,71.472C341.246,71.58 341.203,71.687 341.117,71.795C341.052,71.902 340.945,71.967 340.837,72.031C340.73,72.096 340.622,72.117 340.493,72.117ZM340.945,74.484C340.88,74.892 340.837,75.301 340.794,75.688C340.751,76.097 340.73,76.484 340.687,76.85C340.665,77.237 340.644,77.581 340.644,77.925C340.622,78.248 340.622,78.549 340.622,78.829C340.622,79.194 340.622,79.495 340.644,79.753C340.644,80.033 340.665,80.248 340.687,80.42C340.708,80.592 340.751,80.743 340.773,80.85C340.816,80.958 340.859,81.044 340.923,81.109C340.966,81.152 341.031,81.195 341.096,81.216C341.182,81.238 341.268,81.238 341.354,81.238C341.526,81.238 341.698,81.216 341.891,81.152C342.085,81.109 342.279,81.044 342.494,80.958L342.58,81.216C342.451,81.324 342.3,81.431 342.107,81.539C341.913,81.646 341.719,81.732 341.504,81.818C341.289,81.904 341.096,81.969 340.88,82.034C340.687,82.077 340.515,82.12 340.364,82.12C340.278,82.12 340.171,82.098 340.106,82.098C340.02,82.077 339.955,82.034 339.891,81.969C339.826,81.904 339.762,81.818 339.697,81.689C339.654,81.582 339.611,81.41 339.59,81.195C339.547,80.98 339.525,80.721 339.504,80.399C339.482,80.098 339.482,79.71 339.482,79.259C339.482,79.001 339.482,78.699 339.482,78.377C339.482,78.033 339.504,77.689 339.504,77.301C339.525,76.936 339.525,76.527 339.547,76.097C339.568,75.688 339.59,75.236 339.611,74.806C339.633,74.699 339.654,74.613 339.697,74.57C339.74,74.505 339.805,74.462 339.869,74.419C339.934,74.376 339.999,74.354 340.085,74.354C340.149,74.333 340.235,74.333 340.278,74.333C340.515,74.333 340.751,74.376 340.945,74.484Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M345.723,70.526C345.573,71.192 345.444,71.881 345.357,72.591C345.25,73.3 345.164,73.989 345.099,74.699C345.271,74.699 345.422,74.699 345.573,74.699C345.745,74.699 345.874,74.72 346.024,74.72C346.153,74.72 346.282,74.72 346.39,74.72L346.648,74.72C346.971,74.72 347.229,74.699 347.444,74.656C347.465,74.72 347.465,74.785 347.487,74.828C347.487,74.871 347.508,74.914 347.508,74.957C347.508,75.107 347.444,75.215 347.315,75.258C347.186,75.301 346.992,75.322 346.756,75.322C346.519,75.322 346.261,75.322 345.981,75.279C345.702,75.258 345.379,75.236 345.035,75.215C344.992,75.581 344.97,75.946 344.949,76.312C344.927,76.656 344.906,77 344.884,77.301C344.863,77.624 344.863,77.925 344.841,78.183C344.841,78.463 344.841,78.699 344.841,78.893C344.841,79.259 344.863,79.603 344.884,79.883C344.927,80.184 344.992,80.442 345.099,80.635C345.207,80.85 345.336,81.001 345.508,81.109C345.68,81.216 345.895,81.281 346.153,81.281C346.347,81.281 346.562,81.238 346.777,81.173C347.014,81.109 347.293,81.001 347.573,80.85C347.681,80.958 347.724,81.066 347.745,81.195C347.53,81.345 347.315,81.474 347.078,81.582C346.863,81.711 346.648,81.818 346.433,81.883C346.218,81.969 346.024,82.034 345.809,82.098C345.616,82.141 345.422,82.163 345.25,82.163C345.013,82.163 344.798,82.12 344.605,82.034C344.411,81.948 344.239,81.797 344.11,81.56C343.959,81.345 343.852,81.066 343.766,80.678C343.701,80.313 343.658,79.84 343.658,79.28C343.658,78.958 343.658,78.635 343.68,78.291C343.68,77.947 343.701,77.624 343.723,77.258C343.723,76.914 343.744,76.57 343.787,76.226C343.809,75.882 343.83,75.538 343.873,75.193C343.594,75.215 343.336,75.215 343.099,75.236C342.862,75.258 342.669,75.279 342.497,75.322C342.497,75.301 342.475,75.279 342.475,75.258C342.475,75.172 342.518,75.107 342.583,75.043C342.669,74.978 342.755,74.935 342.884,74.871C343.013,74.828 343.163,74.785 343.357,74.742C343.529,74.699 343.723,74.677 343.938,74.677C344.024,73.946 344.11,73.236 344.196,72.591C344.282,71.945 344.368,71.365 344.476,70.848C344.476,70.762 344.519,70.676 344.562,70.633C344.605,70.569 344.669,70.526 344.734,70.483C344.798,70.44 344.863,70.418 344.927,70.418C345.013,70.397 345.078,70.397 345.142,70.397C345.271,70.397 345.379,70.397 345.465,70.418C345.551,70.44 345.637,70.483 345.723,70.526Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M357.984,69.536C358.435,69.536 358.865,69.579 359.253,69.665C359.64,69.751 359.984,69.837 360.264,69.966C360.543,70.095 360.78,70.225 360.93,70.354C361.103,70.504 361.167,70.633 361.167,70.741C361.167,70.977 361.167,71.3 361.146,71.687C361.124,72.074 361.103,72.526 361.081,73.085L360.629,73.064L360.629,72.784C360.629,72.548 360.608,72.311 360.543,72.096C360.5,71.881 360.414,71.666 360.285,71.472C360.135,71.279 359.962,71.085 359.769,70.934C359.575,70.784 359.36,70.655 359.145,70.547C358.908,70.44 358.65,70.375 358.392,70.311C358.134,70.268 357.854,70.225 357.596,70.225C357.123,70.225 356.693,70.311 356.306,70.44C355.94,70.59 355.596,70.784 355.295,71.042C354.994,71.3 354.714,71.623 354.499,71.967C354.284,72.311 354.09,72.698 353.94,73.107C353.811,73.516 353.703,73.967 353.617,74.419C353.552,74.871 353.509,75.322 353.509,75.796C353.509,76.355 353.552,76.871 353.66,77.344C353.746,77.839 353.875,78.291 354.047,78.699C354.219,79.108 354.413,79.474 354.649,79.797C354.886,80.119 355.166,80.399 355.445,80.635C355.746,80.85 356.069,81.023 356.413,81.152C356.757,81.259 357.123,81.324 357.51,81.324C357.768,81.324 358.048,81.302 358.328,81.238C358.607,81.173 358.887,81.087 359.188,80.958C359.468,80.829 359.769,80.678 360.07,80.485C360.35,80.291 360.651,80.076 360.952,79.797C360.973,79.818 361.016,79.84 361.038,79.861C361.081,79.883 361.124,79.904 361.146,79.926C361.189,79.947 361.21,79.99 361.232,80.012C361.253,80.055 361.253,80.076 361.253,80.098C361.253,80.119 361.253,80.162 361.21,80.205C360.887,80.571 360.522,80.894 360.178,81.152C359.833,81.41 359.468,81.603 359.124,81.775C358.779,81.926 358.414,82.034 358.048,82.098C357.704,82.184 357.338,82.206 356.973,82.206C356.499,82.206 356.069,82.141 355.639,82.012C355.209,81.883 354.8,81.689 354.434,81.453C354.069,81.195 353.725,80.915 353.445,80.549C353.144,80.205 352.886,79.818 352.671,79.388C352.455,78.958 352.305,78.484 352.197,77.968C352.068,77.452 352.025,76.914 352.025,76.355C352.025,75.817 352.068,75.258 352.176,74.699C352.305,74.161 352.455,73.623 352.671,73.128C352.907,72.612 353.165,72.139 353.509,71.709C353.832,71.279 354.219,70.891 354.671,70.569C355.101,70.246 355.596,69.988 356.155,69.816C356.714,69.622 357.317,69.536 357.984,69.536Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M362.354,78.872C362.354,78.506 362.397,78.14 362.462,77.796C362.526,77.43 362.634,77.065 362.784,76.721C362.935,76.398 363.107,76.075 363.322,75.774C363.537,75.473 363.774,75.215 364.053,74.978C364.333,74.763 364.634,74.591 364.978,74.462C365.322,74.333 365.688,74.268 366.075,74.268C366.527,74.268 366.936,74.354 367.28,74.548C367.624,74.72 367.904,74.978 368.14,75.279C368.377,75.602 368.549,75.946 368.678,76.355C368.807,76.742 368.85,77.151 368.85,77.559C368.85,77.925 368.829,78.269 368.743,78.635C368.678,79.001 368.57,79.345 368.42,79.689C368.269,80.033 368.097,80.356 367.882,80.657C367.667,80.958 367.43,81.216 367.151,81.431C366.871,81.668 366.57,81.84 366.226,81.969C365.882,82.098 365.516,82.163 365.107,82.163C364.677,82.163 364.268,82.077 363.924,81.883C363.58,81.689 363.3,81.453 363.064,81.13C362.827,80.829 362.655,80.463 362.526,80.076C362.419,79.689 362.354,79.28 362.354,78.872ZM363.602,78.355C363.602,78.592 363.623,78.85 363.666,79.087C363.688,79.345 363.752,79.56 363.838,79.797C363.903,80.033 364.01,80.248 364.118,80.442C364.225,80.635 364.376,80.807 364.527,80.958C364.677,81.109 364.849,81.216 365.043,81.302C365.236,81.388 365.452,81.431 365.688,81.431C365.946,81.431 366.161,81.367 366.355,81.259C366.57,81.152 366.721,81.001 366.871,80.829C367.022,80.635 367.129,80.442 367.215,80.205C367.323,79.969 367.387,79.71 367.452,79.474C367.495,79.216 367.538,78.979 367.559,78.721C367.581,78.484 367.603,78.248 367.603,78.054C367.603,77.818 367.581,77.559 367.538,77.323C367.495,77.086 367.452,76.85 367.366,76.613C367.301,76.398 367.194,76.183 367.086,75.989C366.979,75.796 366.828,75.624 366.678,75.473C366.527,75.322 366.355,75.215 366.161,75.129C365.968,75.043 365.753,75 365.516,75C365.258,75 365.021,75.043 364.828,75.15C364.634,75.279 364.484,75.408 364.333,75.602C364.182,75.774 364.075,75.989 363.989,76.226C363.881,76.462 363.817,76.699 363.752,76.957C363.709,77.194 363.666,77.452 363.645,77.689C363.623,77.947 363.602,78.162 363.602,78.355Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M369.371,74.914C369.607,74.806 369.844,74.742 370.08,74.656C370.317,74.591 370.532,74.527 370.726,74.484C370.898,74.419 371.07,74.376 371.221,74.354C371.371,74.333 371.479,74.311 371.565,74.311C371.78,74.311 371.93,74.376 372.038,74.505C372.167,74.613 372.21,74.828 372.21,75.129C372.21,75.193 372.21,75.344 372.188,75.559C372.188,75.796 372.167,76.075 372.145,76.441C372.102,76.785 372.081,77.172 372.038,77.624C371.995,78.054 371.952,78.527 371.909,79.044L371.93,79.044C372.188,78.162 372.447,77.409 372.726,76.807C373.006,76.204 373.285,75.71 373.565,75.344C373.845,74.978 374.146,74.699 374.426,74.548C374.727,74.376 375.006,74.311 375.286,74.311C375.523,74.311 375.716,74.354 375.888,74.484C376.06,74.613 376.189,74.763 376.297,74.957C376.404,75.15 376.49,75.365 376.534,75.602C376.577,75.839 376.598,76.097 376.598,76.355C376.598,76.57 376.598,76.807 376.577,77.043C376.577,77.301 376.555,77.538 376.534,77.775C376.512,78.011 376.49,78.248 376.469,78.463C376.447,78.678 376.426,78.872 376.404,79.022L376.447,79.022C376.684,78.119 376.942,77.366 377.222,76.764C377.501,76.14 377.781,75.667 378.082,75.279C378.362,74.914 378.663,74.656 378.943,74.484C379.244,74.333 379.523,74.247 379.782,74.247C379.975,74.247 380.126,74.29 380.298,74.354C380.47,74.44 380.62,74.548 380.728,74.72C380.857,74.871 380.965,75.086 381.051,75.344C381.115,75.602 381.158,75.903 381.158,76.269C381.158,76.376 381.158,76.505 381.137,76.678C381.137,76.871 381.115,77.065 381.094,77.28C381.072,77.495 381.051,77.732 381.029,77.968C381.008,78.205 380.986,78.441 380.986,78.678C380.965,78.915 380.943,79.151 380.922,79.345C380.922,79.56 380.922,79.753 380.922,79.904C380.922,80.184 380.922,80.399 380.943,80.571C380.965,80.743 381.008,80.872 381.051,80.98C381.115,81.087 381.18,81.152 381.266,81.173C381.352,81.216 381.459,81.238 381.588,81.238C381.782,81.238 381.954,81.216 382.169,81.152C382.363,81.109 382.556,81.044 382.75,80.958L382.857,81.216C382.728,81.324 382.556,81.431 382.384,81.539C382.191,81.646 381.976,81.732 381.76,81.818C381.545,81.904 381.352,81.969 381.137,82.034C380.922,82.077 380.749,82.12 380.577,82.12C380.384,82.12 380.233,82.055 380.126,81.969C380.018,81.861 379.932,81.732 379.889,81.539C379.825,81.367 379.803,81.173 379.782,80.937C379.76,80.7 379.76,80.442 379.76,80.184C379.76,79.861 379.76,79.517 379.803,79.151C379.825,78.786 379.846,78.463 379.868,78.14C379.889,77.818 379.932,77.516 379.954,77.258C379.975,77 379.975,76.807 379.975,76.678C379.975,76.29 379.911,76.011 379.782,75.817C379.652,75.624 379.459,75.516 379.222,75.516C379.05,75.516 378.857,75.602 378.663,75.774C378.448,75.946 378.254,76.183 378.061,76.484C377.846,76.785 377.652,77.129 377.48,77.538C377.286,77.947 377.114,78.377 376.964,78.85C376.813,79.323 376.684,79.818 376.577,80.334C376.49,80.85 376.404,81.367 376.383,81.883L375.286,82.012L375.2,82.012C375.243,81.281 375.264,80.635 375.307,80.055C375.35,79.495 375.372,79.001 375.393,78.57C375.415,78.14 375.436,77.796 375.458,77.495C375.479,77.194 375.479,76.957 375.479,76.764C375.479,76.376 375.415,76.075 375.286,75.882C375.157,75.688 374.985,75.581 374.77,75.581C374.576,75.581 374.382,75.667 374.189,75.839C373.974,75.989 373.78,76.226 373.565,76.527C373.372,76.807 373.178,77.172 373.006,77.559C372.812,77.968 372.64,78.398 372.49,78.872C372.339,79.323 372.231,79.818 372.124,80.334C372.016,80.85 371.952,81.367 371.93,81.883L370.833,82.012L370.747,82.012C370.769,81.689 370.812,81.324 370.833,80.937C370.855,80.528 370.876,80.141 370.898,79.732C370.919,79.323 370.941,78.936 370.941,78.527C370.962,78.14 370.984,77.753 370.984,77.409C371.005,77.065 371.005,76.764 371.027,76.484C371.027,76.226 371.027,76.011 371.027,75.839C371.027,75.71 371.005,75.581 370.984,75.473C370.962,75.387 370.941,75.301 370.898,75.258C370.855,75.193 370.79,75.15 370.726,75.129C370.661,75.107 370.597,75.086 370.489,75.086C370.36,75.086 370.21,75.107 370.037,75.15C369.865,75.172 369.672,75.215 369.435,75.279L369.371,74.914Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M385.953,67.794C385.889,68.353 385.824,68.934 385.76,69.515C385.695,70.117 385.631,70.719 385.566,71.322C385.502,71.945 385.437,72.548 385.373,73.171C385.33,73.774 385.265,74.397 385.222,75C385.179,75.581 385.136,76.183 385.093,76.764C385.071,77.323 385.028,77.882 385.007,78.42L385.071,78.42C385.351,77.732 385.652,77.129 385.975,76.613C386.276,76.097 386.599,75.667 386.943,75.322C387.265,74.978 387.588,74.699 387.911,74.527C388.233,74.354 388.556,74.268 388.836,74.268C389.072,74.268 389.287,74.311 389.481,74.397C389.653,74.484 389.825,74.591 389.954,74.742C390.105,74.892 390.212,75.064 390.298,75.258C390.384,75.451 390.47,75.667 390.513,75.882C390.578,76.075 390.621,76.29 390.643,76.505C390.664,76.742 390.664,76.936 390.664,77.129C390.664,77.645 390.621,78.14 390.513,78.592C390.406,79.044 390.277,79.452 390.083,79.818C389.911,80.205 389.696,80.528 389.438,80.829C389.201,81.109 388.922,81.367 388.642,81.56C388.341,81.754 388.04,81.904 387.717,82.012C387.395,82.12 387.072,82.163 386.749,82.163C386.534,82.163 386.319,82.141 386.082,82.098C385.867,82.034 385.652,81.969 385.459,81.861C385.243,81.775 385.05,81.625 384.856,81.474C384.663,81.324 384.512,81.13 384.362,80.915C384.233,81.216 384.103,81.474 383.996,81.668C383.867,81.861 383.738,82.012 383.63,82.141L383.372,82.077C383.544,81.539 383.695,80.894 383.781,80.141C383.888,79.388 383.953,78.549 383.996,77.602C384.017,77.194 384.039,76.721 384.06,76.204C384.082,75.688 384.125,75.15 384.146,74.57C384.168,73.989 384.211,73.408 384.233,72.806C384.276,72.203 384.297,71.623 384.319,71.063C384.362,70.483 384.383,69.945 384.426,69.45C384.448,68.934 384.469,68.482 384.491,68.095C384.491,68.009 384.534,67.944 384.577,67.88C384.62,67.815 384.684,67.772 384.749,67.729C384.835,67.686 384.921,67.665 385.007,67.643C385.093,67.622 385.201,67.622 385.287,67.622C385.502,67.622 385.717,67.686 385.953,67.794ZM389.395,77.624C389.395,77.387 389.373,77.151 389.33,76.893C389.309,76.656 389.244,76.419 389.158,76.204C389.094,76.011 388.986,75.839 388.857,75.71C388.728,75.581 388.578,75.516 388.384,75.516C388.169,75.516 387.954,75.559 387.76,75.667C387.545,75.774 387.352,75.925 387.158,76.097C386.964,76.29 386.771,76.484 386.599,76.721C386.405,76.957 386.254,77.215 386.082,77.473C385.932,77.732 385.803,77.99 385.674,78.248C385.545,78.527 385.437,78.764 385.351,79.001C385.265,79.259 385.201,79.474 385.136,79.667C385.093,79.861 385.071,80.012 385.071,80.119C385.114,80.334 385.201,80.528 385.308,80.721C385.416,80.894 385.566,81.044 385.717,81.173C385.867,81.281 386.039,81.388 386.233,81.453C386.427,81.517 386.62,81.56 386.835,81.56C387.179,81.56 387.524,81.474 387.825,81.281C388.147,81.109 388.405,80.85 388.642,80.528C388.879,80.184 389.051,79.775 389.18,79.28C389.33,78.807 389.395,78.248 389.395,77.624Z"/>
<path android:fillColor="#ffffff" android:fillType="nonZero" android:pathData="M391.898,78.872C391.898,78.506 391.941,78.14 392.005,77.796C392.07,77.43 392.178,77.065 392.328,76.721C392.479,76.398 392.651,76.075 392.866,75.774C393.081,75.473 393.318,75.215 393.597,74.978C393.877,74.763 394.178,74.591 394.522,74.462C394.866,74.333 395.232,74.268 395.619,74.268C396.071,74.268 396.48,74.354 396.824,74.548C397.168,74.72 397.447,74.978 397.684,75.279C397.921,75.602 398.093,75.946 398.222,76.355C398.351,76.742 398.394,77.151 398.394,77.559C398.394,77.925 398.372,78.269 398.286,78.635C398.222,79.001 398.114,79.345 397.964,79.689C397.813,80.033 397.641,80.356 397.426,80.657C397.211,80.958 396.974,81.216 396.695,81.431C396.415,81.668 396.114,81.84 395.77,81.969C395.426,82.098 395.06,82.163 394.651,82.163C394.221,82.163 393.812,82.077 393.468,81.883C393.124,81.689 392.844,81.453 392.608,81.13C392.371,80.829 392.199,80.463 392.07,80.076C391.962,79.689 391.898,79.28 391.898,78.872ZM393.145,78.355C393.145,78.592 393.167,78.85 393.21,79.087C393.232,79.345 393.296,79.56 393.382,79.797C393.447,80.033 393.554,80.248 393.662,80.442C393.769,80.635 393.92,80.807 394.07,80.958C394.221,81.109 394.393,81.216 394.587,81.302C394.78,81.388 394.995,81.431 395.232,81.431C395.49,81.431 395.705,81.367 395.899,81.259C396.114,81.152 396.264,81.001 396.415,80.829C396.566,80.635 396.673,80.442 396.759,80.205C396.867,79.969 396.931,79.71 396.996,79.474C397.039,79.216 397.082,78.979 397.103,78.721C397.125,78.484 397.146,78.248 397.146,78.054C397.146,77.818 397.125,77.559 397.082,77.323C397.039,77.086 396.996,76.85 396.91,76.613C396.845,76.398 396.738,76.183 396.63,75.989C396.523,75.796 396.372,75.624 396.221,75.473C396.071,75.322 395.899,75.215 395.705,75.129C395.512,75.043 395.296,75 395.06,75C394.802,75 394.565,75.043 394.372,75.15C394.178,75.279 394.027,75.408 393.877,75.602C393.726,75.774 393.619,75.989 393.533,76.226C393.425,76.462 393.361,76.699 393.296,76.957C393.253,77.194 393.21,77.452 393.189,77.689C393.167,77.947 393.145,78.162 393.145,78.355Z"/>
<path android:fillColor="#232121" android:pathData="M416.973,95.845L209.326,96.482C213.769,127.785 224.276,159.089 239.861,190.392L440.758,189.436C438.993,157.477 432.454,126.107 416.973,95.845Z"/>
<path android:fillColor="#9C9C9C" android:pathData="M413.058,100.938L215.693,101.496C219.916,128.9 229.903,156.303 244.717,183.707L435.666,182.87C433.989,154.892 427.773,127.43 413.058,100.938Z"/>
<path android:fillColor="#6C684D" android:pathData="M425.643,129.529C423.305,120.953 419.946,108.243 414.674,106.986L240.117,108.259C235.949,108.259 232.565,111.649 232.565,115.823L232.387,159.04C233.967,163.413 237.931,169.147 241.146,177.34L423.255,177.022L425.643,129.529Z"/>
<path android:fillAlpha="0.41" android:fillColor="#FF000000" android:pathData="M347.126,132.454L341.648,126.976L330.691,126.976L325.213,132.454L325.213,152.05L330.691,157.528L341.648,157.528L347.126,152.05L347.126,132.454Z"/>
<path android:fillColor="#00000000"
android:pathData="M342.29,135.852L334.916,142.279L334.916,129.426L342.29,135.852Z"
android:strokeColor="#ffffff" android:strokeWidth="0.51"/>
<path android:fillColor="#00000000"
android:pathData="M342.29,148.474L334.916,154.9L334.916,142.047L342.29,148.474Z"
android:strokeColor="#ffffff" android:strokeWidth="0.51"/>
<path android:fillColor="#ffffff" android:pathData="M331.435,136.157l4.641,5.732l-1.146,0.928l-4.641,-5.732z"/>
<path android:fillColor="#ffffff" android:pathData="M330.048,146.319l5.215,-5.215l0.939,0.939l-5.215,5.215z"/>
<path android:fillColor="#232121" android:pathData="M113.089,117.685a30.88,41.039 78.705,1 0,80.488 -16.075a30.88,41.039 78.705,1 0,-80.488 16.075z"/>
<path android:fillColor="#FF000000" android:pathData="M128.597,114.092a16.385,26.463 78.705,1 0,51.901 -10.366a16.385,26.463 78.705,1 0,-51.901 10.366z"/>
<path android:fillColor="#232121" android:pathData="M132.048,113.5a14.004,22.618 78.705,1 0,44.359 -8.86a14.004,22.618 78.705,1 0,-44.359 8.86z"/>
<path android:fillColor="#222323" android:pathData="M168.678,130.425L175.653,150.194L155.566,156.186L148.59,136.419L168.678,130.425Z"/>
<path android:fillColor="#232121" android:pathData="M129.006,182.945a30.88,41.039 78.705,1 0,80.488 -16.075a30.88,41.039 78.705,1 0,-80.488 16.075z"/>
<path android:fillColor="#FF000000" android:pathData="M144.514,179.352a16.385,26.463 78.705,1 0,51.901 -10.366a16.385,26.463 78.705,1 0,-51.901 10.366z"/>
<path android:fillColor="#232121" android:pathData="M148.26,178.465a14.004,22.618 78.705,1 0,44.359 -8.86a14.004,22.618 78.705,1 0,-44.359 8.86z"/>
<path android:fillColor="#ffffff" android:pathData="M179.325,165.012l2.125,2.334l-12.868,15.36l-2.125,-2.334z"/>
<path android:fillColor="#ffffff" android:pathData="M161.089,172.594l2.659,-2.261l6.739,10.387l-2.659,2.261z"/>
<path android:fillColor="#ffffff" android:pathData="M123.737,65.763l9.903,0l0,14.96l-9.903,0z"/>
<path android:fillAlpha="0.44" android:fillColor="#ffffff" android:pathData="M127.108,61.55l10.114,0l0,12.853l-10.114,0z"/>
<path android:fillAlpha="0.44" android:fillColor="#ffffff" android:pathData="M149.479,99.794l10.114,0l0,12.853l-10.114,0z"/>
<path android:fillColor="#ffffff" android:pathData="M146.107,104.008l9.903,0l0,14.96l-9.903,0z"/>
<path android:fillColor="#ffffff" android:pathData="M139.922,204.626l2.125,2.334l-12.868,15.36l-2.125,-2.334z"/>
<path android:fillColor="#ffffff" android:pathData="M121.686,212.207l2.659,-2.261l6.739,10.387l-2.659,2.261z"/>
<path android:fillColor="#FF000000" android:pathData="M441.078,223.997C441.078,220.735 438.431,218.088 435.17,218.088L203.775,218.088C200.513,218.088 197.866,220.735 197.866,223.997L246.254,291.943L441.078,291.943L441.078,223.997Z"/>
<path android:fillColor="#A7A9A6" android:pathData="M435.666,228.08C435.666,224.984 433.181,222.47 430.119,222.47L212.884,222.47C209.822,222.47 207.337,224.984 207.337,228.08L252.764,292.58L435.666,292.58L435.666,228.08Z"/>
<path android:fillAlpha="0.41" android:fillColor="#FF000000" android:pathData="M435.666,228.078C435.666,224.983 433.154,222.471 430.057,222.471L322.894,222.471L322.894,292.579L435.666,292.579L435.666,228.078Z"/>
<path android:fillAlpha="0.4" android:fillColor="#443F29" android:pathData="M255.574,221.745L203.277,222.401L252.322,292.719L255.719,292.726L255.574,221.745Z"/>
<path android:fillAlpha="0.4" android:fillColor="#443F29" android:pathData="M255.09,292.268L255.09,222.162L272.626,239.688L272.626,274.741L255.09,292.268Z"/>
<path android:fillAlpha="0.4" android:fillColor="#443F29" android:pathData="M272.625,250.743l13.111,0l0,12.947l-13.111,0z"/>
<path android:fillAlpha="0.55" android:fillColor="#FF000000" android:pathData="M285.53,222.427l6.227,0l0,70.472l-6.227,0z"/>
<path android:fillAlpha="0.55" android:fillColor="#FF000000" android:pathData="M316.504,222.427l6.391,0l0,70.472l-6.391,0z"/>
<path android:fillAlpha="0.55" android:fillColor="#FF000000" android:pathData="M302.41,221.935l7.374,0l0,70.472l-7.374,0z"/>
<path android:fillColor="#FF000000" android:fillType="nonZero" android:pathData="M355.056,265.709L355.056,264.691L361.93,264.691C361.685,264.448 361.439,264.124 361.194,263.719C360.936,263.325 360.752,262.966 360.629,262.654L361.673,262.654C361.955,263.221 362.299,263.719 362.704,264.147C363.109,264.575 363.502,264.876 363.882,265.05L363.882,265.709L355.056,265.709Z"/>
<path android:fillColor="#FF000000" android:fillType="nonZero" android:pathData="M355.056,268.859L356.283,268.859L356.283,270.016L355.056,270.016C354.601,270.016 354.245,269.935 353.963,269.785C353.681,269.634 353.472,269.403 353.312,269.067L353.779,268.789C353.877,269.009 354.024,269.16 354.221,269.264C354.417,269.368 354.687,269.426 355.056,269.437L355.056,268.859Z"/>
<path android:fillColor="#FF000000" android:fillType="nonZero" android:pathData="M357.364,271.53L357.449,272.595C356.897,272.676 356.492,272.85 356.21,273.139C355.927,273.428 355.792,273.776 355.792,274.181C355.792,274.678 355.989,275.095 356.381,275.431C356.774,275.766 357.29,275.94 357.941,275.94C358.554,275.94 359.045,275.778 359.401,275.454C359.745,275.118 359.929,274.702 359.929,274.169C359.929,273.845 359.856,273.544 359.696,273.278C359.536,273.023 359.328,272.815 359.082,272.665L359.205,271.704L363.723,272.503L363.723,276.623L362.692,276.623L362.692,273.324L360.334,272.873C360.703,273.371 360.887,273.891 360.887,274.435C360.887,275.165 360.617,275.778 360.089,276.276C359.549,276.773 358.874,277.016 358.039,277.016C357.241,277.016 356.553,276.796 355.976,276.357C355.264,275.836 354.908,275.107 354.908,274.181C354.908,273.428 355.129,272.815 355.571,272.341C356.025,271.866 356.615,271.588 357.364,271.53Z"/>
<path android:fillColor="#FF000000" android:fillType="nonZero" android:pathData="M377.436,266.363L377.436,265.145L385.185,265.145C384.908,264.855 384.631,264.467 384.355,263.983C384.064,263.512 383.856,263.083 383.718,262.71L384.894,262.71C385.212,263.388 385.6,263.983 386.057,264.495C386.513,265.007 386.956,265.367 387.385,265.574L387.385,266.363L377.436,266.363Z"/>
<path android:fillColor="#FF000000" android:fillType="nonZero" android:pathData="M280.26,261.937L280.395,262.923C279.835,263.035 279.431,263.226 279.185,263.494C278.938,263.774 278.815,264.099 278.815,264.491C278.815,264.951 278.972,265.343 279.297,265.668C279.622,265.981 280.014,266.138 280.484,266.138C280.944,266.138 281.313,265.992 281.604,265.701C281.907,265.41 282.053,265.029 282.053,264.57C282.053,264.391 282.008,264.155 281.941,263.875L282.803,263.987C282.792,264.055 282.792,264.111 282.792,264.144C282.792,264.57 282.904,264.939 283.117,265.276C283.341,265.612 283.677,265.78 284.136,265.78C284.495,265.78 284.797,265.656 285.032,265.421C285.267,265.175 285.379,264.861 285.379,264.469C285.379,264.088 285.267,263.763 285.021,263.517C284.786,263.259 284.427,263.091 283.946,263.024L284.114,262.038C284.775,262.161 285.29,262.43 285.648,262.856C286.018,263.282 286.197,263.808 286.197,264.447C286.197,264.883 286.096,265.287 285.917,265.656C285.727,266.026 285.469,266.306 285.144,266.496C284.819,266.698 284.472,266.799 284.114,266.799C283.766,266.799 283.453,266.698 283.162,266.519C282.882,266.328 282.657,266.06 282.489,265.69C282.377,266.16 282.153,266.53 281.806,266.799C281.459,267.057 281.033,267.191 280.507,267.191C279.812,267.191 279.219,266.933 278.726,266.418C278.244,265.914 277.998,265.264 277.998,264.491C277.998,263.786 278.21,263.203 278.625,262.733C279.051,262.273 279.588,262.005 280.26,261.937Z"/>
<path android:fillColor="#FF000000" android:fillType="nonZero" android:pathData="M278.143,268.694L279.263,268.694L279.263,269.814L278.143,269.814C277.729,269.814 277.404,269.736 277.146,269.59C276.889,269.445 276.698,269.221 276.553,268.896L276.978,268.627C277.068,268.84 277.202,268.985 277.381,269.086C277.561,269.187 277.807,269.243 278.143,269.254L278.143,268.694Z"/>
<path android:fillColor="#FF000000" android:fillType="nonZero" android:pathData="M278.143,274.988L278.143,274.002L284.416,274.002C284.192,273.767 283.968,273.453 283.744,273.061C283.509,272.68 283.341,272.333 283.229,272.03L284.181,272.03C284.439,272.579 284.752,273.061 285.122,273.475C285.491,273.89 285.85,274.181 286.197,274.349L286.197,274.988L278.143,274.988Z"/>
<path android:fillColor="#FF000000" android:fillType="nonZero" android:pathData="M280.249,277.509L280.327,278.539C279.823,278.618 279.454,278.786 279.196,279.066C278.938,279.346 278.815,279.682 278.815,280.074C278.815,280.556 278.995,280.959 279.353,281.284C279.711,281.609 280.182,281.777 280.776,281.777C281.336,281.777 281.784,281.62 282.109,281.306C282.422,280.981 282.59,280.578 282.59,280.063C282.59,279.749 282.523,279.458 282.377,279.2C282.232,278.954 282.041,278.752 281.817,278.607L281.929,277.677L286.052,278.45L286.052,282.438L285.111,282.438L285.111,279.245L282.96,278.808C283.296,279.29 283.464,279.794 283.464,280.32C283.464,281.026 283.218,281.62 282.736,282.101C282.243,282.583 281.627,282.818 280.865,282.818C280.137,282.818 279.51,282.605 278.983,282.18C278.334,281.676 278.009,280.97 278.009,280.074C278.009,279.346 278.21,278.752 278.614,278.293C279.028,277.834 279.566,277.565 280.249,277.509Z"/>
<path android:fillColor="#FF000000" android:fillType="nonZero" android:pathData="M332.515,268.572L331.486,268.572L331.486,262.779C331.743,262.779 332,262.815 332.233,262.913C332.625,263.06 333.017,263.293 333.396,263.611C333.788,263.942 334.229,264.407 334.732,265.02C335.503,265.975 336.128,266.624 336.581,266.955C337.046,267.286 337.475,267.457 337.879,267.457C338.308,267.457 338.663,267.31 338.957,267.004C339.251,266.698 339.398,266.294 339.398,265.804C339.398,265.289 339.239,264.873 338.933,264.567C338.626,264.248 338.185,264.101 337.634,264.089L337.757,262.987C338.577,263.06 339.202,263.342 339.643,263.844C340.072,264.334 340.292,264.995 340.292,265.828C340.292,266.673 340.059,267.335 339.594,267.825C339.129,268.315 338.541,268.56 337.855,268.56C337.5,268.56 337.157,268.498 336.826,268.351C336.483,268.204 336.128,267.972 335.748,267.641C335.381,267.298 334.866,266.747 334.205,265.975C333.666,265.326 333.298,264.91 333.103,264.726C332.907,264.542 332.711,264.395 332.515,264.273L332.515,268.572Z"/>
<path android:fillColor="#FF000000" android:pathData="M269.728,231.777l165.621,0l0,2.547l-165.621,0z"/>
<path android:fillColor="#FF000000" android:pathData="M289.011,234.139l2.065,-0l0,22.125l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M281.931,233.313l2.065,0l0,13.275l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M353.793,233.752l2.064,0l0,13.275l-2.064,0z"/>
<path android:fillColor="#FF000000" android:pathData="M345.107,234.08l2.064,0l0,13.276l-2.064,0z"/>
<path android:fillColor="#FF000000" android:pathData="M335.764,234.244l2.065,0l0,22.125l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M326.096,233.751l2.065,0l0,13.275l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M316.774,233.043l2.065,0l0,13.275l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M298.012,233.869l2.065,0l0,13.275l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M307.334,233.397l2.065,0l0,13.275l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M428.196,233.26l2.065,0l0,22.125l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M418.856,234.243l2.065,0l0,13.275l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M410.334,234.243l2.065,0l0,13.274l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M400.992,234.571l2.065,0l0,13.275l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M391.159,233.914l2.064,0l0,13.275l-2.064,0z"/>
<path android:fillColor="#FF000000" android:pathData="M382.309,234.078l2.065,0l0,22.125l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M372.804,234.079l2.13,0l0,13.275l-2.13,0z"/>
<path android:fillColor="#FF000000" android:pathData="M363.606,234.125l2.065,0l0,13.275l-2.065,0z"/>
<path android:fillColor="#FF000000" android:pathData="M358.382,233.26l2.066,0l0,23.436l-2.066,0z"/>
</vector>

View file

@ -0,0 +1,456 @@
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".plugins.pump.combov2.ComboV2Fragment">
<LinearLayout
android:visibility="gone"
android:id="@+id/combov2_fragment_unpaired_ui"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="center"
android:text="@string/combov2_not_paired"
android:textSize="14sp" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingTop="10dp"
app:drawableTopCompat="@drawable/ic_combov2" />
</LinearLayout>
<RelativeLayout
android:visibility="visible"
android:id="@+id/combov2_fragment_main_ui"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/combov2_driver_state_label"
android:textSize="14sp" />
<TextView
android:id="@+id/combov2_driver_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/lastconnection_label"
android:textSize="14sp" />
<TextView
android:id="@+id/combov2_last_connection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/combov2_current_activity_label"
android:textSize="14sp" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/combov2_current_activity_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<ProgressBar
android:id="@+id/combov2_current_activity_progress"
android:layout_width="match_parent"
android:layout_height="4dip"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:max="100"
style="@android:style/Widget.ProgressBar.Horizontal"
/>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/battery_label"
android:textSize="14sp" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/combov2_battery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:text=""
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/reservoir_label"
android:textSize="14sp" />
<TextView
android:id="@+id/combov2_reservoir"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/lastbolus_label"
android:textSize="14sp" />
<TextView
android:id="@+id/combov2_last_bolus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/tempbasal_label"
android:textSize="14sp" />
<TextView
android:id="@+id/combov2_current_tbr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/basebasalrate_label"
android:textSize="14sp" />
<TextView
android:id="@+id/combov2_base_basal_rate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/serialnumber"
android:textSize="14sp" />
<TextView
android:id="@+id/combov2_pump_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/bluetooth_address"
android:textSize="14sp" />
<TextView
android:id="@+id/combov2_bluetooth_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:orientation="horizontal"
android:gravity="center">
<info.nightscout.androidaps.plugins.pump.combov2.ComboV2RTDisplayFrameView
android:id="@+id/combov2_rt_display_frame"
android:layout_width="192dp"
android:layout_height="64dp"
/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingTop="10dp"
app:drawableTopCompat="@drawable/ic_combov2" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<info.nightscout.androidaps.utils.ui.SingleClickButton
android:id="@+id/combov2_refresh_button"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.5"
android:drawableTop="@drawable/ic_actions_refill"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:text="@string/refresh" />
</LinearLayout>
</RelativeLayout>
</FrameLayout>
</layout>

View file

@ -0,0 +1,256 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".plugins.pump.combov2.activities.ComboV2PairingActivity">
<ScrollView
android:visibility="visible"
android:id="@+id/combov2_pairing_section_initial"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:singleLine="false"
android:text="@string/combov2_pairing_start_steps"
android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/combov2_start_pairing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/combov2_start_pairing" />
</LinearLayout>
</LinearLayout>
</ScrollView>
<ScrollView
android:visibility="gone"
android:id="@+id/combov2_pairing_section_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="16dp"
android:singleLine="false"
android:text="@string/combov2_pairing_in_progress"
android:textSize="16sp" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:background="@color/list_delimiter" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:padding="16dp"
android:singleLine="false"
android:text="@string/combov2_steps_if_no_connection_during_pairing"
android:textSize="16sp" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:background="@color/list_delimiter" />
<TextView
android:id="@+id/combov2_current_pairing_step_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:gravity="center"
android:padding="16dp"
android:background="@color/pumpStatusBackground"
android:text=""
android:textSize="16sp" />
<ProgressBar
android:id="@+id/combov2_pairing_progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_marginBottom="20dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:maxHeight="5dp"
android:minHeight="3dp"
android:max="100" />
<LinearLayout
android:id="@+id/combov2_pin_entry_ui"
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginBottom="20dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:text="@string/combov2_enter_pin"
android:textSize="16sp" />
<EditText
android:id="@+id/combov2_pin_entry_edit"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:maxLength="12"
android:inputType="number"
android:digits="0123456789 "
android:minHeight="48dp"
android:hint="@string/combov2_pin_hint"
android:autofillHints="@string/combov2_pin_entry_hint"
android:text="" />
<Button
android:id="@+id/combov2_enter_pin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok" />
</LinearLayout>
<TextView
android:id="@+id/combov2_pin_failure_indicator"
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:gravity="center"
android:text="@string/combov2_pairing_pin_failure"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/combov2_cancel_pairing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/combov2_cancel_pairing" />
</LinearLayout>
</LinearLayout>
</ScrollView>
<LinearLayout
android:visibility="gone"
android:id="@+id/combov2_pairing_section_finished"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:gravity="center"
android:text="@string/combov2_pairing_finished_successfully"
android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/combov2_pairing_finished_ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:visibility="gone"
android:id="@+id/combov2_pairing_section_aborted"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/combov2_pairing_aborted_reason_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:gravity="center"
android:text=""
android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/combov2_pairing_aborted"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</layout>

View file

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="combov2_plugin_name">Accu-Chek Combo (new plugin)</string>
<string name="combov2_plugin_shortname" translatable="false">COMBOV2</string>
<string name="combov2_plugin_description">Pump integration for Accu-Chek Combo pumps, successor to Ruffy (EXPERIMENTAL)</string>
<string name="combov2_could_not_connect">Could not connect to the pump</string>
<string name="combov2_not_paired">Not paired to a pump</string>
<string name="combov2_pump_terminated_connection">Pump terminated connection</string>
<string name="combov2_warning">Combo warning</string>
<string name="combov2_error">Combo error</string>
<string name="combov2_warning_4">Call hotline for update</string>
<string name="combov2_warning_10">Bluetooth fault; redo pairing</string>
<string name="combov2_error_1">Reservoir empty</string>
<string name="combov2_error_2">Battery empty</string>
<string name="combov2_error_4">Occlusion</string>
<string name="combov2_error_5">End of backup pump operation</string>
<string name="combov2_error_6">Mechanical error</string>
<string name="combov2_error_7">Electronic error</string>
<string name="combov2_error_8">Power interrupt</string>
<string name="combov2_error_9">End of loan pump operation</string>
<string name="combov2_error_10">Reservoir error</string>
<string name="combov2_error_11">Infusion set not primed</string>
<string name="combov2_extended_bolus_not_supported">Extended bolus is not supported</string>
<string name="combov2_title">Accu-Check Combo</string>
<string name="combov2_pair_with_pump_title">Pair with pump</string>
<string name="combov2_unpair_pump_title">Unpair pump</string>
<string name="combov2_driver_state_label">Driver state</string>
<string name="combov2_current_activity_label">Current activity</string>
<string name="bluetooth_address">Bluetooth address</string>
<string name="key_combov2_settings" translatable="false">combov2_settings</string>
<string name="key_combov2_pair_with_pump" translatable="false">combov2_pair_with_pump</string>
<string name="key_combov2_unpair_pump" translatable="false">combov2_unpair_pump</string>
<string name="combov2_start_pairing">Start pairing</string>
<string name="combov2_pairing_in_progress">Combo pairing in progress</string>
<string name="combov2_pairing_start_steps">Steps to perform pairing with your Combo:\n \n 1.
On your pump, navigate to the Bluetooth Settings\n 2. Check if a device is already shown
as paired; if so, go to "Delete device" screen to delete/unpair that device\n 3. Go to the
"Add device" screen, and initiate pairing\n 4. Click on the Start pairingbutton below</string>
<string name="combov2_steps_if_no_connection_during_pairing">If no connection is established
after more than ~5 minutes:\n \n 1. Press Back or the "Cancel Pairing" button\n 2. Cancel
the pairing on the Combo (press both UP and MENU buttons at the same time to cancel pairing)\n 3. Try to pair
again</string>
<string name="combov2_pin_entry_hint">0123456789</string>
<string name="combov2_enter_pin">Enter PIN</string>
<string name="combov2_cancel_pairing">Cancel pairing</string>
<string name="combov2_pin_hint">10-digit PIN</string>
<string name="combov2_pairing_finished_successfully">Successfully paired with Combo</string>
<string name="combov2_pairing_cancelled">Pairing with Combo cancelled by user</string>
<string name="combov2_pairing_combo_scan_timeout_reached">Combo scan timeout reached</string>
<string name="combov2_pairing_failed_due_to_error">Pairing failed due to error: %1$s</string>
<string name="combov2_pairing_aborted_unknown_reasons">Pairing aborted for unknown reasons</string>
<string name="combov2_scanning_for_pump">Scanning for pump</string>
<string name="combov2_establishing_bt_connection">Establishing Bluetooth connection (attempt no. %1$d)</string>
<string name="combov2_pairing_performing_handshake">Performing handshake with pump</string>
<string name="combov2_pairing_pump_requests_pin">Pump requests 10-digit PIN</string>
<string name="combov2_pairing_finishing">Finishing pairing</string>
<string name="combov2_no_connection_for_n_mins">No connection for %1$d minutes</string>
<string name="combov2_less_than_one_minute_ago">Less than 1 minute ago</string>
<string name="combov2_setting_current_pump_time">Setting current pump time</string>
<string name="combov2_setting_current_pump_date">Setting current pump date</string>
<string name="combov2_not_initialized">Not initialized</string>
<string name="combov2_checking_pump">Checking pump</string>
<string name="combov2_ready">Ready</string>
<string name="combov2_suspended">Suspended</string>
<string name="combov2_pump_is_suspended">Pump is suspended</string>
<string name="combov2_executing_command">Executing command</string>
<string name="combov2_getting_basal_profile_cmddesc">Getting basal profile</string>
<string name="combov2_setting_basal_profile_cmddesc">Setting basal profile</string>
<string name="combov2_setting_tbr_cmddesc">Setting %1$d%% TBR for %2$d minutes</string>
<string name="combov2_cancelling_tbr">Cancelling ongoing TBR</string>
<string name="combov2_delivering_bolus_cmddesc">Delivering %1$.1f U bolus</string>
<string name="combov2_fetching_tdd_history_cmddesc">Fetching TDD history</string>
<string name="combov2_updating_pump_datetime_cmddesc">Updating pump datetime</string>
<string name="combov2_updating_pump_status_cmddesc">Updating pump status</string>
<string name="combov2_pairing_pin_failure">PIN did not work. Check if there was a typo. If this
keeps happening, cancel and retry pairing.</string>
<string name="key_combov2_discovery_duration" translatable="false">combov2_discovery_duration</string>
<string name="combov2_discovery_duration">Discovery duration (in seconds)</string>
<string name="key_combov2_verbose_logging" translatable="false">combov2_verbose_logging</string>
<string name="combov2_verbose_logging">Enable verbose Combo logging</string>
<string name="combov2_getting_basal_profile">Getting basal profile; %1$d factor(s) read</string>
<string name="combov2_setting_basal_profile">Setting basal profile; %1$d factor(s) written</string>
<string name="combov2_delivering_bolus">Delivering bolus (%1$.1f of %2$.1f U delivered)</string>
<string name="combov2_cannot_deliver_pump_suspended">Cannot deliver treatment - pump is suspended</string>
<string name="combov2_insufficient_insulin_in_reservoir">Insufficient insulin in reservoir</string>
<string name="combov2_bolus_cancelled">Bolus cancelled</string>
<string name="combov2_bolus_delivery_failed">Bolus delivery failed. It appears no bolus was delivered. To be sure, please check the pump to avoid a double bolus and then bolus again. To guard against bugs, boluses are not automatically retried.</string>
<string name="combov2_bolus_not_delivered">Bolus not delivered</string>
<string name="combov2_cannot_access_pump_data">Cannot access pump data; the pump must be paired again</string>
<string name="combov2_unaccounted_bolus_detected_cancelling_bolus">Unaccounted bolus deliveries detected. Cancelling bolus for safety reasons.</string>
<string name="combov2_incorrect_active_basal_profile">Incorrect active basal profile; profile 1 must be the active one, not profile %1$d</string>
<string name="combov2_unrecognized_alert">Unrecognized Combo alert</string>
<string name="combov2_combo_alert">Combo alert</string>
<string name="combov2_last_bolus">%1$.1f %2$s (%3$s)</string>
<string name="combov2_current_tbr">%1$d%% (%2$d min remaining)</string>
<string name="combov2_current_tbr_less_than_1min">%1$d%% (less than 1 min remaining)</string>
<string name="combov2_load_tdds_cancelled">Loading TDDs cancelled</string>
<string name="combov2_retrieving_tdds_failed">Retrieving TDDs failed</string>
<string name="combov2_no_activity">{fa-bed}</string>
<string name="combov2_battery_empty_indicator">{fa-battery-empty}</string>
<string name="combov2_battery_low_indicator">{fa-battery-quarter}</string>
<string name="combov2_battery_full_indicator">{fa-battery-full}</string>
<string name="combov2_battery_low_warning">Pump battery is low</string>
<string name="combov2_reservoir_low_warning">Pump reservoir level is low</string>
<string name="combov2_setting_tbr_succeeded">Setting TBR succeeded</string>
<string name="combov2_setting_tbr_failed">Setting TBR failed</string>
<string name="combov2_cannot_set_absolute_tbr_if_basal_zero">Cannot set absolute TBR if base basal rate is zero</string>
<string name="combov2_pair_with_pump_summary">Pair AndroidAPS and Android with a currently unpaired Accu-Chek Combo pump</string>
<string name="combov2_unpair_pump_summary">Unpair AndroidAPS and Android from the currently paired Accu-Chek Combo pump</string>
<string name="combov2_unknown_tbr_detected">Unknown TBR was detected and stopped; percentage: %1$d%%; remaining duration: %2$s</string>
<string name="combov2_connection_error">Connection error: %1$s</string>
<string name="combov2_short_status_last_connection">LastConn: %1$d min ago</string>
<string name="combov2_short_status_alert">Alert: %s</string>
<string name="combov2_short_status_last_bolus">LastBolus: %1$sU @ %2$s</string>
<string name="combov2_short_status_temp_basal">Temp: %s</string>
<string name="combov2_short_status_reservoir">Reserv: %dU</string>
<string name="combov2_short_status_battery_state_empty">empty</string>
<string name="combov2_short_status_battery_state_low">low</string>
<string name="combov2_short_status_battery_state_full">full</string>
<string name="combov2_short_status_battery_state">Batt: %s</string>
</resources>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
android:key="@string/key_combov2_settings"
android:title="@string/combov2_title"
app:initialExpandedChildrenCount="0">
<Preference
android:key="@string/key_combov2_pair_with_pump"
android:title="@string/combov2_pair_with_pump_title"
android:summary="@string/combov2_pair_with_pump_summary"
android:shouldDisableView="true">
<intent
android:targetClass="info.nightscout.androidaps.plugins.pump.combov2.activities.ComboV2PairingActivity"
android:targetPackage="info.nightscout.androidaps" />
</Preference>
<Preference
android:title="@string/combov2_unpair_pump_title"
android:summary="@string/combov2_unpair_pump_summary"
android:key="@string/key_combov2_unpair_pump"
android:shouldDisableView="true"/>
<SeekBarPreference
android:key="@string/key_combov2_discovery_duration"
android:title="@string/combov2_discovery_duration"
android:min="30"
android:max="300"
android:inputType="number"
android:defaultValue="300" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_combov2_verbose_logging"
android:title="@string/combov2_verbose_logging" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>

View file

@ -9,6 +9,7 @@ include ':ui'
include ':implementation'
include ':plugins'
include ':pump:combo'
include ':pump:combov2'
include ':pump:combov2:comboctl'
include ':pump:dana'
include ':pump:danar'