Merge remote-tracking branch 'Nightscout/dev' into Autotune/TuneWeekDaysClean

This commit is contained in:
Philoul 2023-06-03 23:12:01 +02:00
commit 0ce6d8d0d6
315 changed files with 11286 additions and 2626 deletions

View file

@ -5,7 +5,7 @@ version: 2.1
# Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects. # Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.
orbs: orbs:
android: circleci/android@1.0.3 android: circleci/android@1.0.3
codecov: codecov/codecov@1.2.0 codecov: codecov/codecov@3.2.4
jobs: jobs:
# Below is the definition of your job to build and test your app, you can rename and customize it as you want. # Below is the definition of your job to build and test your app, you can rename and customize it as you want.

View file

@ -34,7 +34,7 @@ dependencies {
api 'org.slf4j:slf4j-api:1.7.36' // 2.0.x breaks logging. Code change needed api 'org.slf4j:slf4j-api:1.7.36' // 2.0.x breaks logging. Code change needed
api 'com.github.tony19:logback-android:2.0.0' api 'com.github.tony19:logback-android:2.0.0'
api "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version" api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinx_serialization_version"
api "org.apache.commons:commons-lang3:$commonslang3_version" api "org.apache.commons:commons-lang3:$commonslang3_version"
//RxBus //RxBus

View file

@ -3,6 +3,7 @@ package info.nightscout.rx.weardata
import info.nightscout.rx.events.Event import info.nightscout.rx.events.Event
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.joda.time.DateTime
import java.util.Objects import java.util.Objects
@Serializable @Serializable
@ -90,6 +91,16 @@ sealed class EventData : Event() {
@Serializable @Serializable
data class ActionQuickWizardPreCheck(val guid: String) : EventData() data class ActionQuickWizardPreCheck(val guid: String) : EventData()
@Serializable
data class ActionHeartRate(
val duration: Long,
val timestamp: Long,
val beatsPerMinute: Double,
val device: String): EventData() {
override fun toString() =
"HR ${beatsPerMinute.toInt()} at ${DateTime(timestamp)} for ${duration / 1000.0}sec $device"
}
@Serializable @Serializable
data class ActionTempTargetPreCheck( data class ActionTempTargetPreCheck(
val command: TempTargetCommand, val command: TempTargetCommand,

View file

@ -16,8 +16,12 @@ fun PackageManager.safeGetInstalledPackages(flags: Int): List<PackageInfo> =
* Safe version of queryBroadcastReceivers depending on Android version running * Safe version of queryBroadcastReceivers depending on Android version running
*/ */
fun PackageManager.safeQueryBroadcastReceivers(intent: Intent, flags: Int): List<ResolveInfo> = fun PackageManager.safeQueryBroadcastReceivers(intent: Intent, flags: Int): List<ResolveInfo> =
try {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) queryBroadcastReceivers(intent, PackageManager.ResolveInfoFlags.of(flags.toLong())) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) queryBroadcastReceivers(intent, PackageManager.ResolveInfoFlags.of(flags.toLong()))
else @Suppress("DEPRECATION") queryBroadcastReceivers(intent, flags) else @Suppress("DEPRECATION") queryBroadcastReceivers(intent, flags)
} catch (ignored: Exception) {
emptyList()
}
/** /**
* Safe version of getPackageInfo depending on Android version running * Safe version of getPackageInfo depending on Android version running

View file

@ -16,8 +16,6 @@ import androidx.annotation.RawRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
interface ResourceHelper { interface ResourceHelper {
fun updateContext(ctx: Context?)
fun gs(@StringRes id: Int): String fun gs(@StringRes id: Int): String
fun gs(@StringRes id: Int, vararg args: Any?): String fun gs(@StringRes id: Int, vararg args: Any?): String
fun gq(@PluralsRes id: Int, quantity: Int, vararg args: Any?): String fun gq(@PluralsRes id: Int, quantity: Int, vararg args: Any?): String

View file

@ -111,7 +111,7 @@ android {
defaultConfig { defaultConfig {
multiDexEnabled true multiDexEnabled true
versionCode 1500 versionCode 1500
version "3.1.0.3-dev-h" version "3.2.0-dev-j"
buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
@ -234,7 +234,7 @@ dependencies {
kapt "com.google.dagger:dagger-compiler:$dagger_version" kapt "com.google.dagger:dagger-compiler:$dagger_version"
// MainApp // MainApp
api "com.uber.rxdogtag2:rxdogtag:2.0.1" api "com.uber.rxdogtag2:rxdogtag:2.0.2"
} }

View file

@ -24,6 +24,7 @@ import android.widget.TextView
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.core.view.MenuCompat
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
@ -302,7 +303,7 @@ class MainActivity : DaggerAppCompatActivityWithResult() {
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu) super.onCreateOptionsMenu(menu)
menu.setGroupDividerEnabled(true) MenuCompat.setGroupDividerEnabled(menu, true)
this.menu = menu this.menu = menu
menuInflater.inflate(R.menu.menu_main, menu) menuInflater.inflate(R.menu.menu_main, menu)
pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences) pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences)

View file

@ -11,9 +11,9 @@ import android.widget.TextView
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
import com.jjoe64.graphview.GraphView import com.jjoe64.graphview.GraphView
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding
import info.nightscout.core.events.EventIobCalculationProgress import info.nightscout.core.events.EventIobCalculationProgress
import info.nightscout.core.ui.activities.TranslatedDaggerAppCompatActivity
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.core.workflow.CalculationWorkflow import info.nightscout.core.workflow.CalculationWorkflow
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
@ -42,7 +42,7 @@ import java.util.GregorianCalendar
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.min import kotlin.math.min
class HistoryBrowseActivity : DaggerAppCompatActivity() { class HistoryBrowseActivity : TranslatedDaggerAppCompatActivity() {
@Inject lateinit var historyBrowserData: HistoryBrowserData @Inject lateinit var historyBrowserData: HistoryBrowserData
@Inject lateinit var injector: HasAndroidInjector @Inject lateinit var injector: HasAndroidInjector
@ -327,6 +327,7 @@ class HistoryBrowseActivity : DaggerAppCompatActivity() {
var useRatioForScale = false var useRatioForScale = false
var useDSForScale = false var useDSForScale = false
var useBGIForScale = false var useBGIForScale = false
var useHRForScale = false
when { when {
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
@ -335,6 +336,7 @@ class HistoryBrowseActivity : DaggerAppCompatActivity() {
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.HR.ordinal] -> useHRForScale = true
} }
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
@ -345,6 +347,7 @@ class HistoryBrowseActivity : DaggerAppCompatActivity() {
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8) if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8)
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(useRatioForScale, if (useRatioForScale) 1.0 else 0.8) if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(useRatioForScale, if (useRatioForScale) 1.0 else 0.8)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && config.isDev()) secondGraphData.addDeviationSlope(useDSForScale, 1.0) if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && config.isDev()) secondGraphData.addDeviationSlope(useDSForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.HR.ordinal] && config.isDev()) secondGraphData.addHeartRate(useHRForScale, 1.0)
// set manual x bounds to have nice steps // set manual x bounds to have nice steps
secondGraphData.formatAxis(historyBrowserData.overviewData.fromTime, historyBrowserData.overviewData.endTime) secondGraphData.formatAxis(historyBrowserData.overviewData.fromTime, historyBrowserData.overviewData.endTime)

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.activities package info.nightscout.androidaps.activities
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -10,7 +9,6 @@ import androidx.preference.PreferenceScreen
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.ActivityPreferencesBinding import info.nightscout.androidaps.databinding.ActivityPreferencesBinding
import info.nightscout.configuration.activities.DaggerAppCompatActivityWithResult import info.nightscout.configuration.activities.DaggerAppCompatActivityWithResult
import info.nightscout.core.ui.locale.LocaleHelper
class PreferencesActivity : DaggerAppCompatActivityWithResult(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback { class PreferencesActivity : DaggerAppCompatActivityWithResult(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
@ -66,10 +64,6 @@ class PreferencesActivity : DaggerAppCompatActivityWithResult(), PreferenceFragm
return true return true
} }
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.wrap(newBase))
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) { when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {

View file

@ -44,6 +44,8 @@ class ConfigImpl @Inject constructor(
engineeringMode = engineeringModeSemaphore.exists() && engineeringModeSemaphore.isFile engineeringMode = engineeringModeSemaphore.exists() && engineeringModeSemaphore.isFile
unfinishedMode = unfinishedModeSemaphore.exists() && unfinishedModeSemaphore.isFile unfinishedMode = unfinishedModeSemaphore.exists() && unfinishedModeSemaphore.isFile
devBranch = BuildConfig.VERSION.contains("-") || BuildConfig.VERSION.matches(Regex(".*[a-zA-Z]+.*")) devBranch = BuildConfig.VERSION.contains("-") || BuildConfig.VERSION.matches(Regex(".*[a-zA-Z]+.*"))
if (BuildConfig.VERSION.contains("-beta") || BuildConfig.VERSION.contains("-rc"))
devBranch = false
} }
override fun isEngineeringModeOrRelease(): Boolean = override fun isEngineeringModeOrRelease(): Boolean =

View file

@ -191,7 +191,7 @@ class KeepAliveWorker(
} }
if (loop.isDisconnected) { if (loop.isDisconnected) {
// do nothing if pump is disconnected // do nothing if pump is disconnected
} else if (runningProfile == null || ((!pump.isThisProfileSet(requestedProfile) || !requestedProfile.isEqual(runningProfile)) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE))) { } else if (runningProfile == null || ((!pump.isThisProfileSet(requestedProfile) || !requestedProfile.isEqual(runningProfile) || (runningProfile is ProfileSealed.EPS && runningProfile.value.originalEnd < dateUtil.now())) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE))) {
rxBus.send(EventProfileSwitchChanged()) rxBus.send(EventProfileSwitchChanged())
} else if (isStatusOutdated && !pump.isBusy()) { } else if (isStatusOutdated && !pump.isBusy()) {
lastReadStatus = now lastReadStatus = now

View file

@ -21,7 +21,7 @@
<string name="ns_announcements">Създаване на известия от NS съобщения</string> <string name="ns_announcements">Създаване на известия от NS съобщения</string>
<string name="ns_alarm_stale_data_value_label">Когато няма данни повече от [мин]</string> <string name="ns_alarm_stale_data_value_label">Когато няма данни повече от [мин]</string>
<string name="ns_alarm_urgent_stale_data_value_label">Много стари данни при повече от [мин]</string> <string name="ns_alarm_urgent_stale_data_value_label">Много стари данни при повече от [мин]</string>
<string name="sensitivity_warning">Когато включите Autosense feature трябва да въвеждате ВСИЧКИ въглехидрати. В противен случай те ще се изчисляват грешно като повишена чувствителност!!</string> <string name="sensitivity_warning">Когато включите Autosense, трябва да въвеждате ВСИЧКИ въглехидрати. В противен случай те ще се изчисляват грешно, като повишена чувствителност!!</string>
<string name="notloadedplugins">Не всички профили са заредени!</string> <string name="notloadedplugins">Не всички профили са заредени!</string>
<string name="valuesnotstored">Стойностите не са запазени!</string> <string name="valuesnotstored">Стойностите не са запазени!</string>
<string name="invalid">НЕВАЛИДНО</string> <string name="invalid">НЕВАЛИДНО</string>

View file

@ -2,41 +2,40 @@
buildscript { buildscript {
ext { ext {
kotlin_version = '1.8.10' kotlin_version = '1.8.21'
core_version = '1.9.0' core_version = '1.10.1'
rxjava_version = '3.1.6' rxjava_version = '3.1.6'
rxandroid_version = '3.0.2' rxandroid_version = '3.0.2'
rxkotlin_version = '3.0.1' rxkotlin_version = '3.0.1'
room_version = '2.5.0' room_version = '2.5.1'
lifecycle_version = '2.5.1' lifecycle_version = '2.6.1'
dagger_version = '2.45' dagger_version = '2.46.1'
coroutines_version = '1.6.4' coroutines_version = '1.7.1'
activity_version = '1.6.1' activity_version = '1.7.2'
fragmentktx_version = '1.5.5' fragmentktx_version = '1.5.7'
ormLite_version = '4.46' ormLite_version = '4.46'
gson_version = '2.10.1' gson_version = '2.10.1'
nav_version = '2.5.3' nav_version = '2.5.3'
appcompat_version = '1.6.1' appcompat_version = '1.6.1'
material_version = '1.8.0' material_version = '1.9.0'
gridlayout_version = '1.0.0' gridlayout_version = '1.0.0'
constraintlayout_version = '2.1.4' constraintlayout_version = '2.1.4'
preferencektx_version = '1.2.0' preferencektx_version = '1.2.0'
commonslang3_version = '3.12.0' commonslang3_version = '3.12.0'
commonscodec_version = '1.15' commonscodec_version = '1.15'
jodatime_version = '2.10.14' jodatime_version = '2.10.14'
work_version = '2.8.0' work_version = '2.8.1'
tink_version = '1.8.0' tink_version = '1.9.0'
json_version = '20220320' json_version = '20220320'
serialization_version = '1.4.1' joda_version = '2.12.5'
joda_version = '2.12.1.1'
swipe_version = '1.1.0' swipe_version = '1.1.0'
junit_version = '4.13.2' junit_version = '4.13.2'
junit_jupiter_version = '5.9.2' junit_jupiter_version = '5.9.3'
mockito_version = '4.6.1' mockito_version = '4.6.1'
dexmaker_version = '1.2' dexmaker_version = '1.2'
retrofit2_version = '2.9.0' retrofit2_version = '2.9.0'
okhttp3_version = '4.10.0' okhttp3_version = '4.11.0'
byteBuddy_version = '1.12.8' byteBuddy_version = '1.12.8'
androidx_junit_version = '1.1.4' androidx_junit_version = '1.1.4'
@ -50,7 +49,7 @@ buildscript {
play_services_location_version = '21.0.1' play_services_location_version = '21.0.1'
kotlinx_datetime_version = '0.4.0' kotlinx_datetime_version = '0.4.0'
kotlinx_serialization_core_version = '1.4.1' kotlinx_serialization_version = '1.5.1'
} }
repositories { repositories {
google() google()
@ -58,9 +57,9 @@ buildscript {
maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2 maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.4.1' classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'com.google.gms:google-services:4.3.14' classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
@ -76,7 +75,7 @@ buildscript {
plugins { plugins {
// Test Gradle build, keep disabled under normal circumstances // Test Gradle build, keep disabled under normal circumstances
// id "com.osacky.doctor" version "0.8.1" // id "com.osacky.doctor" version "0.8.1"
id "org.jlleitschuh.gradle.ktlint" version "11.2.0" id "org.jlleitschuh.gradle.ktlint" version "11.3.2"
id 'org.barfuin.gradle.jacocolog' version '3.1.0' id 'org.barfuin.gradle.jacocolog' version '3.1.0'
id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false
} }

View file

@ -150,4 +150,7 @@ interface OverviewData {
val dsMinScale: Scale val dsMinScale: Scale
var dsMaxSeries: LineGraphSeries<ScaledDataPoint> var dsMaxSeries: LineGraphSeries<ScaledDataPoint>
var dsMinSeries: LineGraphSeries<ScaledDataPoint> var dsMinSeries: LineGraphSeries<ScaledDataPoint>
var heartRateScale: Scale
var heartRateGraphSeries: LineGraphSeries<DataPointWithLabelInterface>
} }

View file

@ -0,0 +1,24 @@
package info.nightscout.core.graph.data
import android.content.Context
import android.graphics.Paint
import info.nightscout.database.entities.HeartRate
import info.nightscout.shared.interfaces.ResourceHelper
class HeartRateDataPoint(
private val data: HeartRate,
private val rh: ResourceHelper,
) : DataPointWithLabelInterface {
override fun getX(): Double = (data.timestamp - data.duration).toDouble()
override fun getY(): Double = data.beatsPerMinute
override fun setY(y: Double) {}
override val label: String = ""
override val duration = data.duration
override val shape = PointsWithLabelGraphSeries.Shape.HEARTRATE
override val size = 1f
override val paintStyle: Paint.Style = Paint.Style.FILL
override fun color(context: Context?): Int = rh.gac(context, info.nightscout.core.ui.R.attr.heartRateColor)
}

View file

@ -54,7 +54,8 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
GENERAL_WITH_DURATION, GENERAL_WITH_DURATION,
COB_FAIL_OVER, COB_FAIL_OVER,
IOB_PREDICTION, IOB_PREDICTION,
BUCKETED_BG BUCKETED_BG,
HEARTRATE,
} }
/** /**
@ -324,6 +325,10 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
mPaint.setStrokeWidth(5); mPaint.setStrokeWidth(5);
canvas.drawRect(endX - 3, bounds.top + py - 3, xPlusLength + 3, bounds.bottom + py + 3, mPaint); canvas.drawRect(endX - 3, bounds.top + py - 3, xPlusLength + 3, bounds.bottom + py + 3, mPaint);
} }
} else if (value.getShape() == Shape.HEARTRATE) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(0);
canvas.drawCircle(endX, endY, 1F, mPaint);
} }
// set values above point // set values above point
} }

View file

@ -9,6 +9,8 @@ import info.nightscout.interfaces.userEntry.ValueWithUnitMapper
interface UserEntryLogger { interface UserEntryLogger {
fun log(action: Action, source: Sources, note: String?, timestamp: Long, vararg listValues: ValueWithUnit?)
fun log(action: Action, source: Sources, note: String?, timestamp: Long, listValues: List<ValueWithUnit?>)
fun log(action: Action, source: Sources, note: String? = "", vararg listValues: ValueWithUnit?) fun log(action: Action, source: Sources, note: String? = "", vararg listValues: ValueWithUnit?)
fun log(action: Action, source: Sources, vararg listValues: ValueWithUnit?) fun log(action: Action, source: Sources, vararg listValues: ValueWithUnit?)
fun log(action: Action, source: Sources, note: String? = "", listValues: List<ValueWithUnit?> = listOf()) fun log(action: Action, source: Sources, note: String? = "", listValues: List<ValueWithUnit?> = listOf())

View file

@ -133,6 +133,7 @@ open class Notification {
const val EOELOW_PATCH_ALERTS = 79 const val EOELOW_PATCH_ALERTS = 79
const val COMBO_PUMP_SUSPENDED = 80 const val COMBO_PUMP_SUSPENDED = 80
const val COMBO_UNKNOWN_TBR = 81 const val COMBO_UNKNOWN_TBR = 81
const val BLUETOOTH_NOT_ENABLED = 82
const val USER_MESSAGE = 1000 const val USER_MESSAGE = 1000

View file

@ -43,7 +43,12 @@ interface StoreDataForDb {
val nsIdDeviceStatuses: MutableList<DeviceStatus> val nsIdDeviceStatuses: MutableList<DeviceStatus>
val nsIdFoods: MutableList<Food> val nsIdFoods: MutableList<Food>
val deleteTreatment: MutableList<String>
val deleteGlucoseValue: MutableList<String>
fun updateDeletedGlucoseValuesInDb()
fun storeTreatmentsToDb() fun storeTreatmentsToDb()
fun updateDeletedTreatmentsInDb()
fun storeGlucoseValuesToDb() fun storeGlucoseValuesToDb()
fun storeFoodsToDb() fun storeFoodsToDb()
fun scheduleNsIdUpdate() fun scheduleNsIdUpdate()

View file

@ -15,7 +15,8 @@ interface OverviewMenus {
BGI, BGI,
SEN, SEN,
ACT, ACT,
DEVSLOPE DEVSLOPE,
HR,
} }
val setting: List<Array<Boolean>> val setting: List<Array<Boolean>>

View file

@ -25,7 +25,6 @@ interface CommandQueue {
fun extendedBolus(insulin: Double, durationInMinutes: Int, callback: Callback?): Boolean fun extendedBolus(insulin: Double, durationInMinutes: Int, callback: Callback?): Boolean
fun cancelTempBasal(enforceNew: Boolean, callback: Callback?): Boolean fun cancelTempBasal(enforceNew: Boolean, callback: Callback?): Boolean
fun cancelExtended(callback: Callback?): Boolean fun cancelExtended(callback: Callback?): Boolean
fun setProfile(profile: Profile, hasNsId: Boolean, callback: Callback?): Boolean
fun readStatus(reason: String, callback: Callback?): Boolean fun readStatus(reason: String, callback: Callback?): Boolean
fun statusInQueue(): Boolean fun statusInQueue(): Boolean
fun loadHistory(type: Byte, callback: Callback?): Boolean fun loadHistory(type: Byte, callback: Callback?): Boolean

View file

@ -23,7 +23,7 @@ dependencies {
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
api 'com.google.guava:guava:31.1-jre' api 'com.google.guava:guava:32.0.0-jre'
api "androidx.activity:activity-ktx:$activity_version" api "androidx.activity:activity-ktx:$activity_version"
api "androidx.appcompat:appcompat:$appcompat_version" api "androidx.appcompat:appcompat:$appcompat_version"

View file

@ -34,5 +34,5 @@ dependencies {
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
api "org.jetbrains.kotlinx:kotlinx-coroutines-rx3:$coroutines_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-rx3:$coroutines_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version" api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinx_serialization_version"
} }

View file

@ -292,14 +292,11 @@ class NSAndroidClientImpl(
lastModified = response.body()?.lastModified lastModified = response.body()?.lastModified
) )
} else throw UnknownResponseNightscoutException() } else throw UnknownResponseNightscoutException()
} else if (response.code() in 400..499) { } else return@callWrapper CreateUpdateResponse(
return@callWrapper CreateUpdateResponse(
response = response.code(), response = response.code(),
identifier = null, identifier = null,
errorResponse = response.errorBody()?.string() ?: response.message() errorResponse = response.errorBody()?.string() ?: response.message()
) )
} else
throw UnsuccessfullNightscoutException(response.errorBody()?.string() ?: response.message())
} }
override suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse = callWrapper(dispatcher) { override suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse = callWrapper(dispatcher) {

View file

@ -45,7 +45,7 @@ internal fun RemoteTreatment.toTreatment(): NSTreatment? {
subject = this.subject, subject = this.subject,
isReadOnly = this.isReadOnly ?: false, isReadOnly = this.isReadOnly ?: false,
isValid = this.isValid ?: true, isValid = this.isValid ?: true,
eventType = this.eventType, eventType = this.eventType ?: EventType.MEAL_BOLUS,
notes = this.notes, notes = this.notes,
pumpId = this.pumpId, pumpId = this.pumpId,
endId = this.endId, endId = this.endId,
@ -68,7 +68,7 @@ internal fun RemoteTreatment.toTreatment(): NSTreatment? {
subject = this.subject, subject = this.subject,
isReadOnly = this.isReadOnly ?: false, isReadOnly = this.isReadOnly ?: false,
isValid = this.isValid ?: true, isValid = this.isValid ?: true,
eventType = this.eventType, eventType = this.eventType ?: EventType.CARBS_CORRECTION,
notes = this.notes, notes = this.notes,
pumpId = this.pumpId, pumpId = this.pumpId,
endId = this.endId, endId = this.endId,

View file

@ -29,7 +29,7 @@ internal data class RemoteTreatment(
@SerializedName("modifiedBy") val modifiedBy: String? = null, // string Name of the security subject (within Nightscout scope) which has patched or deleted the document for the last time. This field is automatically set by the server. @SerializedName("modifiedBy") val modifiedBy: String? = null, // string Name of the security subject (within Nightscout scope) which has patched or deleted the document for the last time. This field is automatically set by the server.
@SerializedName("isValid") val isValid: Boolean? = null, // boolean A flag set by the server only for deleted documents. This field appears only within history operation and for documents which were deleted by API v3 (and they always have a false value) @SerializedName("isValid") val isValid: Boolean? = null, // boolean A flag set by the server only for deleted documents. This field appears only within history operation and for documents which were deleted by API v3 (and they always have a false value)
@SerializedName("isReadOnly") val isReadOnly: Boolean? = null, // boolean A flag set by client that locks the document from any changes. Every document marked with isReadOnly=true is forever immutable and cannot even be deleted. @SerializedName("isReadOnly") val isReadOnly: Boolean? = null, // boolean A flag set by client that locks the document from any changes. Every document marked with isReadOnly=true is forever immutable and cannot even be deleted.
@SerializedName("eventType") val eventType: EventType, // string "BG Check", "Snack Bolus", "Meal Bolus", "Correction Bolus", "Carb Correction", "Combo Bolus", "Announcement", "Note", "Question", "Exercise", "Site Change", "Sensor Start", "Sensor Change", "Pump Battery Change", "Insulin Change", "Temp Basal", "Profile Switch", "D.A.D. Alert", "Temporary Target", "OpenAPS Offline", "Bolus Wizard" @SerializedName("eventType") val eventType: EventType?, // string "BG Check", "Snack Bolus", "Meal Bolus", "Correction Bolus", "Carb Correction", "Combo Bolus", "Announcement", "Note", "Question", "Exercise", "Site Change", "Sensor Start", "Sensor Change", "Pump Battery Change", "Insulin Change", "Temp Basal", "Profile Switch", "D.A.D. Alert", "Temporary Target", "OpenAPS Offline", "Bolus Wizard"
@SerializedName("glucose") val glucose: Double? = null, // double Current glucose @SerializedName("glucose") val glucose: Double? = null, // double Current glucose
@SerializedName("glucoseType") val glucoseType: String? = null, // string example: "Sensor", "Finger", "Manual" @SerializedName("glucoseType") val glucoseType: String? = null, // string example: "Sensor", "Finger", "Manual"
@SerializedName("units") val units: String? = null, // string The units for the glucose value, mg/dl or mmol/l. It is strongly recommended to fill in this field. @SerializedName("units") val units: String? = null, // string The units for the glucose value, mg/dl or mmol/l. It is strongly recommended to fill in this field.

View file

@ -4,7 +4,7 @@ import android.content.Context
import dagger.android.support.DaggerAppCompatActivity import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.core.ui.locale.LocaleHelper import info.nightscout.core.ui.locale.LocaleHelper
open class DialogAppCompatActivity : DaggerAppCompatActivity() { open class TranslatedDaggerAppCompatActivity : DaggerAppCompatActivity() {
override fun attachBaseContext(newBase: Context) { override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.wrap(newBase)) super.attachBaseContext(LocaleHelper.wrap(newBase))
} }

View file

@ -2,19 +2,21 @@ package info.nightscout.core.ui.locale
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.res.Configuration
import android.os.LocaleList import android.os.LocaleList
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import info.nightscout.core.ui.R import info.nightscout.core.ui.R
import java.util.Locale import java.util.Locale
object LocaleHelper { object LocaleHelper {
private fun selectedLanguage(context: Context): String = private fun selectedLanguage(context: Context): String =
PreferenceManager.getDefaultSharedPreferences(context).getString(context.getString(R.string.key_language), "default") PreferenceManager.getDefaultSharedPreferences(context).getString(context.getString(R.string.key_language), "default")
?: "default" ?: "default"
// injection not possible because of use in attachBaseContext // injection not possible because of use in attachBaseContext
//SP.getString(R.string.key_language, Locale.getDefault().language) //SP.getString(R.string.key_language, Locale.getDefault().language)
private fun currentLocale(context: Context): Locale { fun currentLocale(context: Context): Locale {
val language = selectedLanguage(context) val language = selectedLanguage(context)
if (language == "default") return Locale.getDefault() if (language == "default") return Locale.getDefault()
@ -34,18 +36,13 @@ object LocaleHelper {
val locale = currentLocale(context) val locale = currentLocale(context)
Locale.setDefault(locale) Locale.setDefault(locale)
val resources = context.resources
val configuration = resources.configuration
context.createConfigurationContext(configuration)
configuration.setLocale(locale)
} }
fun wrap(ctx: Context): Context { fun wrap(ctx: Context): Context {
// no action for system default language // no action for system default language
if (selectedLanguage(ctx) == "default") return ctx if (selectedLanguage(ctx) == "default") return ctx
val res = ctx.resources val configuration = Configuration()
val configuration = res.configuration
val newLocale = currentLocale(ctx) val newLocale = currentLocale(ctx)
configuration.setLocale(newLocale) configuration.setLocale(newLocale)
val localeList = LocaleList(newLocale) val localeList = LocaleList(newLocale)

View file

@ -164,7 +164,6 @@
<string name="isf_short">Чувств</string> <string name="isf_short">Чувств</string>
<string name="canceling_tbr_failed">Отмяната на временния базал е неуспешно</string> <string name="canceling_tbr_failed">Отмяната на временния базал е неуспешно</string>
<string name="canceling_eb_failed">Неуспешно спиране на удължен болус</string> <string name="canceling_eb_failed">Неуспешно спиране на удължен болус</string>
<string name="virtualpump_uploadstatus_title">Качване на статус в NS или Tidepool</string>
<string name="suspendloop_label">Изключен/забранен цикъл</string> <string name="suspendloop_label">Изключен/забранен цикъл</string>
<string name="iob_label">Активен Инсулин (IOB)</string> <string name="iob_label">Активен Инсулин (IOB)</string>
<!-- Protection--> <!-- Protection-->
@ -298,7 +297,6 @@
<string name="uel_site_change">СМЯНА КАНЮЛА</string> <string name="uel_site_change">СМЯНА КАНЮЛА</string>
<string name="uel_reservoir_change">СМЯНА НА РЕЗЕРВОАР</string> <string name="uel_reservoir_change">СМЯНА НА РЕЗЕРВОАР</string>
<string name="uel_calibration">КАЛИБРАЦИЯ</string> <string name="uel_calibration">КАЛИБРАЦИЯ</string>
<string name="uel_prime_bolus">БОЛУС ПЪЛНЕНЕНЕ</string>
<string name="uel_treatment">ЛЕЧЕНИЕ</string> <string name="uel_treatment">ЛЕЧЕНИЕ</string>
<string name="uel_careportal_ns_refresh">ОБНОВИ ПОРТАЛА ЗА ЛЕЧЕНИЯ NS</string> <string name="uel_careportal_ns_refresh">ОБНОВИ ПОРТАЛА ЗА ЛЕЧЕНИЯ NS</string>
<string name="uel_profile_switch_ns_refresh">ОБНОВИ СМЯНАТА НА ПРОФИЛ NS</string> <string name="uel_profile_switch_ns_refresh">ОБНОВИ СМЯНАТА НА ПРОФИЛ NS</string>
@ -463,10 +461,10 @@
<string name="wizard_carbs_constraint">Нарушение на въхлехидратните ограничения!</string> <string name="wizard_carbs_constraint">Нарушение на въхлехидратните ограничения!</string>
<string name="wizard_explain_calc">Изчисляване (въглехидратно число: %1$.1f, инсулинова чувствителност: %2$.1f)</string> <string name="wizard_explain_calc">Изчисляване (въглехидратно число: %1$.1f, инсулинова чувствителност: %2$.1f)</string>
<string name="wizard_explain_carbs">Въглехидрати: %1$.2fЕ</string> <string name="wizard_explain_carbs">Въглехидрати: %1$.2fЕ</string>
<string name="wizard_explain_cob">Остатъчни въглехидрати: %1$.0fg %2$.2fU</string> <string name="wizard_explain_cob">Активни въглехидрати: %1$.0fгр %2$.2fЕ</string>
<string name="wizard_explain_bg">Кръвна захар: %1$.2fU</string> <string name="wizard_explain_bg">КЗ: %1$.2fЕ</string>
<string name="wizard_explain_iob">Остатъчен инсулин: %1$.2fU</string> <string name="wizard_explain_iob">Остатъчен инсулин: %1$.2fU</string>
<string name="wizard_explain_superbolus">Суперболус: %1$.2fU</string> <string name="wizard_explain_superbolus">Суперболус: %1$.2fЕ</string>
<string name="wizard_explain_trend">15\' тенденция: %1$.2fЕ</string> <string name="wizard_explain_trend">15\' тенденция: %1$.2fЕ</string>
<string name="wizard_explain_percent">Проценти: %1$.2fЕ x %2$d%% ≈ %3$.2fЕ</string> <string name="wizard_explain_percent">Проценти: %1$.2fЕ x %2$d%% ≈ %3$.2fЕ</string>
<string name="wizard_constraint_bolus_size">Нарушение в ограниичението на инсулин!\nНе може да бъде доставен %1$.2fЕ</string> <string name="wizard_constraint_bolus_size">Нарушение в ограниичението на инсулин!\nНе може да бъде доставен %1$.2fЕ</string>
@ -572,7 +570,7 @@
<item quantity="other">%1$d минути</item> <item quantity="other">%1$d минути</item>
</plurals> </plurals>
<!-- Maintenance--> <!-- Maintenance-->
<string name="cleanup_db_confirm">Искате ли да почистите базата данни\nТова ще премахне проследените промени и историята на данните, по-стари от 3 месеца.</string> <string name="cleanup_db_confirm">Искате ли да изтриете базата данни\nТова ще премахне проследените промени и историята на данните, по-стари от 3 месеца.</string>
<string name="cleanup_db_confirm_sync">Искате ли да изтриете базата данни?\nТова ще премахне проследените промени и историята на данните, по-стари от 3 месеца.\nИзвършването му ще ускори драстично пълната синхронизация.</string> <string name="cleanup_db_confirm_sync">Искате ли да изтриете базата данни?\nТова ще премахне проследените промени и историята на данните, по-стари от 3 месеца.\nИзвършването му ще ускори драстично пълната синхронизация.</string>
<string name="cleared_entries">Изчистени записи</string> <string name="cleared_entries">Изчистени записи</string>
</resources> </resources>

View file

@ -164,7 +164,7 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">Rušení dočasného bazálu selhalo</string> <string name="canceling_tbr_failed">Rušení dočasného bazálu selhalo</string>
<string name="canceling_eb_failed">Zastavení prodlouženého bolusu selhalo</string> <string name="canceling_eb_failed">Zastavení prodlouženého bolusu selhalo</string>
<string name="virtualpump_uploadstatus_title">Nahrát stav do NS nebo Tidepoolu</string> <string name="virtualpump_uploadstatus_title">Nahrávat stav pumpy do NS nebo Tidepoolu</string>
<string name="suspendloop_label">Zakázaná/pozastavená smyčka</string> <string name="suspendloop_label">Zakázaná/pozastavená smyčka</string>
<string name="iob_label">Aktivní inzulín (IOB)</string> <string name="iob_label">Aktivní inzulín (IOB)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -164,7 +164,6 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">Abbruch der temporären Basalrate fehlgeschlagen</string> <string name="canceling_tbr_failed">Abbruch der temporären Basalrate fehlgeschlagen</string>
<string name="canceling_eb_failed">Der Abbruch des erweiterten Bolus ist fehlgeschlagen</string> <string name="canceling_eb_failed">Der Abbruch des erweiterten Bolus ist fehlgeschlagen</string>
<string name="virtualpump_uploadstatus_title">Status zu NS oder Tidepool hochladen</string>
<string name="suspendloop_label">Deaktiviere/Pausiere den Loop</string> <string name="suspendloop_label">Deaktiviere/Pausiere den Loop</string>
<string name="iob_label">Aktives Insulin (IOB)</string> <string name="iob_label">Aktives Insulin (IOB)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -164,7 +164,7 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">Error cancelando la basal temporal</string> <string name="canceling_tbr_failed">Error cancelando la basal temporal</string>
<string name="canceling_eb_failed">Error cancelando el bolo extendido</string> <string name="canceling_eb_failed">Error cancelando el bolo extendido</string>
<string name="virtualpump_uploadstatus_title">Subir estado a NS o Tidepool</string> <string name="virtualpump_uploadstatus_title">Subir estado de la bomba a NS o Tidepool</string>
<string name="suspendloop_label">Desactiva/suspende el bucle</string> <string name="suspendloop_label">Desactiva/suspende el bucle</string>
<string name="iob_label">Insulina a bordo (IOB)</string> <string name="iob_label">Insulina a bordo (IOB)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -164,7 +164,7 @@
<string name="isf_short">SI</string> <string name="isf_short">SI</string>
<string name="canceling_tbr_failed">Echec de l\'annulation du basal temporaire</string> <string name="canceling_tbr_failed">Echec de l\'annulation du basal temporaire</string>
<string name="canceling_eb_failed">Échec de l\'annulation du Bolus étendu</string> <string name="canceling_eb_failed">Échec de l\'annulation du Bolus étendu</string>
<string name="virtualpump_uploadstatus_title">Transférer le statut vers NS ou Tidepool</string> <string name="virtualpump_uploadstatus_title">Télécharger l\'état de la pompe sur NS ou Tidepool</string>
<string name="suspendloop_label">Boucle désactivée/suspendue</string> <string name="suspendloop_label">Boucle désactivée/suspendue</string>
<string name="iob_label">Insuline Active (IA)</string> <string name="iob_label">Insuline Active (IA)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -164,7 +164,7 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">Basale temporanea: cancellazione fallita</string> <string name="canceling_tbr_failed">Basale temporanea: cancellazione fallita</string>
<string name="canceling_eb_failed">Bolo esteso: cancellazione fallita</string> <string name="canceling_eb_failed">Bolo esteso: cancellazione fallita</string>
<string name="virtualpump_uploadstatus_title">Carica stato su NS o Tidepool</string> <string name="virtualpump_uploadstatus_title">Carica stato micro su NS o Tidepool</string>
<string name="suspendloop_label">Loop disabilitato/sospeso</string> <string name="suspendloop_label">Loop disabilitato/sospeso</string>
<string name="iob_label">Insulina attiva (IOB)</string> <string name="iob_label">Insulina attiva (IOB)</string>
<!-- Protection--> <!-- Protection-->
@ -449,6 +449,7 @@
<string name="info">INFO</string> <string name="info">INFO</string>
<!-- BolusWizard --> <!-- BolusWizard -->
<string name="bolus_advisor">Consiglio bolo</string> <string name="bolus_advisor">Consiglio bolo</string>
<string name="bolus_advisor_message">Hai una glicemia alta. Invece di mangiare ora, si consiglia di attendere una glicemia migliore. Vuoi fare adesso un bolo di correzione ed essere ricordato quando è il momento di mangiare? In questo caso non verranno registrati carboidrati e dovrai usare di nuovo il calcolatore quando ti verrà mostrato il promemoria.</string>
<string name="cobvsiob">COB vs IOB</string> <string name="cobvsiob">COB vs IOB</string>
<string name="slowabsorptiondetected"><![CDATA[<font color=\'%1$s\'>!!!!! Rilevato assorbimento lento dei carboidrati: %2$d%% del tempo. Ricontrolla il tuo calcolo. COB potrebbero essere sovrastimati e potrebbe essere somministrata più insulina !!!!!</font>]]></string> <string name="slowabsorptiondetected"><![CDATA[<font color=\'%1$s\'>!!!!! Rilevato assorbimento lento dei carboidrati: %2$d%% del tempo. Ricontrolla il tuo calcolo. COB potrebbero essere sovrastimati e potrebbe essere somministrata più insulina !!!!!</font>]]></string>
<string name="partialboluswizard">Eroga parte del risultato del calcolatore [%]</string> <string name="partialboluswizard">Eroga parte del risultato del calcolatore [%]</string>

View file

@ -164,7 +164,7 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">ביטול בזאלי זמני נכשל</string> <string name="canceling_tbr_failed">ביטול בזאלי זמני נכשל</string>
<string name="canceling_eb_failed">ביטול בולוס ממושך נכשל</string> <string name="canceling_eb_failed">ביטול בולוס ממושך נכשל</string>
<string name="virtualpump_uploadstatus_title">סטטוס העלאה לנייטסקאוט או ל-Tidepool</string> <string name="virtualpump_uploadstatus_title">העלה את סטטוס המשאבה לנייטסקאוט או ל-Tidepool</string>
<string name="suspendloop_label">השבתת \\ השהיית לולאה</string> <string name="suspendloop_label">השבתת \\ השהיית לולאה</string>
<string name="iob_label">אינסולין פעיל בגוף (IOB)</string> <string name="iob_label">אינסולין פעיל בגוף (IOB)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -23,7 +23,7 @@
<string name="mgdl">mg/dl</string> <string name="mgdl">mg/dl</string>
<string name="mmol">mmol/l</string> <string name="mmol">mmol/l</string>
<string name="save">Išsaugoti</string> <string name="save">Išsaugoti</string>
<string name="snooze">Snausti</string> <string name="snooze">Nutildyti</string>
<string name="virtual_pump">Virtuali pompa</string> <string name="virtual_pump">Virtuali pompa</string>
<string name="constraints">Apribojimai</string> <string name="constraints">Apribojimai</string>
<string name="superbolus">Superbolus</string> <string name="superbolus">Superbolus</string>
@ -82,7 +82,7 @@
<string name="bluetooth">Bluetooth</string> <string name="bluetooth">Bluetooth</string>
<string name="btwatchdog_title">BT Watchdog</string> <string name="btwatchdog_title">BT Watchdog</string>
<string name="btwatchdog_summary">Vienai sekundei išjungia telefono bluetooth, jei ryšys su pompa nutrūksta. Gali būti veiksminga kai kuriems telefonų modeliams, turintiems BT problemų.</string> <string name="btwatchdog_summary">Vienai sekundei išjungia telefono bluetooth, jei ryšys su pompa nutrūksta. Gali būti veiksminga kai kuriems telefonų modeliams, turintiems BT problemų.</string>
<string name="virtualpump_resultok">Gerai</string> <string name="virtualpump_resultok">OK</string>
<string name="pump_time_updated">Pompos laikas pakeistas</string> <string name="pump_time_updated">Pompos laikas pakeistas</string>
<string name="exit">Išeiti</string> <string name="exit">Išeiti</string>
<string name="removerecord">Ištrinti įrašą</string> <string name="removerecord">Ištrinti įrašą</string>
@ -164,7 +164,7 @@
<string name="isf_short">JIF</string> <string name="isf_short">JIF</string>
<string name="canceling_tbr_failed">Laikinos bazės atšaukti nepavyko</string> <string name="canceling_tbr_failed">Laikinos bazės atšaukti nepavyko</string>
<string name="canceling_eb_failed">Ištęsto boluso atšaukti nepavyko</string> <string name="canceling_eb_failed">Ištęsto boluso atšaukti nepavyko</string>
<string name="virtualpump_uploadstatus_title">Įkelti statusą į NS arba Tidepool</string> <string name="virtualpump_uploadstatus_title">Įkelti pompos statusą į NS arba Tidepool</string>
<string name="suspendloop_label">Ciklas išjungtas/sustabdytas</string> <string name="suspendloop_label">Ciklas išjungtas/sustabdytas</string>
<string name="iob_label">Aktyvus insulinas organizme (AIO)</string> <string name="iob_label">Aktyvus insulinas organizme (AIO)</string>
<!-- Protection--> <!-- Protection-->
@ -232,7 +232,7 @@
<string name="hypo">Hipo</string> <string name="hypo">Hipo</string>
<string name="activity">Aktyvumas</string> <string name="activity">Aktyvumas</string>
<string name="wear">Išmanieji laikrodžiai</string> <string name="wear">Išmanieji laikrodžiai</string>
<string name="automation">Automatiškai</string> <string name="automation">Automatizacija</string>
<string name="custom">Pasirinktinis</string> <string name="custom">Pasirinktinis</string>
<string name="loop">Ciklas</string> <string name="loop">Ciklas</string>
<string name="ns">NS</string> <string name="ns">NS</string>
@ -288,7 +288,7 @@
<string name="uel_resume">ATNAUJINTI</string> <string name="uel_resume">ATNAUJINTI</string>
<string name="uel_suspend">SUSTABDYTI</string> <string name="uel_suspend">SUSTABDYTI</string>
<string name="uel_hw_pump_allowed">HW POMPA LEIDŽIAMA</string> <string name="uel_hw_pump_allowed">HW POMPA LEIDŽIAMA</string>
<string name="uel_clear_pairing_keys">IŠVALYTI PORAVIMO KODUS</string> <string name="uel_clear_pairing_keys">IŠVALYTI SUSIEJIMO KODUS</string>
<string name="uel_accepts_temp_basal">PRIIMTI LAIKINĄ BAZĘ</string> <string name="uel_accepts_temp_basal">PRIIMTI LAIKINĄ BAZĘ</string>
<string name="uel_cancel_temp_basal">ATŠAUKTI LAIKINĄ BAZĘ</string> <string name="uel_cancel_temp_basal">ATŠAUKTI LAIKINĄ BAZĘ</string>
<string name="uel_cancel_bolus">ATŠAUKTI BOLUSĄ</string> <string name="uel_cancel_bolus">ATŠAUKTI BOLUSĄ</string>
@ -387,7 +387,7 @@
<string name="temp_basal_percent">LAIKINA BAZĖ %1$d%% %2$d min</string> <string name="temp_basal_percent">LAIKINA BAZĖ %1$d%% %2$d min</string>
<string name="insight_set_tbr_over_notification">INSIGHT NUSTATYTI LAIKINĄ BAZĘ PRANEŠIMU</string> <string name="insight_set_tbr_over_notification">INSIGHT NUSTATYTI LAIKINĄ BAZĘ PRANEŠIMU</string>
<string name="read_status" comment="10 characters max for READSTATUS translation">STATUSAS %1$s</string> <string name="read_status" comment="10 characters max for READSTATUS translation">STATUSAS %1$s</string>
<string name="keepalive_status_outdated" comment="26 characters max for translation">Tęsti. Būklė per sena.</string> <string name="keepalive_status_outdated" comment="26 characters max for translation">Tęsti. Statusas senas.</string>
<string name="keepalive_basal_outdated" comment="26 characters max for translation">Tęsti. Bazė per sena.</string> <string name="keepalive_basal_outdated" comment="26 characters max for translation">Tęsti. Bazė per sena.</string>
<string name="sms" comment="26 characters max for translation">SMS</string> <string name="sms" comment="26 characters max for translation">SMS</string>
<string name="formatPercent">%1$.0f%%</string> <string name="formatPercent">%1$.0f%%</string>
@ -513,7 +513,7 @@
<string name="connection_error">Pompos prisijungimo klaida</string> <string name="connection_error">Pompos prisijungimo klaida</string>
<string name="reading_pump_history">Skaitoma pompos istorija</string> <string name="reading_pump_history">Skaitoma pompos istorija</string>
<string name="password_cleared">Slaptažodis išvalytas!</string> <string name="password_cleared">Slaptažodis išvalytas!</string>
<string name="pairing">Sujungiama</string> <string name="pairing">Susiejimas</string>
<string name="initializing">Inicijuojama ...</string> <string name="initializing">Inicijuojama ...</string>
<!-- Constraints--> <!-- Constraints-->
<string name="limitingbasalratio">Ribojamas maksimalus bazės dydis%1$.2f vv/val dėl %2$s</string> <string name="limitingbasalratio">Ribojamas maksimalus bazės dydis%1$.2f vv/val dėl %2$s</string>
@ -525,7 +525,7 @@
<!-- Dialogs--> <!-- Dialogs-->
<string name="confirmation">Patvirtinimas</string> <string name="confirmation">Patvirtinimas</string>
<string name="message">Pranešimas</string> <string name="message">Pranešimas</string>
<string name="ok">Gerai</string> <string name="ok">OK</string>
<string name="cancel">Atšaukti</string> <string name="cancel">Atšaukti</string>
<string name="dismiss">ATMESTI</string> <string name="dismiss">ATMESTI</string>
<string name="yes">Taip</string> <string name="yes">Taip</string>
@ -544,13 +544,13 @@
<string name="need_connect_permission">Programai reikalinga Bluetooth prieigos teisė</string> <string name="need_connect_permission">Programai reikalinga Bluetooth prieigos teisė</string>
<!-- Combo--> <!-- Combo-->
<string name="user_request" comment="26 characters max for translation">Vartotojo užklausa</string> <string name="user_request" comment="26 characters max for translation">Vartotojo užklausa</string>
<string name="pump_paired" comment="26 characters max for translation">Pompa suporuota</string> <string name="pump_paired" comment="26 characters max for translation">Pompa susieta</string>
<!-- BlePreCheck--> <!-- BlePreCheck-->
<string name="ble_not_supported">Bluetooth Low Energy nepalaikoma.</string> <string name="ble_not_supported">Bluetooth Low Energy nepalaikoma.</string>
<string name="ble_not_supported_or_not_paired">Nepalaikomas Bluetooth Low Energy arba įrenginys nesuporuotas.</string> <string name="ble_not_supported_or_not_paired">Nepalaikomas BLE arba įrenginys nesusietas.</string>
<string name="ble_not_enabled">Bluetooth neįjungta.</string> <string name="ble_not_enabled">Bluetooth neįjungta.</string>
<string name="location_not_found_title">Vietovės nustatymas neįjungtas</string> <string name="location_not_found_title">Vietovės nustatymas neįjungtas</string>
<string name="location_not_found_message">Vietos nustatymo paslauga turi būti įjungta, kad Bluetooth aptikimas veiktų naujesniuose įrenginiuose. AAPS neseka Jūsų lokacijos, o vietos nustatymo paslauga gali būti išjungta po sėkmingo įrenginių suporavimo.</string> <string name="location_not_found_message">Vietos nustatymo paslauga turi būti įjungta, kad Bluetooth aptikimas veiktų naujesniuose įrenginiuose. AAPS neseka Jūsų lokacijos, o vietos nustatymo paslauga gali būti išjungta po sėkmingo įrenginių susiejimo.</string>
<!-- Preferences --> <!-- Preferences -->
<string name="nav_plugin_preferences">Įskiepių nustatymai</string> <string name="nav_plugin_preferences">Įskiepių nustatymai</string>
<!-- SmsCommunicator --> <!-- SmsCommunicator -->

View file

@ -216,6 +216,7 @@
<item name="inRangeBackground">@color/inRangeBackground</item> <item name="inRangeBackground">@color/inRangeBackground</item>
<item name="devSlopePosColor">@color/devSlopePos</item> <item name="devSlopePosColor">@color/devSlopePos</item>
<item name="devSlopeNegColor">@color/devSlopeNeg</item> <item name="devSlopeNegColor">@color/devSlopeNeg</item>
<item name="heartRateColor">@color/heartRate</item>
<item name="deviationGreyColor">@color/deviationGrey</item> <item name="deviationGreyColor">@color/deviationGrey</item>
<item name="deviationBlackColor">@color/deviationBlack</item> <item name="deviationBlackColor">@color/deviationBlack</item>
<item name="deviationGreenColor">@color/deviationGreen</item> <item name="deviationGreenColor">@color/deviationGreen</item>

View file

@ -164,7 +164,6 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">Annuleren van tijdelijke basaal mislukt</string> <string name="canceling_tbr_failed">Annuleren van tijdelijke basaal mislukt</string>
<string name="canceling_eb_failed">Annuleren van vertraagde bolus is mislukt</string> <string name="canceling_eb_failed">Annuleren van vertraagde bolus is mislukt</string>
<string name="virtualpump_uploadstatus_title">Upload status naar NS of Tidepool</string>
<string name="suspendloop_label">Uitgeschakelde/onderbroken loop</string> <string name="suspendloop_label">Uitgeschakelde/onderbroken loop</string>
<string name="iob_label">Insuline aan boord (IOB)</string> <string name="iob_label">Insuline aan boord (IOB)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -164,7 +164,7 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">Kunne ikke avbryte midlertidig basal</string> <string name="canceling_tbr_failed">Kunne ikke avbryte midlertidig basal</string>
<string name="canceling_eb_failed">Kunne ikke avbryte forlenget bolus</string> <string name="canceling_eb_failed">Kunne ikke avbryte forlenget bolus</string>
<string name="virtualpump_uploadstatus_title">Last opp status til NS eller Tidepool</string> <string name="virtualpump_uploadstatus_title">Last opp pumpestatus til NS eller Tidepool</string>
<string name="suspendloop_label">Deaktivert/pauset loop</string> <string name="suspendloop_label">Deaktivert/pauset loop</string>
<string name="iob_label">Aktivt insulin (IOB)</string> <string name="iob_label">Aktivt insulin (IOB)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -164,7 +164,7 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">Anulowanie bazy tymczasowej nie powiodło się</string> <string name="canceling_tbr_failed">Anulowanie bazy tymczasowej nie powiodło się</string>
<string name="canceling_eb_failed">Anulowanie przedłużonego bolusa nie powiodło się</string> <string name="canceling_eb_failed">Anulowanie przedłużonego bolusa nie powiodło się</string>
<string name="virtualpump_uploadstatus_title">Prześlij status do NS lub Tidepool</string> <string name="virtualpump_uploadstatus_title">Prześlij status pompy do NS lub Tidepool</string>
<string name="suspendloop_label">Wyłączona/zawieszona pętla</string> <string name="suspendloop_label">Wyłączona/zawieszona pętla</string>
<string name="iob_label">Aktywna insulina (IOB)</string> <string name="iob_label">Aktywna insulina (IOB)</string>
<!-- Protection--> <!-- Protection-->
@ -532,7 +532,7 @@
<string name="no">Nie</string> <string name="no">Nie</string>
<string name="close">Zamknij</string> <string name="close">Zamknij</string>
<!-- TwoMessagesDialog --> <!-- TwoMessagesDialog -->
<string name="password_preferences_decrypt_prompt">Zostaniesz poproszony o hasło główne, które jest potrzebne do odszyfrowania zaimportowanych preferencji.</string> <string name="password_preferences_decrypt_prompt">Zostaniesz poproszony o hasło główne, które jest potrzebne do odszyfrowania zaimportowanych ustawień.</string>
<!-- NumberPicker --> <!-- NumberPicker -->
<string name="a11y_min_button_description">zmniejszenie %1$s o %2$s</string> <string name="a11y_min_button_description">zmniejszenie %1$s o %2$s</string>
<string name="a11y_plus_button_description">zwiększenie %1$s o %2$s</string> <string name="a11y_plus_button_description">zwiększenie %1$s o %2$s</string>

View file

@ -164,7 +164,6 @@
<string name="isf_short">FSI</string> <string name="isf_short">FSI</string>
<string name="canceling_tbr_failed">Cancelamento do basal temporário falhou</string> <string name="canceling_tbr_failed">Cancelamento do basal temporário falhou</string>
<string name="canceling_eb_failed">Falhou o cancelamento do bolus extendido</string> <string name="canceling_eb_failed">Falhou o cancelamento do bolus extendido</string>
<string name="virtualpump_uploadstatus_title">Carregar status para NS ou Tidepool</string>
<string name="suspendloop_label">Loop Desativado/Suspenso</string> <string name="suspendloop_label">Loop Desativado/Suspenso</string>
<string name="iob_label">Insulina ativa (IA)</string> <string name="iob_label">Insulina ativa (IA)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -164,7 +164,7 @@
<string name="isf_short">ISF (чувствительность к инсулину)</string> <string name="isf_short">ISF (чувствительность к инсулину)</string>
<string name="canceling_tbr_failed">Отмена врем базала не состоялась</string> <string name="canceling_tbr_failed">Отмена врем базала не состоялась</string>
<string name="canceling_eb_failed">Сбой отмены пролонгированного болюса</string> <string name="canceling_eb_failed">Сбой отмены пролонгированного болюса</string>
<string name="virtualpump_uploadstatus_title">Статус dsuheprb в NS или Tidepool</string> <string name="virtualpump_uploadstatus_title">Передавать статус помпы в NS или Tidepool</string>
<string name="suspendloop_label">Отключенный/приостановленный цикл</string> <string name="suspendloop_label">Отключенный/приостановленный цикл</string>
<string name="iob_label">Активный инсулин (IOB)</string> <string name="iob_label">Активный инсулин (IOB)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -164,7 +164,7 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">Zrušenie dočasného bazálu zlyhalo</string> <string name="canceling_tbr_failed">Zrušenie dočasného bazálu zlyhalo</string>
<string name="canceling_eb_failed">Zastavenie predĺženého bolusu zlyhalo</string> <string name="canceling_eb_failed">Zastavenie predĺženého bolusu zlyhalo</string>
<string name="virtualpump_uploadstatus_title">Nahrať stav do NS, alebo Tidepool</string> <string name="virtualpump_uploadstatus_title">Nahrávať stav pumpy do NS, alebo Tidepoolu</string>
<string name="suspendloop_label">Deaktivovaný/pozastavený uzavretý okruh</string> <string name="suspendloop_label">Deaktivovaný/pozastavený uzavretý okruh</string>
<string name="iob_label">Aktívny inzulín (IOB)</string> <string name="iob_label">Aktívny inzulín (IOB)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -164,7 +164,7 @@
<string name="isf_short">IDF İnsülin Duyarlılık Faktörü</string> <string name="isf_short">IDF İnsülin Duyarlılık Faktörü</string>
<string name="canceling_tbr_failed">Geçici bazal iptali başarısız oldu</string> <string name="canceling_tbr_failed">Geçici bazal iptali başarısız oldu</string>
<string name="canceling_eb_failed">Yayma bolusun iptal edilmesi başarısız oldu</string> <string name="canceling_eb_failed">Yayma bolusun iptal edilmesi başarısız oldu</string>
<string name="virtualpump_uploadstatus_title">Durumu NS\'a veya Tidepool\'a yükleyin</string> <string name="virtualpump_uploadstatus_title">Pompa durumunu NS veya Tidepool\'a yükleyin</string>
<string name="suspendloop_label">Döngüyü Devre Dışı bırakma/Askıya alma</string> <string name="suspendloop_label">Döngüyü Devre Dışı bırakma/Askıya alma</string>
<string name="iob_label">Aktif İnsülin (AİNS)</string> <string name="iob_label">Aktif İnsülin (AİNS)</string>
<!-- Protection--> <!-- Protection-->

View file

@ -181,6 +181,7 @@
<attr name="bolusDataPointColor" format="reference|color" /> <attr name="bolusDataPointColor" format="reference|color" />
<attr name="profileSwitchColor" format="reference|color" /> <attr name="profileSwitchColor" format="reference|color" />
<attr name="originalBgValueColor" format="reference|color" /> <attr name="originalBgValueColor" format="reference|color" />
<attr name="heartRateColor" format="reference|color" />
<attr name="therapyEvent_NS_MBG" format="reference|color" /> <attr name="therapyEvent_NS_MBG" format="reference|color" />
<attr name="therapyEvent_FINGER_STICK_BG_VALUE" format="reference|color" /> <attr name="therapyEvent_FINGER_STICK_BG_VALUE" format="reference|color" />
<attr name="therapyEvent_EXERCISE" format="reference|color" /> <attr name="therapyEvent_EXERCISE" format="reference|color" />

View file

@ -153,6 +153,7 @@
<color name="bgi">#00EEEE</color> <color name="bgi">#00EEEE</color>
<color name="devSlopePos">#FFFFFF00</color> <color name="devSlopePos">#FFFFFF00</color>
<color name="devSlopeNeg">#FFFF00FF</color> <color name="devSlopeNeg">#FFFF00FF</color>
<color name="heartRate">#FFFFFF66</color>
<color name="actionsConfirm">#F6CE22</color> <color name="actionsConfirm">#F6CE22</color>
<color name="deviations">#FF0000</color> <color name="deviations">#FF0000</color>
<color name="cobAlert">#7484E2</color> <color name="cobAlert">#7484E2</color>

View file

@ -165,7 +165,7 @@
<string name="isf_short">ISF</string> <string name="isf_short">ISF</string>
<string name="canceling_tbr_failed">Canceling of temporary basal failed</string> <string name="canceling_tbr_failed">Canceling of temporary basal failed</string>
<string name="canceling_eb_failed">Canceling of extended bolus failed</string> <string name="canceling_eb_failed">Canceling of extended bolus failed</string>
<string name="virtualpump_uploadstatus_title">Upload status to NS or Tidepool</string> <string name="virtualpump_uploadstatus_title">Upload pump status to NS or Tidepool</string>
<string name="suspendloop_label">Disabled/Suspended loop</string> <string name="suspendloop_label">Disabled/Suspended loop</string>
<string name="iob_label">Insulin on Board (IOB)</string> <string name="iob_label">Insulin on Board (IOB)</string>

View file

@ -219,6 +219,7 @@
<item name="inRangeBackground">@color/inRangeBackground</item> <item name="inRangeBackground">@color/inRangeBackground</item>
<item name="devSlopePosColor">@color/devSlopePos</item> <item name="devSlopePosColor">@color/devSlopePos</item>
<item name="devSlopeNegColor">@color/devSlopeNeg</item> <item name="devSlopeNegColor">@color/devSlopeNeg</item>
<item name="heartRateColor">@color/heartRate</item>
<item name="deviationGreyColor">@color/deviationGrey</item> <item name="deviationGreyColor">@color/deviationGrey</item>
<item name="deviationBlackColor">@color/deviationBlack</item> <item name="deviationBlackColor">@color/deviationBlack</item>
<item name="deviationGreenColor">@color/deviationGreen</item> <item name="deviationGreenColor">@color/deviationGreen</item>

View file

@ -22,7 +22,7 @@ dependencies {
implementation project(':app-wear-shared:shared') implementation project(':app-wear-shared:shared')
//Firebase //Firebase
api platform('com.google.firebase:firebase-bom:31.2.2') api platform('com.google.firebase:firebase-bom:32.1.0')
api "com.google.firebase:firebase-analytics-ktx" api "com.google.firebase:firebase-analytics-ktx"
api "com.google.firebase:firebase-crashlytics-ktx" api "com.google.firebase:firebase-crashlytics-ktx"
// StatsActivity not in use now // StatsActivity not in use now

View file

@ -5,6 +5,7 @@ import android.view.ActionMode
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import androidx.core.view.MenuCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
@ -68,7 +69,7 @@ class ActionModeHelper<T>(val rh: ResourceHelper, val activity: FragmentActivity
} else if (fragment?.isResumed == true) { } else if (fragment?.isResumed == true) {
menu.add(Menu.FIRST, R.id.nav_remove_items, 0, rh.gs(R.string.remove_items)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, R.id.nav_remove_items, 0, rh.gs(R.string.remove_items)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(Menu.FIRST, R.id.nav_sort_items, 0, rh.gs(R.string.sort_items)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, R.id.nav_sort_items, 0, rh.gs(R.string.sort_items)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.setGroupDividerEnabled(true) MenuCompat.setGroupDividerEnabled(menu, true)
} }
} }

View file

@ -7,6 +7,7 @@ plugins {
} }
apply from: "${project.rootDir}/core/main/android_dependencies.gradle" apply from: "${project.rootDir}/core/main/android_dependencies.gradle"
apply from: "${project.rootDir}/core/main/test_dependencies.gradle"
android { android {

View file

@ -0,0 +1,44 @@
package info.nightscout.database.entities
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.database.entities.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.database.entities.interfaces.TraceableDBEntry
import java.util.*
/** Heart rate values measured by a user smart watch or the like. */
@Entity(
tableName = TABLE_HEART_RATE,
indices = [Index("id"), Index("timestamp")]
)
data class HeartRate(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
/** Duration milliseconds */
override var duration: Long,
/** Milliseconds since the epoch. End of the sampling period, i.e. the value is
* sampled from timestamp-duration to timestamp. */
override var timestamp: Long,
var beatsPerMinute: Double,
/** Source device that measured the heart rate. */
var device: String,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = null
) : TraceableDBEntry, DBEntryWithTimeAndDuration {
fun contentEqualsTo(other: HeartRate): Boolean {
return this === other || (
duration == other.duration &&
timestamp == other.timestamp &&
beatsPerMinute == other.beatsPerMinute &&
isValid == other.isValid)
}
}

View file

@ -8,6 +8,7 @@ const val TABLE_CARBS = "carbs"
const val TABLE_DEVICE_STATUS = "deviceStatus" const val TABLE_DEVICE_STATUS = "deviceStatus"
const val TABLE_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches" const val TABLE_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches"
const val TABLE_EXTENDED_BOLUSES = "extendedBoluses" const val TABLE_EXTENDED_BOLUSES = "extendedBoluses"
const val TABLE_HEART_RATE = "heartRate"
const val TABLE_GLUCOSE_VALUES = "glucoseValues" const val TABLE_GLUCOSE_VALUES = "glucoseValues"
const val TABLE_FOODS = "foods" const val TABLE_FOODS = "foods"
const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks" const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks"

View file

@ -8,6 +8,7 @@ import info.nightscout.database.entities.Carbs
import info.nightscout.database.entities.EffectiveProfileSwitch import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.database.entities.ExtendedBolus import info.nightscout.database.entities.ExtendedBolus
import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.entities.HeartRate
import info.nightscout.database.entities.MultiwaveBolusLink import info.nightscout.database.entities.MultiwaveBolusLink
import info.nightscout.database.entities.OfflineEvent import info.nightscout.database.entities.OfflineEvent
import info.nightscout.database.entities.PreferenceChange import info.nightscout.database.entities.PreferenceChange
@ -35,5 +36,6 @@ data class NewEntries(
val temporaryTarget: List<TemporaryTarget>, val temporaryTarget: List<TemporaryTarget>,
val therapyEvents: List<TherapyEvent>, val therapyEvents: List<TherapyEvent>,
val totalDailyDoses: List<TotalDailyDose>, val totalDailyDoses: List<TotalDailyDose>,
val versionChanges: List<VersionChange> val versionChanges: List<VersionChange>,
val heartRates: List<HeartRate>,
) )

View file

@ -0,0 +1,36 @@
package info.nightscout.database.entities
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.jupiter.api.Test
class HeartRateTest {
@Test
fun contentEqualsTo_equals() {
val hr1 = createHeartRate()
assertTrue(hr1.contentEqualsTo(hr1))
assertTrue(hr1.contentEqualsTo(hr1.copy()))
assertTrue(hr1.contentEqualsTo(hr1.copy (id = 2, version = 2, dateCreated = 1L, referenceId = 4L)))
}
@Test
fun contentEqualsTo_notEquals() {
val hr1 = createHeartRate()
assertFalse(hr1.contentEqualsTo(hr1.copy(duration = 60_001L)))
assertFalse(hr1.contentEqualsTo(hr1.copy(timestamp = 2L)))
assertFalse(hr1.contentEqualsTo(hr1.copy(duration = 60_001L)))
assertFalse(hr1.contentEqualsTo(hr1.copy(beatsPerMinute = 100.0)))
assertFalse(hr1.contentEqualsTo(hr1.copy(isValid = false)))
}
companion object {
fun createHeartRate(timestamp: Long? = null, beatsPerMinute: Double = 80.0) =
HeartRate(
timestamp = timestamp ?: System.currentTimeMillis(),
duration = 60_0000L,
beatsPerMinute = beatsPerMinute,
device = "T",
)
}
}

View file

@ -8,6 +8,8 @@ plugins {
apply from: "${project.rootDir}/core/main/android_dependencies.gradle" apply from: "${project.rootDir}/core/main/android_dependencies.gradle"
apply from: "${project.rootDir}/core/main/android_module_dependencies.gradle" apply from: "${project.rootDir}/core/main/android_module_dependencies.gradle"
apply from: "${project.rootDir}/core/main/test_dependencies.gradle"
apply from: "${project.rootDir}/core/main/jacoco_global.gradle"
android { android {
@ -20,6 +22,9 @@ android {
} }
} }
} }
sourceSets {
androidTest.assets.srcDirs += files("$projectDir/schemas")
}
} }
dependencies { dependencies {
@ -44,6 +49,8 @@ dependencies {
api "com.google.dagger:dagger-android:$dagger_version" api "com.google.dagger:dagger-android:$dagger_version"
api "com.google.dagger:dagger-android-support:$dagger_version" api "com.google.dagger:dagger-android-support:$dagger_version"
androidTestImplementation "androidx.room:room-testing:$room_version"
} }
allOpen { allOpen {

View file

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 23, "version": 23,
"identityHash": "173734db5f4f35f6295ed953d8124794", "identityHash": "a3ee37800b6cda170d0ea64799ed7876",
"entities": [ "entities": [
{ {
"tableName": "apsResults", "tableName": "apsResults",
@ -3689,12 +3689,153 @@
] ]
} }
] ]
},
{
"tableName": "heartRate",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `duration` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `beatsPerMinute` REAL NOT NULL, `device` TEXT NOT NULL, `utcOffset` INTEGER NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "beatsPerMinute",
"columnName": "beatsPerMinute",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "device",
"columnName": "device",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "utcOffset",
"columnName": "utcOffset",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dateCreated",
"columnName": "dateCreated",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isValid",
"columnName": "isValid",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "referenceId",
"columnName": "referenceId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "interfaceIDs_backing.nightscoutSystemId",
"columnName": "nightscoutSystemId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "interfaceIDs_backing.nightscoutId",
"columnName": "nightscoutId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "interfaceIDs_backing.pumpType",
"columnName": "pumpType",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "interfaceIDs_backing.pumpSerial",
"columnName": "pumpSerial",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "interfaceIDs_backing.temporaryId",
"columnName": "temporaryId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "interfaceIDs_backing.pumpId",
"columnName": "pumpId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "interfaceIDs_backing.startId",
"columnName": "startId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "interfaceIDs_backing.endId",
"columnName": "endId",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_heartRate_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_heartRate_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_heartRate_timestamp",
"unique": false,
"columnNames": [
"timestamp"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_heartRate_timestamp` ON `${TABLE_NAME}` (`timestamp`)"
}
],
"foreignKeys": []
} }
], ],
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '173734db5f4f35f6295ed953d8124794')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a3ee37800b6cda170d0ea64799ed7876')"
] ]
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,126 @@
package info.nightscout.database.impl
import android.content.Context
import androidx.room.Room
import androidx.room.testing.MigrationTestHelper
import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import info.nightscout.database.entities.HeartRate
import info.nightscout.database.entities.TABLE_HEART_RATE
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
internal class HeartRateDaoTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
private fun createDatabase() =
Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build()
private fun getDbObjects(supportDb: SupportSQLiteDatabase, type: String): Set<String> {
val names = mutableSetOf<String>()
supportDb.query("SELECT name FROM sqlite_master WHERE type = '$type'").use { c ->
while (c.moveToNext()) names.add(c.getString(0))
}
return names
}
private fun getTableNames(db: SupportSQLiteDatabase) = getDbObjects(db, "table")
private fun getIndexNames(db: SupportSQLiteDatabase) = getDbObjects(db, "index")
private fun insertAndFind(database: AppDatabase) {
val hr1 = createHeartRate()
val id = database.heartRateDao.insert(hr1)
val hr2 = database.heartRateDao.findById(id)
assertTrue(hr1.contentEqualsTo(hr2!!))
}
@Test
fun new_insertAndFind() {
createDatabase().use { db -> insertAndFind(db) }
}
@Test
fun migrate_createsTableAndIndices() {
val helper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
AppDatabase::class.java
)
val startVersion = 22
val supportDb = helper.createDatabase(TEST_DB_NAME, startVersion)
assertFalse(getTableNames(supportDb).contains(TABLE_HEART_RATE))
DatabaseModule().migrations.filter { m -> m.startVersion >= startVersion }.forEach { m -> m.migrate(supportDb) }
assertTrue(getTableNames(supportDb).contains(TABLE_HEART_RATE))
assertTrue(getIndexNames(supportDb).contains("index_heartRate_id"))
assertTrue(getIndexNames(supportDb).contains("index_heartRate_timestamp"))
}
@Test
fun migrate_insertAndFind() {
val helper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
AppDatabase::class.java
)
// Create the database for version 22 (that's missing the heartRate table).
// helper.createDatabase removes the db file if it already exists.
val supportDb = helper.createDatabase(TEST_DB_NAME, 22)
assertFalse(getTableNames(supportDb).contains(TABLE_HEART_RATE))
// Room.databaseBuilder will use the previously created db file that has version 22.
Room.databaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java, TEST_DB_NAME)
.addMigrations(*DatabaseModule().migrations)
.build().use { db -> insertAndFind(db) }
}
@Test
fun getFromTime() {
createDatabase().use { db ->
val dao = db.heartRateDao
val timestamp = System.currentTimeMillis()
val hr1 = createHeartRate(timestamp = timestamp, beatsPerMinute = 80.0)
val hr2 = createHeartRate(timestamp = timestamp + 1, beatsPerMinute = 150.0)
dao.insertNewEntry(hr1)
dao.insertNewEntry(hr2)
assertEquals(listOf(hr1, hr2), dao.getFromTime(timestamp))
assertEquals(listOf(hr2), dao.getFromTime(timestamp + 1))
assertTrue(dao.getFromTime(timestamp + 2).isEmpty())
}
}
@Test
fun getFromTimeToTime() {
createDatabase().use { db ->
val dao = db.heartRateDao
val timestamp = System.currentTimeMillis()
val hr1 = createHeartRate(timestamp = timestamp, beatsPerMinute = 80.0)
val hr2 = createHeartRate(timestamp = timestamp + 1, beatsPerMinute = 150.0)
val hr3 = createHeartRate(timestamp = timestamp + 2, beatsPerMinute = 160.0)
dao.insertNewEntry(hr1)
dao.insertNewEntry(hr2)
dao.insertNewEntry(hr3)
assertEquals(listOf(hr1, hr2, hr3), dao.getFromTimeToTime(timestamp, timestamp + 2))
assertEquals(listOf(hr1, hr2), dao.getFromTimeToTime(timestamp, timestamp + 1))
assertEquals(listOf(hr2), dao.getFromTimeToTime(timestamp + 1, timestamp + 1))
assertTrue(dao.getFromTimeToTime(timestamp + 3, timestamp + 10).isEmpty())
}
}
companion object {
private const val TEST_DB_NAME = "testDatabase"
fun createHeartRate(timestamp: Long? = null, beatsPerMinute: Double = 80.0) =
HeartRate(
timestamp = timestamp ?: System.currentTimeMillis(),
duration = 60_0000L,
beatsPerMinute = beatsPerMinute,
device = "T",
)
}
}

View file

@ -0,0 +1,57 @@
package info.nightscout.database.impl.transactions
import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import info.nightscout.database.impl.AppDatabase
import info.nightscout.database.impl.AppRepository
import info.nightscout.database.impl.HeartRateDaoTest
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class InsertOrUpdateHeartRateTransactionTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var db: AppDatabase
private lateinit var repo: AppRepository
@Before
fun setupUp() {
db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build()
repo = AppRepository(db)
}
@After
fun shutdown() {
db.close()
}
@Test
fun createNewEntry() {
val hr1 = HeartRateDaoTest.createHeartRate()
val result = repo.runTransactionForResult(InsertOrUpdateHeartRateTransaction(hr1)).blockingGet()
assertEquals(listOf(hr1), result.inserted)
assertTrue(result.updated.isEmpty())
}
@Test
fun updateEntry() {
val hr1 = HeartRateDaoTest.createHeartRate()
val id = db.heartRateDao.insertNewEntry(hr1)
assertNotEquals(0, id)
val hr2 = hr1.copy(id = id, beatsPerMinute = 181.0)
val result = repo.runTransactionForResult(InsertOrUpdateHeartRateTransaction(hr2)).blockingGet()
assertEquals(listOf(hr2), result.updated)
assertTrue(result.inserted.isEmpty())
val hr3 = db.heartRateDao.findById(id)!!
assertTrue(hr2.contentEqualsTo(hr3))
}
}

View file

@ -33,6 +33,7 @@ import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.database.entities.ExtendedBolus import info.nightscout.database.entities.ExtendedBolus
import info.nightscout.database.entities.Food import info.nightscout.database.entities.Food
import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.entities.HeartRate
import info.nightscout.database.entities.MultiwaveBolusLink import info.nightscout.database.entities.MultiwaveBolusLink
import info.nightscout.database.entities.OfflineEvent import info.nightscout.database.entities.OfflineEvent
import info.nightscout.database.entities.PreferenceChange import info.nightscout.database.entities.PreferenceChange
@ -43,18 +44,20 @@ import info.nightscout.database.entities.TherapyEvent
import info.nightscout.database.entities.TotalDailyDose import info.nightscout.database.entities.TotalDailyDose
import info.nightscout.database.entities.UserEntry import info.nightscout.database.entities.UserEntry
import info.nightscout.database.entities.VersionChange import info.nightscout.database.entities.VersionChange
import info.nightscout.database.impl.daos.HeartRateDao
import java.io.Closeable
const val DATABASE_VERSION = 23 const val DATABASE_VERSION = 24
@Database(version = DATABASE_VERSION, @Database(version = DATABASE_VERSION,
entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class, entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class,
EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class, EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class,
TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class, TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class,
MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class,
Food::class, DeviceStatus::class, OfflineEvent::class], Food::class, DeviceStatus::class, OfflineEvent::class, HeartRate::class],
exportSchema = true) exportSchema = true)
@TypeConverters(Converters::class) @TypeConverters(Converters::class)
internal abstract class AppDatabase : RoomDatabase() { internal abstract class AppDatabase : Closeable, RoomDatabase() {
abstract val glucoseValueDao: GlucoseValueDao abstract val glucoseValueDao: GlucoseValueDao
@ -96,4 +99,5 @@ internal abstract class AppDatabase : RoomDatabase() {
abstract val offlineEventDao: OfflineEventDao abstract val offlineEventDao: OfflineEventDao
abstract val heartRateDao: HeartRateDao
} }

View file

@ -101,6 +101,7 @@ import kotlin.math.roundToInt
//database.foodDao.deleteOlderThan(than) //database.foodDao.deleteOlderThan(than)
removed.add(Pair("DeviceStatus", database.deviceStatusDao.deleteOlderThan(than))) removed.add(Pair("DeviceStatus", database.deviceStatusDao.deleteOlderThan(than)))
removed.add(Pair("OfflineEvent", database.offlineEventDao.deleteOlderThan(than))) removed.add(Pair("OfflineEvent", database.offlineEventDao.deleteOlderThan(than)))
removed.add(Pair("HeartRate", database.heartRateDao.deleteOlderThan(than)))
if (deleteTrackedChanges) { if (deleteTrackedChanges) {
removed.add(Pair("GlucoseValue", database.glucoseValueDao.deleteTrackedChanges())) removed.add(Pair("GlucoseValue", database.glucoseValueDao.deleteTrackedChanges()))
@ -119,6 +120,7 @@ import kotlin.math.roundToInt
removed.add(Pair("ApsResult", database.apsResultDao.deleteTrackedChanges())) removed.add(Pair("ApsResult", database.apsResultDao.deleteTrackedChanges()))
//database.foodDao.deleteHistory() //database.foodDao.deleteHistory()
removed.add(Pair("OfflineEvent", database.offlineEventDao.deleteTrackedChanges())) removed.add(Pair("OfflineEvent", database.offlineEventDao.deleteTrackedChanges()))
removed.add(Pair("HeartRate", database.heartRateDao.deleteTrackedChanges()))
} }
val ret = StringBuilder() val ret = StringBuilder()
removed removed
@ -143,8 +145,8 @@ import kotlin.math.roundToInt
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
//BG READINGS -- including invalid/history records //BG READINGS -- including invalid/history records
fun findBgReadingByNSIdSingle(nsId: String): Single<ValueWrapper<GlucoseValue>> = fun findBgReadingByNSId(nsId: String): GlucoseValue? =
database.glucoseValueDao.findByNSIdMaybe(nsId).toWrappedSingle() database.glucoseValueDao.findByNSId(nsId)
fun getModifiedBgReadingsDataFromId(lastId: Long): Single<List<GlucoseValue>> = fun getModifiedBgReadingsDataFromId(lastId: Long): Single<List<GlucoseValue>> =
database.glucoseValueDao.getModifiedFrom(lastId) database.glucoseValueDao.getModifiedFrom(lastId)
@ -186,6 +188,9 @@ import kotlin.math.roundToInt
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
// TEMP TARGETS // TEMP TARGETS
fun findTemporaryTargetByNSId(nsId: String): TemporaryTarget? =
database.temporaryTargetDao.findByNSId(nsId)
/* /*
* returns a Pair of the next entity to sync and the ID of the "update". * returns a Pair of the next entity to sync and the ID of the "update".
* The update id might either be the entry id itself if it is a new entry - or the id * The update id might either be the entry id itself if it is a new entry - or the id
@ -253,6 +258,9 @@ import kotlin.math.roundToInt
// PROFILE SWITCH // PROFILE SWITCH
fun findProfileSwitchByNSId(nsId: String): ProfileSwitch? =
database.profileSwitchDao.findByNSId(nsId)
fun getNextSyncElementProfileSwitch(id: Long): Maybe<Pair<ProfileSwitch, ProfileSwitch>> = fun getNextSyncElementProfileSwitch(id: Long): Maybe<Pair<ProfileSwitch, ProfileSwitch>> =
database.profileSwitchDao.getNextModifiedOrNewAfter(id) database.profileSwitchDao.getNextModifiedOrNewAfter(id)
.flatMap { nextIdElement -> .flatMap { nextIdElement ->
@ -309,6 +317,9 @@ import kotlin.math.roundToInt
database.profileSwitchDao.getLastId() database.profileSwitchDao.getLastId()
// EFFECTIVE PROFILE SWITCH // EFFECTIVE PROFILE SWITCH
fun findEffectiveProfileSwitchByNSId(nsId: String): EffectiveProfileSwitch? =
database.effectiveProfileSwitchDao.findByNSId(nsId)
/* /*
* returns a Pair of the next entity to sync and the ID of the "update". * returns a Pair of the next entity to sync and the ID of the "update".
* The update id might either be the entry id itself if it is a new entry - or the id * The update id might either be the entry id itself if it is a new entry - or the id
@ -373,6 +384,9 @@ import kotlin.math.roundToInt
* *
* It is a Maybe as there might be no next element. * It is a Maybe as there might be no next element.
* */ * */
fun findTherapyEventByNSId(nsId: String): TherapyEvent? =
database.therapyEventDao.findByNSId(nsId)
fun getNextSyncElementTherapyEvent(id: Long): Maybe<Pair<TherapyEvent, TherapyEvent>> = fun getNextSyncElementTherapyEvent(id: Long): Maybe<Pair<TherapyEvent, TherapyEvent>> =
database.therapyEventDao.getNextModifiedOrNewAfter(id) database.therapyEventDao.getNextModifiedOrNewAfter(id)
.flatMap { nextIdElement -> .flatMap { nextIdElement ->
@ -431,6 +445,9 @@ import kotlin.math.roundToInt
database.therapyEventDao.getLastId() database.therapyEventDao.getLastId()
// FOOD // FOOD
fun findFoodByNSId(nsId: String): Food? =
database.foodDao.findByNSId(nsId)
/* /*
* returns a Pair of the next entity to sync and the ID of the "update". * returns a Pair of the next entity to sync and the ID of the "update".
* The update id might either be the entry id itself if it is a new entry - or the id * The update id might either be the entry id itself if it is a new entry - or the id
@ -465,6 +482,9 @@ import kotlin.math.roundToInt
database.foodDao.getLastId() database.foodDao.getLastId()
// BOLUS // BOLUS
fun findBolusByNSId(nsId: String): Bolus? =
database.bolusDao.findByNSId(nsId)
/* /*
* returns a Pair of the next entity to sync and the ID of the "update". * returns a Pair of the next entity to sync and the ID of the "update".
* The update id might either be the entry id itself if it is a new entry - or the id * The update id might either be the entry id itself if it is a new entry - or the id
@ -531,6 +551,9 @@ import kotlin.math.roundToInt
database.bolusDao.getLastId() database.bolusDao.getLastId()
// CARBS // CARBS
fun findCarbsByNSId(nsId: String): Carbs? =
database.carbsDao.findByNSId(nsId)
private fun expandCarbs(carbs: Carbs): List<Carbs> = private fun expandCarbs(carbs: Carbs): List<Carbs> =
if (carbs.duration == 0L) { if (carbs.duration == 0L) {
listOf(carbs) listOf(carbs)
@ -646,6 +669,9 @@ import kotlin.math.roundToInt
database.carbsDao.getLastId() database.carbsDao.getLastId()
// BOLUS CALCULATOR RESULT // BOLUS CALCULATOR RESULT
fun findBolusCalculatorResultByNSId(nsId: String): BolusCalculatorResult? =
database.bolusCalculatorResultDao.findByNSId(nsId)
/* /*
* returns a Pair of the next entity to sync and the ID of the "update". * returns a Pair of the next entity to sync and the ID of the "update".
* The update id might either be the entry id itself if it is a new entry - or the id * The update id might either be the entry id itself if it is a new entry - or the id
@ -709,6 +735,9 @@ import kotlin.math.roundToInt
database.deviceStatusDao.getLastId() database.deviceStatusDao.getLastId()
// TEMPORARY BASAL // TEMPORARY BASAL
fun findTemporaryBasalByNSId(nsId: String): TemporaryBasal? =
database.temporaryBasalDao.findByNSId(nsId)
/* /*
* returns a Pair of the next entity to sync and the ID of the "update". * returns a Pair of the next entity to sync and the ID of the "update".
* The update id might either be the entry id itself if it is a new entry - or the id * The update id might either be the entry id itself if it is a new entry - or the id
@ -773,6 +802,9 @@ import kotlin.math.roundToInt
database.temporaryBasalDao.getLastId() database.temporaryBasalDao.getLastId()
// EXTENDED BOLUS // EXTENDED BOLUS
fun findExtendedBolusByNSId(nsId: String): ExtendedBolus? =
database.extendedBolusDao.findByNSId(nsId)
/* /*
* returns a Pair of the next entity to sync and the ID of the "update". * returns a Pair of the next entity to sync and the ID of the "update".
* The update id might either be the entry id itself if it is a new entry - or the id * The update id might either be the entry id itself if it is a new entry - or the id
@ -844,6 +876,9 @@ import kotlin.math.roundToInt
} }
// OFFLINE EVENT // OFFLINE EVENT
fun findOfflineEventByNSId(nsId: String): OfflineEvent? =
database.offlineEventDao.findByNSId(nsId)
/* /*
* returns a Pair of the next entity to sync and the ID of the "update". * returns a Pair of the next entity to sync and the ID of the "update".
* The update id might either be the entry id itself if it is a new entry - or the id * The update id might either be the entry id itself if it is a new entry - or the id
@ -897,6 +932,11 @@ import kotlin.math.roundToInt
fun getLastOfflineEventId(): Long? = fun getLastOfflineEventId(): Long? =
database.offlineEventDao.getLastId() database.offlineEventDao.getLastId()
fun getHeartRatesFromTime(timeMillis: Long) = database.heartRateDao.getFromTime(timeMillis)
fun getHeartRatesFromTimeToTime(startMillis: Long, endMillis: Long) =
database.heartRateDao.getFromTimeToTime(startMillis, endMillis)
suspend fun collectNewEntriesSince(since: Long, until: Long, limit: Int, offset: Int) = NewEntries( suspend fun collectNewEntriesSince(since: Long, until: Long, limit: Int, offset: Int) = NewEntries(
apsResults = database.apsResultDao.getNewEntriesSince(since, until, limit, offset), apsResults = database.apsResultDao.getNewEntriesSince(since, until, limit, offset),
apsResultLinks = database.apsResultLinkDao.getNewEntriesSince(since, until, limit, offset), apsResultLinks = database.apsResultLinkDao.getNewEntriesSince(since, until, limit, offset),
@ -915,6 +955,7 @@ import kotlin.math.roundToInt
therapyEvents = database.therapyEventDao.getNewEntriesSince(since, until, limit, offset), therapyEvents = database.therapyEventDao.getNewEntriesSince(since, until, limit, offset),
totalDailyDoses = database.totalDailyDoseDao.getNewEntriesSince(since, until, limit, offset), totalDailyDoses = database.totalDailyDoseDao.getNewEntriesSince(since, until, limit, offset),
versionChanges = database.versionChangeDao.getNewEntriesSince(since, until, limit, offset), versionChanges = database.versionChangeDao.getNewEntriesSince(since, until, limit, offset),
heartRates = database.heartRateDao.getNewEntriesSince(since, until, limit, offset),
) )
} }
@ -923,4 +964,3 @@ inline fun <reified T : Any> Maybe<T>.toWrappedSingle(): Single<ValueWrapper<T>>
this.map { ValueWrapper.Existing(it) as ValueWrapper<T> } this.map { ValueWrapper.Existing(it) as ValueWrapper<T> }
.switchIfEmpty(Maybe.just(ValueWrapper.Absent())) .switchIfEmpty(Maybe.just(ValueWrapper.Absent()))
.toSingle() .toSingle()

View file

@ -1,12 +1,14 @@
package info.nightscout.database.impl package info.nightscout.database.impl
import android.content.Context import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase.Callback import androidx.room.RoomDatabase.Callback
import androidx.room.migration.Migration import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import info.nightscout.database.entities.TABLE_HEART_RATE
import javax.inject.Qualifier import javax.inject.Qualifier
import javax.inject.Singleton import javax.inject.Singleton
@ -22,13 +24,7 @@ open class DatabaseModule {
internal fun provideAppDatabase(context: Context, @DbFileName fileName: String) = internal fun provideAppDatabase(context: Context, @DbFileName fileName: String) =
Room Room
.databaseBuilder(context, AppDatabase::class.java, fileName) .databaseBuilder(context, AppDatabase::class.java, fileName)
// .addMigrations(migration5to6) .addMigrations(*migrations)
// .addMigrations(migration6to7)
// .addMigrations(migration7to8)
// .addMigrations(migration11to12)
.addMigrations(migration20to21)
.addMigrations(migration21to22)
.addMigrations(migration22to23)
.addCallback(object : Callback() { .addCallback(object : Callback() {
override fun onOpen(db: SupportSQLiteDatabase) { override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db) super.onOpen(db)
@ -89,4 +85,36 @@ open class DatabaseModule {
} }
} }
private val migration23to24 = object : Migration(23, 24) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"""CREATE TABLE IF NOT EXISTS `$TABLE_HEART_RATE` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`duration` INTEGER NOT NULL,
`timestamp` INTEGER NOT NULL,
`beatsPerMinute` REAL NOT NULL,
`device` TEXT NOT NULL,
`utcOffset` INTEGER NOT NULL,
`version` INTEGER NOT NULL,
`dateCreated` INTEGER NOT NULL,
`isValid` INTEGER NOT NULL,
`referenceId` INTEGER,
`nightscoutSystemId` TEXT,
`nightscoutId` TEXT,
`pumpType` TEXT,
`pumpSerial` TEXT,
`temporaryId` INTEGER,
`pumpId` INTEGER, `startId` INTEGER,
`endId` INTEGER)""".trimIndent()
)
database.execSQL("""CREATE INDEX IF NOT EXISTS `index_heartRate_id` ON `$TABLE_HEART_RATE` (`id`)""")
database.execSQL("""CREATE INDEX IF NOT EXISTS `index_heartRate_timestamp` ON `$TABLE_HEART_RATE` (`timestamp`)""")
// Custom indexes must be dropped on migration to pass room schema checking after upgrade
dropCustomIndexes(database)
}
}
/** List of all migrations for easy reply in tests. */
@VisibleForTesting
internal val migrations = arrayOf(migration20to21, migration21to22, migration22to23, migration23to24)
} }

View file

@ -41,6 +41,8 @@ import info.nightscout.database.impl.daos.delegated.DelegatedTotalDailyDoseDao
import info.nightscout.database.impl.daos.delegated.DelegatedUserEntryDao import info.nightscout.database.impl.daos.delegated.DelegatedUserEntryDao
import info.nightscout.database.impl.daos.delegated.DelegatedVersionChangeDao import info.nightscout.database.impl.daos.delegated.DelegatedVersionChangeDao
import info.nightscout.database.entities.interfaces.DBEntry import info.nightscout.database.entities.interfaces.DBEntry
import info.nightscout.database.impl.daos.HeartRateDao
import info.nightscout.database.impl.daos.delegated.DelegatedHeartRateDao
internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val database: AppDatabase) { internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val database: AppDatabase) {
@ -64,5 +66,6 @@ internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val datab
val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao) val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao)
val deviceStatusDao: DeviceStatusDao = DelegatedDeviceStatusDao(changes, database.deviceStatusDao) val deviceStatusDao: DeviceStatusDao = DelegatedDeviceStatusDao(changes, database.deviceStatusDao)
val offlineEventDao: OfflineEventDao = DelegatedOfflineEventDao(changes, database.offlineEventDao) val offlineEventDao: OfflineEventDao = DelegatedOfflineEventDao(changes, database.offlineEventDao)
val heartRateDao: HeartRateDao = DelegatedHeartRateDao(changes, database.heartRateDao)
fun clearAllTables() = database.clearAllTables() fun clearAllTables() = database.clearAllTables()
} }

View file

@ -29,7 +29,7 @@ internal interface GlucoseValueDao : TraceableDao<GlucoseValue> {
fun getLastId(): Long? fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE nightscoutId = :nsId AND referenceId IS NULL") @Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSIdMaybe(nsId: String): Maybe<GlucoseValue> fun findByNSId(nsId: String): GlucoseValue?
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE timestamp = :timestamp AND sourceSensor = :sourceSensor AND referenceId IS NULL") @Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE timestamp = :timestamp AND sourceSensor = :sourceSensor AND referenceId IS NULL")
fun findByTimestampAndSensor(timestamp: Long, sourceSensor: GlucoseValue.SourceSensor): GlucoseValue? fun findByTimestampAndSensor(timestamp: Long, sourceSensor: GlucoseValue.SourceSensor): GlucoseValue?

View file

@ -0,0 +1,31 @@
package info.nightscout.database.impl.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.database.entities.HeartRate
import info.nightscout.database.entities.TABLE_HEART_RATE
@Dao
internal interface HeartRateDao : TraceableDao<HeartRate> {
@Query("SELECT * FROM $TABLE_HEART_RATE WHERE id = :id")
override fun findById(id: Long): HeartRate?
@Query("DELETE FROM $TABLE_HEART_RATE")
override fun deleteAllEntries()
@Query("DELETE FROM $TABLE_HEART_RATE WHERE timestamp < :than")
override fun deleteOlderThan(than: Long): Int
@Query("DELETE FROM $TABLE_HEART_RATE WHERE referenceId IS NOT NULL")
override fun deleteTrackedChanges(): Int
@Query("SELECT * FROM $TABLE_HEART_RATE WHERE timestamp >= :timestamp ORDER BY timestamp")
fun getFromTime(timestamp: Long): List<HeartRate>
@Query("SELECT * FROM $TABLE_HEART_RATE WHERE timestamp BETWEEN :startMillis AND :endMillis ORDER BY timestamp")
fun getFromTimeToTime(startMillis: Long, endMillis: Long): List<HeartRate>
@Query("SELECT * FROM $TABLE_HEART_RATE WHERE timestamp > :since AND timestamp <= :until LIMIT :limit OFFSET :offset")
fun getNewEntriesSince(since: Long, until: Long, limit: Int, offset: Int): List<HeartRate>
}

View file

@ -0,0 +1,20 @@
package info.nightscout.database.impl.daos.delegated
import info.nightscout.database.entities.HeartRate
import info.nightscout.database.entities.interfaces.DBEntry
import info.nightscout.database.impl.daos.HeartRateDao
internal class DelegatedHeartRateDao(
changes: MutableList<DBEntry>,
private val dao:HeartRateDao): DelegatedDao(changes), HeartRateDao by dao {
override fun insertNewEntry(entry: HeartRate): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: HeartRate): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,20 @@
package info.nightscout.database.impl.transactions
import info.nightscout.database.entities.HeartRate
class InsertOrUpdateHeartRateTransaction(private val heartRate: HeartRate):
Transaction<InsertOrUpdateHeartRateTransaction.TransactionResult>() {
override fun run(): TransactionResult {
val existing = if (heartRate.id == 0L) null else database.heartRateDao.findById(heartRate.id)
return if (existing == null) {
database.heartRateDao.insertNewEntry(heartRate).let {
TransactionResult(listOf(heartRate), emptyList()) }
} else {
database.heartRateDao.updateExistingEntry(heartRate)
TransactionResult(emptyList(), listOf(heartRate))
}
}
data class TransactionResult(val inserted: List<HeartRate>, val updated: List<HeartRate>)
}

View file

@ -8,10 +8,11 @@ class InvalidateBolusCalculatorResultTransaction(val id: Long) : Transaction<Inv
val result = TransactionResult() val result = TransactionResult()
val bolusCalculatorResult = database.bolusCalculatorResultDao.findById(id) val bolusCalculatorResult = database.bolusCalculatorResultDao.findById(id)
?: throw IllegalArgumentException("There is no such BolusCalculatorResult with the specified ID.") ?: throw IllegalArgumentException("There is no such BolusCalculatorResult with the specified ID.")
if (bolusCalculatorResult.isValid) {
bolusCalculatorResult.isValid = false bolusCalculatorResult.isValid = false
database.bolusCalculatorResultDao.updateExistingEntry(bolusCalculatorResult) database.bolusCalculatorResultDao.updateExistingEntry(bolusCalculatorResult)
result.invalidated.add(bolusCalculatorResult) result.invalidated.add(bolusCalculatorResult)
}
return result return result
} }

View file

@ -8,9 +8,11 @@ class InvalidateBolusTransaction(val id: Long) : Transaction<InvalidateBolusTran
val result = TransactionResult() val result = TransactionResult()
val bolus = database.bolusDao.findById(id) val bolus = database.bolusDao.findById(id)
?: throw IllegalArgumentException("There is no such Bolus with the specified ID.") ?: throw IllegalArgumentException("There is no such Bolus with the specified ID.")
if (bolus.isValid) {
bolus.isValid = false bolus.isValid = false
database.bolusDao.updateExistingEntry(bolus) database.bolusDao.updateExistingEntry(bolus)
result.invalidated.add(bolus) result.invalidated.add(bolus)
}
return result return result
} }

View file

@ -8,9 +8,11 @@ class InvalidateCarbsTransaction(val id: Long) : Transaction<InvalidateCarbsTran
val result = TransactionResult() val result = TransactionResult()
val carbs = database.carbsDao.findById(id) val carbs = database.carbsDao.findById(id)
?: throw IllegalArgumentException("There is no such Carbs with the specified ID.") ?: throw IllegalArgumentException("There is no such Carbs with the specified ID.")
if (carbs.isValid) {
carbs.isValid = false carbs.isValid = false
database.carbsDao.updateExistingEntry(carbs) database.carbsDao.updateExistingEntry(carbs)
result.invalidated.add(carbs) result.invalidated.add(carbs)
}
return result return result
} }

View file

@ -0,0 +1,23 @@
package info.nightscout.database.impl.transactions
import info.nightscout.database.entities.EffectiveProfileSwitch
class InvalidateEffectiveProfileSwitchTransaction(val id: Long) : Transaction<InvalidateEffectiveProfileSwitchTransaction.TransactionResult>() {
override fun run(): TransactionResult {
val result = TransactionResult()
val effectiveProfileSwitch = database.effectiveProfileSwitchDao.findById(id)
?: throw IllegalArgumentException("There is no such EffectiveProfileSwitch with the specified ID.")
if (effectiveProfileSwitch.isValid) {
effectiveProfileSwitch.isValid = false
database.effectiveProfileSwitchDao.updateExistingEntry(effectiveProfileSwitch)
result.invalidated.add(effectiveProfileSwitch)
}
return result
}
class TransactionResult {
val invalidated = mutableListOf<EffectiveProfileSwitch>()
}
}

View file

@ -8,9 +8,11 @@ class InvalidateExtendedBolusTransaction(val id: Long) : Transaction<InvalidateE
val result = TransactionResult() val result = TransactionResult()
val extendedBolus = database.extendedBolusDao.findById(id) val extendedBolus = database.extendedBolusDao.findById(id)
?: throw IllegalArgumentException("There is no such Extended Bolus with the specified ID.") ?: throw IllegalArgumentException("There is no such Extended Bolus with the specified ID.")
if (extendedBolus.isValid) {
extendedBolus.isValid = false extendedBolus.isValid = false
database.extendedBolusDao.updateExistingEntry(extendedBolus) database.extendedBolusDao.updateExistingEntry(extendedBolus)
result.invalidated.add(extendedBolus) result.invalidated.add(extendedBolus)
}
return result return result
} }

View file

@ -5,7 +5,9 @@ class InvalidateFoodTransaction(val id: Long) : Transaction<Unit>() {
override fun run() { override fun run() {
val food = database.foodDao.findById(id) val food = database.foodDao.findById(id)
?: throw IllegalArgumentException("There is no such Food with the specified ID.") ?: throw IllegalArgumentException("There is no such Food with the specified ID.")
if (food.isValid) {
food.isValid = false food.isValid = false
database.foodDao.updateExistingEntry(food) database.foodDao.updateExistingEntry(food)
} }
}
} }

View file

@ -11,9 +11,11 @@ class InvalidateGlucoseValueTransaction(val id: Long) : Transaction<InvalidateGl
val result = TransactionResult() val result = TransactionResult()
val glucoseValue = database.glucoseValueDao.findById(id) val glucoseValue = database.glucoseValueDao.findById(id)
?: throw IllegalArgumentException("There is no such GlucoseValue with the specified ID.") ?: throw IllegalArgumentException("There is no such GlucoseValue with the specified ID.")
if (glucoseValue.isValid) {
glucoseValue.isValid = false glucoseValue.isValid = false
database.glucoseValueDao.updateExistingEntry(glucoseValue) database.glucoseValueDao.updateExistingEntry(glucoseValue)
result.invalidated.add(glucoseValue) result.invalidated.add(glucoseValue)
}
return result return result
} }

View file

@ -1,22 +0,0 @@
package info.nightscout.database.impl.transactions
import info.nightscout.database.entities.ProfileSwitch
class InvalidateNsIdProfileSwitchTransaction(val nsId: String) : Transaction<InvalidateNsIdProfileSwitchTransaction.TransactionResult>() {
override fun run() : TransactionResult{
val result = TransactionResult()
val current = database.profileSwitchDao.findByNSId(nsId)
if (current != null) {
current.isValid = false
database.profileSwitchDao.updateExistingEntry(current)
result.invalidated.add(current)
}
return result
}
class TransactionResult {
val invalidated = mutableListOf<ProfileSwitch>()
}
}

View file

@ -1,10 +1,23 @@
package info.nightscout.database.impl.transactions package info.nightscout.database.impl.transactions
class InvalidateOfflineEventTransaction(val id: Long) : Transaction<Unit>() { import info.nightscout.database.entities.OfflineEvent
override fun run() {
class InvalidateOfflineEventTransaction(val id: Long) : Transaction<InvalidateOfflineEventTransaction.TransactionResult>() {
override fun run(): TransactionResult {
val result = TransactionResult()
val offlineEvent = database.offlineEventDao.findById(id) val offlineEvent = database.offlineEventDao.findById(id)
?: throw IllegalArgumentException("There is no such OfflineEvent with the specified ID.") ?: throw IllegalArgumentException("There is no such OfflineEvent with the specified ID.")
if (offlineEvent.isValid) {
offlineEvent.isValid = false offlineEvent.isValid = false
database.offlineEventDao.updateExistingEntry(offlineEvent) database.offlineEventDao.updateExistingEntry(offlineEvent)
result.invalidated.add(offlineEvent)
}
return result
}
class TransactionResult {
val invalidated = mutableListOf<OfflineEvent>()
} }
} }

View file

@ -8,9 +8,11 @@ class InvalidateProfileSwitchTransaction(val id: Long) : Transaction<InvalidateP
val result = TransactionResult() val result = TransactionResult()
val profileSwitch = database.profileSwitchDao.findById(id) val profileSwitch = database.profileSwitchDao.findById(id)
?: throw IllegalArgumentException("There is no such ProfileSwitch with the specified ID.") ?: throw IllegalArgumentException("There is no such ProfileSwitch with the specified ID.")
if (profileSwitch.isValid) {
profileSwitch.isValid = false profileSwitch.isValid = false
database.profileSwitchDao.updateExistingEntry(profileSwitch) database.profileSwitchDao.updateExistingEntry(profileSwitch)
result.invalidated.add(profileSwitch) result.invalidated.add(profileSwitch)
}
return result return result
} }

View file

@ -8,9 +8,11 @@ class InvalidateTemporaryBasalTransaction(val id: Long) : Transaction<Invalidate
val result = TransactionResult() val result = TransactionResult()
val temporaryBasal = database.temporaryBasalDao.findById(id) val temporaryBasal = database.temporaryBasalDao.findById(id)
?: throw IllegalArgumentException("There is no such Temporary Basal with the specified ID.") ?: throw IllegalArgumentException("There is no such Temporary Basal with the specified ID.")
if (temporaryBasal.isValid) {
temporaryBasal.isValid = false temporaryBasal.isValid = false
database.temporaryBasalDao.updateExistingEntry(temporaryBasal) database.temporaryBasalDao.updateExistingEntry(temporaryBasal)
result.invalidated.add(temporaryBasal) result.invalidated.add(temporaryBasal)
}
return result return result
} }

View file

@ -10,9 +10,11 @@ class InvalidateTemporaryBasalTransactionWithPumpId(val pumpId: Long, val pumpTy
val result = TransactionResult() val result = TransactionResult()
val temporaryBasal = database.temporaryBasalDao.findByPumpIds(pumpId, pumpType, pumpSerial) val temporaryBasal = database.temporaryBasalDao.findByPumpIds(pumpId, pumpType, pumpSerial)
?: throw IllegalArgumentException("There is no such Temporary Basal with the specified temp ID.") ?: throw IllegalArgumentException("There is no such Temporary Basal with the specified temp ID.")
if (temporaryBasal.isValid) {
temporaryBasal.isValid = false temporaryBasal.isValid = false
database.temporaryBasalDao.updateExistingEntry(temporaryBasal) database.temporaryBasalDao.updateExistingEntry(temporaryBasal)
result.invalidated.add(temporaryBasal) result.invalidated.add(temporaryBasal)
}
return result return result
} }

View file

@ -8,9 +8,11 @@ class InvalidateTemporaryBasalWithTempIdTransaction(val tempId: Long) : Transact
val result = TransactionResult() val result = TransactionResult()
val temporaryBasal = database.temporaryBasalDao.findByTempId(tempId) val temporaryBasal = database.temporaryBasalDao.findByTempId(tempId)
?: throw IllegalArgumentException("There is no such Temporary Basal with the specified temp ID.") ?: throw IllegalArgumentException("There is no such Temporary Basal with the specified temp ID.")
if (temporaryBasal.isValid) {
temporaryBasal.isValid = false temporaryBasal.isValid = false
database.temporaryBasalDao.updateExistingEntry(temporaryBasal) database.temporaryBasalDao.updateExistingEntry(temporaryBasal)
result.invalidated.add(temporaryBasal) result.invalidated.add(temporaryBasal)
}
return result return result
} }

View file

@ -1,10 +1,23 @@
package info.nightscout.database.impl.transactions package info.nightscout.database.impl.transactions
class InvalidateTemporaryTargetTransaction(val id: Long) : Transaction<Unit>() { import info.nightscout.database.entities.TemporaryTarget
override fun run() {
class InvalidateTemporaryTargetTransaction(val id: Long) : Transaction<InvalidateTemporaryTargetTransaction.TransactionResult>() {
override fun run(): TransactionResult {
val result = TransactionResult()
val temporaryTarget = database.temporaryTargetDao.findById(id) val temporaryTarget = database.temporaryTargetDao.findById(id)
?: throw IllegalArgumentException("There is no such TemporaryTarget with the specified ID.") ?: throw IllegalArgumentException("There is no such TemporaryTarget with the specified ID.")
if (temporaryTarget.isValid) {
temporaryTarget.isValid = false temporaryTarget.isValid = false
database.temporaryTargetDao.updateExistingEntry(temporaryTarget) database.temporaryTargetDao.updateExistingEntry(temporaryTarget)
result.invalidated.add(temporaryTarget)
}
return result
}
class TransactionResult {
val invalidated = mutableListOf<TemporaryTarget>()
} }
} }

View file

@ -8,9 +8,11 @@ class InvalidateTherapyEventTransaction(val id: Long) : Transaction<InvalidateTh
val result = TransactionResult() val result = TransactionResult()
val therapyEvent = database.therapyEventDao.findById(id) val therapyEvent = database.therapyEventDao.findById(id)
?: throw IllegalArgumentException("There is no such TherapyEvent with the specified ID.") ?: throw IllegalArgumentException("There is no such TherapyEvent with the specified ID.")
if (therapyEvent.isValid) {
therapyEvent.isValid = false therapyEvent.isValid = false
database.therapyEventDao.updateExistingEntry(therapyEvent) database.therapyEventDao.updateExistingEntry(therapyEvent)
result.invalidated.add(therapyEvent) result.invalidated.add(therapyEvent)
}
return result return result
} }

View file

@ -24,3 +24,11 @@ android.useAndroidX=true
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
# Cache is causeing issues with CircleCI nad maybe Studio 2021 # Cache is causeing issues with CircleCI nad maybe Studio 2021
# org.gradle.unsafe.configuration-cache=true # org.gradle.unsafe.configuration-cache=true
# After migration to kotlin 1.8.21
#e: org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during psi2ir
# File being compiled: (37,18) in /home/circleci/project/core/interfaces/src/main/java/info/nightscout/interfaces/pump/defs/PumpCapability.kt
# The root cause org.jetbrains.kotlin.psi2ir.generators.ErrorExpressionException was thrown at: org.jetbrains.kotlin.psi2ir.generators.ErrorExpressionGenerator.generateErrorCall(ErrorExpressionGenerator.kt:100)
# null: KtCallExpression
# https://youtrack.jetbrains.com/issue/KT-58027
kapt.use.jvm.ir=false

View file

@ -29,16 +29,14 @@ class UserEntryLoggerImpl @Inject constructor(
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
override fun log(action: Action, source: Sources, note: String?, vararg listValues: ValueWithUnit?) = log(action, source, note, listValues.toList()) override fun log(action: Action, source: Sources, note: String?, timestamp: Long, vararg listValues: ValueWithUnit?) = log(action, source, note, timestamp, listValues.toList())
override fun log(action: Action, source: Sources, vararg listValues: ValueWithUnit?) = log(action, source, "", listValues.toList()) override fun log(action: Action, source: Sources, note: String?, timestamp: Long, listValues: List<ValueWithUnit?>) {
override fun log(action: Action, source: Sources, note: String?, listValues: List<ValueWithUnit?>) {
val filteredValues = listValues.toList().filterNotNull() val filteredValues = listValues.toList().filterNotNull()
log( log(
listOf( listOf(
UserEntry( UserEntry(
timestamp = dateUtil.now(), timestamp = timestamp,
action = action, action = action,
source = source, source = source,
note = note ?: "", note = note ?: "",
@ -48,6 +46,12 @@ class UserEntryLoggerImpl @Inject constructor(
) )
} }
override fun log(action: Action, source: Sources, note: String?, vararg listValues: ValueWithUnit?) = log(action, source, note, listValues.toList())
override fun log(action: Action, source: Sources, vararg listValues: ValueWithUnit?) = log(action, source, "", listValues.toList())
override fun log(action: Action, source: Sources, note: String?, listValues: List<ValueWithUnit?>) = log(action, source, note, dateUtil.now(), listValues)
override fun log(entries: List<UserEntry>) { override fun log(entries: List<UserEntry>) {
compositeDisposable += repository.runTransactionForResult(UserEntryTransaction(entries)) compositeDisposable += repository.runTransactionForResult(UserEntryTransaction(entries))
.subscribeOn(aapsSchedulers.io) .subscribeOn(aapsSchedulers.io)

View file

@ -87,6 +87,7 @@ class OverviewDataImpl @Inject constructor(
dsMinSeries = LineGraphSeries() dsMinSeries = LineGraphSeries()
treatmentsSeries = PointsWithLabelGraphSeries() treatmentsSeries = PointsWithLabelGraphSeries()
epsSeries = PointsWithLabelGraphSeries() epsSeries = PointsWithLabelGraphSeries()
heartRateGraphSeries = LineGraphSeries()
} }
override fun initRange() { override fun initRange() {
@ -322,4 +323,6 @@ class OverviewDataImpl @Inject constructor(
override val dsMinScale = Scale() override val dsMinScale = Scale()
override var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries() override var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
override var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries() override var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
override var heartRateScale = Scale()
override var heartRateGraphSeries: LineGraphSeries<DataPointWithLabelInterface> = LineGraphSeries()
} }

View file

@ -71,7 +71,7 @@ class ProfileFunctionImpl @Inject constructor(
override fun getProfileNameWithRemainingTime(): String = override fun getProfileNameWithRemainingTime(): String =
getProfileName(System.currentTimeMillis(), customized = true, showRemainingTime = true) getProfileName(System.currentTimeMillis(), customized = true, showRemainingTime = true)
fun getProfileName(time: Long, customized: Boolean, showRemainingTime: Boolean): String { private fun getProfileName(time: Long, customized: Boolean, showRemainingTime: Boolean): String {
var profileName = rh.gs(info.nightscout.core.ui.R.string.no_profile_set) var profileName = rh.gs(info.nightscout.core.ui.R.string.no_profile_set)
val profileSwitch = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet() val profileSwitch = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet()

View file

@ -273,7 +273,13 @@ class PumpSyncImplementation @Inject constructor(
pumpSerial = pumpSerial pumpSerial = pumpSerial
) )
) )
uel.log(UserEntry.Action.CAREPORTAL, pumpType.source.toDbSource(), note, ValueWithUnit.Timestamp(timestamp), ValueWithUnit.TherapyEventType(type.toDBbEventType())) uel.log(
action = UserEntry.Action.CAREPORTAL,
source = pumpType.source.toDbSource(),
note = note,
timestamp = timestamp,
ValueWithUnit.Timestamp(timestamp), ValueWithUnit.TherapyEventType(type.toDBbEventType())
)
repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(therapyEvent)) repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(therapyEvent))
.doOnError { .doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving TherapyEvent", it) aapsLogger.error(LTag.DATABASE, "Error while saving TherapyEvent", it)

View file

@ -113,12 +113,13 @@ class CommandQueueImplementation @Inject constructor(
return@subscribe return@subscribe
} }
aapsLogger.debug(LTag.PROFILE, "onEventProfileSwitchChanged") aapsLogger.debug(LTag.PROFILE, "onEventProfileSwitchChanged")
val effective = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet()
profileFunction.getRequestedProfile()?.let { profileFunction.getRequestedProfile()?.let {
setProfile(ProfileSealed.PS(it), it.interfaceIDs.nightscoutId != null, object : Callback() { setProfile(ProfileSealed.PS(it), it.interfaceIDs.nightscoutId != null, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
uiInteraction.runAlarm(result.comment, rh.gs(info.nightscout.core.ui.R.string.failed_update_basal_profile), info.nightscout.core.ui.R.raw.boluserror) uiInteraction.runAlarm(result.comment, rh.gs(info.nightscout.core.ui.R.string.failed_update_basal_profile), info.nightscout.core.ui.R.raw.boluserror)
} else if (result.enacted) { } else if (result.enacted || effective is ValueWrapper.Existing && effective.value.originalEnd < dateUtil.now() && effective.value.originalDuration != 0L) {
val nonCustomized = ProfileSealed.PS(it).convertToNonCustomizedProfile(dateUtil) val nonCustomized = ProfileSealed.PS(it).convertToNonCustomizedProfile(dateUtil)
EffectiveProfileSwitch( EffectiveProfileSwitch(
timestamp = dateUtil.now(), timestamp = dateUtil.now(),
@ -421,7 +422,7 @@ class CommandQueueImplementation @Inject constructor(
} }
// returns true if command is queued // returns true if command is queued
override fun setProfile(profile: Profile, hasNsId: Boolean, callback: Callback?): Boolean { fun setProfile(profile: ProfileSealed, hasNsId: Boolean, callback: Callback?): Boolean {
if (isRunning(CommandType.BASAL_PROFILE)) { if (isRunning(CommandType.BASAL_PROFILE)) {
aapsLogger.debug(LTag.PUMPQUEUE, "Command is already executed") aapsLogger.debug(LTag.PUMPQUEUE, "Command is already executed")
callback?.result(PumpEnactResult(injector).success(true).enacted(false))?.run() callback?.result(PumpEnactResult(injector).success(true).enacted(false))?.run()

View file

@ -19,6 +19,7 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import info.nightscout.core.ui.getThemeColor import info.nightscout.core.ui.getThemeColor
import info.nightscout.core.ui.locale.LocaleHelper
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import java.util.Locale import java.util.Locale
@ -29,15 +30,12 @@ import javax.inject.Inject
*/ */
class ResourceHelperImpl @Inject constructor(var context: Context, private val fabricPrivacy: FabricPrivacy) : ResourceHelper { class ResourceHelperImpl @Inject constructor(var context: Context, private val fabricPrivacy: FabricPrivacy) : ResourceHelper {
override fun updateContext(ctx: Context?) { override fun gs(@StringRes id: Int): String =
ctx?.let { context = it } context.createConfigurationContext(Configuration().apply { setLocale(LocaleHelper.currentLocale(context)) }).resources.getString(id)
}
override fun gs(@StringRes id: Int): String = context.getString(id)
override fun gs(@StringRes id: Int, vararg args: Any?): String { override fun gs(@StringRes id: Int, vararg args: Any?): String {
return try { return try {
context.getString(id, *args) context.createConfigurationContext(Configuration().apply { setLocale(LocaleHelper.currentLocale(context)) }).resources.getString(id, *args)
} catch (exception: Exception) { } catch (exception: Exception) {
val resourceName = context.resources.getResourceEntryName(id) val resourceName = context.resources.getResourceEntryName(id)
val resourceValue = context.getString(id) val resourceValue = context.getString(id)

View file

@ -3,21 +3,21 @@
<string name="alert_r7_description"><![CDATA[Kiekis: <b>%1$d%%</b>\nTrukmė: <b>%2$s h</b>]]></string> <string name="alert_r7_description"><![CDATA[Kiekis: <b>%1$d%%</b>\nTrukmė: <b>%2$s h</b>]]></string>
<string name="alert_w31_description"><![CDATA[Rezervuaro tūris: <b>%1$s U</b>]]></string> <string name="alert_w31_description"><![CDATA[Rezervuaro tūris: <b>%1$s U</b>]]></string>
<string name="alert_w32_description">Pakeiskite bateriją.</string> <string name="alert_w32_description">Pakeiskite bateriją.</string>
<string name="alert_w33_description">Nustatykite laiką/datą.</string> <string name="alert_w33_description">Nustatyti laiką/datą.</string>
<string name="alert_w34_description">Kreipkitės į Accu-Chek palaikymo tarnybą.</string> <string name="alert_w34_description">Kreipkitės į Accu-Chek palaikymo tarnybą.</string>
<string name="alert_w36_description"><![CDATA[Kiekis: <b>%1$d%%</b><br/>Trukmė: <b>%2$s h</b>]]></string> <string name="alert_w36_description"><![CDATA[Kiekis: <b>%1$d%%</b><br/>Trukmė: <b>%2$s h</b>]]></string>
<string name="alert_w38_description"><![CDATA[Suprogramuota: <b>%1$s U</b><br/>Suleista: <b>%2$s U</b>]]></string> <string name="alert_w38_description"><![CDATA[Suprogramuota: <b>%1$s U</b><br/>Suleista: <b>%2$s U</b>]]></string>
<string name="alert_m20_description">Įdėkite rezervuarą.</string> <string name="alert_m20_description">Įdėkite rezervuarą.</string>
<string name="alert_m21_description">Pakeiskite rezervuarą.</string> <string name="alert_m21_description">Pakeiskite rezervuarą.</string>
<string name="alert_m22_description">Pakeiskite bateriją.</string> <string name="alert_m22_description">Pakeiskite bateriją.</string>
<string name="alert_m23_description">Patikrinkite pompos būseną.</string> <string name="alert_m23_description">Patikrinkite pompos statusą.</string>
<string name="alert_m24_description">Pakeiskite infuzijos rinkinį.</string> <string name="alert_m24_description">Pakeiskite infuzijos rinkinį.</string>
<string name="alert_m25_description">Kreipkitės į Accu-Chek palaikymo tarnybą.</string> <string name="alert_m25_description">Kreipkitės į Accu-Chek palaikymo tarnybą.</string>
<string name="alert_m26_description">Pakeiskite rezervuarą.</string> <string name="alert_m26_description">Pakeiskite rezervuarą.</string>
<string name="alert_m27_description">Iš naujo paleiskite duomenų atsisiuntimą.</string> <string name="alert_m27_description">Iš naujo paleiskite duomenų atsisiuntimą.</string>
<string name="alert_m28_description">Patikrinkite pompos būseną.</string> <string name="alert_m28_description">Patikrinkite pompos statusą.</string>
<string name="alert_m29_description">Nustatykite baterijos tipą.</string> <string name="alert_m29_description">Nustatyti baterijos tipą.</string>
<string name="alert_m30_description">Nustatykite rezervuaro tipą.</string> <string name="alert_m30_description">Nustatyti rezervuaro tipą.</string>
<string name="alert_e6_description">Pakeiskite bateriją ir rezervuarą.</string> <string name="alert_e6_description">Pakeiskite bateriją ir rezervuarą.</string>
<string name="alert_e10_description">Pakeiskite rezervuarą.</string> <string name="alert_e10_description">Pakeiskite rezervuarą.</string>
<string name="alert_e13_description">Pakeiskite kalbą.</string> <string name="alert_e13_description">Pakeiskite kalbą.</string>

View file

@ -4,7 +4,7 @@
<string name="disable_tbr_over_notification">Išjungti pranešimus apie LBD pabaigą\n(pompos nustatymai)</string> <string name="disable_tbr_over_notification">Išjungti pranešimus apie LBD pabaigą\n(pompos nustatymai)</string>
<string name="not_paired">Nesusieta</string> <string name="not_paired">Nesusieta</string>
<string name="recovering">Atstatoma</string> <string name="recovering">Atstatoma</string>
<string name="insight_status">Būsena</string> <string name="insight_status">Statusas</string>
<string name="recovery_duration">Atkūrimo trukmė</string> <string name="recovery_duration">Atkūrimo trukmė</string>
<string name="last_connected">Paskutinis prisijungimas</string> <string name="last_connected">Paskutinis prisijungimas</string>
<string name="start_pump">Paleisti pompą</string> <string name="start_pump">Paleisti pompą</string>
@ -21,9 +21,9 @@
<string name="eb_formatter">%1$.2f / %2$.2f vv per %3$d min</string> <string name="eb_formatter">%1$.2f / %2$.2f vv per %3$d min</string>
<string name="insight_last_bolus">Paskutinis bolusas</string> <string name="insight_last_bolus">Paskutinis bolusas</string>
<string name="searching_for_devices">Ieškoma įrenginių…</string> <string name="searching_for_devices">Ieškoma įrenginių…</string>
<string name="pairing_completed">Sujungimas sėkmingas</string> <string name="pairing_completed">Sėkmingai susieta</string>
<string name="code_compare">Ar kodas, kurį matote įrenginyje, sutampa su pompos kodu?</string> <string name="code_compare">Ar kodas, kurį matote įrenginyje, sutampa su pompos kodu?</string>
<string name="insight_pairing">Insight sujungimas</string> <string name="insight_pairing">Insight susiejimas</string>
<string name="insight_local">Accu-Chek Insight</string> <string name="insight_local">Accu-Chek Insight</string>
<string name="insight_alert_formatter">%1$s: %2$s</string> <string name="insight_alert_formatter">%1$s: %2$s</string>
<string name="tube_changed">Kateteris pakeistas</string> <string name="tube_changed">Kateteris pakeistas</string>
@ -36,7 +36,7 @@
<string name="timeout_during_handshake">Ryšio užmezgimui skirtas laikas baigėsi - iš naujo nustatykite bluetooth</string> <string name="timeout_during_handshake">Ryšio užmezgimui skirtas laikas baigėsi - iš naujo nustatykite bluetooth</string>
<string name="pump_stopped">Pompa sustabdyta</string> <string name="pump_stopped">Pompa sustabdyta</string>
<string name="pump_started">Pompa paleista</string> <string name="pump_started">Pompa paleista</string>
<string name="short_status_last_connected">Paskutinis prisijungimas: prieš %1$d min</string> <string name="short_status_last_connected">Pask. prijung: prieš %1$d min</string>
<string name="short_status_tbr">LBD: %1$d%% - %2$d / %3$d min</string> <string name="short_status_tbr">LBD: %1$d%% - %2$d / %3$d min</string>
<string name="short_status_extended">Ištęstas: %1$.2f / %2$.2f V %3$d min</string> <string name="short_status_extended">Ištęstas: %1$.2f / %2$.2f V %3$d min</string>
<string name="short_status_multiwave">Daugiabangis: %1$.2f / %2$.2f vv %3$d min</string> <string name="short_status_multiwave">Daugiabangis: %1$.2f / %2$.2f vv %3$d min</string>
@ -52,7 +52,7 @@
<string name="bluetooth_address">Bluetooth adresas</string> <string name="bluetooth_address">Bluetooth adresas</string>
<string name="system_id_appendix">Sistemos ID priedėlis</string> <string name="system_id_appendix">Sistemos ID priedėlis</string>
<string name="manufacturing_date">Pagaminimo data</string> <string name="manufacturing_date">Pagaminimo data</string>
<string name="delete_pairing">Panaikinti sąsają</string> <string name="delete_pairing">Panaikinti susiejimą</string>
<string name="log_site_changes">Įrašyti adatos pakeitimą</string> <string name="log_site_changes">Įrašyti adatos pakeitimą</string>
<string name="log_reservoir_changes">Įrašyti rezervuaro keitimus</string> <string name="log_reservoir_changes">Įrašyti rezervuaro keitimus</string>
<string name="log_tube_changes">Įrašyti infuzijos vamzdelio pakeitimą</string> <string name="log_tube_changes">Įrašyti infuzijos vamzdelio pakeitimą</string>
@ -65,6 +65,6 @@
<string name="max_recovery_duration">Didž. atkūrimo trukmė [s]</string> <string name="max_recovery_duration">Didž. atkūrimo trukmė [s]</string>
<string name="min_recovery_duration">Min. atkūrimo trukmė [s]</string> <string name="min_recovery_duration">Min. atkūrimo trukmė [s]</string>
<string name="pump_alert">Pompos aliarmas</string> <string name="pump_alert">Pompos aliarmas</string>
<string name="pairing_information">Sąsajos informacija</string> <string name="pairing_information">Susiejimo informacija</string>
<string name="insight_refresh_button" comment="26 characters max for translation">Insight Mygtukas Naujinti</string> <string name="insight_refresh_button" comment="26 characters max for translation">Naujinti Insight mygtukas </string>
</resources> </resources>

View file

@ -10,6 +10,7 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.MenuCompat
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
@ -72,7 +73,7 @@ class OpenAPSFragment : DaggerFragment(), MenuProvider {
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
menu.add(Menu.FIRST, ID_MENU_RUN, 0, rh.gs(R.string.openapsma_run)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_RUN, 0, rh.gs(R.string.openapsma_run)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.setGroupDividerEnabled(true) MenuCompat.setGroupDividerEnabled(menu, true)
} }
override fun onMenuItemSelected(item: MenuItem): Boolean = override fun onMenuItemSelected(item: MenuItem): Boolean =

View file

@ -9,6 +9,7 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.MenuCompat
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
@ -74,7 +75,7 @@ class LoopFragment : DaggerFragment(), MenuProvider {
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
menu.add(Menu.FIRST, ID_MENU_RUN, 0, rh.gs(R.string.run_now)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_RUN, 0, rh.gs(R.string.run_now)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.setGroupDividerEnabled(true) MenuCompat.setGroupDividerEnabled(menu, true)
} }
override fun onMenuItemSelected(item: MenuItem): Boolean = override fun onMenuItemSelected(item: MenuItem): Boolean =

View file

@ -25,7 +25,7 @@
<string name="description_ama">2017 m. algoritmas</string> <string name="description_ama">2017 m. algoritmas</string>
<string name="description_smb">Naujausias algoritmas patyrusiems vartotojams</string> <string name="description_smb">Naujausias algoritmas patyrusiems vartotojams</string>
<string name="description_smb_dynamic_isf">Naujausias algoritmas patyrusiems naudotojams su dinaminiu/automatiniu JIF</string> <string name="description_smb_dynamic_isf">Naujausias algoritmas patyrusiems naudotojams su dinaminiu/automatiniu JIF</string>
<string name="openapsama_bolus_snooze_dia_divisor">Boluso snaudimo daliklis</string> <string name="openapsama_bolus_snooze_dia_divisor">Atidėjimo po boluso IVT daliklis</string>
<string name="openapsma_run">Paleisti dabar</string> <string name="openapsma_run">Paleisti dabar</string>
<string name="openapsma_last_run_label">Paskutinis veiksmas</string> <string name="openapsma_last_run_label">Paskutinis veiksmas</string>
<string name="openapsma_input_parameters_label">Įvesties parametrai</string> <string name="openapsma_input_parameters_label">Įvesties parametrai</string>
@ -48,7 +48,7 @@
<string name="openapsama_min_5m_carb_impact_summary">Numatytoji reikšmė: 3.0 (AMA) arba 8.0 (SMB). Tai parametras, nurodantis angliavandenių poveikį glikemijai kas 5 minutes nuo jų suvartojimo. Numatytoji reikšmė yra 3 mg/dl per 5min. Šis skaičius turi įtakos apskaičiavimams, kaip greitai mažės AAO, kokia bus glikemijos kitimo prognozė, ypač kai ji krenta daugiau nei tikėtasi, arba nedidėja tiek, kiek tikėtasi.</string> <string name="openapsama_min_5m_carb_impact_summary">Numatytoji reikšmė: 3.0 (AMA) arba 8.0 (SMB). Tai parametras, nurodantis angliavandenių poveikį glikemijai kas 5 minutes nuo jų suvartojimo. Numatytoji reikšmė yra 3 mg/dl per 5min. Šis skaičius turi įtakos apskaičiavimams, kaip greitai mažės AAO, kokia bus glikemijos kitimo prognozė, ypač kai ji krenta daugiau nei tikėtasi, arba nedidėja tiek, kiek tikėtasi.</string>
<string name="openapsama_max_daily_safety_multiplier_summary">Numatytoji reikšmė: 3 tai pagrindinis OpenAPS saugiklis. Jis apriboja Jūsų valandinę bazę iki trigubos maksimalios valandinės bazės (standartiniu atveju). Jums greičiausiai neprireiks šios reikšmės keisti, tačiau turėtumėte žinoti, kad ji naudojama kaip saugiklis apskaičiuojant \"3x maksimali dienos bazė; 4x dabartinė valandinė bazė\".</string> <string name="openapsama_max_daily_safety_multiplier_summary">Numatytoji reikšmė: 3 tai pagrindinis OpenAPS saugiklis. Jis apriboja Jūsų valandinę bazę iki trigubos maksimalios valandinės bazės (standartiniu atveju). Jums greičiausiai neprireiks šios reikšmės keisti, tačiau turėtumėte žinoti, kad ji naudojama kaip saugiklis apskaičiuojant \"3x maksimali dienos bazė; 4x dabartinė valandinė bazė\".</string>
<string name="openapsama_current_basal_safety_multiplier_summary">Numatytoji reikšmė: 4 tai antras pagrindinis OpenAPS saugiklis, apskaičiuojant \"3x maksimali dienos bazė; 4x dabartinė valandinė bazė\". Jis reiškia, kad jūsų valandinė bazė, nepriklausomai nuo to, kokia maksimali valandinė bazė suprogramuota pompoje, negali būti didesnė, nei keturguba dabartinė valandinė bazė. Tai apsaugo Jus nuo pavojingų situacijų, kai nustatoma pernelyg didelė valandinė bazė, pilnai nesuprantant, kaip veikia algoritmas. Numatytoji reikšmė yra 4x; daugumai vartotojų niekada neprireikia šio skaičiaus keisti, o pajutus, kad \"atsitrenkiama\" į saugiklį, rekomenduojama peržiūrėti kitus nustatymus.</string> <string name="openapsama_current_basal_safety_multiplier_summary">Numatytoji reikšmė: 4 tai antras pagrindinis OpenAPS saugiklis, apskaičiuojant \"3x maksimali dienos bazė; 4x dabartinė valandinė bazė\". Jis reiškia, kad jūsų valandinė bazė, nepriklausomai nuo to, kokia maksimali valandinė bazė suprogramuota pompoje, negali būti didesnė, nei keturguba dabartinė valandinė bazė. Tai apsaugo Jus nuo pavojingų situacijų, kai nustatoma pernelyg didelė valandinė bazė, pilnai nesuprantant, kaip veikia algoritmas. Numatytoji reikšmė yra 4x; daugumai vartotojų niekada neprireikia šio skaičiaus keisti, o pajutus, kad \"atsitrenkiama\" į saugiklį, rekomenduojama peržiūrėti kitus nustatymus.</string>
<string name="openapsama_bolus_snooze_dia_divisor_summary">Numatytoji reikšmė: 2\nBoluso snaudimas aktyvuojamas iškart po to, kai susileidžiate bolusą maistui. Ši funkcija neleidžia sistemai nustatyti mažų LBD iškart po valgio. Pvz.: jei IVT yra 3 val, tai boluso snaudimas pamažu deaktyvuojamas per 1,5 val (3 val. / 2).</string> <string name="openapsama_bolus_snooze_dia_divisor_summary">Numatytoji reikšmė: 2\nAtidėjimas po boluso aktyvuojamas iškart po to, kai susileidžiate bolusą maistui. Ši funkcija neleidžia sistemai nustatyti mažų laikinų bazių iškart po valgio. Pvz.: jei IVT yra 3 val, tai atidėjimas po boluso pamažu deaktyvuojamas per 1,5 val (3 val. / 2).</string>
<string name="openapsama_link_to_preference_json_doc_txt">Dėmesio!\nPaprastai neturėtumėte keisti šių, žemiau esančių, reikšmių. Prašome PASPAUSTI ČIA ir PERSKAITYKITE tekstą ir įsitikinkite, kad SUPRANTATE prieš keisdami bet kurią iš šių verčių.</string> <string name="openapsama_link_to_preference_json_doc_txt">Dėmesio!\nPaprastai neturėtumėte keisti šių, žemiau esančių, reikšmių. Prašome PASPAUSTI ČIA ir PERSKAITYKITE tekstą ir įsitikinkite, kad SUPRANTATE prieš keisdami bet kurią iš šių verčių.</string>
<string name="always_use_short_avg">Visada naudoti trumpo laikotarpio vidutinį pokyti vietoj paprasto pokyčio</string> <string name="always_use_short_avg">Visada naudoti trumpo laikotarpio vidutinį pokyti vietoj paprasto pokyčio</string>
<string name="always_use_short_avg_summary">Naudinga, kai duomenys, gaunami iš nefiltruoto šaltinio, tokio kaip xDrip+, tampa nestabilūs.</string> <string name="always_use_short_avg_summary">Naudinga, kai duomenys, gaunami iš nefiltruoto šaltinio, tokio kaip xDrip+, tampa nestabilūs.</string>

View file

@ -16,6 +16,7 @@ import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.core.util.forEach import androidx.core.util.forEach
import androidx.core.view.MenuCompat
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
@ -61,7 +62,6 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener, MenuProvider {
const val ID_MENU_ADD = 504 const val ID_MENU_ADD = 504
const val ID_MENU_RUN = 505 const val ID_MENU_RUN = 505
const val ID_MENU_EDIT_MOVE = 506
} }
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
@ -98,8 +98,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener, MenuProvider {
actionHelper.onCreateOptionsMenu(menu, inflater) actionHelper.onCreateOptionsMenu(menu, inflater)
menu.add(Menu.FIRST, ID_MENU_ADD, 0, rh.gs(R.string.add_automation)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_ADD, 0, rh.gs(R.string.add_automation)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(Menu.FIRST, ID_MENU_RUN, 0, rh.gs(R.string.run_automations)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_RUN, 0, rh.gs(R.string.run_automations)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(Menu.FIRST, ID_MENU_EDIT_MOVE, 0, rh.gs(R.string.remove_sort)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) MenuCompat.setGroupDividerEnabled(menu, true)
menu.setGroupDividerEnabled(true)
} }
override fun onMenuItemSelected(item: MenuItem): Boolean = override fun onMenuItemSelected(item: MenuItem): Boolean =
@ -115,11 +114,6 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener, MenuProvider {
true true
} }
ID_MENU_EDIT_MOVE -> {
actionHelper.startAction()
true
}
else -> super.onContextItemSelected(item) else -> super.onContextItemSelected(item)
} }

View file

@ -116,7 +116,6 @@
<string name="system_automation">Системна автомация</string> <string name="system_automation">Системна автомация</string>
<string name="run_automations">Стартирай автомаций</string> <string name="run_automations">Стартирай автомаций</string>
<string name="add_automation">Добавяне на правило</string> <string name="add_automation">Добавяне на правило</string>
<string name="remove_sort">Премахни/сортирай</string>
<string name="stop_processing">Спри изпълнението</string> <string name="stop_processing">Спри изпълнението</string>
<!-- WeekdayPicker --> <!-- WeekdayPicker -->
<string name="monday_short">П</string> <string name="monday_short">П</string>

View file

@ -116,7 +116,6 @@
<string name="system_automation">Automatizace systému</string> <string name="system_automation">Automatizace systému</string>
<string name="run_automations">Spustit automatizace</string> <string name="run_automations">Spustit automatizace</string>
<string name="add_automation">Přidat pravidlo</string> <string name="add_automation">Přidat pravidlo</string>
<string name="remove_sort">Odstranit/řadit</string>
<string name="stop_processing">Zastavit zpracovávání</string> <string name="stop_processing">Zastavit zpracovávání</string>
<!-- WeekdayPicker --> <!-- WeekdayPicker -->
<string name="monday_short">Po</string> <string name="monday_short">Po</string>

View file

@ -116,7 +116,6 @@
<string name="system_automation">System automatisering</string> <string name="system_automation">System automatisering</string>
<string name="run_automations">Kør automatisering</string> <string name="run_automations">Kør automatisering</string>
<string name="add_automation">Tilføj regel</string> <string name="add_automation">Tilføj regel</string>
<string name="remove_sort">Fjern/sorter</string>
<string name="stop_processing">Stop afvikling</string> <string name="stop_processing">Stop afvikling</string>
<!-- WeekdayPicker --> <!-- WeekdayPicker -->
<string name="monday_short">Ma</string> <string name="monday_short">Ma</string>

View file

@ -116,7 +116,6 @@
<string name="system_automation">System-Automatisierung</string> <string name="system_automation">System-Automatisierung</string>
<string name="run_automations">Automatisierungen ausführen</string> <string name="run_automations">Automatisierungen ausführen</string>
<string name="add_automation">Regel hinzufügen</string> <string name="add_automation">Regel hinzufügen</string>
<string name="remove_sort">Entfernen/sortieren</string>
<string name="stop_processing">Verarbeitung beenden</string> <string name="stop_processing">Verarbeitung beenden</string>
<!-- WeekdayPicker --> <!-- WeekdayPicker -->
<string name="monday_short">Mo</string> <string name="monday_short">Mo</string>

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