Merge branch 'nightscout:dev' into dev

This commit is contained in:
miyeongkim 2023-07-06 17:48:43 +09:00 committed by GitHub
commit a3577304ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
280 changed files with 6165 additions and 877 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:
android: circleci/android@1.0.3
codecov: codecov/codecov@1.2.0
codecov: codecov/codecov@3.2.4
jobs:
# Below is the definition of your job to build and test your app, you can rename and customize it as you want.

View file

@ -3,6 +3,7 @@ package info.nightscout.rx.weardata
import info.nightscout.rx.events.Event
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.joda.time.DateTime
import java.util.Objects
@Serializable
@ -90,6 +91,16 @@ sealed class EventData : Event() {
@Serializable
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
data class ActionTempTargetPreCheck(
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
*/
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()))
else @Suppress("DEPRECATION") queryBroadcastReceivers(intent, flags)
} catch (ignored: Exception) {
emptyList()
}
/**
* Safe version of getPackageInfo depending on Android version running

View file

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

View file

@ -111,7 +111,7 @@ android {
defaultConfig {
multiDexEnabled true
versionCode 1500
version "3.2.0-dev-i"
version "3.2.0-dev-j"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'

View file

@ -24,6 +24,7 @@ import android.widget.TextView
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.core.view.MenuCompat
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayoutMediator
@ -302,7 +303,7 @@ class MainActivity : DaggerAppCompatActivityWithResult() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menu.setGroupDividerEnabled(true)
MenuCompat.setGroupDividerEnabled(menu, true)
this.menu = menu
menuInflater.inflate(R.menu.menu_main, menu)
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.jjoe64.graphview.GraphView
import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding
import info.nightscout.core.events.EventIobCalculationProgress
import info.nightscout.core.ui.activities.TranslatedDaggerAppCompatActivity
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.core.workflow.CalculationWorkflow
import info.nightscout.interfaces.Config
@ -42,7 +42,7 @@ import java.util.GregorianCalendar
import javax.inject.Inject
import kotlin.math.min
class HistoryBrowseActivity : DaggerAppCompatActivity() {
class HistoryBrowseActivity : TranslatedDaggerAppCompatActivity() {
@Inject lateinit var historyBrowserData: HistoryBrowserData
@Inject lateinit var injector: HasAndroidInjector
@ -327,6 +327,7 @@ class HistoryBrowseActivity : DaggerAppCompatActivity() {
var useRatioForScale = false
var useDSForScale = false
var useBGIForScale = false
var useHRForScale = false
when {
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = 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.SEN.ordinal] -> useRatioForScale = 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]
@ -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.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.HR.ordinal] && config.isDev()) secondGraphData.addHeartRate(useHRForScale, 1.0)
// set manual x bounds to have nice steps
secondGraphData.formatAxis(historyBrowserData.overviewData.fromTime, historyBrowserData.overviewData.endTime)

View file

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

View file

@ -191,7 +191,7 @@ class KeepAliveWorker(
}
if (loop.isDisconnected) {
// 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())
} else if (isStatusOutdated && !pump.isBusy()) {
lastReadStatus = now

View file

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

View file

@ -150,4 +150,7 @@ interface OverviewData {
val dsMinScale: Scale
var dsMaxSeries: 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,
COB_FAIL_OVER,
IOB_PREDICTION,
BUCKETED_BG
BUCKETED_BG,
HEARTRATE,
}
/**
@ -324,6 +325,10 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
mPaint.setStrokeWidth(5);
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
}

View file

@ -7,5 +7,6 @@ interface APS {
var lastDetermineBasalAdapter: DetermineBasalAdapter?
var lastAutosensResult: AutosensResult
operator fun invoke(initiator: String, tempBasalFallback: Boolean)
fun isEnabled(): Boolean
fun invoke(initiator: String, tempBasalFallback: Boolean)
}

View file

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

View file

@ -25,7 +25,6 @@ interface CommandQueue {
fun extendedBolus(insulin: Double, durationInMinutes: Int, callback: Callback?): Boolean
fun cancelTempBasal(enforceNew: Boolean, 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 statusInQueue(): 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 'com.google.guava:guava:31.1-jre'
api 'com.google.guava:guava:32.0.1-jre'
api "androidx.activity:activity-ktx:$activity_version"
api "androidx.appcompat:appcompat:$appcompat_version"

View file

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

View file

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

View file

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

View file

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

View file

@ -23,7 +23,7 @@
<string name="mgdl">mg/dl</string>
<string name="mmol">mmol/l</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="constraints">Apribojimai</string>
<string name="superbolus">Superbolus</string>
@ -82,7 +82,7 @@
<string name="bluetooth">Bluetooth</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="virtualpump_resultok">Gerai</string>
<string name="virtualpump_resultok">OK</string>
<string name="pump_time_updated">Pompos laikas pakeistas</string>
<string name="exit">Išeiti</string>
<string name="removerecord">Ištrinti įrašą</string>
@ -232,7 +232,7 @@
<string name="hypo">Hipo</string>
<string name="activity">Aktyvumas</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="loop">Ciklas</string>
<string name="ns">NS</string>
@ -525,7 +525,7 @@
<!-- Dialogs-->
<string name="confirmation">Patvirtinimas</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="dismiss">ATMESTI</string>
<string name="yes">Taip</string>

View file

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

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="biometric_title">Требуется авторизация</string>
<string name="biometric_description">Поместите палец на устройство считывания отпечатков, чтобы подтвердить свою идентичность</string>
<string name="biometric_description">Поместите палец на устройство считывания отпечатков, чтобы подтвердить свою личность</string>
<string name="settings_protection">Защита настроек</string>
<string name="application_protection">Защита приложения</string>
<string name="bolus_protection">Защита болюсов</string>
<string name="settings_password">Пароль параметров</string>
<string name="settings_pin">ПИН-код настроек</string>
<string name="application_password">Пароль приложения</string>
<string name="application_pin">PIN-код приложения</string>
<string name="bolus_password">Пароль болюсов</string>
<string name="bolus_pin">Пин-код болюса</string>
<string name="protection_timeout_title">Тайм-аут удержания пароля и пин-кода [s]</string>
<string name="settings_password">Пароль для настроек</string>
<string name="settings_pin">ПИН для настроек</string>
<string name="application_password">Пароль для приложения</string>
<string name="application_pin">ПИН для приложения</string>
<string name="bolus_password">Пароль для болюсов</string>
<string name="bolus_pin">ПИН для болюсов</string>
<string name="protection_timeout_title">Тайм-аут удержания пароля и ПИН-кода [s]</string>
<string name="protection_timeout_summary">Время до ввода пароля или PIN-кода</string>
<string name="biometric">Биометрия</string>
<string name="custom_password">Настраиваемый пароль</string>
@ -20,12 +20,12 @@
<string name="unsecure_fallback_biometric">Небезопасный резервный вход</string>
<string name="unsecure_fallback_descriotion_biometric">Биометрической защите требуется главный пароль для безопасности.\n\n Установите главный пароль!</string>
<string name="password_set">Пароль задан!</string>
<string name="pin_set">PIN-код установлен!</string>
<string name="pin_set">ПИН установлен!</string>
<string name="password_not_set">Пароль не задан</string>
<string name="pin_not_set">PIN-код не задан</string>
<string name="pin_not_set">ПИН не задан</string>
<string name="password_not_changed">Пароль не был изменён</string>
<string name="pin_not_changed">PIN-код не изменён</string>
<string name="pin_cleared">PIN-код очищен!</string>
<string name="pin_not_changed">ПИН не изменён</string>
<string name="pin_cleared">ПИН очищен!</string>
<string name="password_hint">Введите пароль здесь</string>
<string name="pin_hint">Введите PIN-код здесь</string>
<string name="pin_hint">Введите ПИН здесь</string>
</resources>

View file

@ -6,9 +6,9 @@
<string name="profile_set_ok">базальный профиль помпы обновлен</string>
<string name="invalid_input">введенные данные неверны</string>
<string name="bolus_delivering">Подается болюс %1$.2f ед</string>
<string name="constraint_applied">применено ограничение!</string>
<string name="constraint_applied">Применено ограничение!</string>
<string name="tempbasals_iobtotal_label_string">Общий IOB:</string>
<string name="tt_label">Врем цель (TT)</string>
<string name="tt_label">ВЦ</string>
<string name="pump_unreachable">Помпа недоступна</string>
<string name="insulin_unit_shortname">Ед</string>
<string name="pump_base_basal_rate">%1$.2f ед/ч</string>
@ -25,12 +25,12 @@
<string name="save">Сохранить</string>
<string name="snooze">Отложить</string>
<string name="virtual_pump">Виртуальная помпа</string>
<string name="constraints">ограничения</string>
<string name="constraints">Ограничения</string>
<string name="superbolus">Суперболюс</string>
<string name="pump_paused">Помпа приостановлена</string>
<string name="and">И</string>
<string name="patient_name_default" comment="This is default patient display name, when user does not provide real one">Пользователь</string>
<string name="result">результат</string>
<string name="result">Результат</string>
<string name="settings">Настройки</string>
<string name="statuslights">Индикаторы состояния</string>
<string name="do_ns_upload_title">Выгружать (передавать данные) ГК в Nightscout</string>
@ -40,7 +40,7 @@
<string name="ue_export_to_csv">Экспорт пользовательских записей в Excel (csv)</string>
<string name="confirm">Подтверждаю</string>
<string name="pump">Помпа</string>
<string name="missed_bg_readings">Пропущенные данные СК</string>
<string name="missed_bg_readings">Пропущенные данные ГК</string>
<string name="treatments_iob_label_string">IOB акт инс:</string>
<string name="mute5min">Отключить уведомления на 5 минут</string>
<string name="mute">Отключить звук</string>
@ -49,10 +49,10 @@
<string name="extendedbolusdeliveryerror">Ошибка подачи пролонгированного болюса</string>
<string name="aps_mode_title">Режим APS</string>
<string name="extended_bolus">Пролонгированный болюс</string>
<string name="paused">на паузе</string>
<string name="paused">На паузе</string>
<string name="tdd_total">Суммарный суточный инсулин TDD</string>
<string name="goingtodeliver">Будет подано %1$.2f ед инс</string>
<string name="waitingforpump">ожидание помпы</string>
<string name="waitingforpump">Ожидание помпы</string>
<string name="androidaps_start">AAPS запущен</string>
<string name="formatsignedinsulinunits">%1$+.2f ед</string>
<string name="format_carbs">%1$d гр</string>
@ -60,18 +60,18 @@
<string name="format_mins">%1$d мин</string>
<string name="objectives">Цели</string>
<string name="please_wait">Подождите…</string>
<string name="stop">стоп</string>
<string name="stop">Стоп</string>
<string name="carbs">Углеводы</string>
<string name="invalid_profile">Недопустимый профиль!</string>
<string name="no_profile_set">ПРОФИЛЬ НЕ ЗАДАН</string>
<string name="active"><![CDATA[Активен]]></string>
<string name="date">дата</string>
<string name="date">Дата</string>
<string name="units_label">единицы</string>
<string name="dia_label">DIA (время действия инсулина)</string>
<string name="ic_label">Углеводный коэффициент IC (ГУ/ед. инс)</string>
<string name="isf_label">ISF (чувствительность к инсулину)</string>
<string name="basal_label">базал</string>
<string name="target_label">Целевое значение СК:</string>
<string name="target_label">Целевое значение ГК:</string>
<string name="dia_long_label">Продолжительность действия инсулина</string>
<string name="ic_long_label">Углеводный коэффициент IC (ГУ/ед. инс.)</string>
<string name="isf_long_label">Фактор Чувствительности к Инсулину (ISF)</string>
@ -86,10 +86,10 @@
<string name="pump_time_updated">Время на помпе обновлено</string>
<string name="exit">Выйти</string>
<string name="removerecord">Удалить запись</string>
<string name="loopisdisabled">Зцикл не работает</string>
<string name="loopisdisabled">Цикл не работает</string>
<string name="alarm">Оповещения об опасности</string>
<string name="disableloop">Деактивировать цикличность</string>
<string name="enableloop">Активировать цикличность</string>
<string name="disableloop">Деактивировать цикл</string>
<string name="enableloop">Активировать цикл</string>
<string name="resumeloop">Возобновить цикл</string>
<string name="suspendloop">Приостановить цикл</string>
<string name="duration_min_label">Длительность (мин)</string>
@ -98,8 +98,8 @@
<string name="exists">существует</string>
<string name="notexists">не существует</string>
<string name="glucose">Гликемия</string>
<string name="iob">IOB акт инс</string>
<string name="cob">АктУгл COB</string>
<string name="iob">IOB (актИнс)</string>
<string name="cob">COB (актУгл)</string>
<string name="name_short">Имя:</string>
<string name="time">Время</string>
<string name="ns_wifi_ssids">SSID для Wi-Fi</string>
@ -117,7 +117,7 @@
<string name="duration_label">Длительность действия</string>
<string name="shortgramm">грамм</string>
<string name="pumpsuspended">Работа помпы остановлена</string>
<string name="notconfigured">Не сконфигурировано</string>
<string name="notconfigured">Не настроено</string>
<string name="loopsuspended">ЗЦ остановлен</string>
<string name="trend_arrow">Стрелка тренда</string>
<string name="a11y_autosens_label">Auto sens</string>
@ -157,7 +157,7 @@
<string name="overview_insulin_label">Инсулин</string>
<string name="stoptemptarget">Остановить врем цель</string>
<string name="closedloop">Замкнутый цикл</string>
<string name="openloop">открытый цикл</string>
<string name="openloop">Открытый цикл</string>
<string name="lowglucosesuspend">Приостановка помпы на низкой ГК</string>
<string name="dia">Время действия инсулина DIA</string>
<string name="ic_short">IC углкоэф ГУ/инс</string>

View file

@ -181,6 +181,7 @@
<attr name="bolusDataPointColor" format="reference|color" />
<attr name="profileSwitchColor" 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_FINGER_STICK_BG_VALUE" format="reference|color" />
<attr name="therapyEvent_EXERCISE" format="reference|color" />

View file

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

View file

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

View file

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

View file

@ -5,6 +5,7 @@ import android.view.ActionMode
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import info.nightscout.shared.interfaces.ResourceHelper
@ -68,7 +69,7 @@ class ActionModeHelper<T>(val rh: ResourceHelper, val activity: FragmentActivity
} 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_sort_items, 0, rh.gs(R.string.sort_items)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.setGroupDividerEnabled(true)
MenuCompat.setGroupDividerEnabled(menu, true)
}
}

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="count_selected">%1$d выбрано</string>
<string name="count_selected">Выбрано: %1$d</string>
<string name="remove_items">Удалить элементы</string>
<string name="sort_items">Сортировать элементы</string>
<string name="remove_selected_items">Удалить выбранные элементы</string>

View file

@ -53,6 +53,8 @@ files:
translation: /pump/pump-core/src/main/res/values-%android_code%/strings.xml
- source: /pump/eopatch/src/main/res/values/strings.xml
translation: /pump/eopatch/src/main/res/values-%android_code%/strings.xml
- source: /pump/eopatch/src/main/res/values/strings_alarm.xml
translation: /pump/eopatch/src/main/res/values-%android_code%/strings_alarm.xml
- source: /pump/diaconn/src/main/res/values/strings.xml
translation: /pump/diaconn/src/main/res/values-%android_code%/strings.xml
- source: /pump/pump-common/src/main/res/values/strings.xml

View file

@ -7,6 +7,7 @@ plugins {
}
apply from: "${project.rootDir}/core/main/android_dependencies.gradle"
apply from: "${project.rootDir}/core/main/test_dependencies.gradle"
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_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches"
const val TABLE_EXTENDED_BOLUSES = "extendedBoluses"
const val TABLE_HEART_RATE = "heartRate"
const val TABLE_GLUCOSE_VALUES = "glucoseValues"
const val TABLE_FOODS = "foods"
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.ExtendedBolus
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.entities.HeartRate
import info.nightscout.database.entities.MultiwaveBolusLink
import info.nightscout.database.entities.OfflineEvent
import info.nightscout.database.entities.PreferenceChange
@ -35,5 +36,6 @@ data class NewEntries(
val temporaryTarget: List<TemporaryTarget>,
val therapyEvents: List<TherapyEvent>,
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_module_dependencies.gradle"
apply from: "${project.rootDir}/core/main/test_dependencies.gradle"
apply from: "${project.rootDir}/core/main/jacoco_global.gradle"
android {
@ -20,6 +22,9 @@ android {
}
}
}
sourceSets {
androidTest.assets.srcDirs += files("$projectDir/schemas")
}
}
dependencies {
@ -44,6 +49,8 @@ dependencies {
api "com.google.dagger:dagger-android:$dagger_version"
api "com.google.dagger:dagger-android-support:$dagger_version"
androidTestImplementation "androidx.room:room-testing:$room_version"
}
allOpen {

View file

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 23,
"identityHash": "173734db5f4f35f6295ed953d8124794",
"identityHash": "a3ee37800b6cda170d0ea64799ed7876",
"entities": [
{
"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": [],
"setupQueries": [
"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.Food
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.entities.HeartRate
import info.nightscout.database.entities.MultiwaveBolusLink
import info.nightscout.database.entities.OfflineEvent
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.UserEntry
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,
entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class,
EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class,
TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::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)
@TypeConverters(Converters::class)
internal abstract class AppDatabase : RoomDatabase() {
internal abstract class AppDatabase : Closeable, RoomDatabase() {
abstract val glucoseValueDao: GlucoseValueDao
@ -96,4 +99,5 @@ internal abstract class AppDatabase : RoomDatabase() {
abstract val offlineEventDao: OfflineEventDao
abstract val heartRateDao: HeartRateDao
}

View file

@ -101,6 +101,7 @@ import kotlin.math.roundToInt
//database.foodDao.deleteOlderThan(than)
removed.add(Pair("DeviceStatus", database.deviceStatusDao.deleteOlderThan(than)))
removed.add(Pair("OfflineEvent", database.offlineEventDao.deleteOlderThan(than)))
removed.add(Pair("HeartRate", database.heartRateDao.deleteOlderThan(than)))
if (deleteTrackedChanges) {
removed.add(Pair("GlucoseValue", database.glucoseValueDao.deleteTrackedChanges()))
@ -119,6 +120,7 @@ import kotlin.math.roundToInt
removed.add(Pair("ApsResult", database.apsResultDao.deleteTrackedChanges()))
//database.foodDao.deleteHistory()
removed.add(Pair("OfflineEvent", database.offlineEventDao.deleteTrackedChanges()))
removed.add(Pair("HeartRate", database.heartRateDao.deleteTrackedChanges()))
}
val ret = StringBuilder()
removed
@ -930,6 +932,11 @@ import kotlin.math.roundToInt
fun getLastOfflineEventId(): Long? =
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(
apsResults = database.apsResultDao.getNewEntriesSince(since, until, limit, offset),
apsResultLinks = database.apsResultLinkDao.getNewEntriesSince(since, until, limit, offset),
@ -948,6 +955,7 @@ import kotlin.math.roundToInt
therapyEvents = database.therapyEventDao.getNewEntriesSince(since, until, limit, offset),
totalDailyDoses = database.totalDailyDoseDao.getNewEntriesSince(since, until, limit, offset),
versionChanges = database.versionChangeDao.getNewEntriesSince(since, until, limit, offset),
heartRates = database.heartRateDao.getNewEntriesSince(since, until, limit, offset),
)
}
@ -956,4 +964,3 @@ inline fun <reified T : Any> Maybe<T>.toWrappedSingle(): Single<ValueWrapper<T>>
this.map { ValueWrapper.Existing(it) as ValueWrapper<T> }
.switchIfEmpty(Maybe.just(ValueWrapper.Absent()))
.toSingle()

View file

@ -1,12 +1,14 @@
package info.nightscout.database.impl
import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.room.Room
import androidx.room.RoomDatabase.Callback
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import dagger.Module
import dagger.Provides
import info.nightscout.database.entities.TABLE_HEART_RATE
import javax.inject.Qualifier
import javax.inject.Singleton
@ -22,13 +24,7 @@ open class DatabaseModule {
internal fun provideAppDatabase(context: Context, @DbFileName fileName: String) =
Room
.databaseBuilder(context, AppDatabase::class.java, fileName)
// .addMigrations(migration5to6)
// .addMigrations(migration6to7)
// .addMigrations(migration7to8)
// .addMigrations(migration11to12)
.addMigrations(migration20to21)
.addMigrations(migration21to22)
.addMigrations(migration22to23)
.addMigrations(*migrations)
.addCallback(object : Callback() {
override fun onOpen(db: SupportSQLiteDatabase) {
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.DelegatedVersionChangeDao
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) {
@ -64,5 +66,6 @@ internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val datab
val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao)
val deviceStatusDao: DeviceStatusDao = DelegatedDeviceStatusDao(changes, database.deviceStatusDao)
val offlineEventDao: OfflineEventDao = DelegatedOfflineEventDao(changes, database.offlineEventDao)
val heartRateDao: HeartRateDao = DelegatedHeartRateDao(changes, database.heartRateDao)
fun clearAllTables() = database.clearAllTables()
}

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

@ -24,3 +24,11 @@ android.useAndroidX=true
android.nonTransitiveRClass=true
# Cache is causeing issues with CircleCI nad maybe Studio 2021
# 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

@ -87,6 +87,7 @@ class OverviewDataImpl @Inject constructor(
dsMinSeries = LineGraphSeries()
treatmentsSeries = PointsWithLabelGraphSeries()
epsSeries = PointsWithLabelGraphSeries()
heartRateGraphSeries = LineGraphSeries()
}
override fun initRange() {
@ -322,4 +323,6 @@ class OverviewDataImpl @Inject constructor(
override val dsMinScale = Scale()
override var dsMaxSeries: 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 =
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)
val profileSwitch = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet()

View file

@ -113,12 +113,13 @@ class CommandQueueImplementation @Inject constructor(
return@subscribe
}
aapsLogger.debug(LTag.PROFILE, "onEventProfileSwitchChanged")
val effective = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet()
profileFunction.getRequestedProfile()?.let {
setProfile(ProfileSealed.PS(it), it.interfaceIDs.nightscoutId != null, object : Callback() {
override fun run() {
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)
} 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)
EffectiveProfileSwitch(
timestamp = dateUtil.now(),
@ -421,7 +422,7 @@ class CommandQueueImplementation @Inject constructor(
}
// 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)) {
aapsLogger.debug(LTag.PUMPQUEUE, "Command is already executed")
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.core.content.ContextCompat
import info.nightscout.core.ui.getThemeColor
import info.nightscout.core.ui.locale.LocaleHelper
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.shared.interfaces.ResourceHelper
import java.util.Locale
@ -29,15 +30,12 @@ import javax.inject.Inject
*/
class ResourceHelperImpl @Inject constructor(var context: Context, private val fabricPrivacy: FabricPrivacy) : ResourceHelper {
override fun updateContext(ctx: Context?) {
ctx?.let { context = it }
}
override fun gs(@StringRes id: Int): String = context.getString(id)
override fun gs(@StringRes id: Int): String =
context.createConfigurationContext(Configuration().apply { setLocale(LocaleHelper.currentLocale(context)) }).resources.getString(id)
override fun gs(@StringRes id: Int, vararg args: Any?): String {
return try {
context.getString(id, *args)
context.createConfigurationContext(Configuration().apply { setLocale(LocaleHelper.currentLocale(context)) }).resources.getString(id, *args)
} catch (exception: Exception) {
val resourceName = context.resources.getResourceEntryName(id)
val resourceValue = context.getString(id)

View file

@ -3,7 +3,7 @@
<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_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_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>
@ -16,8 +16,8 @@
<string name="alert_m26_description">Pakeiskite rezervuarą.</string>
<string name="alert_m27_description">Iš naujo paleiskite duomenų atsisiuntimą.</string>
<string name="alert_m28_description">Patikrinkite pompos statusą.</string>
<string name="alert_m29_description">Nustatykite baterijos tipą.</string>
<string name="alert_m30_description">Nustatykite rezervuaro tipą.</string>
<string name="alert_m29_description">Nustatyti baterijos tipą.</string>
<string name="alert_m30_description">Nustatyti rezervuaro tipą.</string>
<string name="alert_e6_description">Pakeiskite bateriją ir rezervuarą.</string>
<string name="alert_e10_description">Pakeiskite rezervuarą.</string>
<string name="alert_e13_description">Pakeiskite kalbą.</string>

View file

@ -10,6 +10,7 @@ import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.view.MenuCompat
import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle
import dagger.android.support.DaggerFragment
@ -72,7 +73,7 @@ class OpenAPSFragment : DaggerFragment(), MenuProvider {
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.setGroupDividerEnabled(true)
MenuCompat.setGroupDividerEnabled(menu, true)
}
override fun onMenuItemSelected(item: MenuItem): Boolean =

View file

@ -9,6 +9,7 @@ import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.view.MenuCompat
import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle
import dagger.android.support.DaggerFragment
@ -74,7 +75,7 @@ class LoopFragment : DaggerFragment(), MenuProvider {
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.setGroupDividerEnabled(true)
MenuCompat.setGroupDividerEnabled(menu, true)
}
override fun onMenuItemSelected(item: MenuItem): Boolean =

View file

@ -218,7 +218,7 @@ class LoopPlugin @Inject constructor(
val start = dateUtil.now()
while (start + T.mins(maxMinutes).msecs() > dateUtil.now()) {
if (commandQueue.size() == 0 && commandQueue.performing() == null) return true
SystemClock.sleep(100)
SystemClock.sleep(1000)
}
return false
}
@ -247,10 +247,16 @@ class LoopPlugin @Inject constructor(
return
}
if (!isEmptyQueue()) {
aapsLogger.debug(LTag.APS, rh.gs(info.nightscout.core.ui.R.string.pump_busy))
rxBus.send(EventLoopSetLastRunGui(rh.gs(info.nightscout.core.ui.R.string.pump_busy)))
return
}
// Check if pump info is loaded
if (pump.baseBasalRate < 0.01) return
val usedAPS = activePlugin.activeAPS
if ((usedAPS as PluginBase).isEnabled()) {
if (usedAPS.isEnabled()) {
usedAPS.invoke(initiator, tempBasalFallback)
apsResult = usedAPS.lastAPSResult
}
@ -261,12 +267,6 @@ class LoopPlugin @Inject constructor(
return
}
if (!isEmptyQueue()) {
aapsLogger.debug(LTag.APS, rh.gs(info.nightscout.core.ui.R.string.pump_busy))
rxBus.send(EventLoopSetLastRunGui(rh.gs(info.nightscout.core.ui.R.string.pump_busy)))
return
}
// Prepare for pumps using % basals
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
apsResult.usePercent = true

View file

@ -25,7 +25,7 @@
<string name="description_ama">2017 m. algoritmas</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="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_last_run_label">Paskutinis veiksmas</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_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_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="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>

View file

@ -16,6 +16,7 @@ import android.widget.ImageView
import android.widget.LinearLayout
import androidx.annotation.DrawableRes
import androidx.core.util.forEach
import androidx.core.view.MenuCompat
import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.ItemTouchHelper
@ -97,7 +98,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener, MenuProvider {
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_RUN, 0, rh.gs(R.string.run_automations)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.setGroupDividerEnabled(true)
MenuCompat.setGroupDividerEnabled(menu, true)
}
override fun onMenuItemSelected(item: MenuItem): Boolean =

View file

@ -27,13 +27,13 @@
<string name="sendsmsactionlabel">Siųsti SMS: %1$s</string>
<string name="sendsmsactiondescription">Siųsti SMS į visus numerius</string>
<string name="sendsmsactiontext">Išsiųsti SMS žinutę su tekstu</string>
<string name="starttemptarget">Nustatykite laikiną tikslą</string>
<string name="islesser">mažesnis nei</string>
<string name="isequalorlesser">lygus arba mažesnis nei</string>
<string name="isequal">yra lygus</string>
<string name="isequalorgreater">yra lygus arba didesnis nei</string>
<string name="isgreater">didesnis nei</string>
<string name="isnotavailable">negalimas</string>
<string name="starttemptarget">Pradėti laikiną tikslą</string>
<string name="islesser">mažiau nei</string>
<string name="isequalorlesser">lygu arba mažiau nei</string>
<string name="isequal">lygu</string>
<string name="isequalorgreater">lygu arba daugiau nei</string>
<string name="isgreater">daugiau nei</string>
<string name="isnotavailable">nėra duomenų</string>
<string name="glucoseisnotavailable">Glikemija nepasiekiama</string>
<string name="glucosecomparedmgdl">Glikemija %1$s %2$.0f %3$s</string>
<string name="glucosecomparedmmol">Glikemija %1$s %2$.1f %3$s</string>

View file

@ -22,16 +22,16 @@ class TriggerWifiSsidTest : TriggerTestBase() {
@Test fun shouldRunTest() {
val e = EventNetworkChange()
`when`(receiverStatusStore.lastNetworkEvent).thenReturn(e)
var t: TriggerWifiSsid = TriggerWifiSsid(injector).setValue("aSSID").comparator(Comparator.Compare.IS_EQUAL)
var t: TriggerWifiSsid = TriggerWifiSsid(injector).setValue("aSSID 1").comparator(Comparator.Compare.IS_EQUAL)
e.wifiConnected = false
Assert.assertFalse(t.shouldRun())
e.wifiConnected = true
e.ssid = "otherSSID"
Assert.assertFalse(t.shouldRun())
e.wifiConnected = true
e.ssid = "aSSID"
e.ssid = "aSSID 1"
Assert.assertTrue(t.shouldRun())
t = TriggerWifiSsid(injector).setValue("aSSID").comparator(Comparator.Compare.IS_NOT_AVAILABLE)
t = TriggerWifiSsid(injector).setValue("aSSID 1").comparator(Comparator.Compare.IS_NOT_AVAILABLE)
e.wifiConnected = false
Assert.assertTrue(t.shouldRun())

View file

@ -33,7 +33,6 @@ open class DaggerAppCompatActivityWithResult : DaggerAppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTheme(info.nightscout.core.ui.R.style.AppTheme_NoActionBar)
rh.updateContext(this)
compositeDisposable.add(rxBus.toObservable(EventThemeSwitch::class.java).subscribe {
recreate()

View file

@ -1,12 +1,10 @@
package info.nightscout.configuration.activities
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import info.nightscout.configuration.R
import info.nightscout.core.ui.locale.LocaleHelper
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.protection.ProtectionCheck
@ -60,8 +58,4 @@ class SingleFragmentActivity : DaggerAppCompatActivityWithResult() {
if (plugin?.preferencesId != -1) menuInflater.inflate(R.menu.menu_single_fragment, menu)
return super.onCreateOptionsMenu(menu)
}
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.wrap(newBase))
}
}

View file

@ -5,14 +5,14 @@ import android.view.View
import android.widget.CheckBox
import android.widget.LinearLayout
import android.widget.TextView
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.configuration.R
import info.nightscout.configuration.databinding.ActivityLogsettingBinding
import info.nightscout.core.ui.activities.TranslatedDaggerAppCompatActivity
import info.nightscout.rx.interfaces.L
import info.nightscout.rx.interfaces.LogElement
import javax.inject.Inject
class LogSettingActivity : DaggerAppCompatActivity() {
class LogSettingActivity : TranslatedDaggerAppCompatActivity() {
@Inject lateinit var l: L

View file

@ -1,6 +1,5 @@
package info.nightscout.configuration.maintenance.activities
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
@ -10,12 +9,11 @@ import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.configuration.R
import info.nightscout.configuration.databinding.MaintenanceImportListActivityBinding
import info.nightscout.configuration.databinding.MaintenanceImportListItemBinding
import info.nightscout.configuration.maintenance.PrefsFileContract
import info.nightscout.core.ui.locale.LocaleHelper
import info.nightscout.core.ui.activities.TranslatedDaggerAppCompatActivity
import info.nightscout.interfaces.maintenance.PrefFileListProvider
import info.nightscout.interfaces.maintenance.PrefsFile
import info.nightscout.interfaces.maintenance.PrefsMetadataKey
@ -23,7 +21,7 @@ import info.nightscout.interfaces.maintenance.PrefsStatus
import info.nightscout.shared.interfaces.ResourceHelper
import javax.inject.Inject
class PrefImportListActivity : DaggerAppCompatActivity() {
class PrefImportListActivity : TranslatedDaggerAppCompatActivity() {
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var prefFileListProvider: PrefFileListProvider
@ -116,8 +114,4 @@ class PrefImportListActivity : DaggerAppCompatActivity() {
}
return super.onOptionsItemSelected(item)
}
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.wrap(newBase))
}
}

View file

@ -9,6 +9,7 @@ import android.widget.TextView
import dagger.android.HasAndroidInjector
import info.nightscout.configuration.setupwizard.SWNumberValidator
import info.nightscout.core.ui.elements.NumberPicker
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit
import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.profile.ProfileFunction
@ -16,12 +17,15 @@ import info.nightscout.shared.SafeParse
import java.text.DecimalFormat
import javax.inject.Inject
class SWEditNumberWithUnits(injector: HasAndroidInjector, private val init: Double, private val min: Double, private val max: Double) : SWItem(injector, Type.UNIT_NUMBER) {
class SWEditNumberWithUnits(injector: HasAndroidInjector, private val init: Double, private val minMmol: Double, private val maxMmol: Double) : SWItem(injector, Type.UNIT_NUMBER) {
@Inject lateinit var profileFunction: ProfileFunction
private val validator: SWNumberValidator =
SWNumberValidator { value -> value in min..max }
if (profileFunction.getUnits() == GlucoseUnit.MMOL)
SWNumberValidator { value -> value in minMmol..maxMmol }
else
SWNumberValidator { value -> value in minMmol * Constants.MMOLL_TO_MGDL..maxMmol * Constants.MMOLL_TO_MGDL }
private var updateDelay = 0
override fun generateDialog(layout: LinearLayout) {
@ -44,7 +48,10 @@ class SWEditNumberWithUnits(injector: HasAndroidInjector, private val init: Doub
var initValue = sp.getDouble(preferenceId, init)
initValue = Profile.toCurrentUnits(profileFunction.getUnits(), initValue)
val numberPicker = NumberPicker(context)
if (profileFunction.getUnits() == GlucoseUnit.MMOL) numberPicker.setParams(initValue, min, max, 0.1, DecimalFormat("0.0"), false, null, watcher) else numberPicker.setParams(initValue, min * 18, max * 18, 1.0, DecimalFormat("0"), false, null, watcher)
if (profileFunction.getUnits() == GlucoseUnit.MMOL)
numberPicker.setParams(initValue, minMmol, maxMmol, 0.1, DecimalFormat("0.0"), false, null, watcher)
else
numberPicker.setParams(initValue, minMmol * Constants.MMOLL_TO_MGDL, maxMmol * Constants.MMOLL_TO_MGDL, 1.0, DecimalFormat("0"), false, null, watcher)
layout.addView(numberPicker)
val c = TextView(context)

View file

@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".constraints.objectives.ObjectivesFragment">
tools:context="info.nightscout.plugins.constraints.objectives.ObjectivesFragment">
<LinearLayout
android:layout_width="match_parent"

View file

@ -18,7 +18,6 @@
<string name="objectives_autosens_objective">Pas basale en verhoudings aan as nodig, en dan aktiveer auto-sens</string>
<string name="objectives_autosens_gate">1 week suksesvolle dagtyd lus met gereelde karb toevoegings</string>
<string name="objectives_smb_objective">Skakel bykomende nutsfunskies aan vir bedags gebruik, soos bv. SMB</string>
<string name="objectives_smb_gate">Jy moet asb die wiki lees en verhoog maksIAB om SMBs te laat werk! \'n Goeie begin is maksIAB = gemiddelde ete bolus + 3 x maks daaglikse basale</string>
<string name="objectives_manualenacts">Per hand aksies</string>
<string name="accomplished">Bereik: %1$s</string>
<string name="objectives_useprofileswitch">Stel profiel 90% vir 10 min (Lang-pers profiel naam op Oorsig)</string>

View file

@ -24,7 +24,6 @@
<string name="objectives_autosens_gate">Една седмица успешно дневно използване с редовно въвеждане на въглехидрати</string>
<string name="objectives_autosens_learned">Ако вашият резултат от autosens не се колебае около 100%, вашият профил вероятно е грешен.</string>
<string name="objectives_smb_objective">Добавяне на допълнителни функции за използване през деня, като SMB</string>
<string name="objectives_smb_gate">Трябва да прочетете wiki и увеличите maxIOB за да може SMB да работи добре! Добро начало е maxIOB = средния болус за хранене + 3 пъти най-големия базал от профила</string>
<string name="objectives_smb_learned">Използването на SMB трябва да е вашата основна цел. Алгоритъмът Oref1 е създаден, за да ви помогне и с вашите болуси. Не трябва да давате пълен болус за вашата храна, а само част от него и оставете AAPS да ви даде останалото, ако е необходимо. По този начин имате по-голям диапазон за грешно изчислени въглехидрати. Знаете ли, че можете да зададете процент от резултата от болус калкулатора, за да намалите размера на болуса?</string>
<string name="objectives_auto_objective">Разрешаване на автоматизация</string>
<string name="objectives_auto_gate">Прочетете документацията за това как работи автоматизацията. Настройте първите си прости правила. Нека AAPS показва само известия. Когато сте сигурни, автоматизацията се задейства в точното време, за да заменете известяването с реални действия. (https: //androidaps.readthedocs.io/en/latest/EN/Usage/Automation.html)</string>

View file

@ -18,7 +18,6 @@
<string name="objectives_autosens_objective">Ajustar basals i ràtios si cal, i després activar auto-sens</string>
<string name="objectives_autosens_gate">1 setmana d\'èxit en mode llaç tancat durant el dia, amb introducció regular de carbohidrats</string>
<string name="objectives_smb_objective">Activant funcions addicionals d\'ús diurn, com l\'SMB (super micro bolus)</string>
<string name="objectives_smb_gate">Heu de llegir la wiki i augmentar la maxIOB per a què l\'SMB funcioni correctament! Una bona manera de començar és amb maxIOB = bolus d\'àpat mig + 3 x màxima basal diària</string>
<string name="objectives_auto_objective">Activant l\'automatització</string>
<string name="objectives_manualenacts">Activació manual</string>
<string name="accomplished">Completat: %1$s</string>

View file

@ -24,7 +24,7 @@
<string name="objectives_autosens_gate">Jeden týden úspěšného používání s běžným příjmem sacharidů</string>
<string name="objectives_autosens_learned">Pokud váš výsledek autosens neosciluje kolem 100%, nastavení Vašeho profilu pravděpodobně špatné.</string>
<string name="objectives_smb_objective">Povolit další funkce pro běžné používání jako SMB</string>
<string name="objectives_smb_gate">Přečíst si dokumentaci a zvýšit maximální IOB, aby mohlo SMB fungovat. Pro začátek se dá použít velikost běžného bolusu + 3x maximální denní bazál</string>
<string name="objectives_smb_gate">Přečíst si dokumentaci a zvýšit maximání IOB, aby mohlo SMB fungovat. Pro začátek se dá použít velikost běžného bolusu + 3x maximální denní bazál</string>
<string name="objectives_smb_learned">Použití SMB je vaším cílem. Oref1 algoritmus byl navržen tak, aby vám pomohl i s vašimi bolusy. Neměli byste dávat úplný bolus pro vaše jídlo, ale jen jeho část a nechtat AAPS v případě potřeby dát zbytek. Takto máte větší rezervu pro špatně vypočtené sacharidy. Věděli jste, že můžete nastavit procento bolusové kalkulačky pro snížení velikosti bolusu?</string>
<string name="objectives_auto_objective">Povolení automatizace</string>
<string name="objectives_auto_gate">Přečtěte si na wiki, jak automatizace funguje. Nejdříve nastavte pouze jednoduchá pravidla. Namísto provádění akcí nechte AAPS zobrazovat pouze oznámení. Pokud jste si jistí, že je automatizace spouštěna v pravý čas, můžete oznámení nahradit prováděním akce. (https://wiki.aaps.app/cs/latest/Usage/Automation.html)</string>

View file

@ -18,7 +18,6 @@
<string name="objectives_autosens_objective">Justér om nødvendigt basaler og forhold og aktivér derefter auto-sens</string>
<string name="objectives_autosens_gate">1 uges vellykket looping i dagtimerne med alle måltider tastet ind</string>
<string name="objectives_smb_objective">Aktivering af yderligere funktioner til brug i dagtimerne, såsom SMB</string>
<string name="objectives_smb_gate">Du skal læse wikien og hæve maxIOB for at få SMB\'er til at fungere fint! En god start er maxIOB=gennemsnitlig måltidsbolus + 3 x max daglig basal</string>
<string name="objectives_auto_objective">Aktiverer automatisering</string>
<string name="objectives_manualenacts">Manuelle handlinger</string>
<string name="accomplished">Udført: %1$s</string>

View file

@ -24,8 +24,6 @@
<string name="objectives_autosens_gate">Loope eine Woche tagsüber mit regelmäßiger Kohlenhydrat-Eingabe</string>
<string name="objectives_autosens_learned">Wenn Ihr Autosens Ergebnis nicht stündlich schwankt, um die angestrebten 100 % ist das verwendete Profil wahrscheinlich falsch.</string>
<string name="objectives_smb_objective">Aktiviere zusätzliche Funktionen für die Nutzung tagsüber wie z. B. SMB</string>
<string name="objectives_smb_gate">Lies das Wiki und erhöhe maxIOB, damit der SMB gut funktioniert. Ein guter Anfang ist
die Formel maxIOB = durchschnittlicher Essensbolus + 3 x höchste Basalrate</string>
<string name="objectives_smb_learned">Die Verwendung von SMB ist Dein Ziel. Der Oref1-Algorithmus wurde entwickelt, um Dir auch mit Boli zu helfen. Du solltest nicht den vollen Bolus für Dein Essen geben, sondern nur einen Teil davon abgeben und es AAPS überlassen bei Bedarf den notwendigen Rest entsprechend dem Blutzuckerverlauf zugeben zu lassen. Auf diese Weise hast Du mehr Platz für falsch berechnete Kohlenhydrate. Wusstest Du, dass Du einen Prozentsatz bei Verwendung des Bolusrechners festlegen kannst, um die Größe des Bolus zu reduzieren?</string>
<string name="objectives_auto_objective">Automatisierung aktivieren</string>
<string name="objectives_auto_gate">Lies in der Dokumentation nach, wie die Automation funktioniert. Richte Dir erst einfache Regeln ein. Zunächst sollte AAPS keine Änderungen vornehmen, sondern Dir nur eine Benachrichtigung anzeigen. Wenn Du Dir sicher bist, dass die Automation im richtigen Moment angetriggert wird, dann kannst Du die Benachrichtigung durch eine Aktion ersetzen (https://androidaps.readthedocs.io/de/latest/CROWDIN/de/Usage/Automation.html)</string>

View file

@ -18,7 +18,6 @@
<string name="objectives_autosens_objective">Ρυθμίστε τον βασικό ρυθμό και τις αναλογίες αν χρειάζεται, και μετά ενεργοποιήστε το auto-sens</string>
<string name="objectives_autosens_gate">1 επιτυχής εβδομάδα ημερήσιου κυκλώματος με κανονική εισαγωγή υδατανθράκων</string>
<string name="objectives_smb_objective">Ενεργοποιώντας επιπρόσθετα χαρακτηριστικά για χρήση κατά την ημέρα, όπως το SMB</string>
<string name="objectives_smb_gate">Πρέπει να διαβάσετε τον οδηγό wiki και να αυξήσετε το ανώτατο όριο του IOB για να μπορέσει να λειτουργήσει σωστά το SMB! Μια καλή αρχή είναι μέγιστο IOB= μέσο bolus γεύματος + 3 x το μέγιστο ημερήσιο βασικού ρυθμού</string>
<string name="objectives_manualenacts">Ξεκινήστε χειροκίνητα</string>
<string name="accomplished">Επιτεύχθηκε: %1$s</string>
<string name="objectives_useprofileswitch">Θέστε το προφίλ στο 90% για 10 λεπτά (Παρατεταμένο πάτημα του προφίλ στην Επισκόπηση)</string>

View file

@ -24,7 +24,7 @@
<string name="objectives_autosens_gate">Una semana con éxito en bucle cerrado durante el día con entrada regular de carbohidratos</string>
<string name="objectives_autosens_learned">Si el resultado de tu autosens no oscila en torno al 100% es probable que tu perfil esté equivocado.</string>
<string name="objectives_smb_objective">Habilitar funciones adicionales para uso durante el día como SMB</string>
<string name="objectives_smb_gate">¡Debes leer el wiki y subir maxIOB para que SMB funcione bien! Para comenzar un buen valor sería maxIOB = bolo de comida medio + 3 x máximo basal diario</string>
<string name="objectives_smb_gate">Debes leer la wiki y aumentar el valor de maxIOB para que SMB funcione bien. Un buen comienzo sería maxIOB = bolo de comida promedio + 3x máxima basal diaria</string>
<string name="objectives_smb_learned">Utilizar SMB es su objetivo. El algoritmo Oref1 fue diseñado para ayudarle también con sus bolos. No debe dar un bolo completo para su comida, sino sólo una parte y dejar que AAPS le dé el resto si es necesario. De esta forma tendrás más espacio para los carbohidratos mal calculados. ¿Sabías que puedes establecer un porcentaje del resultado de la calculadora de bolo para reducir el tamaño del bolo?</string>
<string name="objectives_auto_objective">Activar automatización</string>
<string name="objectives_auto_gate">Lee la documentación sobre el funcionamiento de la automatización. Configure sus primeras reglas simples. En lugar de una acción, deja que AAPS muestre sólo una notificación. Cuando esté seguro de que la automatización se activa en el momento adecuado, sustituya la notificación por una acción real. (https://wiki.aaps.app/en/latest/Usage/Automation.html)</string>

View file

@ -24,7 +24,7 @@
<string name="objectives_autosens_gate">1 semaine de Boucle Fermée en journée en saisissant régulièrement les glucides</string>
<string name="objectives_autosens_learned">Si le résultat de votre autosens n\'est pas autour de 100%, votre profil est probablement erroné.</string>
<string name="objectives_smb_objective">Activation de fonctionnalités supplémentaires pour l\'utilisation en journée, telles que la fonction SMB</string>
<string name="objectives_smb_gate">Lisez le wiki et augmentez le maxIA pour que les SMBs fonctionnent correctement ! Un bon début est maxIA = moyenne des Bolus Repas + 3 x maximum débit Basal quotidien</string>
<string name="objectives_smb_gate">Vous devez lire le wiki et augmenter le maxIA pour faire fonctionner les SMB ! Un bon début est maxIA = moyenne des bolus repas + 3 fois le basal le plus élevé</string>
<string name="objectives_smb_learned">L\'utilisation de SMB est votre objectif. L\'algorithme Oref1 a été conçu pour vous aider également avec vos bolus. Vous ne devriez pas donner un bolus complet pour votre nourriture, mais seulement une partie de celui-ci et laisser AAPS vous donner le reste si nécessaire. De cette façon, vous avez plus de latitude pour les glucides mal calculés. Saviez-vous que vous pouvez définir un pourcentage à appliquer au résultat de la calculatrice de bolus pour réduire la taille du bolus ?</string>
<string name="objectives_auto_objective">Activation de l\'automatisation</string>
<string name="objectives_auto_gate">Lisez la documentation sur le fonctionnement de l\'automatisation fonctionne. Configurez vos premières règles simples. Au lieu de mettre une action, configurez une notification. Quand vous êtes sûr que l\'automatisation est déclenchée au bon moment, remplacez la notification par une action réelle. (https://wiki.aaps.app/fr/latest/Usage/Automation.html)</string>

View file

@ -24,7 +24,7 @@
<string name="objectives_autosens_gate">שבוע של הפעלה מוצלחת של הלולאה במשך שעות היום, עם רישום ערכי פחמימות בקביעות</string>
<string name="objectives_autosens_learned">אם תוצאת הזיהוי האוטומטי שלכם לא מתנודדת סביב 100%, כנראה שהפרופיל טעון שיפור.</string>
<string name="objectives_smb_objective">הפעלת פונקציות נוספות לשימוש במשך היום, כגון סופר מיקרו בולוסים (SMB)</string>
<string name="objectives_smb_gate">חובה לקרוא את הויקי ולהעלות את ערך ה-maxIOB כדי להפעיל את ה- SMB כראוי. התחלה טובה תהיה maxIOB=בולוס ממוצע + 3 פעמים ערך הבזאלי היומי המקסימלי</string>
<string name="objectives_smb_gate">חובה לקרוא את הויקי ולהעלות את ערך האינסולין הפעיל המרבי (maxIOB) כדי להפעיל את ה- SMB כראוי. התחלה טובה תהיה maxIOB=בולוס ארוחה טיפוסי + 3 פעמים ערך הבזאלי המקסימלי ביממה</string>
<string name="objectives_smb_learned">שימוש ב-SMB הוא המטרה שלכם. אלגוריתם Oref1 תוכנן לעזור גם עם הבולוסים שלכם. אתם לא צריכים להזריק בולוס מלא למזון שלכם אלא רק על חלק ממנו ולתת ל-AAPS לטפל בשאר במידת הצורך. כך קל יותר להכיל טעויות בספירת פחמימות. הידעתם שאפשר להגדיר אחוז מתוצאת מחשבון הבולוס כדי להקטין את גודל הבולוס?</string>
<string name="objectives_auto_objective">הפעלת אוטומציה</string>
<string name="objectives_auto_gate">קראו את המסמכים על אופן הפעולה של אוטומציות. הגדירו את התנאים הפשוטים הראשונים שלכם. במקום פעולה תנו ל-AAPS להציג הודעה בלבד. כאשר אתם בטוחים שהאוטומציה מופעלת בזמן הנכון, החליפו את ההודעה בפעולה אמיתית. (https://wiki.aaps.app/he/latest/Usage/Automation.html)</string>

View file

@ -18,7 +18,6 @@
<string name="objectives_autosens_objective">필요하면 Basal과 비율을 조절하고, auto-sens를 활성화합니다</string>
<string name="objectives_autosens_gate">섭취한 탄수화물양을 입력하고 1주일동안 낮시간대에 loop를 성공적으로 사용하여 봅니다</string>
<string name="objectives_smb_objective">낮시간대에 SMB(Super Micro Bolus)같은 추가기능을 활성화해 사용해봅니다</string>
<string name="objectives_smb_gate">SMB가 잘 작동하게 하기위해서 wiki를 반드시 읽은 다음 maxIOB 값을 올려보세요! maxIOB=평균 식사 Bolus + 3 x 최대하루 Basal이면 적당한 시작값입니다</string>
<string name="objectives_auto_objective">자동화 사용</string>
<string name="objectives_manualenacts">수동 주입</string>
<string name="accomplished">완료: %1$s</string>

View file

@ -24,7 +24,6 @@
<string name="objectives_autosens_gate">1 savaitę praleiskite sėkmingai naudodami uždarąjį ciklą dienos metu ir įvesdami visus valgomus angliavandenius</string>
<string name="objectives_autosens_learned">Jei Jūsų autosens rezultatai nesvyruoja apie 100%, tikėtina, kad profilis turi klaidų.</string>
<string name="objectives_smb_objective">Dienos metu aktyvuokite papildomas funkcijas, tokias kaip SMB (Super Mikro Bolusas)</string>
<string name="objectives_smb_gate">Norėdami, kad SMB veiktų gerai, turite perskaityti dokumentaciją ir padidinti max AIO! Pradžiai patartina skaičiuoti taip: maxAIO=(didžiausia valandinė bazė x 3) + vidutinis bolusas</string>
<string name="objectives_smb_learned">SMB yra Jūsų tikslas. Oref1 algoritmas sukurtas taip, kad taptų Jums pagalba su bolusavimu. Jūs galite nesileisti viso boluso maistui, susileiskite dalį, o AAPS padarys likusį darbą, jei reikės. Taip išvengsite klaidų dėl neteisingai apskaičiuotų AV. Ar žinote, kad galite skaičiuotuve nurodyti, kiek % apskaičiuoto boluso suleisti?</string>
<string name="objectives_auto_objective">Automatizavimo įjungimas</string>
<string name="objectives_auto_gate">Perskaitykite dokumentaciją apie automatizacijos veikimą. Nustatykite kelias paprastas taisykles. Nustatykite, kad AAPS tik parodytų pranešimą, o ne atliktų realų veiksmą. Kai įsitikinsite, kad automatizacija suveikia reikalingu metu, vietoj pranešimo įjunkite veiksmą. (https://wiki.aaps.app/en/latest/Usage/Automation.html)</string>
@ -40,7 +39,7 @@
<string name="objectives_usedisconnectpump">Imituokite maudynes duše. Atjunkite pompą 1 valandai (ilgai paspauskite Atviras Ciklas)</string>
<string name="objectives_usereconnectpump">... ir iš naujo prisijunkite tuo pačiu būdu</string>
<string name="objectives_usetemptarget">Nustatykite 10 min trukmės laikiną tikslą (ilgai spausti ant dabartinio tikslo)</string>
<string name="objectives_useactions">Konfigūracijoje įjunkite Veiksmų įskiepį, nustatykite jį matomą ir jo turinio rodymą viršutiniame meniu</string>
<string name="objectives_useactions">Konfigūracijoje įjunkite Veiksmų įskiepį, padarykite jį ir jo turinį matomą viršutiniame meniu</string>
<string name="objectives_useloop">Parodyti Ciklo įskiepio turinį</string>
<string name="objectives_usescale">Panaudokite vaizdo dydžio keitimo funkciją ilgai spaudžiant ant glikemijos kreivės</string>
<string name="objectives_exam_objective">Patvirtinkite savo žinias</string>

View file

@ -24,7 +24,6 @@
<string name="objectives_autosens_gate">Gedurende 1 week succesvol overdag loopen met regelmatige invoer van koolhydraten</string>
<string name="objectives_autosens_learned">Als het resultaat van autosens zich niet rond de 100% beweegt dan is je profiel waarschijnlijk niet juist.</string>
<string name="objectives_smb_objective">Activeren van extra functies overdag zoals SMB (super micro bolus)</string>
<string name="objectives_smb_gate">Lees de wiki en verhoog maxIOB om SMB goed werkend te krijgen. Een goed begin is maxIOB=gemiddelde maaltijdbolus + 3 x max dagelijkse basaal</string>
<string name="objectives_smb_learned">Het gebruik van SMB is het uiteindelijke doel. Het Oref1 algoritme is ontworpen om je ook te helpen met je bolussen. Je hoeft geen volledige bolus te geven voor jouw voeding, maar slechts een deel ervan en AAPS zal de rest geven als dat nodig is. Op deze manier heb je meer ruimte voor verkeerd berekende koolhydraten. Wist je dat je een percentage in de bolus calculator kan instellen om de hoeveelheid bolus te verminderen?</string>
<string name="objectives_auto_objective">Automatisering inschakelen</string>
<string name="objectives_auto_gate">Lees eerst de documentatie over hoe automatisering werkt. Maak daarna je eerste simpele regels, waarbij je in plaats van acties eerst gebruikt maakt van de notificaties. Wanneer je er dan zeker van bent, dat je automatisering op de goede momenten wordt geactiveerd, kan je de noticifcatie omzetten naar een echte actie.

View file

@ -24,7 +24,6 @@
<string name="objectives_autosens_gate">1 tydzień sukcesu w działaniu pętli w ciągu dnia z regularnym wprowadzaniem spożywanych węglowodanów</string>
<string name="objectives_autosens_learned">Jeśli wynik autosens nie oscyluje w okolicach 100%, prawdopodobnie Twój profil nie jest poprawny.</string>
<string name="objectives_smb_objective">Włączanie dodatkowych funkcji do użytku, jak SMB (Super Mikro Bolusy)</string>
<string name="objectives_smb_gate">Musisz przeczytać wiki i zwiększyć maxIOB, aby SMB działało dobrze! Dobrym początkiem jest maxIOB = średni bolus + 3 x maks. dzienna dawka bazowa</string>
<string name="objectives_smb_learned">Używanie SMB jest Twoim celem. Algorytm Oref1 został zaprojektowany, aby pomóc Ci również w bolusach. Nie należy podawać pełnego bolusa do jedzenia, ale tylko jego część, a w razie potrzeby pozostawić resztę AAPS. W ten sposób masz więcej miejsca na błędnie obliczone węglowodany. Czy wiesz, że można w kalkulatorze ustawić procent wyniku bolusa, aby zmniejszyć rozmiar bolusa?</string>
<string name="objectives_auto_objective">Włączanie automatyzacji</string>
<string name="objectives_auto_gate">Przeczytaj dokumentację dotyczącą działania automatyzacji. Skonfiguruj swoje pierwsze proste zasady. Zamiast wykonania działania niech AAPS wyświetli tylko powiadomienie. Gdy masz pewność, że automatyzacja zostaje uruchomiona we właściwym czasie, zastąp powiadomienie rzeczywistą akcją. (https://wiki.aaps.app/en/latest/Usage/Automation.html)</string>

View file

@ -24,7 +24,6 @@
<string name="objectives_autosens_gate">1 semana de looping durante o dia com sucesso com entrada regular de carboidratos</string>
<string name="objectives_autosens_learned">Se o resultado do seu autosens não está oscilando por volta de 100% seu perfil provavelmente está errado.</string>
<string name="objectives_smb_objective">Activando recursos adicionais para uso durante o dia, como SMB</string>
<string name="objectives_smb_gate">Deverá ler a wiki e aumentar a IA máx para que os SMBs funcionem corretamente! Inicialmente poderá considerar maxIA=média dos bólus + 3 x a basal diária máxima</string>
<string name="objectives_smb_learned">Usar SMB é o seu objetivo. O algoritmo Oref1 foi projetado para ajudá-lo também com seus bolus. Você não deve dar bolus completo para sua comida, apenas parte dela e deixar o AAPS lhe dar o resto, se necessário. Desta forma, você tem mais espaço para carboidratos mal calculado. Você sabia que pode definir uma porcentagem do resultado da calculadora de bolus para reduzir o tamanho do bolus?</string>
<string name="objectives_auto_objective">Ativando a automação</string>
<string name="objectives_auto_gate">Leia os documentos de como a automação funciona. Configure suas primeiras regras simples. Em vez de efetuar uma ação deixe AAPS apresentar apenas uma notificação. Quando você tiver certeza de que a automação é acionada no momento certo substitua a notificação por uma ação real. (https://wiki.aaps.app/en/latest/Usage/Automation.html)</string>

View file

@ -18,7 +18,6 @@
<string name="objectives_autosens_objective">Ajuste as basais e os rácios, se necessário, e, em seguida, active o auto-sens</string>
<string name="objectives_autosens_gate">1 semana de looping durante o dia com sucesso com entrada regular de hidratos de carbono</string>
<string name="objectives_smb_objective">Activando recursos adicionais para uso durante o dia, como SMB</string>
<string name="objectives_smb_gate">Deverá ler a wiki e aumentar a IA máx para que os SMBs funcionem devidamente! Inicialmente poderá considerar maxIA=média dos bólus + 3 x a basal máxima</string>
<string name="objectives_auto_objective">A ativar a automatização</string>
<string name="objectives_manualenacts">Execução manual</string>
<string name="accomplished">Concluído: %1$s</string>

View file

@ -18,7 +18,6 @@
<string name="objectives_autosens_objective">Ajustarea bazalelor și a factorilor dacă este necesar și apoi activarea auto-sens</string>
<string name="objectives_autosens_gate">O săptămână de buclă închisă încheiată cu succes în condițiile introducerii regulate a carbohidraților</string>
<string name="objectives_smb_objective">Se activează opțiuni pentru uzul în timpul zilei, cum ar fi SMB</string>
<string name="objectives_smb_gate">Trebuie sa citiți wiki și să măriți maxIOB pentru a obține SMB corect! Un start bun este maxIOB=media bolusurilor + 3x maxima bazalei din zi</string>
<string name="objectives_auto_objective">Activarea automatizării</string>
<string name="objectives_manualenacts">Acțiuni manuale</string>
<string name="accomplished">Îndeplinit: %1$s</string>

View file

@ -5,8 +5,8 @@
<string name="nth_objective">Цель %1$d</string>
<string name="objectivenotstarted">Цель %1$d не начата</string>
<string name="objectivenotfinished">Цель %1$d не завершена</string>
<string name="objectives_0_objective">Настройка визуализации и мониторинга, анализ базала и коэффициентов</string>
<string name="objectives_0_gate">Убедитесь, что величина ГК и данные по инсулину помпы передаются в Nightscout</string>
<string name="objectives_0_objective">Настройка интеграции с Nightscout, анализ базала и коэффициентов</string>
<string name="objectives_0_gate">Убедитесь, что данные о гликемии и подаваемом AAPSом инсулине отображаются в Nightscout и актуальны</string>
<string name="objectives_0_learned">Вы сделали базовую настройку экосистемы AAPS. Nightscout не нужен для работы AAPS, но может быть полезен для отчетов и мониторинга других пациентов. Необходимости в постоянном подключении к NS нет, если вы используете NS только для себя. Вы можете установить загрузку, например, только на домашний wifi, чтобы сэкономить заряд батареи.</string>
<string name="objectives_openloop_objective">Старт незамкнутого цикла</string>
<string name="objectives_openloop_gate">Начинайте работу в режиме незамкнутого цикла и ручной подстройки величины временного базала. Установите и применяйте временные цели и временные цели по умолчанию (напр. углеводы при нагрузке или купировании гипо)</string>
@ -14,9 +14,9 @@
<string name="objectives_maxbasal_objective">Глубже понимаем работу системы в режиме незамкнутого цикла, включая ее рекомендации по временным базалам</string>
<string name="objectives_maxbasal_gate">На основе накопленного опыта, определяем максимальную величину базала и задаем ее в помпе и в настройки AndroidAPS</string>
<string name="objectives_maxbasal_learned">Примите меры предосторожности и корректируйте, если необходимо, параметры безопасности.</string>
<string name="objectives_maxiobzero_objective">Начинаем замыкать цикл с прекращением подачи инсулина при низком значении Ск (режим Low Glucose Suspend)</string>
<string name="objectives_maxiobzero_objective">Начинаем замыкать цикл с прекращением подачи инсулина при низком значении ГК (режим Low Glucose Suspend)</string>
<string name="objectives_maxiobzero_gate">Работа в замкнутом цикле с макс активным инсулином IOB = 0 на протяжении нескольких дней избегая событий типа приостановка на низких ГК Low Suspend</string>
<string name="objectives_maxiobzero_learned">Установка MaxIOB на нуль предотвращает от гипо и не добавит инсулина выше базальной скорости (за исключением отрицательных значений IOB)</string>
<string name="objectives_maxiobzero_learned">Установка MaxIOB на нуль предотвращает гипо и не добавит инсулина выше базальной скорости (за исключением отрицательных значений IOB)</string>
<string name="objectives_maxiob_objective">Настройка замкнутого цикла с поднятием макс величины IOB выше 0 и постепенным понижением целевой ГК</string>
<string name="objectives_maxiob_gate">Работа несколько дней и по кр мере одну ночь без срабатывания оповещений о низкой ГК</string>
<string name="objectives_maxiob_learned">Обновляйте MaxIOB по мере взросления ребенка. Не допускайте, чтобы система подавала больше инсулина, чем вы можете покрыть едой = действительно, плохая идея выставлять высокие значения.</string>
@ -24,7 +24,7 @@
<string name="objectives_autosens_gate">1 неделя успешной дневной работы с регулярным введением углеводов</string>
<string name="objectives_autosens_learned">Если результаты autosense не колеблются около 100%, то возможно ваш профиль неверный.</string>
<string name="objectives_smb_objective">Активация таких доп функций для дневного времени как супер микро болюс SMB</string>
<string name="objectives_smb_gate">Прочтите wiki и увеличьте maxIOB чтобы супер микро болюс SMB заработал как положено! Хорошее начало maxIOB = средний болюс на еду + троекратный максимальный суточный базал</string>
<string name="objectives_smb_gate">Прочтите статью wiki и увеличивайте maxIOB так, чтобы SMB заработал как положено! Для начала рассчитайте maxIOB как средний болюс на еду + макс. суточная базальная скорость*3</string>
<string name="objectives_smb_learned">Ваша цель - это использование СМБ. Алгоритм Oref1 предназначен также, чтобы помочь вам с болюсами. Вы не должны подавать полный болюс на еду, а только часть его, предоставив AAPS подать при необходимости остальную часть болюса. Таким образом вы можете иметь большее пространство для ошибки в подсчетах углеводов. Вы знаете, что можно установить проценты для уменьшения размера болюса?</string>
<string name="objectives_auto_objective">Включение автоматизации</string>
<string name="objectives_auto_gate">Прочтите документацию по автоматизации. Настройте свои первые простые правила. Вместо действия позвольте AAPS только выводить уведомления. Если вы уверены, что автоматизация инициируется в нужное время, замените уведомление реальными действиями. (https://wiki.aaps.app/en/latest/Usage/Automation.html)</string>
@ -49,9 +49,9 @@
<string name="objectives_exam_learned_prerequisites2">AAPS может работать в автономном режиме.</string>
<string name="objectives_exam_learned_basaltest">Следует пересмотреть и обновить профиль. Лучший профиль = лучшие результаты.</string>
<string name="objectives_exam_learned_dia">Продолжительность действия инсулина DIA в AAPS имеет иное значение (время до полного поглощения инсулина), чем в классической помповой терапии (время до поглощения основной массы инсулина).</string>
<string name="objectives_exam_learned_isf">Вы изучили понятие чувствительность к инсулину ISF, и как она влияет на коррекцию ГК.</string>
<string name="objectives_exam_learned_ic">Вы изучили понятие углеводный коэффициент IC и как он влияет на количество инсулина, необходимого для покрытия углеводов.</string>
<string name="objectives_exam_learned_hypott">Временная цель Hypo используется только для предотвращения чрезмерной коррекции после гипо, когда накапливается отрицательный IOB. Необходимо принять дополнительные меры, чтобы предотвратить ситуацию в будущем.</string>
<string name="objectives_exam_learned_isf">Вы изучили понятие чувствительность к инсулину ISF и то, как она влияет на коррекцию ГК.</string>
<string name="objectives_exam_learned_ic">Вы изучили понятие углеводный коэффициент IC и то, как он влияет на количество инсулина, необходимого для покрытия углеводов.</string>
<string name="objectives_exam_learned_hypott">Временная цель Гипо используется только для предотвращения чрезмерной коррекции после гипо, когда накапливается отрицательный IOB. Необходимо принять дополнительные меры, чтобы предотвратить ситуацию в будущем.</string>
<string name="objectives_exam_learned_profileswitch">Использование процентов не повлияет на целевую ГК, а только на базу, ISF и IC таким образом, чтобы дать больше инсулина (выше 100%%) или меньше инсулина (ниже 100%%).</string>
<string name="objectives_exam_learned_profileswitchtime">Используя сдвиг по времени, можно адаптировать профиль к нерегулярным событиям типа позднего пробуждения.</string>
<string name="objectives_exam_learned_profileswitch4">Редактирование профиля не является достаточным для внесения изменений. Все еще нужно переключиться на этот профиль и активировать изменения.</string>

View file

@ -18,7 +18,6 @@
<string name="objectives_autosens_objective">Justera basaler och kvoter om det behövs. Aktivera sedan autosens</string>
<string name="objectives_autosens_gate">1 veckas lyckad looping dagtid, där alla måltider lagts in</string>
<string name="objectives_smb_objective">Aktiverar ytterligare funktioner för användning dagtid, t ex SMB</string>
<string name="objectives_smb_gate">Du måste läsa på wikin och öka max IOB för att få SMB att fungera bra. En bra start är att sätta max IOB till din genomsnittliga måltidsbolus plus 3 gånger den högsta basalen du har under ett dygn</string>
<string name="objectives_auto_objective">Aktivera automatisering</string>
<string name="objectives_manualenacts">Manuella justeringar</string>
<string name="accomplished">Utfört: %1$s</string>

View file

@ -18,7 +18,6 @@
<string name="objectives_autosens_objective">必要时调整胰岛素基础率和比率然后再启用auto-sens胰岛素敏感系数自动调整功能</string>
<string name="objectives_autosens_gate">规律的进行碳水记录,成功在一个星期的白天开启闭环</string>
<string name="objectives_smb_objective">在白天启用额外的功能,例如微型大剂量 SMB</string>
<string name="objectives_smb_gate">你应该阅读wiki指南以调整 maxIOB(活性胰岛素的最大值) 设置让SMB(微型大剂量) 更好的工作。建议是 maxIOB值=餐时剂量的平均值+基础率的最大值的3倍。</string>
<string name="objectives_auto_objective">启用自动操作功能插件</string>
<string name="objectives_manualenacts">手动执行</string>
<string name="accomplished">已完成: %1$s</string>

View file

@ -26,7 +26,7 @@
<string name="objectives_autosens_gate">1 week successful daytime looping with regular carb entry</string>
<string name="objectives_autosens_learned">If your autosens result is not oscillating around 100% your profile probably wrong.</string>
<string name="objectives_smb_objective">Enabling additional features for daytime use, such as SMB</string>
<string name="objectives_smb_gate">You must read the wiki and rise maxIOB to get SMBs working fine! A good start is maxIOB=average mealbolus + 3 x max daily basal</string>
<string name="objectives_smb_gate">You must read the wiki and raise maxIOB to get SMBs working fine! A good start is maxIOB=average mealbolus + 3 x max daily basal</string>
<string name="objectives_smb_learned">Using SMB is your goal. Oref1 algorithm was designed to help you with your boluses as well. You should not give full bolus for your food but only part of it and let AAPS give you the rest if needed. This way you have more space for miscalculated carbs. Did you know that you can set a percentage of bolus calculator result to reduce the size of bolus?</string>
<string name="objectives_auto_objective">Enabling automation</string>
<string name="objectives_auto_gate">Read the docs on how automation works. Set up your first simple rules. Instead of action let AAPS display only notification. When you are sure automation is triggered at the right time replace notification by real action. (https://wiki.aaps.app/en/latest/Usage/Automation.html)</string>

View file

@ -1,9 +1,7 @@
package info.nightscout.plugins.general.actions
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -59,7 +57,6 @@ class ActionsFragment : DaggerFragment() {
@Inject lateinit var sp: SP
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var ctx: Context
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var statusLightHandler: StatusLightHandler
@Inject lateinit var fabricPrivacy: FabricPrivacy
@ -78,31 +75,22 @@ class ActionsFragment : DaggerFragment() {
private val pumpCustomActions = HashMap<String, CustomAction>()
private val pumpCustomButtons = ArrayList<SingleClickButton>()
private lateinit var dm: DisplayMetrics
private var _binding: ActionsFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
//check screen width
dm = DisplayMetrics()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
@Suppress("DEPRECATION")
activity?.display?.getRealMetrics(dm)
} else {
@Suppress("DEPRECATION")
activity?.windowManager?.defaultDisplay?.getMetrics(dm)
}
_binding = ActionsFragmentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
ActionsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
skinProvider.activeSkin().preProcessLandscapeActionsLayout(dm, binding)
val screenWidth = activity?.window?.decorView?.width ?: 0
val screenHeight = activity?.window?.decorView?.height ?: 0
val isLandscape = screenHeight < screenWidth
skinProvider.activeSkin().preProcessLandscapeActionsLayout(isLandscape, binding)
binding.profileSwitch.setOnClickListener {
activity?.let { activity ->
@ -285,7 +273,7 @@ class ActionsFragment : DaggerFragment() {
binding.tddStats.visibility = pump.pumpDescription.supportsTDDs.toVisibility()
val isPatchPump = pump.pumpDescription.isPatchPump
binding.status.apply {
cannulaOrPatch.text = if (isPatchPump) rh.gs(R.string.patch_pump) else rh.gs(R.string.cannula)
cannulaOrPatch.text = if (cannulaOrPatch.text.isEmpty()) "" else if (isPatchPump) rh.gs(R.string.patch_pump) else rh.gs(R.string.cannula)
val imageResource = if (isPatchPump) info.nightscout.core.main.R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula
cannulaOrPatch.setCompoundDrawablesWithIntrinsicBounds(imageResource, 0, 0, 0)
batteryLayout.visibility = (!isPatchPump || pump.pumpDescription.useHardwareLink).toVisibility()
@ -293,7 +281,11 @@ class ActionsFragment : DaggerFragment() {
cannulaUsage.visibility = isPatchPump.not().toVisibility()
if (!config.NSCLIENT) {
statusLightHandler.updateStatusLights(cannulaAge, cannulaUsage, insulinAge, reservoirLevel, sensorAge, sensorLevel, pbAge, batteryLevel)
statusLightHandler.updateStatusLights(
cannulaAge, cannulaUsage, insulinAge,
reservoirLevel, sensorAge, sensorLevel,
pbAge, pbLevel
)
sensorLevelLabel.text = if (activeBgSource.sensorBatteryLevel == -1) "" else rh.gs(R.string.level_label)
} else {
statusLightHandler.updateStatusLights(cannulaAge, cannulaUsage, insulinAge, null, sensorAge, null, pbAge, null)

View file

@ -188,7 +188,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
smallHeight = screenHeight <= Constants.SMALL_HEIGHT
val landscape = screenHeight < screenWidth
skinProvider.activeSkin().preProcessLandscapeOverviewLayout(dm, binding, landscape, rh.gb(info.nightscout.shared.R.bool.isTablet), smallHeight)
skinProvider.activeSkin().preProcessLandscapeOverviewLayout(binding, landscape, rh.gb(info.nightscout.shared.R.bool.isTablet), smallHeight)
binding.nsclientCard.visibility = config.NSCLIENT.toVisibility()
binding.notifications.setHasFixedSize(false)
@ -841,11 +841,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (it.value.originalPercentage != 100 || it.value.originalTimeshift != 0L || it.value.originalDuration != 0L)
info.nightscout.core.ui.R.attr.ribbonWarningColor
else info.nightscout.core.ui.R.attr.ribbonDefaultColor
} else if (it is ProfileSealed.PS) {
info.nightscout.core.ui.R.attr.ribbonDefaultColor
} else {
info.nightscout.core.ui.R.attr.ribbonDefaultColor
}
} else info.nightscout.core.ui.R.attr.ribbonDefaultColor
} ?: info.nightscout.core.ui.R.attr.ribbonCriticalColor
val profileTextColor = profile?.let {
@ -853,11 +849,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (it.value.originalPercentage != 100 || it.value.originalTimeshift != 0L || it.value.originalDuration != 0L)
info.nightscout.core.ui.R.attr.ribbonTextWarningColor
else info.nightscout.core.ui.R.attr.ribbonTextDefaultColor
} else if (it is ProfileSealed.PS) {
info.nightscout.core.ui.R.attr.ribbonTextDefaultColor
} else {
info.nightscout.core.ui.R.attr.ribbonTextDefaultColor
}
} else info.nightscout.core.ui.R.attr.ribbonTextDefaultColor
} ?: info.nightscout.core.ui.R.attr.ribbonTextDefaultColor
setRibbon(binding.activeProfile, profileTextColor, profileBackgroundColor, profileFunction.getProfileNameWithRemainingTime())
}
@ -899,14 +891,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.statusLightsLayout.apply {
cannulaOrPatch.setImageResource(if (isPatchPump) info.nightscout.core.main.R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula)
cannulaOrPatch.contentDescription = rh.gs(if (isPatchPump) R.string.statuslights_patch_pump_age else R.string.statuslights_cannula_age)
cannulaOrPatch.scaleX = if (isPatchPump) 1.4f else 2f
cannulaOrPatch.scaleY = cannulaOrPatch.scaleX
insulinAge.visibility = isPatchPump.not().toVisibility()
batteryLayout.visibility = (!isPatchPump || pump.pumpDescription.useHardwareLink).toVisibility()
pbAge.visibility = (pump.pumpDescription.isBatteryReplaceable || pump.isBatteryChangeLoggingEnabled()).toVisibility()
val useBatteryLevel = (pump.model() == PumpType.OMNIPOD_EROS)
|| (pump.model() != PumpType.ACCU_CHEK_COMBO && pump.model() != PumpType.OMNIPOD_DASH)
batteryLevel.visibility = useBatteryLevel.toVisibility()
pbLevel.visibility = useBatteryLevel.toVisibility()
statusLightsLayout.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility()
}
statusLightHandler.updateStatusLights(
@ -917,7 +907,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.statusLightsLayout.sensorAge,
null,
binding.statusLightsLayout.pbAge,
binding.statusLightsLayout.batteryLevel
binding.statusLightsLayout.pbLevel
)
}
@ -1040,6 +1030,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
var useRatioForScale = false
var useDSForScale = false
var useBGIForScale = false
var useHRForScale = false
when {
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
@ -1048,6 +1039,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = 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]
@ -1062,6 +1054,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (useDSForScale) 1.0 else 0.8,
useRatioForScale
)
if (menuChartSettings[g + 1][OverviewMenus.CharType.HR.ordinal]) secondGraphData.addHeartRate(useHRForScale, if (useHRForScale) 1.0 else 0.8)
// set manual x bounds to have nice steps
secondGraphData.formatAxis(overviewData.fromTime, overviewData.endTime)
@ -1077,7 +1070,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal]
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.HR.ordinal]
).toVisibility()
secondaryGraphsData[g].performUpdate()
}

View file

@ -47,7 +47,8 @@ class OverviewMenusImpl @Inject constructor(
BGI(R.string.overview_show_bgi, info.nightscout.core.ui.R.attr.bgiColor, info.nightscout.core.ui.R.attr.menuTextColor, primary = false, secondary = true, shortnameId = R.string.bgi_shortname),
SEN(R.string.overview_show_sensitivity, info.nightscout.core.ui.R.attr.ratioColor, info.nightscout.core.ui.R.attr.menuTextColorInverse, primary = false, secondary = true, shortnameId = R.string.sensitivity_shortname),
ACT(R.string.overview_show_activity, info.nightscout.core.ui.R.attr.activityColor, info.nightscout.core.ui.R.attr.menuTextColor, primary = true, secondary = false, shortnameId = R.string.activity_shortname),
DEVSLOPE(R.string.overview_show_deviation_slope, info.nightscout.core.ui.R.attr.devSlopePosColor, info.nightscout.core.ui.R.attr.menuTextColor, primary = false, secondary = true, shortnameId = R.string.devslope_shortname)
DEVSLOPE(R.string.overview_show_deviation_slope, info.nightscout.core.ui.R.attr.devSlopePosColor, info.nightscout.core.ui.R.attr.menuTextColor, primary = false, secondary = true, shortnameId = R.string.devslope_shortname),
HR(R.string.overview_show_heartRate, info.nightscout.core.ui.R.attr.heartRateColor, info.nightscout.core.ui.R.attr.menuTextColor, primary = false, secondary = true, shortnameId = R.string.heartRate_shortname),
}
companion object {

View file

@ -258,5 +258,14 @@ class GraphData(
// draw it
graph.onDataChanged(false, false)
}
}
fun addHeartRate(useForScale: Boolean, scale: Double) {
val maxHR = overviewData.heartRateGraphSeries.highestValueY
if (useForScale) {
minY = 0.0
maxY = maxHR
}
addSeries(overviewData.heartRateGraphSeries)
overviewData.heartRateScale.multiplier = maxY * scale / maxHR
}
}

View file

@ -12,7 +12,7 @@ import android.view.View
import android.view.WindowManager
import com.google.common.primitives.Ints.min
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.core.ui.activities.TranslatedDaggerAppCompatActivity
import info.nightscout.core.ui.dialogs.OKDialog
import info.nightscout.core.ui.toast.ToastUtils
import info.nightscout.core.utils.fabric.FabricPrivacy
@ -28,7 +28,7 @@ import info.nightscout.shared.interfaces.ResourceHelper
import net.glxn.qrgen.android.QRCode
import javax.inject.Inject
class SmsCommunicatorOtpActivity : DaggerAppCompatActivity() {
class SmsCommunicatorOtpActivity : TranslatedDaggerAppCompatActivity() {
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var smsCommunicator: SmsCommunicator

View file

@ -19,6 +19,7 @@ import info.nightscout.database.ValueWrapper
import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.BolusCalculatorResult
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.entities.HeartRate
import info.nightscout.database.entities.TemporaryBasal
import info.nightscout.database.entities.TemporaryTarget
import info.nightscout.database.entities.TotalDailyDose
@ -28,6 +29,7 @@ import info.nightscout.database.entities.interfaces.end
import info.nightscout.database.impl.AppRepository
import info.nightscout.database.impl.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.database.impl.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.database.impl.transactions.InsertOrUpdateHeartRateTransaction
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit
@ -308,6 +310,10 @@ class DataHandlerMobile @Inject constructor(
aapsLogger.debug(LTag.WEAR, "WearException received $it from ${it.sourceNodeId}")
fabricPrivacy.logWearException(it)
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventData.ActionHeartRate::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ handleHeartRate(it) }, fabricPrivacy::logException)
}
private fun handleTddStatus() {
@ -1230,4 +1236,15 @@ class DataHandlerMobile @Inject constructor(
@Synchronized private fun sendError(errorMessage: String) {
rxBus.send(EventMobileToWear(EventData.ConfirmAction(rh.gs(info.nightscout.core.ui.R.string.error), errorMessage, returnCommand = EventData.Error(dateUtil.now())))) // ignore return path
}
/** Stores heart rate events coming from the Wear device. */
private fun handleHeartRate(actionHeartRate: EventData.ActionHeartRate) {
aapsLogger.debug(LTag.WEAR, "Heart rate received $actionHeartRate from ${actionHeartRate.sourceNodeId}")
val hr = HeartRate(
duration = actionHeartRate.duration,
timestamp = actionHeartRate.timestamp,
beatsPerMinute = actionHeartRate.beatsPerMinute,
device = actionHeartRate.device)
repository.runTransaction(InsertOrUpdateHeartRateTransaction(hr)).blockingAwait()
}
}

View file

@ -255,6 +255,7 @@ class ProfilePlugin @Inject constructor(
isEdited = false
createAndStoreConvertedProfile()
aapsLogger.debug(LTag.PROFILE, "Accepted ${profiles.size} profiles")
storeSettings()
rxBus.send(EventLocalProfileChanged())
} else
aapsLogger.debug(LTag.PROFILE, "ProfileStore not accepted")

View file

@ -1,6 +1,5 @@
package info.nightscout.plugins.skins
import android.util.DisplayMetrics
import info.nightscout.interfaces.Config
import info.nightscout.plugins.R
import info.nightscout.plugins.databinding.OverviewFragmentBinding
@ -14,8 +13,8 @@ class SkinClassic @Inject constructor(private val config: Config) : SkinInterfac
override val mainGraphHeight: Int get() = 200
override val secondaryGraphHeight: Int get() = 100
override fun preProcessLandscapeOverviewLayout(dm: DisplayMetrics, binding: OverviewFragmentBinding, isLandscape: Boolean, isTablet: Boolean, isSmallHeight: Boolean) {
super.preProcessLandscapeOverviewLayout(dm, binding, isLandscape, isTablet, isSmallHeight)
override fun preProcessLandscapeOverviewLayout(binding: OverviewFragmentBinding, isLandscape: Boolean, isTablet: Boolean, isSmallHeight: Boolean) {
super.preProcessLandscapeOverviewLayout(binding, isLandscape, isTablet, isSmallHeight)
if (!config.NSCLIENT && (isSmallHeight || isLandscape)) moveButtonsLayout(binding.root)
}
}

View file

@ -1,6 +1,5 @@
package info.nightscout.plugins.skins
import android.util.DisplayMetrics
import android.util.TypedValue.COMPLEX_UNIT_PX
import android.view.View
import android.widget.LinearLayout
@ -18,16 +17,11 @@ interface SkinInterface {
val secondaryGraphHeight: Int // in dp
// no pre processing by default
fun preProcessLandscapeActionsLayout(dm: DisplayMetrics, binding: ActionsFragmentBinding) {
fun preProcessLandscapeActionsLayout(isLandscape: Boolean, binding: ActionsFragmentBinding) {
}
fun preProcessLandscapeOverviewLayout(dm: DisplayMetrics, binding: OverviewFragmentBinding, isLandscape: Boolean, isTablet: Boolean, isSmallHeight: Boolean) {
// pre-process landscape mode
val screenWidth = dm.widthPixels
val screenHeight = dm.heightPixels
val landscape = screenHeight < screenWidth
if (landscape) {
fun preProcessLandscapeOverviewLayout(binding: OverviewFragmentBinding, isLandscape: Boolean, isTablet: Boolean, isSmallHeight: Boolean) {
if (isLandscape) {
val iobLayout = binding.infoLayout.iobLayout
val iobLayoutParams = iobLayout.layoutParams as ConstraintLayout.LayoutParams
val timeLayout = binding.infoLayout.timeLayout
@ -59,7 +53,7 @@ interface SkinInterface {
for (v in texts) v.setTextSize(COMPLEX_UNIT_PX, v.textSize * 1.3f)
}
binding.statusLightsLayout.apply {
val texts = listOf(cannulaAge, insulinAge, reservoirLevel, sensorAge, pbAge, batteryLevel)
val texts = listOf(cannulaAge, insulinAge, reservoirLevel, sensorAge, pbAge, pbLevel)
for (v in texts) v.setTextSize(COMPLEX_UNIT_PX, v.textSize * 1.3f)
}
timeLayout.orientation = LinearLayout.HORIZONTAL

View file

@ -1,6 +1,5 @@
package info.nightscout.plugins.skins
import android.util.DisplayMetrics
import info.nightscout.interfaces.Config
import info.nightscout.plugins.R
import info.nightscout.plugins.databinding.OverviewFragmentBinding
@ -14,8 +13,8 @@ class SkinLargeDisplay @Inject constructor(private val config: Config) : SkinInt
override val mainGraphHeight: Int get() = 400
override val secondaryGraphHeight: Int get() = 150
override fun preProcessLandscapeOverviewLayout(dm: DisplayMetrics, binding: OverviewFragmentBinding, isLandscape: Boolean, isTablet: Boolean, isSmallHeight: Boolean) {
super.preProcessLandscapeOverviewLayout(dm, binding, isLandscape, isTablet, isSmallHeight)
override fun preProcessLandscapeOverviewLayout(binding: OverviewFragmentBinding, isLandscape: Boolean, isTablet: Boolean, isSmallHeight: Boolean) {
super.preProcessLandscapeOverviewLayout(binding, isLandscape, isTablet, isSmallHeight)
if (!config.NSCLIENT && (isSmallHeight || isLandscape)) moveButtonsLayout(binding.root)
}
}

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