Merge branch 'nightscout:dev' into dev

This commit is contained in:
bubbledevteam 2021-10-05 20:16:08 +08:00 committed by GitHub
commit b57ca6f94c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1619 changed files with 60155 additions and 30621 deletions

View file

@ -68,6 +68,6 @@ workflows:
# Inside the workflow, you provide the jobs you want to run, e.g this workflow runs the build-and-test job above. # Inside the workflow, you provide the jobs you want to run, e.g this workflow runs the build-and-test job above.
# CircleCI will run this workflow on every commit. # CircleCI will run this workflow on every commit.
# For more details on extending your workflow, see the configuration docs: https://circleci.com/docs/2.0/configuration-reference/#workflows # For more details on extending your workflow, see the configuration docs: https://circleci.com/docs/2.0/configuration-reference/#workflows
sample: dotests:
jobs: jobs:
- build-and-test - build-and-test

5
.editorconfig Normal file
View file

@ -0,0 +1,5 @@
root = true
[*{kt,kts}]
disabled_rules=no-wildcard-imports

2
.gitignore vendored
View file

@ -11,8 +11,6 @@ build/
.idea/* .idea/*
!.idea/codeStyles/ !.idea/codeStyles/
full/ full/
debug/
release/
app/com.crashlytics.settings.json app/com.crashlytics.settings.json
app/session_analytics.tap app/session_analytics.tap
.project .project

View file

@ -1,11 +1,13 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<option name="AUTODETECT_INDENTS" value="false" /> <option name="AUTODETECT_INDENTS" value="false" />
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" /> <option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="6" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="6" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="6" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="6" />
<option name="BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES" value="1" /> <option name="BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES" value="1" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<codeStyleSettings language="JAVA"> <codeStyleSettings language="JAVA">
<option name="METHOD_ANNOTATION_WRAP" value="0" /> <option name="METHOD_ANNOTATION_WRAP" value="0" />
@ -124,14 +126,22 @@
</arrangement> </arrangement>
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="kotlin"> <codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="RIGHT_MARGIN" value="120" />
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" /> <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
<option name="LINE_COMMENT_ADD_SPACE" value="true" /> <option name="LINE_COMMENT_ADD_SPACE" value="true" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" /> <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" /> <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" /> <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" /> <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
<option name="ASSIGNMENT_WRAP" value="5" />
<option name="METHOD_ANNOTATION_WRAP" value="5" /> <option name="METHOD_ANNOTATION_WRAP" value="5" />
<option name="FIELD_ANNOTATION_WRAP" value="0" /> <option name="CLASS_ANNOTATION_WRAP" value="1" />
<option name="FIELD_ANNOTATION_WRAP" value="1" />
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
<option name="ENUM_CONSTANTS_WRAP" value="5" />
<indentOptions> <indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" /> <option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions> </indentOptions>

View file

@ -2,6 +2,7 @@
<dictionary name="project-dictionary"> <dictionary name="project-dictionary">
<words> <words>
<w>aaps</w> <w>aaps</w>
<w>abcdef</w>
<w>acked</w> <w>acked</w>
<w>actionstring</w> <w>actionstring</w>
<w>allowednumbers</w> <w>allowednumbers</w>
@ -14,8 +15,10 @@
<w>basals</w> <w>basals</w>
<w>bgcheck</w> <w>bgcheck</w>
<w>bgsource</w> <w>bgsource</w>
<w>boluscalc</w>
<w>bolusing</w> <w>bolusing</w>
<w>carb</w> <w>carb</w>
<w>carbratio</w>
<w>carbs</w> <w>carbs</w>
<w>carbsreq</w> <w>carbsreq</w>
<w>careportal</w> <w>careportal</w>
@ -46,6 +49,7 @@
<w>medtronic</w> <w>medtronic</w>
<w>mgdl</w> <w>mgdl</w>
<w>mmol</w> <w>mmol</w>
<w>motol</w>
<w>multiwave</w> <w>multiwave</w>
<w>netinsulin</w> <w>netinsulin</w>
<w>netratio</w> <w>netratio</w>
@ -55,9 +59,14 @@
<w>okcancel</w> <w>okcancel</w>
<w>omnipod</w> <w>omnipod</w>
<w>openaps</w> <w>openaps</w>
<w>openhumans</w>
<w>oref</w> <w>oref</w>
<w>otpauth</w>
<w>passcode</w> <w>passcode</w>
<w>pdus</w>
<w>philoul</w>
<w>poctech</w> <w>poctech</w>
<w>pred</w>
<w>profileswitch</w> <w>profileswitch</w>
<w>pumpbtcomm</w> <w>pumpbtcomm</w>
<w>quickwizard</w> <w>quickwizard</w>
@ -66,14 +75,18 @@
<w>refresheventsfromnightscout</w> <w>refresheventsfromnightscout</w>
<w>rileylink</w> <w>rileylink</w>
<w>roboelectric</w> <w>roboelectric</w>
<w>sgvs</w>
<w>sitechange</w> <w>sitechange</w>
<w>smscommunicator</w> <w>smscommunicator</w>
<w>sntp</w>
<w>sooil</w> <w>sooil</w>
<w>soundid</w> <w>soundid</w>
<w>splitted</w> <w>splitted</w>
<w>ssid</w>
<w>superbolus</w> <w>superbolus</w>
<w>targethigh</w> <w>targethigh</w>
<w>targetlow</w> <w>targetlow</w>
<w>tbrs</w>
<w>tdds</w> <w>tdds</w>
<w>tempbasal</w> <w>tempbasal</w>
<w>tempbasals</w> <w>tempbasals</w>
@ -82,9 +95,11 @@
<w>tidepool</w> <w>tidepool</w>
<w>timeshift</w> <w>timeshift</w>
<w>tirs</w> <w>tirs</w>
<w>totp</w>
<w>uart</w> <w>uart</w>
<w>wizzardpage</w> <w>wizzardpage</w>
<w>xdrip</w> <w>xdrip</w>
<w>xstream</w>
<w>ypso</w> <w>ypso</w>
<w>ypsomed</w> <w>ypsomed</w>
<w>ypsopump</w> <w>ypsopump</w>

View file

@ -1,26 +1,24 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-allopen'
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.hiya.jacoco-android' apply plugin: 'com.hiya.jacoco-android'
apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'com.google.firebase.crashlytics'
apply from: "${project.rootDir}/gradle/android_dependencies.gradle" apply from: "${project.rootDir}/gradle/android_dependencies.gradle"
apply from: "${project.rootDir}/gradle/jacoco_global.gradle"
jacoco {
toolVersion = "0.8.6"
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
repositories { repositories {
mavenCentral() mavenCentral()
google() google()
} }
allOpen {
// allows mocking for classes w/o directly opening them for release builds
annotation 'info.nightscout.androidaps.annotations.OpenForTesting'
}
def generateGitBuild = { -> def generateGitBuild = { ->
StringBuilder stringBuilder = new StringBuilder() StringBuilder stringBuilder = new StringBuilder()
try { try {
@ -111,7 +109,7 @@ android {
defaultConfig { defaultConfig {
multiDexEnabled true multiDexEnabled true
versionCode 1500 versionCode 1500
version "2.8.2.1-dev-e4" version "2.8.2.4-dev"
buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
@ -186,19 +184,20 @@ dependencies {
implementation project(':danars') implementation project(':danars')
implementation project(':danar') implementation project(':danar')
implementation project(':insight') implementation project(':insight')
implementation project(':pump-common')
implementation project(':rileylink') implementation project(':rileylink')
implementation project(':medtronic') implementation project(':medtronic')
implementation project(':omnipod-common') implementation project(':omnipod-common')
implementation project(':omnipod-eros') implementation project(':omnipod-eros')
implementation project(':omnipod-dash') implementation project(':omnipod-dash')
implementation project(':diaconn')
implementation project(':openhumans')
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
/* Dagger2 - We are going to use dagger.android which includes /* Dagger2 - We are going to use dagger.android which includes
* support for Activity and fragment injection so we need to include * support for Activity and fragment injection so we need to include
* the following dependencies */ * the following dependencies */
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version" annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version" kapt "com.google.dagger:dagger-android-processor:$dagger_version"

View file

@ -1,42 +1,7 @@
package info.nightscout.androidaps package info.nightscout.androidaps
import android.os.SystemClock
import android.view.View
import android.view.ViewGroup
import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.scrollTo
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withClassName
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withTagValue
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest import androidx.test.filters.LargeTest
import androidx.test.rule.ActivityTestRule
import androidx.test.rule.GrantPermissionRule
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.source.RandomBgPlugin
import info.nightscout.androidaps.setupwizard.SetupWizardActivity
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.extensions.isRunningTest
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.hamcrest.TypeSafeMatcher
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@LargeTest @LargeTest

View file

@ -52,7 +52,8 @@
android:name="com.google.android.gms.car.application" android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" /> android:resource="@xml/automotive_app_desc" />
<activity android:name=".MainActivity"> <activity android:name=".MainActivity"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -60,7 +61,8 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".activities.PreferencesActivity" /> <activity android:name=".activities.PreferencesActivity" />
<activity android:name=".plugins.general.overview.activities.QuickWizardListActivity"> <activity android:name=".plugins.general.overview.activities.QuickWizardListActivity"
android:exported="false">
<intent-filter> <intent-filter>
<action android:name="info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity" /> <action android:name="info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity" />
@ -68,7 +70,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".plugins.general.maintenance.activities.PrefImportListActivity" /> <activity android:name=".plugins.general.maintenance.activities.PrefImportListActivity" />
<activity android:name=".historyBrowser.HistoryBrowseActivity" /> <activity android:name=".activities.HistoryBrowseActivity" />
<activity android:name=".activities.TreatmentsActivity" /> <activity android:name=".activities.TreatmentsActivity" />
<activity android:name=".activities.SurveyActivity" /> <activity android:name=".activities.SurveyActivity" />
<activity android:name=".activities.ProfileHelperActivity" <activity android:name=".activities.ProfileHelperActivity"
@ -119,7 +121,7 @@
<receiver android:name=".receivers.KeepAliveReceiver" /> <receiver android:name=".receivers.KeepAliveReceiver" />
<!-- Receive ignore 5m, 15m, 30m requests for carb notifications --> <!-- Receive ignore 5m, 15m, 30m requests for carb notifications -->
<receiver android:name=".plugins.aps.loop.CarbSuggestionReceiver"></receiver> <receiver android:name=".plugins.aps.loop.CarbSuggestionReceiver" />
<!-- Auto start --> <!-- Auto start -->
<receiver <receiver
@ -241,20 +243,6 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".plugins.general.openhumans.OpenHumansLoginActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="setup-openhumans"
android:scheme="androidaps" />
</intent-filter>
</activity>
<uses-library android:name="org.apache.http.legacy" android:required="false"/> <uses-library android:name="org.apache.http.legacy" android:required="false"/>
</application> </application>

View file

@ -1,7 +1,7 @@
<configuration> <configuration>
<!-- Create a file appender for a log in the application's data directory --> <!-- Create a file appender for a log in the application's data directory -->
<property name="EXT_FILES_DIR" scope="context" <property name="EXT_FILES_DIR" scope="context"
value="${EXT_DIR:-/sdcard}/Android/data/${PACKAGE_NAME}/files" /> value="${EXT_DIR:-/sdcard}/AAPS/logs/${PACKAGE_NAME}" />
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${EXT_FILES_DIR}/AndroidAPS.log</file> <file>${EXT_FILES_DIR}/AndroidAPS.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

View file

@ -34,7 +34,7 @@ import info.nightscout.androidaps.databinding.ActivityMainBinding
import info.nightscout.androidaps.events.EventAppExit import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity import info.nightscout.androidaps.activities.HistoryBrowseActivity
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.IconsProvider import info.nightscout.androidaps.interfaces.IconsProvider
@ -95,7 +95,6 @@ class MainActivity : NoSplashAppCompatActivity() {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
@kotlin.ExperimentalStdlibApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Iconify.with(FontAwesomeModule()) Iconify.with(FontAwesomeModule())
@ -166,7 +165,7 @@ class MainActivity : NoSplashAppCompatActivity() {
actionBarDrawerToggle.syncState() actionBarDrawerToggle.syncState()
} }
public override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
disposable.clear() disposable.clear()
} }
@ -361,7 +360,6 @@ class MainActivity : NoSplashAppCompatActivity() {
// Correct place for calling setUserStats() would be probably MainApp // Correct place for calling setUserStats() would be probably MainApp
// but we need to have it called at least once a day. Thus this location // but we need to have it called at least once a day. Thus this location
@kotlin.ExperimentalStdlibApi
private fun setUserStats() { private fun setUserStats() {
if (!fabricPrivacy.fabricEnabled()) return if (!fabricPrivacy.fabricEnabled()) return
val closedLoopEnabled = if (constraintChecker.isClosedLoopAllowed().value()) "CLOSED_LOOP_ENABLED" else "CLOSED_LOOP_DISABLED" val closedLoopEnabled = if (constraintChecker.isClosedLoopAllowed().value()) "CLOSED_LOOP_ENABLED" else "CLOSED_LOOP_DISABLED"

View file

@ -6,7 +6,6 @@ import android.content.IntentFilter
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
import android.os.Build import android.os.Build
import com.j256.ormlite.android.apptools.OpenHelperManager
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DaggerApplication import dagger.android.DaggerApplication
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
@ -15,11 +14,10 @@ import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction
import info.nightscout.androidaps.database.transactions.VersionChangeTransaction import info.nightscout.androidaps.database.transactions.VersionChangeTransaction
import info.nightscout.androidaps.db.CompatDBHelper import info.nightscout.androidaps.db.CompatDBHelper
import info.nightscout.androidaps.db.DatabaseHelper import info.nightscout.androidaps.di.StaticInjector
import info.nightscout.androidaps.db.StaticInjector
import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent
import info.nightscout.androidaps.interfaces.ConfigBuilder
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.ConfigBuilder
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
@ -34,6 +32,7 @@ import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver
import info.nightscout.androidaps.utils.ActivityMonitor import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.locale.LocaleHelper.update import info.nightscout.androidaps.utils.locale.LocaleHelper.update
import info.nightscout.androidaps.utils.protection.PasswordCheck
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.plusAssign
@ -58,12 +57,12 @@ class MainApp : DaggerApplication() {
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize @Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var passwordCheck: PasswordCheck
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
aapsLogger.debug("onCreate") aapsLogger.debug("onCreate")
update(this) update(this)
dbHelper = OpenHelperManager.getHelper(this, DatabaseHelper::class.java)
var gitRemote: String? = BuildConfig.REMOTE var gitRemote: String? = BuildConfig.REMOTE
var commitHash: String? = BuildConfig.HEAD var commitHash: String? = BuildConfig.HEAD
@ -90,6 +89,7 @@ class MainApp : DaggerApplication() {
keepAliveManager.setAlarm(this) keepAliveManager.setAlarm(this)
doMigrations() doMigrations()
uel.log(UserEntry.Action.START_AAPS, UserEntry.Sources.Aaps) uel.log(UserEntry.Action.START_AAPS, UserEntry.Sources.Aaps)
passwordCheck.passwordResetCheck(this)
} }
private fun doMigrations() { private fun doMigrations() {
@ -134,9 +134,4 @@ class MainApp : DaggerApplication() {
keepAliveManager.cancelAlarm(this) keepAliveManager.cancelAlarm(this)
super.onTerminate() super.onTerminate()
} }
companion object {
lateinit var dbHelper: DatabaseHelper
}
} }

View file

@ -0,0 +1,406 @@
package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.graphics.Color
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import com.jjoe64.graphview.GraphView
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventCustomCalculationFinished
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.general.overview.OverviewMenus
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.*
import javax.inject.Inject
import kotlin.math.min
class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var sensitivityOref1Plugin: SensitivityOref1Plugin
@Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
@Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
@Inject lateinit var repository: AppRepository
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var overviewMenus: OverviewMenus
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var config: Config
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var nsDeviceStatus: NSDeviceStatus
@Inject lateinit var translator: Translator
private val disposable = CompositeDisposable()
private val secondaryGraphs = ArrayList<GraphView>()
private val secondaryGraphsLabel = ArrayList<TextView>()
private var axisWidth: Int = 0
private var rangeToDisplay = 24 // for graph
// private var start: Long = 0
private lateinit var iobCobCalculator: IobCobCalculatorPlugin
private lateinit var overviewData: OverviewData
private lateinit var binding: ActivityHistorybrowseBinding
private var destroyed = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHistorybrowseBinding.inflate(layoutInflater)
setContentView(binding.root)
// We don't want to use injected singletons but own instance working on top of different data
iobCobCalculator = IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction, activePlugin, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository)
overviewData = OverviewData(injector, aapsLogger, resourceHelper, dateUtil, sp, activePlugin, defaultValueHelper, profileFunction, config, loopPlugin, nsDeviceStatus, repository, overviewMenus, iobCobCalculator, translator)
binding.left.setOnClickListener {
adjustTimeRange(overviewData.fromTime - T.hours(rangeToDisplay.toLong()).msecs())
loadAll("onClickLeft")
}
binding.right.setOnClickListener {
adjustTimeRange(overviewData.fromTime + T.hours(rangeToDisplay.toLong()).msecs())
loadAll("onClickRight")
}
binding.end.setOnClickListener {
setTime(dateUtil.now())
loadAll("onClickEnd")
}
binding.zoom.setOnClickListener {
rangeToDisplay += 6
rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay
setTime(overviewData.fromTime)
loadAll("rangeChange")
}
binding.zoom.setOnLongClickListener {
Calendar.getInstance().also { calendar ->
calendar.timeInMillis = overviewData.fromTime
calendar[Calendar.MILLISECOND] = 0
calendar[Calendar.SECOND] = 0
calendar[Calendar.MINUTE] = 0
calendar[Calendar.HOUR_OF_DAY] = 0
setTime(calendar.timeInMillis)
}
loadAll("onLongClickZoom")
true
}
// create an OnDateSetListener
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
Calendar.getInstance().also { calendar ->
calendar.timeInMillis = overviewData.fromTime
calendar[Calendar.YEAR] = year
calendar[Calendar.MONTH] = monthOfYear
calendar[Calendar.DAY_OF_MONTH] = dayOfMonth
calendar[Calendar.MILLISECOND] = 0
calendar[Calendar.SECOND] = 0
calendar[Calendar.MINUTE] = 0
calendar[Calendar.HOUR_OF_DAY] = 0
setTime(calendar.timeInMillis)
binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime)
}
loadAll("onClickDate")
}
binding.date.setOnClickListener {
val cal = Calendar.getInstance()
cal.timeInMillis = overviewData.fromTime
DatePickerDialog(this, dateSetListener,
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH),
cal.get(Calendar.DAY_OF_MONTH)
).show()
}
val dm = DisplayMetrics()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
display?.getRealMetrics(dm)
else
@Suppress("DEPRECATION") windowManager.defaultDisplay.getMetrics(dm)
axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80
binding.bgGraph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
binding.bgGraph.gridLabelRenderer?.reloadStyles()
binding.bgGraph.gridLabelRenderer?.labelVerticalWidth = axisWidth
overviewMenus.setupChartMenu(binding.chartMenuButton)
prepareGraphsIfNeeded(overviewMenus.setting.size)
savedInstanceState?.let { bundle ->
rangeToDisplay = bundle.getInt("rangeToDisplay", 0)
overviewData.fromTime = bundle.getLong("start", 0)
overviewData.toTime = bundle.getLong("end", 0)
}
}
override fun onPause() {
super.onPause()
disposable.clear()
iobCobCalculator.stopCalculation("onPause")
}
@Synchronized
override fun onDestroy() {
destroyed = true
super.onDestroy()
}
override fun onResume() {
super.onResume()
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
// catch only events from iobCobCalculator
if (it.cause is EventCustomCalculationFinished)
refreshLoop("EventAutosensCalculationFinished")
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({
if (it.cause is EventCustomCalculationFinished)
binding.overviewIobcalculationprogess.text = it.progress
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventRefreshOverview::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGUI("EventRefreshOverview") }, fabricPrivacy::logException)
)
disposable += rxBus
.toObservable(EventBucketedDataCreated::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
rxBus.send(EventRefreshOverview("EventBucketedDataCreated"))
}, fabricPrivacy::logException)
if (overviewData.fromTime == 0L) {
// set start of current day
setTime(dateUtil.now())
loadAll("onResume")
} else {
updateGUI("onResume")
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("rangeToDisplay", rangeToDisplay)
outState.putLong("start", overviewData.fromTime)
outState.putLong("end", overviewData.toTime)
}
private fun prepareGraphsIfNeeded(numOfGraphs: Int) {
if (numOfGraphs != secondaryGraphs.size - 1) {
//aapsLogger.debug("New secondary graph count ${numOfGraphs-1}")
// rebuild needed
secondaryGraphs.clear()
secondaryGraphsLabel.clear()
binding.iobGraph.removeAllViews()
for (i in 1 until numOfGraphs) {
val relativeLayout = RelativeLayout(this)
relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val graph = GraphView(this)
graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(100)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) }
graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
graph.gridLabelRenderer?.reloadStyles()
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
graph.gridLabelRenderer?.numVerticalLabels = 3
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray
relativeLayout.addView(graph)
val label = TextView(this)
val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) }
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP)
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
label.layoutParams = layoutParams
relativeLayout.addView(label)
secondaryGraphsLabel.add(label)
binding.iobGraph.addView(relativeLayout)
secondaryGraphs.add(graph)
}
}
}
@Suppress("SameParameterValue")
private fun loadAll(from: String) {
updateDate()
Thread {
overviewData.prepareBgData("$from")
overviewData.prepareTreatmentsData(from)
rxBus.send(EventRefreshOverview("loadAll_$from"))
overviewData.prepareTemporaryTargetData(from)
rxBus.send(EventRefreshOverview("loadAll_$from"))
overviewData.prepareBasalData(from)
rxBus.send(EventRefreshOverview(from))
aapsLogger.debug(LTag.UI, "loadAll $from finished")
runCalculation(from)
}.start()
}
private fun setTime(start: Long) {
GregorianCalendar().also { calendar ->
calendar.timeInMillis = start
calendar[Calendar.MILLISECOND] = 0
calendar[Calendar.SECOND] = 0
calendar[Calendar.MINUTE] = 0
calendar[Calendar.HOUR_OF_DAY] -= (calendar[Calendar.HOUR_OF_DAY] % rangeToDisplay)
adjustTimeRange(calendar.timeInMillis)
}
}
private fun adjustTimeRange(start: Long) {
overviewData.fromTime = start
overviewData.toTime = overviewData.fromTime + T.hours(rangeToDisplay.toLong()).msecs()
overviewData.endTime = overviewData.toTime
}
private fun runCalculation(from: String) {
Thread {
iobCobCalculator.stopCalculation(from)
iobCobCalculator.stopCalculationTrigger = false
iobCobCalculator.runCalculation(from, overviewData.toTime, bgDataReload = true, limitDataToOldestAvailable = false, cause = EventCustomCalculationFinished())
}.start()
}
@Volatile
var runningRefresh = false
private fun refreshLoop(from: String) {
if (runningRefresh) return
runningRefresh = true
overviewData.prepareIobAutosensData(from)
rxBus.send(EventRefreshOverview(from))
aapsLogger.debug(LTag.UI, "refreshLoop finished")
runningRefresh = false
}
fun updateDate() {
binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime)
binding.zoom.text = rangeToDisplay.toString()
}
@Suppress("UNUSED_PARAMETER")
@SuppressLint("SetTextI18n")
fun updateGUI(from: String) {
aapsLogger.debug(LTag.UI, "updateGui $from")
updateDate()
val pump = activePlugin.activePump
val graphData = GraphData(injector, binding.bgGraph, overviewData)
val menuChartSettings = overviewMenus.setting
graphData.addInRangeArea(overviewData.fromTime, overviewData.endTime, defaultValueHelper.determineLowLine(), defaultValueHelper.determineHighLine())
graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
if (buildHelper.isDev()) graphData.addBucketedData()
graphData.addTreatments()
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
graphData.addActivity(0.8)
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
graphData.addBasals()
graphData.addTargetLine()
graphData.addNowLine(dateUtil.now())
// set manual x bounds to have nice steps
graphData.setNumVerticalLabels()
graphData.formatAxis(overviewData.fromTime, overviewData.endTime)
graphData.performUpdate()
// 2nd graphs
prepareGraphsIfNeeded(menuChartSettings.size)
val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
val now = System.currentTimeMillis()
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
val secondGraphData = GraphData(injector, secondaryGraphs[g], overviewData)
var useABSForScale = false
var useIobForScale = false
var useCobForScale = false
var useDevForScale = false
var useRatioForScale = false
var useDSForScale = false
var useBGIForScale = false
when {
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
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
}
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(useABSForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(useIobForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(useCobForScale, if (useCobForScale) 1.0 else 0.5)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(useDevForScale, 1.0)
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] && buildHelper.isDev()) secondGraphData.addDeviationSlope(useDSForScale, 1.0)
// set manual x bounds to have nice steps
secondGraphData.formatAxis(overviewData.fromTime, overviewData.endTime)
secondGraphData.addNowLine(now)
secondaryGraphsData.add(secondGraphData)
}
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1)
secondaryGraphs[g].visibility = (
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] ||
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]
).toVisibility()
secondaryGraphsData[g].performUpdate()
}
}
}

View file

@ -14,11 +14,13 @@ import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugin.general.openhumans.OpenHumansUploader
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
@ -29,7 +31,6 @@ import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin
import info.nightscout.androidaps.plugins.general.wear.WearPlugin import info.nightscout.androidaps.plugins.general.wear.WearPlugin
@ -99,6 +100,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
@Inject lateinit var passwordCheck: PasswordCheck @Inject lateinit var passwordCheck: PasswordCheck
@Inject lateinit var nsSettingStatus: NSSettingsStatus @Inject lateinit var nsSettingStatus: NSSettingsStatus
@Inject lateinit var openHumansUploader: OpenHumansUploader @Inject lateinit var openHumansUploader: OpenHumansUploader
@Inject lateinit var diaconnG8Plugin: DiaconnG8Plugin
override fun onAttach(context: Context) { override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
@ -173,6 +175,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
addPreferencesFromResourceIfEnabled(localInsightPlugin, rootKey, config.PUMPDRIVERS) addPreferencesFromResourceIfEnabled(localInsightPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(comboPlugin, rootKey, config.PUMPDRIVERS) addPreferencesFromResourceIfEnabled(comboPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(medtronicPumpPlugin, rootKey, config.PUMPDRIVERS) addPreferencesFromResourceIfEnabled(medtronicPumpPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(diaconnG8Plugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResource(R.xml.pref_pump, rootKey, config.PUMPDRIVERS) addPreferencesFromResource(R.xml.pref_pump, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(virtualPumpPlugin, rootKey) addPreferencesFromResourceIfEnabled(virtualPumpPlugin, rootKey)
addPreferencesFromResourceIfEnabled(insulinOrefFreePeakPlugin, rootKey) addPreferencesFromResourceIfEnabled(insulinOrefFreePeakPlugin, rootKey)
@ -182,7 +185,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
addPreferencesFromResourceIfEnabled(automationPlugin, rootKey) addPreferencesFromResourceIfEnabled(automationPlugin, rootKey)
addPreferencesFromResourceIfEnabled(wearPlugin, rootKey) addPreferencesFromResourceIfEnabled(wearPlugin, rootKey)
addPreferencesFromResourceIfEnabled(statusLinePlugin, rootKey) addPreferencesFromResourceIfEnabled(statusLinePlugin, rootKey)
addPreferencesFromResource(R.xml.pref_alerts, rootKey) // TODO not organized well addPreferencesFromResource(R.xml.pref_alerts, rootKey)
addPreferencesFromResource(R.xml.pref_datachoices, rootKey) addPreferencesFromResource(R.xml.pref_datachoices, rootKey)
addPreferencesFromResourceIfEnabled(maintenancePlugin, rootKey) addPreferencesFromResourceIfEnabled(maintenancePlugin, rootKey)
addPreferencesFromResourceIfEnabled(openHumansUploader, rootKey) addPreferencesFromResourceIfEnabled(openHumansUploader, rootKey)

View file

@ -7,10 +7,8 @@ import javax.inject.Inject
class RequestDexcomPermissionActivity : DialogAppCompatActivity() { class RequestDexcomPermissionActivity : DialogAppCompatActivity() {
@Inject lateinit var dexcomPlugin: DexcomPlugin @Inject lateinit var dexcomPlugin: DexcomPlugin
@kotlin.ExperimentalStdlibApi
private val requestCode = "AndroidAPS <3".map { it.code }.sum() private val requestCode = "AndroidAPS <3".map { it.code }.sum()
@kotlin.ExperimentalStdlibApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
requestPermissions(arrayOf(DexcomPlugin.PERMISSION), requestCode) requestPermissions(arrayOf(DexcomPlugin.PERMISSION), requestCode)

View file

@ -19,7 +19,7 @@ class SingleFragmentActivity : DaggerAppCompatActivityWithResult() {
private var plugin: PluginBase? = null private var plugin: PluginBase? = null
public override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_single_fragment) setContentView(R.layout.activity_single_fragment)
plugin = pluginStore.plugins[intent.getIntExtra("plugin", -1)] plugin = pluginStore.plugins[intent.getIntExtra("plugin", -1)]
@ -52,7 +52,7 @@ class SingleFragmentActivity : DaggerAppCompatActivityWithResult() {
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu)
} }
public override fun attachBaseContext(newBase: Context) { override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.wrap(newBase)) super.attachBaseContext(LocaleHelper.wrap(newBase))
} }
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.activities package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
@ -21,14 +22,28 @@ class StatsActivity : NoSplashAppCompatActivity() {
private lateinit var binding: ActivityStatsBinding private lateinit var binding: ActivityStatsBinding
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityStatsBinding.inflate(layoutInflater) binding = ActivityStatsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
binding.tdds.text = tddCalculator.stats() binding.tdds.text = getString(R.string.tdd) + ": " + getString(R.string.calculation_in_progress)
binding.tir.text = tirCalculator.stats() binding.tir.text = getString(R.string.tir) + ": " + getString(R.string.calculation_in_progress)
binding.activity.text = activityMonitor.stats() binding.activity.text = getString(R.string.activitymonitor) + ": " + getString(R.string.calculation_in_progress)
Thread {
val tdds = tddCalculator.stats()
runOnUiThread { binding.tdds.text = tdds }
}.start()
Thread {
val tir = tirCalculator.stats()
runOnUiThread { binding.tir.text = tir }
}.start()
Thread {
val activity = activityMonitor.stats()
runOnUiThread { binding.activity.text = activity }
}.start()
binding.ok.setOnClickListener { finish() } binding.ok.setOnClickListener { finish() }
binding.reset.setOnClickListener { binding.reset.setOnClickListener {

View file

@ -31,7 +31,7 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T

View file

@ -24,7 +24,7 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.activities.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder import info.nightscout.androidaps.activities.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy

View file

@ -29,7 +29,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientR
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.activities.fragments.TreatmentsProfileSwitchFragment.RecyclerProfileViewAdapter.ProfileSwitchViewHolder import info.nightscout.androidaps.activities.fragments.TreatmentsProfileSwitchFragment.RecyclerProfileViewAdapter.ProfileSwitchViewHolder
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy

View file

@ -28,7 +28,7 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy

View file

@ -190,7 +190,6 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
override fun getItemCount(): Int = tempBasalList.size override fun getItemCount(): Int = tempBasalList.size
@Deprecated("remove remove functionality after finish")
inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = TreatmentsTempbasalsItemBinding.bind(itemView) val binding = TreatmentsTempbasalsItemBinding.bind(itemView)

View file

@ -19,7 +19,7 @@ import info.nightscout.androidaps.interfaces.ImportExportPrefs
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T

View file

@ -11,6 +11,7 @@ import org.json.JSONObject
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.math.abs
@Singleton @Singleton
class DefaultProfile @Inject constructor(val dateUtil: DateUtil) { class DefaultProfile @Inject constructor(val dateUtil: DateUtil) {
@ -120,12 +121,12 @@ class DefaultProfile @Inject constructor(val dateUtil: DateUtil) {
val high = map.ceilingEntry(key) val high = map.ceilingEntry(key)
var res: Array<Double>? = null var res: Array<Double>? = null
if (low != null && high != null) { if (low != null && high != null) {
res = if (Math.abs(key - low.key) < Math.abs(key - high.key)) res = if (abs(key - low.key) < abs(key - high.key))
low.value low.value
else else
high.value high.value
} else if (low != null || high != null) { } else if (low != null || high != null) {
res = if (low != null) low.value else high.value res = if (low != null) low.value else high!!.value
} }
return res return res
} }

View file

@ -82,5 +82,9 @@ class CompatDBHelper @Inject constructor(
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged") aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged")
rxBus.send(EventProfileSwitchChanged()) rxBus.send(EventProfileSwitchChanged())
} }
it.filterIsInstance<OfflineEvent>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange")
rxBus.send(EventOfflineChange())
}
} }
} }

View file

@ -1,296 +0,0 @@
package info.nightscout.androidaps.db;
import android.content.Context;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
import com.j256.ormlite.dao.CloseableIterator;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.DeleteBuilder;
import com.j256.ormlite.stmt.PreparedQuery;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.stmt.Where;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.interfaces.ActivePlugin;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.utils.DateUtil;
/**
* This Helper contains all resource to provide a central DB management functionality. Only methods handling
* data-structure (and not the DB content) should be contained in here (meaning DDL and not SQL).
* <p>
* This class can safely be called from Services, but should not call Services to avoid circular dependencies.
* One major issue with this (right now) are the scheduled events, which are put into the service. Therefor all
* direct calls to the corresponding methods (eg. resetDatabases) should be done by a central service.
*/
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
@Inject AAPSLogger aapsLogger;
@Inject RxBusWrapper rxBus;
@Inject VirtualPumpPlugin virtualPumpPlugin;
@Inject OpenHumansUploader openHumansUploader;
@Inject ActivePlugin activePlugin;
@Inject DateUtil dateUtil;
public static final String DATABASE_NAME = "AndroidAPSDb";
private static final int DATABASE_VERSION = 13;
public static Long earliestDataChange = null;
private int oldVersion = 0;
private int newVersion = 0;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
StaticInjector.Companion.getInstance().androidInjector().inject(this);
onCreate(getWritableDatabase(), getConnectionSource());
//onUpgrade(getWritableDatabase(), getConnectionSource(), 1,1);
}
@Override
public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {
try {
aapsLogger.info(LTag.DATABASE, "onCreate");
TableUtils.createTableIfNotExists(connectionSource, OmnipodHistoryRecord.class);
TableUtils.createTableIfNotExists(connectionSource, OHQueueItem.class);
} catch (SQLException e) {
aapsLogger.error("Can't create database", e);
throw new RuntimeException(e);
}
}
@Override
public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) {
try {
this.oldVersion = oldVersion;
this.newVersion = newVersion;
if (oldVersion < 7) {
aapsLogger.info(LTag.DATABASE, "onUpgrade");
onCreate(database, connectionSource);
}
TableUtils.createTableIfNotExists(connectionSource, OHQueueItem.class);
} catch (SQLException e) {
aapsLogger.error("Can't drop databases", e);
throw new RuntimeException(e);
}
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
aapsLogger.info(LTag.DATABASE, "Do nothing for downgrading...");
aapsLogger.info(LTag.DATABASE, "oldVersion: {}, newVersion: {}", oldVersion, newVersion);
}
public int getOldVersion() {
return oldVersion;
}
public int getNewVersion() {
return newVersion;
}
public long size(String database) {
return DatabaseUtils.queryNumEntries(getReadableDatabase(), database);
}
// --------------------- DB resets ---------------------
public void resetDatabases() {
try {
TableUtils.dropTable(connectionSource, OmnipodHistoryRecord.class, true);
TableUtils.createTableIfNotExists(connectionSource, OmnipodHistoryRecord.class);
updateEarliestDataChange(0);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
virtualPumpPlugin.setFakingStatus(true);
new java.util.Timer().schedule(
new java.util.TimerTask() {
@Override
public void run() {
rxBus.send(new EventRefreshOverview("resetDatabases", false));
}
},
3000
);
}
// ------------------ getDao -------------------------------------------
private Dao<OmnipodHistoryRecord, Long> getDaoPodHistory() throws SQLException {
return getDao(OmnipodHistoryRecord.class);
}
private Dao<OHQueueItem, Long> getDaoOpenHumansQueue() throws SQLException {
return getDao(OHQueueItem.class);
}
public static void updateEarliestDataChange(long newDate) {
if (earliestDataChange == null) {
earliestDataChange = newDate;
return;
}
if (newDate < earliestDataChange) {
earliestDataChange = newDate;
}
}
// ---------------- Food handling ---------------
// ---------------- PodHistory handling ---------------
public void createOrUpdate(OmnipodHistoryRecord omnipodHistoryRecord) {
try {
getDaoPodHistory().createOrUpdate(omnipodHistoryRecord);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public List<OmnipodHistoryRecord> getAllOmnipodHistoryRecordsFromTimeStamp(long from, boolean ascending) {
try {
Dao<OmnipodHistoryRecord, Long> daoPodHistory = getDaoPodHistory();
List<OmnipodHistoryRecord> podHistories;
QueryBuilder<OmnipodHistoryRecord, Long> queryBuilder = daoPodHistory.queryBuilder();
queryBuilder.orderBy("date", ascending);
//queryBuilder.limit(100L);
Where where = queryBuilder.where();
where.ge("date", from);
PreparedQuery<OmnipodHistoryRecord> preparedQuery = queryBuilder.prepare();
podHistories = daoPodHistory.query(preparedQuery);
return podHistories;
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<>();
}
public OmnipodHistoryRecord findOmnipodHistoryRecordByPumpId(long pumpId) {
try {
Dao<OmnipodHistoryRecord, Long> daoPodHistory = getDaoPodHistory();
QueryBuilder<OmnipodHistoryRecord, Long> queryBuilder = daoPodHistory.queryBuilder();
queryBuilder.orderBy("date", false);
Where<OmnipodHistoryRecord, Long> where = queryBuilder.where();
where.eq("pumpId", pumpId);
PreparedQuery<OmnipodHistoryRecord> preparedQuery = queryBuilder.prepare();
return daoPodHistory.queryForFirst(preparedQuery);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return null;
}
/*
TODO implement again for database branch // Copied from xDrip+
String calculateDirection(BgReading bgReading) {
// Rework to get bgreaings from internal DB and calculate on that base
List<BgReading> bgReadingsList = MainApp.getDbHelper().getAllBgreadingsDataFromTime(bgReading.date - T.mins(10).msecs(), false);
if (bgReadingsList == null || bgReadingsList.size() < 2)
return "NONE";
BgReading current = bgReadingsList.get(1);
BgReading previous = bgReadingsList.get(0);
if (bgReadingsList.get(1).date < bgReadingsList.get(0).date) {
current = bgReadingsList.get(0);
previous = bgReadingsList.get(1);
}
double slope;
// Avoid division by 0
if (current.date == previous.date)
slope = 0;
else
slope = (previous.value - current.value) / (previous.date - current.date);
// aapsLogger.error(LTag.GLUCOSE, "Slope is :" + slope + " delta " + (previous.value - current.value) + " date difference " + (current.date - previous.date));
double slope_by_minute = slope * 60000;
String arrow = "NONE";
if (slope_by_minute <= (-3.5)) {
arrow = "DoubleDown";
} else if (slope_by_minute <= (-2)) {
arrow = "SingleDown";
} else if (slope_by_minute <= (-1)) {
arrow = "FortyFiveDown";
} else if (slope_by_minute <= (1)) {
arrow = "Flat";
} else if (slope_by_minute <= (2)) {
arrow = "FortyFiveUp";
} else if (slope_by_minute <= (3.5)) {
arrow = "SingleUp";
} else if (slope_by_minute <= (40)) {
arrow = "DoubleUp";
}
// aapsLogger.error(LTag.GLUCOSE, "Direction set to: " + arrow);
return arrow;
}
*/
// ---------------- Open Humans Queue handling ---------------
public void clearOpenHumansQueue() {
try {
TableUtils.clearTable(connectionSource, OHQueueItem.class);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void createOrUpdate(OHQueueItem item) {
try {
getDaoOpenHumansQueue().createOrUpdate(item);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void removeAllOHQueueItemsWithIdSmallerThan(long id) {
try {
DeleteBuilder<OHQueueItem, Long> deleteBuilder = getDaoOpenHumansQueue().deleteBuilder();
deleteBuilder.where().le("id", id);
deleteBuilder.delete();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public List<OHQueueItem> getAllOHQueueItems(Long maxEntries) {
try {
return getDaoOpenHumansQueue()
.queryBuilder()
.orderBy("id", true)
.limit(maxEntries)
.query();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return Collections.emptyList();
}
public long getOHQueueSize() {
try {
return getDaoOpenHumansQueue().countOf();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return 0L;
}
}

View file

@ -1,84 +0,0 @@
package info.nightscout.androidaps.db;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.j256.ormlite.dao.CloseableIterator;
import java.sql.SQLException;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
@Deprecated
@Singleton
public class DatabaseHelperProvider implements DatabaseHelperInterface {
@Inject DatabaseHelperProvider() {
}
@Override public void createOrUpdate(@NonNull OmnipodHistoryRecord record) {
MainApp.Companion.getDbHelper().createOrUpdate(record);
}
@Override public boolean createOrUpdate(@NonNull TemporaryBasal tempBasal) {
// return MainApp.Companion.getDbHelper().createOrUpdate(tempBasal);
return false;
}
@Nullable @Override public TemporaryBasal findTempBasalByPumpId(long id) {
// return MainApp.Companion.getDbHelper().findTempBasalByPumpId(id);
return null;
}
@Deprecated
@NonNull @Override public List<TemporaryBasal> getTemporaryBasalsDataFromTime(long mills, boolean ascending) {
// return MainApp.Companion.getDbHelper().getTemporaryBasalsDataFromTime(mills, ascending);
return null;
}
@NonNull @Override public List<OmnipodHistoryRecord> getAllOmnipodHistoryRecordsFromTimestamp(long timestamp, boolean ascending) {
return MainApp.Companion.getDbHelper().getAllOmnipodHistoryRecordsFromTimeStamp(timestamp, ascending);
}
@Nullable @Override public OmnipodHistoryRecord findOmnipodHistoryRecordByPumpId(long pumpId) {
return MainApp.Companion.getDbHelper().findOmnipodHistoryRecordByPumpId(pumpId);
}
@Override public void delete(@NonNull ExtendedBolus extendedBolus) {
// MainApp.Companion.getDbHelper().delete(extendedBolus);
}
@Nullable @Override public ExtendedBolus getExtendedBolusByPumpId(long pumpId) {
// return MainApp.Companion.getDbHelper().getExtendedBolusByPumpId(pumpId);
return null;
}
@Override public void resetDatabases() {
MainApp.Companion.getDbHelper().resetDatabases();
}
@Override public void createOrUpdate(@NonNull OHQueueItem record) {
MainApp.Companion.getDbHelper().createOrUpdate(record);
}
@NonNull @Override public List<OHQueueItem> getAllOHQueueItems(long maxEntries) {
return MainApp.Companion.getDbHelper().getAllOHQueueItems(maxEntries);
}
@Override public long getOHQueueSize() {
return MainApp.Companion.getDbHelper().getOHQueueSize();
}
@Override public void clearOpenHumansQueue() {
MainApp.Companion.getDbHelper().clearOpenHumansQueue();
}
@Override public void removeAllOHQueueItemsWithIdSmallerThan(long id) {
MainApp.Companion.getDbHelper().removeAllOHQueueItemsWithIdSmallerThan(id);
}
}

View file

@ -1,2 +0,0 @@
package info.nightscout.androidaps.db

View file

@ -4,9 +4,8 @@ import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.MainActivity import info.nightscout.androidaps.MainActivity
import info.nightscout.androidaps.activities.* import info.nightscout.androidaps.activities.*
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity import info.nightscout.androidaps.activities.HistoryBrowseActivity
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansLoginActivity
import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity
import info.nightscout.androidaps.plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity import info.nightscout.androidaps.plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity
import info.nightscout.androidaps.setupwizard.SetupWizardActivity import info.nightscout.androidaps.setupwizard.SetupWizardActivity
@ -28,6 +27,5 @@ abstract class ActivitiesModule {
@ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity @ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity
@ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity @ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity
@ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity @ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity
@ContributesAndroidInjector abstract fun contributesOpenHumansLoginActivity(): OpenHumansLoginActivity
} }

View file

@ -8,15 +8,19 @@ import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.automation.di.AutomationModule import info.nightscout.androidaps.automation.di.AutomationModule
import info.nightscout.androidaps.combo.di.ComboModule import info.nightscout.androidaps.combo.di.ComboModule
import info.nightscout.androidaps.dana.di.DanaHistoryModule import info.nightscout.androidaps.dana.di.DanaHistoryModule
import info.nightscout.androidaps.di.CoreModule
import info.nightscout.androidaps.dana.di.DanaModule import info.nightscout.androidaps.dana.di.DanaModule
import info.nightscout.androidaps.danar.di.DanaRModule import info.nightscout.androidaps.danar.di.DanaRModule
import info.nightscout.androidaps.danars.di.DanaRSModule import info.nightscout.androidaps.danars.di.DanaRSModule
import info.nightscout.androidaps.database.DatabaseModule import info.nightscout.androidaps.database.DatabaseModule
import info.nightscout.androidaps.di.CoreModule
import info.nightscout.androidaps.diaconn.di.DiaconnG8Module
import info.nightscout.androidaps.insight.di.InsightDatabaseModule import info.nightscout.androidaps.insight.di.InsightDatabaseModule
import info.nightscout.androidaps.insight.di.InsightModule import info.nightscout.androidaps.insight.di.InsightModule
import info.nightscout.androidaps.plugin.general.openhumans.dagger.OpenHumansModule
import info.nightscout.androidaps.plugins.pump.common.di.PumpCommonModule
import info.nightscout.androidaps.plugins.pump.common.di.RileyLinkModule import info.nightscout.androidaps.plugins.pump.common.di.RileyLinkModule
import info.nightscout.androidaps.plugins.pump.medtronic.di.MedtronicModule import info.nightscout.androidaps.plugins.pump.medtronic.di.MedtronicModule
import info.nightscout.androidaps.plugins.pump.omnipod.dash.dagger.OmnipodDashModule
import info.nightscout.androidaps.plugins.pump.omnipod.eros.dagger.OmnipodErosModule import info.nightscout.androidaps.plugins.pump.omnipod.eros.dagger.OmnipodErosModule
import javax.inject.Singleton import javax.inject.Singleton
@ -36,8 +40,10 @@ import javax.inject.Singleton
CommandQueueModule::class, CommandQueueModule::class,
ObjectivesModule::class, ObjectivesModule::class,
WizardModule::class, WizardModule::class,
PumpCommonModule::class,
RileyLinkModule::class, RileyLinkModule::class,
MedtronicModule::class, MedtronicModule::class,
OmnipodDashModule::class,
OmnipodErosModule::class, OmnipodErosModule::class,
APSModule::class, APSModule::class,
PreferencesModule::class, PreferencesModule::class,
@ -54,7 +60,8 @@ import javax.inject.Singleton
InsightModule::class, InsightModule::class,
InsightDatabaseModule::class, InsightDatabaseModule::class,
WorkersModule::class, WorkersModule::class,
OHUploaderModule::class DiaconnG8Module::class,
OpenHumansModule::class
] ]
) )
interface AppComponent : AndroidInjector<MainApp> { interface AppComponent : AndroidInjector<MainApp> {

View file

@ -8,22 +8,25 @@ import dagger.Provides
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.db.DatabaseHelperProvider
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.PluginStore import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImplementation import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImplementation
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
import info.nightscout.androidaps.plugins.general.nsclient.DataSyncSelectorImplementation import info.nightscout.androidaps.plugins.general.nsclient.DataSyncSelectorImplementation
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.PumpSyncImplementation import info.nightscout.androidaps.plugins.pump.PumpSyncImplementation
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.CommandQueue import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.androidNotification.NotificationHolderImpl import info.nightscout.androidaps.utils.androidNotification.NotificationHolderImpl
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.buildHelper.BuildHelperImpl
import info.nightscout.androidaps.utils.buildHelper.ConfigImpl import info.nightscout.androidaps.utils.buildHelper.ConfigImpl
import info.nightscout.androidaps.utils.resources.IconsProviderImplementation import info.nightscout.androidaps.utils.resources.IconsProviderImplementation
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -56,9 +59,11 @@ open class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideStorage(): Storage { fun provideStorage(): Storage = FileStorage()
return FileStorage()
} @Provides
@Singleton
fun provideBuildHelper(config: Config, fileListProvider: PrefFileListProvider): BuildHelper = BuildHelperImpl(config, fileListProvider)
@Provides @Provides
@Singleton @Singleton
@ -66,29 +71,35 @@ open class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideProfileFunction(aapsLogger: AAPSLogger, sp: SP, resourceHelper: ResourceHelper, activePlugin: ActivePlugin, repository: AppRepository, dateUtil: DateUtil): ProfileFunction { fun provideProfileFunction(
return ProfileFunctionImplementation(aapsLogger, sp, resourceHelper, activePlugin, repository, dateUtil) aapsLogger: AAPSLogger, sp: SP, rxBus: RxBusWrapper, resourceHelper:
} ResourceHelper, activePlugin:
ActivePlugin, repository: AppRepository, dateUtil: DateUtil, config: Config, hardLimits: HardLimits
): ProfileFunction =
ProfileFunctionImplementation(
aapsLogger, sp, rxBus, resourceHelper, activePlugin, repository, dateUtil,
config, hardLimits
)
@Module @Module
interface AppBindings { interface AppBindings {
@Binds fun bindContext(mainApp: MainApp): Context @Binds fun bindContext(mainApp: MainApp): Context
@Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector @Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector
@Binds fun bindActivePluginProvider(pluginStore: PluginStore): ActivePlugin @Binds fun bindActivePluginProvider(pluginStore: PluginStore): ActivePlugin
@Binds fun bindCommandQueueProvider(commandQueue: CommandQueue): CommandQueueProvider @Binds fun bindCommandQueueProvider(commandQueue: CommandQueue): CommandQueueProvider
@Binds fun bindConfigInterface(config: ConfigImpl): Config @Binds fun bindConfigInterface(config: ConfigImpl): Config
@Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilder @Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilder
@Binds fun bindTreatmentsInterface(treatmentsPlugin: TreatmentsPlugin): TreatmentsInterface
@Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface
@Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder @Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs @Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs
@Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider @Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider
@Binds fun bindLoopInterface(loopPlugin: LoopPlugin): LoopInterface @Binds fun bindLoopInterface(loopPlugin: LoopPlugin): Loop
@Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator @Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator @Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
@Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector @Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
@Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync @Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync
} }
} }

View file

@ -2,9 +2,7 @@ package info.nightscout.androidaps.dependencyInjection
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.db.DatabaseHelper
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.treatments.TreatmentService
import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.BolusWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
@ -14,9 +12,6 @@ abstract class DataClassesModule {
@ContributesAndroidInjector abstract fun glucoseStatusInjector(): GlucoseStatus @ContributesAndroidInjector abstract fun glucoseStatusInjector(): GlucoseStatus
@ContributesAndroidInjector abstract fun databaseHelperInjector(): DatabaseHelper
@ContributesAndroidInjector abstract fun treatmentServiceInjector(): TreatmentService
@ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard @ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard
@ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry @ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry
} }

View file

@ -20,8 +20,6 @@ import info.nightscout.androidaps.plugins.general.automation.dialogs.EditTrigger
import info.nightscout.androidaps.plugins.general.food.FoodFragment import info.nightscout.androidaps.plugins.general.food.FoodFragment
import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment
import info.nightscout.androidaps.plugins.general.nsclient.NSClientFragment import info.nightscout.androidaps.plugins.general.nsclient.NSClientFragment
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansFragment
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansLoginActivity
import info.nightscout.androidaps.plugins.general.overview.OverviewFragment import info.nightscout.androidaps.plugins.general.overview.OverviewFragment
import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorFragment import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorFragment
@ -70,8 +68,6 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesVirtualPumpFragment(): VirtualPumpFragment @ContributesAndroidInjector abstract fun contributesVirtualPumpFragment(): VirtualPumpFragment
@ContributesAndroidInjector abstract fun contributesOpenHumansFragment(): OpenHumansFragment
@ContributesAndroidInjector abstract fun contributesCalibrationDialog(): CalibrationDialog @ContributesAndroidInjector abstract fun contributesCalibrationDialog(): CalibrationDialog
@ContributesAndroidInjector abstract fun contributesCarbsDialog(): CarbsDialog @ContributesAndroidInjector abstract fun contributesCarbsDialog(): CarbsDialog
@ContributesAndroidInjector abstract fun contributesCareDialog(): CareDialog @ContributesAndroidInjector abstract fun contributesCareDialog(): CareDialog
@ -95,7 +91,5 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesWizardDialog(): WizardDialog @ContributesAndroidInjector abstract fun contributesWizardDialog(): WizardDialog
@ContributesAndroidInjector abstract fun contributesWizardInfoDialog(): WizardInfoDialog @ContributesAndroidInjector abstract fun contributesWizardInfoDialog(): WizardInfoDialog
@ContributesAndroidInjector abstract fun contributesExchangeAuthTokenDialog(): OpenHumansLoginActivity.ExchangeAuthTokenDialog
@ContributesAndroidInjector abstract fun contributesPasswordCheck(): PasswordCheck @ContributesAndroidInjector abstract fun contributesPasswordCheck(): PasswordCheck
} }

View file

@ -1,12 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.openhumans.OHUploadWorker
@Module
@Suppress("unused")
abstract class OHUploaderModule {
@ContributesAndroidInjector abstract fun contributesOHUploadWorkerInjector(): OHUploadWorker
}

View file

@ -17,7 +17,6 @@ abstract class ObjectivesModule {
@ContributesAndroidInjector abstract fun objective5Injector(): Objective5 @ContributesAndroidInjector abstract fun objective5Injector(): Objective5
@ContributesAndroidInjector abstract fun objective6Injector(): Objective6 @ContributesAndroidInjector abstract fun objective6Injector(): Objective6
@ContributesAndroidInjector abstract fun objective7Injector(): Objective7 @ContributesAndroidInjector abstract fun objective7Injector(): Objective7
@ContributesAndroidInjector abstract fun objective8Injector(): Objective8
@ContributesAndroidInjector abstract fun objective9Injector(): Objective9 @ContributesAndroidInjector abstract fun objective9Injector(): Objective9
@ContributesAndroidInjector abstract fun objective10Injector(): Objective10 @ContributesAndroidInjector abstract fun objective10Injector(): Objective10

View file

@ -8,7 +8,9 @@ import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.plugin.general.openhumans.OpenHumansUploader
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
@ -25,7 +27,6 @@ import info.nightscout.androidaps.plugins.general.dataBroadcaster.DataBroadcastP
import info.nightscout.androidaps.plugins.general.food.FoodPlugin import info.nightscout.androidaps.plugins.general.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin
import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
@ -41,13 +42,11 @@ import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.plugins.source.* import info.nightscout.androidaps.plugins.source.*
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import javax.inject.Qualifier import javax.inject.Qualifier
@Module @Module
@ -149,11 +148,11 @@ abstract class PluginsModule {
@IntKey(140) @IntKey(140)
abstract fun bindComboPlugin(plugin: ComboPlugin): PluginBase abstract fun bindComboPlugin(plugin: ComboPlugin): PluginBase
// @Binds @Binds
// @PumpDriver @PumpDriver
// @IntoMap @IntoMap
// @IntKey(150) @IntKey(150)
// abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase
// @Binds // @Binds
// @PumpDriver // @PumpDriver
@ -161,6 +160,12 @@ abstract class PluginsModule {
// @IntKey(155) // @IntKey(155)
// abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase // abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase
@Binds
@PumpDriver
@IntoMap
@IntKey(155)
abstract fun bindDiaconnG8Plugin(plugin: DiaconnG8Plugin): PluginBase
@Binds @Binds
@NotNSClient @NotNSClient
@IntoMap @IntoMap
@ -203,12 +208,6 @@ abstract class PluginsModule {
@IntKey(250) @IntKey(250)
abstract fun bindAutomationPlugin(plugin: AutomationPlugin): PluginBase abstract fun bindAutomationPlugin(plugin: AutomationPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(260)
abstract fun bindTreatmentsPlugin(plugin: TreatmentsPlugin): PluginBase
@Binds @Binds
@AllConfigs @AllConfigs
@IntoMap @IntoMap
@ -347,6 +346,12 @@ abstract class PluginsModule {
// @IntKey(480) // @IntKey(480)
// abstract fun bindOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase // abstract fun bindOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase
@Binds
@NotNSClient
@IntoMap
@IntKey(480)
abstract fun bindsOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase
@Binds @Binds
@AllConfigs @AllConfigs
@IntoMap @IntoMap

View file

@ -17,7 +17,7 @@ import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogCarbsBinding import info.nightscout.androidaps.databinding.DialogCarbsBinding
import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
@ -27,7 +27,6 @@ import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.CommandQueue import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.*
@ -47,7 +46,6 @@ class CarbsDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@ -228,7 +226,7 @@ class CarbsDialog : DialogFragmentWithDate() {
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY), ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY),
ValueWithUnit.fromGlucoseUnit(activityTT, units.asText), ValueWithUnit.fromGlucoseUnit(activityTT, units.asText),
ValueWithUnit.Minute(activityTTDuration)) ValueWithUnit.Minute(activityTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(activityTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(activityTTDuration.toLong()),
reason = TemporaryTarget.Reason.ACTIVITY, reason = TemporaryTarget.Reason.ACTIVITY,
@ -247,7 +245,7 @@ class CarbsDialog : DialogFragmentWithDate() {
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText), ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
ValueWithUnit.Minute(eatingSoonTTDuration)) ValueWithUnit.Minute(eatingSoonTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON, reason = TemporaryTarget.Reason.EATING_SOON,
@ -266,7 +264,7 @@ class CarbsDialog : DialogFragmentWithDate() {
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA), ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA),
ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText), ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText),
ValueWithUnit.Minute(hypoTTDuration)) ValueWithUnit.Minute(hypoTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(hypoTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(hypoTTDuration.toLong()),
reason = TemporaryTarget.Reason.HYPOGLYCEMIA, reason = TemporaryTarget.Reason.HYPOGLYCEMIA,

View file

@ -191,7 +191,7 @@ class FillDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
} }
} }
}) })

View file

@ -16,7 +16,7 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogInsulinBinding import info.nightscout.androidaps.databinding.DialogInsulinBinding
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
@ -186,7 +186,7 @@ class InsulinDialog : DialogFragmentWithDate() {
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText), ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
ValueWithUnit.Minute(eatingSoonTTDuration)) ValueWithUnit.Minute(eatingSoonTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON, reason = TemporaryTarget.Reason.EATING_SOON,
@ -224,7 +224,7 @@ class InsulinDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
} }
} }
}) })

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -12,9 +13,13 @@ import androidx.fragment.app.FragmentManager
import dagger.android.support.DaggerDialogFragment import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.databinding.DialogLoopBinding import info.nightscout.androidaps.databinding.DialogLoopBinding
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
@ -29,8 +34,14 @@ import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
class LoopDialog : DaggerDialogFragment() { class LoopDialog : DaggerDialogFragment() {
@ -48,19 +59,27 @@ class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository
@Inject lateinit var objectivePlugin: ObjectivesPlugin
private var showOkCancel: Boolean = true private var showOkCancel: Boolean = true
private var _binding: DialogLoopBinding? = null private var _binding: DialogLoopBinding? = null
private var loopHandler = Handler() private var loopHandler = Handler(Looper.getMainLooper())
private var refreshDialog: Runnable? = null private lateinit var refreshDialog: Runnable
// This property is only valid between onCreateView and // This property is only valid between onCreateView and
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
val disposable = CompositeDisposable()
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) dialog?.window?.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
} }
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -118,6 +137,7 @@ class LoopDialog : DaggerDialogFragment() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
loopHandler.removeCallbacksAndMessages(null) loopHandler.removeCallbacksAndMessages(null)
disposable.clear()
} }
var task: Runnable? = null var task: Runnable? = null
@ -141,21 +161,74 @@ class LoopDialog : DaggerDialogFragment() {
aapsLogger.debug("UpdateGUI from $from") aapsLogger.debug("UpdateGUI from $from")
val pumpDescription: PumpDescription = activePlugin.activePump.pumpDescription val pumpDescription: PumpDescription = activePlugin.activePump.pumpDescription
val closedLoopAllowed = constraintChecker.isClosedLoopAllowed(Constraint(true)) val closedLoopAllowed = constraintChecker.isClosedLoopAllowed(Constraint(true))
val closedLoopAllowed2 = objectivePlugin.objectives[ObjectivesPlugin.MAXIOB_OBJECTIVE].isCompleted
val lgsEnabled = constraintChecker.isLgsAllowed(Constraint(true)) val lgsEnabled = constraintChecker.isLgsAllowed(Constraint(true))
val apsMode = sp.getString(R.string.key_aps_mode, "open") val apsMode = sp.getString(R.string.key_aps_mode, "open")
if (profileFunction.isProfileValid("LoopDialogUpdateGUI")) { val pump = activePlugin.activePump
if (loopPlugin.isEnabled(PluginType.LOOP)) {
binding.overviewDisconnect15m.visibility = pumpDescription.tempDurationStep15mAllowed.toVisibility()
binding.overviewDisconnect30m.visibility = pumpDescription.tempDurationStep30mAllowed.toVisibility()
when { when {
closedLoopAllowed.value() -> { pump.isSuspended() -> {
binding.overviewCloseloop.visibility = (apsMode != "closed").toVisibility() binding.overviewLoop.visibility = View.GONE
binding.overviewLgsloop.visibility = (apsMode != "lgs").toVisibility() binding.overviewSuspend.visibility = View.GONE
binding.overviewOpenloop.visibility = (apsMode != "open").toVisibility() binding.overviewPump.visibility = View.GONE
} }
lgsEnabled.value() -> { !profileFunction.isProfileValid("LoopDialogUpdateGUI") -> {
binding.overviewLoop.visibility = View.GONE
binding.overviewSuspend.visibility = View.GONE
binding.overviewPump.visibility = View.GONE
}
loopPlugin.isDisconnected -> {
binding.overviewLoop.visibility = View.GONE
binding.overviewSuspend.visibility = View.GONE
binding.overviewPump.visibility = View.VISIBLE
binding.overviewPumpHeader.text = resourceHelper.gs(R.string.reconnect)
binding.overviewDisconnectButtons.visibility = View.VISIBLE
binding.overviewReconnect.visibility = View.VISIBLE
}
!loopPlugin.isEnabled(PluginType.LOOP) -> {
binding.overviewLoop.visibility = View.VISIBLE
binding.overviewEnable.visibility = View.VISIBLE
binding.overviewDisable.visibility = View.GONE
binding.overviewSuspend.visibility = View.GONE
binding.overviewPump.visibility = View.VISIBLE
binding.overviewReconnect.visibility = View.GONE
}
loopPlugin.isSuspended -> {
binding.overviewLoop.visibility = View.GONE
binding.overviewSuspend.visibility = View.VISIBLE
binding.overviewSuspendHeader.text = resourceHelper.gs(R.string.resumeloop)
binding.overviewSuspendButtons.visibility = View.VISIBLE
binding.overviewResume.visibility = View.VISIBLE
binding.overviewPump.visibility = View.GONE
binding.overviewReconnect.visibility = View.GONE
}
else -> {
binding.overviewLoop.visibility = View.VISIBLE
binding.overviewEnable.visibility = View.GONE
when {
apsMode == "closed" -> {
binding.overviewCloseloop.visibility = View.GONE binding.overviewCloseloop.visibility = View.GONE
binding.overviewLgsloop.visibility = (apsMode != "lgs").toVisibility() binding.overviewLgsloop.visibility = View.VISIBLE
binding.overviewOpenloop.visibility = (apsMode != "open").toVisibility() binding.overviewOpenloop.visibility = View.VISIBLE
}
apsMode == "lgs" -> {
binding.overviewCloseloop.visibility = closedLoopAllowed.value().toVisibility() //show Close loop button only if Close loop allowed
binding.overviewLgsloop.visibility = View.GONE
binding.overviewOpenloop.visibility = View.VISIBLE
}
apsMode == "open" -> {
binding.overviewCloseloop.visibility = closedLoopAllowed2.toVisibility() //show CloseLoop button only if Objective 6 is completed (closedLoopAllowed always false in open loop mode)
binding.overviewLgsloop.visibility = lgsEnabled.value().toVisibility()
binding.overviewOpenloop.visibility = View.GONE
} }
else -> { else -> {
@ -164,49 +237,18 @@ class LoopDialog : DaggerDialogFragment() {
binding.overviewOpenloop.visibility = View.GONE binding.overviewOpenloop.visibility = View.GONE
} }
} }
binding.overviewEnable.visibility = View.GONE binding.overviewSuspend.visibility = View.VISIBLE
binding.overviewDisable.visibility = View.VISIBLE
if (!loopPlugin.isSuspended) {
binding.overviewSuspendHeader.text = resourceHelper.gs(R.string.suspendloop) binding.overviewSuspendHeader.text = resourceHelper.gs(R.string.suspendloop)
binding.overviewResume.visibility = View.GONE
binding.overviewSuspendButtons.visibility = View.VISIBLE binding.overviewSuspendButtons.visibility = View.VISIBLE
binding.overviewSuspend.visibility = View.VISIBLE binding.overviewResume.visibility = View.GONE
} else {
if (!loopPlugin.isDisconnected) { binding.overviewPump.visibility = View.VISIBLE
binding.overviewSuspendHeader.text = resourceHelper.gs(R.string.resumeloop)
binding.overviewResume.visibility = View.VISIBLE
binding.overviewSuspendButtons.visibility = View.GONE
binding.overviewSuspend.visibility = View.VISIBLE
} else
binding.overviewSuspend.visibility = View.GONE
}
} else {
binding.overviewEnable.visibility = View.VISIBLE
binding.overviewDisable.visibility = View.GONE
binding.overviewSuspend.visibility = View.GONE
}
if (!loopPlugin.isDisconnected) {
binding.overviewPumpHeader.text = resourceHelper.gs(R.string.disconnectpump) binding.overviewPumpHeader.text = resourceHelper.gs(R.string.disconnectpump)
binding.overviewDisconnect15m.visibility = pumpDescription.tempDurationStep15mAllowed.toVisibility()
binding.overviewDisconnect30m.visibility = pumpDescription.tempDurationStep30mAllowed.toVisibility()
binding.overviewDisconnectButtons.visibility = View.VISIBLE binding.overviewDisconnectButtons.visibility = View.VISIBLE
binding.overviewReconnect.visibility = View.GONE binding.overviewReconnect.visibility = View.GONE
} else {
binding.overviewPumpHeader.text = resourceHelper.gs(R.string.reconnect)
binding.overviewDisconnectButtons.visibility = View.GONE
binding.overviewReconnect.visibility = View.VISIBLE
}
binding.overviewLoop.visibility = (!loopPlugin.isSuspended && !loopPlugin.isDisconnected).toVisibility()
}
val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileSource.profile
if (profile == null || profileStore == null) {
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofile))
dismiss()
return
} }
}
} }
private fun onClickOkCancelEnabled(v: View): Boolean { private fun onClickOkCancelEnabled(v: View): Boolean {
@ -238,7 +280,6 @@ class LoopDialog : DaggerDialogFragment() {
} }
fun onClick(v: View): Boolean { fun onClick(v: View): Boolean {
val profile = profileFunction.getProfile() ?: return true
when (v.id) { when (v.id) {
R.id.overview_closeloop -> { R.id.overview_closeloop -> {
uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog) uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog)
@ -266,7 +307,7 @@ class LoopDialog : DaggerDialogFragment() {
loopPlugin.setPluginEnabled(PluginType.LOOP, false) loopPlugin.setPluginEnabled(PluginType.LOOP, false)
loopPlugin.setFragmentVisible(PluginType.LOOP, false) loopPlugin.setFragmentVisible(PluginType.LOOP, false)
configBuilder.storeSettings("DisablingLoop") configBuilder.storeSettings("DisablingLoop")
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
@ -274,7 +315,13 @@ class LoopDialog : DaggerDialogFragment() {
} }
} }
}) })
loopPlugin.createOfflineEvent(24 * 60) // upload 24h, we don't know real duration disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.days(365).msecs(), OfflineEvent.Reason.DISABLE_LOOP))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
return true return true
} }
@ -283,88 +330,106 @@ class LoopDialog : DaggerDialogFragment() {
loopPlugin.setPluginEnabled(PluginType.LOOP, true) loopPlugin.setPluginEnabled(PluginType.LOOP, true)
loopPlugin.setFragmentVisible(PluginType.LOOP, true) loopPlugin.setFragmentVisible(PluginType.LOOP, true)
configBuilder.storeSettings("EnablingLoop") configBuilder.storeSettings("EnablingLoop")
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
loopPlugin.createOfflineEvent(0) disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
return true return true
} }
R.id.overview_resume, R.id.overview_reconnect -> { R.id.overview_resume, R.id.overview_reconnect -> {
uel.log(if (v.id == R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog) uel.log(if (v.id == R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog)
loopPlugin.suspendTo(0L) disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
rxBus.send(EventRefreshOverview("suspendmenu")) .subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspend_menu"))
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
} }
} }
}) })
sp.putBoolean(R.string.key_objectiveusereconnect, true) sp.putBoolean(R.string.key_objectiveusereconnect, true)
loopPlugin.createOfflineEvent(0)
return true return true
} }
R.id.overview_suspend_1h -> { R.id.overview_suspend_1h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1)) uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1))
loopPlugin.suspendLoop(60) loopPlugin.suspendLoop(T.hours(1).mins().toInt())
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
return true return true
} }
R.id.overview_suspend_2h -> { R.id.overview_suspend_2h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2)) uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2))
loopPlugin.suspendLoop(120) loopPlugin.suspendLoop(T.hours(2).mins().toInt())
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
return true return true
} }
R.id.overview_suspend_3h -> { R.id.overview_suspend_3h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3)) uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3))
loopPlugin.suspendLoop(180) loopPlugin.suspendLoop(T.hours(3).mins().toInt())
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
return true return true
} }
R.id.overview_suspend_10h -> { R.id.overview_suspend_10h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10)) uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10))
loopPlugin.suspendLoop(600) loopPlugin.suspendLoop(T.hours(10).mins().toInt())
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
return true return true
} }
R.id.overview_disconnect_15m -> { R.id.overview_disconnect_15m -> {
profileFunction.getProfile()?.let { profile ->
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15))
loopPlugin.disconnectPump(15, profile) loopPlugin.goToZeroTemp(T.mins(15).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true return true
} }
R.id.overview_disconnect_30m -> { R.id.overview_disconnect_30m -> {
profileFunction.getProfile()?.let { profile ->
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30))
loopPlugin.disconnectPump(30, profile) loopPlugin.goToZeroTemp(T.mins(30).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true return true
} }
R.id.overview_disconnect_1h -> { R.id.overview_disconnect_1h -> {
profileFunction.getProfile()?.let { profile ->
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(1)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(1))
loopPlugin.disconnectPump(60, profile) loopPlugin.goToZeroTemp(T.hours(1).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
sp.putBoolean(R.string.key_objectiveusedisconnect, true) rxBus.send(EventRefreshOverview("suspend_menu"))
rxBus.send(EventRefreshOverview("suspendmenu")) }
return true return true
} }
R.id.overview_disconnect_2h -> { R.id.overview_disconnect_2h -> {
profileFunction.getProfile()?.let { profile ->
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2))
loopPlugin.disconnectPump(120, profile) loopPlugin.goToZeroTemp(T.hours(2).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true return true
} }
R.id.overview_disconnect_3h -> { R.id.overview_disconnect_3h -> {
profileFunction.getProfile()?.let { profile ->
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3))
loopPlugin.disconnectPump(180, profile) loopPlugin.goToZeroTemp(T.hours(3).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true return true
} }
} }
@ -378,7 +443,7 @@ class LoopDialog : DaggerDialogFragment() {
it.commitAllowingStateLoss() it.commitAllowingStateLoss()
} }
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
aapsLogger.debug(e.localizedMessage) aapsLogger.debug(e.localizedMessage ?: e.toString())
} }
} }
} }

View file

@ -1,6 +1,8 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.os.Bundle import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -8,20 +10,32 @@ import android.widget.ArrayAdapter
import com.google.common.base.Joiner import com.google.common.base.Joiner
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogProfileswitchBinding import info.nightscout.androidaps.databinding.DialogProfileswitchBinding
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class ProfileSwitchDialog : DialogFragmentWithDate() { class ProfileSwitchDialog : DialogFragmentWithDate() {
@ -31,6 +45,10 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var config: Config
@Inject lateinit var hardLimits: HardLimits
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var defaultValueHelper: DefaultValueHelper
private var profileIndex: Int? = null private var profileIndex: Int? = null
@ -42,6 +60,17 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val isDuration = binding.duration.value > 0
val isLowerPercentage = binding.percentage.value < 100
binding.ttLayout.visibility = (isDuration && isLowerPercentage).toVisibility()
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("duration", binding.duration.value) savedInstanceState.putDouble("duration", binding.duration.value)
@ -63,9 +92,11 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.duration.setParams(savedInstanceState?.getDouble("duration") binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok) ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok,
textWatcher)
binding.percentage.setParams(savedInstanceState?.getDouble("percentage") binding.percentage.setParams(savedInstanceState?.getDouble("percentage")
?: 100.0, Constants.CPP_MIN_PERCENTAGE.toDouble(), Constants.CPP_MAX_PERCENTAGE.toDouble(), 5.0, DecimalFormat("0"), false, binding.okcancel.ok) ?: 100.0, Constants.CPP_MIN_PERCENTAGE.toDouble(), Constants.CPP_MAX_PERCENTAGE.toDouble(), 5.0,
DecimalFormat("0"), false, binding.okcancel.ok, textWatcher)
binding.timeshift.setParams(savedInstanceState?.getDouble("timeshift") binding.timeshift.setParams(savedInstanceState?.getDouble("timeshift")
?: 0.0, Constants.CPP_MIN_TIMESHIFT.toDouble(), Constants.CPP_MAX_TIMESHIFT.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok) ?: 0.0, Constants.CPP_MIN_TIMESHIFT.toDouble(), Constants.CPP_MAX_TIMESHIFT.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
@ -97,6 +128,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
binding.reuselayout.visibility = View.GONE binding.reuselayout.visibility = View.GONE
} }
} }
binding.ttLayout.visibility = View.GONE
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -128,7 +160,16 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
if (eventTimeChanged) if (eventTimeChanged)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
val isTT = binding.duration.value > 0 && binding.percentage.value < 100 && binding.tt.isChecked
val target = defaultValueHelper.determineActivityTT()
val units = profileFunction.getUnits()
if (isTT)
actions.add(resourceHelper.gs(R.string.careportal_temporarytarget) + ": " + resourceHelper.gs(R.string.activity))
activity?.let { activity -> activity?.let { activity ->
val ps = profileFunction.buildProfileSwitch(profileStore, profileName, duration, percent, timeShift, eventTime)
val validity = ProfileSealed.PS(ps).isValid(resourceHelper.gs(R.string.careportal_profileswitch), activePlugin.activePump, config, resourceHelper, rxBus, hardLimits)
if (validity.isValid)
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
profileFunction.createProfileSwitch(profileStore, profileFunction.createProfileSwitch(profileStore,
profileName = profileName, profileName = profileName,
@ -144,7 +185,33 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
ValueWithUnit.Percent(percent), ValueWithUnit.Percent(percent),
ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 }, ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 },
ValueWithUnit.Minute(duration).takeIf { duration != 0 }) ValueWithUnit.Minute(duration).takeIf { duration != 0 })
if (isTT) {
disposable += repository.runTransactionForResult(
InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = eventTime,
duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
reason = TemporaryTarget.Reason.ACTIVITY,
lowTarget = Profile.toMgdl(target, profileFunction.getUnits()),
highTarget = Profile.toMgdl(target, profileFunction.getUnits())
)
).subscribe({ result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
}) })
uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(
TemporaryTarget.Reason.ACTIVITY), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
}
})
else {
OKDialog.show(
activity,
resourceHelper.gs(R.string.careportal_profileswitch),
HtmlHelper.fromHtml(Joiner.on("<br/>").join(validity.reasons))
)
return false
}
} }
return true return true
} }

View file

@ -119,7 +119,7 @@ class TempBasalDialog : DialogFragmentWithDate() {
val callback: Callback = object : Callback() { val callback: Callback = object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
} }
} }
} }

View file

@ -17,7 +17,7 @@ import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogTemptargetBinding import info.nightscout.androidaps.databinding.DialogTemptargetBinding
import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
@ -196,7 +196,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
}) })
} else { } else {
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = eventTime, timestamp = eventTime,
duration = TimeUnit.MINUTES.toMillis(duration.toLong()), duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
reason = when (reason) { reason = when (reason) {

View file

@ -174,7 +174,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
} }
} }
}) })

View file

@ -384,7 +384,7 @@ class WizardDialog : DaggerDialogFragment() {
it.commitAllowingStateLoss() it.commitAllowingStateLoss()
} }
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
aapsLogger.debug(e.localizedMessage) aapsLogger.debug(e.localizedMessage ?: "")
} }
} }
} }

View file

@ -0,0 +1,3 @@
package info.nightscout.androidaps.events
class EventTreatmentUpdateGui : EventUpdateGui()

View file

@ -1,379 +0,0 @@
package info.nightscout.androidaps.historyBrowser
import android.app.DatePickerDialog
import android.graphics.Color
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import com.jjoe64.graphview.GraphView
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventCustomCalculationFinished
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.overview.OverviewMenus
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
import javax.inject.Inject
class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var iobCobCalculatorPluginHistory: IobCobCalculatorPluginHistory
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var overviewMenus: OverviewMenus
@Inject lateinit var dateUtil: DateUtil
private val disposable = CompositeDisposable()
private val secondaryGraphs = ArrayList<GraphView>()
private val secondaryGraphsLabel = ArrayList<TextView>()
private var axisWidth: Int = 0
private var rangeToDisplay = 24 // for graph
private var start: Long = 0
private val graphLock = Object()
private var eventCustomCalculationFinished = EventCustomCalculationFinished()
private lateinit var binding: ActivityHistorybrowseBinding
private var destroyed = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHistorybrowseBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.left.setOnClickListener {
start -= T.hours(rangeToDisplay.toLong()).msecs()
runCalculation("onClickLeft")
}
binding.right.setOnClickListener {
start += T.hours(rangeToDisplay.toLong()).msecs()
runCalculation("onClickRight")
}
binding.end.setOnClickListener {
val calendar = Calendar.getInstance()
calendar.timeInMillis = System.currentTimeMillis()
calendar[Calendar.MILLISECOND] = 0
calendar[Calendar.SECOND] = 0
calendar[Calendar.MINUTE] = 0
calendar[Calendar.HOUR_OF_DAY] = 0
start = calendar.timeInMillis
runCalculation("onClickEnd")
}
binding.zoom.setOnClickListener {
rangeToDisplay += 6
rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay
updateGUI("rangeChange", false)
}
binding.zoom.setOnLongClickListener {
val calendar = Calendar.getInstance()
calendar.timeInMillis = start
calendar[Calendar.MILLISECOND] = 0
calendar[Calendar.SECOND] = 0
calendar[Calendar.MINUTE] = 0
calendar[Calendar.HOUR_OF_DAY] = 0
start = calendar.timeInMillis
runCalculation("onLongClickZoom")
true
}
// create an OnDateSetListener
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
val cal = Calendar.getInstance()
cal.timeInMillis = start
cal[Calendar.YEAR] = year
cal[Calendar.MONTH] = monthOfYear
cal[Calendar.DAY_OF_MONTH] = dayOfMonth
cal[Calendar.MILLISECOND] = 0
cal[Calendar.SECOND] = 0
cal[Calendar.MINUTE] = 0
cal[Calendar.HOUR_OF_DAY] = 0
start = cal.timeInMillis
binding.date.text = dateUtil.dateAndTimeString(start)
runCalculation("onClickDate")
}
binding.date.setOnClickListener {
val cal = Calendar.getInstance()
cal.timeInMillis = start
DatePickerDialog(this, dateSetListener,
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH),
cal.get(Calendar.DAY_OF_MONTH)
).show()
}
val dm = DisplayMetrics()
windowManager?.defaultDisplay?.getMetrics(dm)
axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80
binding.bggraph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
binding.bggraph.gridLabelRenderer?.reloadStyles()
binding.bggraph.gridLabelRenderer?.labelVerticalWidth = axisWidth
overviewMenus.setupChartMenu(binding.chartMenuButton)
prepareGraphsIfNeeded(overviewMenus.setting.size)
savedInstanceState?.let { bundle ->
rangeToDisplay = bundle.getInt("rangeToDisplay", 0)
start = bundle.getLong("start", 0)
}
}
public override fun onPause() {
super.onPause()
disposable.clear()
iobCobCalculatorPluginHistory.stopCalculation("onPause")
}
@Synchronized
override fun onDestroy() {
destroyed = true
super.onDestroy()
}
public override fun onResume() {
super.onResume()
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
// catch only events from iobCobCalculatorPluginHistory
if (it.cause is EventCustomCalculationFinished) {
updateGUI("EventAutosensCalculationFinished", bgOnly = false)
}
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ binding.overviewIobcalculationprogess.text = it.progress }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventRefreshOverview::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({
if (it.now) {
updateGUI("EventRefreshOverview", bgOnly = false)
}
}, fabricPrivacy::logException)
)
if (start == 0L) {
// set start of current day
val calendar = Calendar.getInstance()
calendar.timeInMillis = System.currentTimeMillis()
calendar[Calendar.MILLISECOND] = 0
calendar[Calendar.SECOND] = 0
calendar[Calendar.MINUTE] = 0
calendar[Calendar.HOUR_OF_DAY] = 0
start = calendar.timeInMillis
runCalculation("onResume")
} else {
updateGUI("onResume", bgOnly = false)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("rangeToDisplay", rangeToDisplay)
outState.putLong("start", start)
}
private fun prepareGraphsIfNeeded(numOfGraphs: Int) {
synchronized(graphLock) {
if (numOfGraphs != secondaryGraphs.size - 1) {
//aapsLogger.debug("New secondary graph count ${numOfGraphs-1}")
// rebuild needed
secondaryGraphs.clear()
secondaryGraphsLabel.clear()
binding.iobGraph.removeAllViews()
for (i in 1 until numOfGraphs) {
val relativeLayout = RelativeLayout(this)
relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val graph = GraphView(this)
graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(100)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) }
graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
graph.gridLabelRenderer?.reloadStyles()
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
graph.gridLabelRenderer?.numVerticalLabels = 3
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray
relativeLayout.addView(graph)
val label = TextView(this)
val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) }
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP)
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
label.layoutParams = layoutParams
relativeLayout.addView(label)
secondaryGraphsLabel.add(label)
binding.iobGraph.addView(relativeLayout)
secondaryGraphs.add(graph)
}
}
}
}
private fun runCalculation(from: String) {
lifecycleScope.launch(Dispatchers.Default) {
val end = start + T.hours(rangeToDisplay.toLong()).msecs()
iobCobCalculatorPluginHistory.stopCalculation(from)
iobCobCalculatorPluginHistory.clearCache()
iobCobCalculatorPluginHistory.runCalculation(from, end, bgDataReload = true, limitDataToOldestAvailable = false, cause = eventCustomCalculationFinished)
}
}
@Synchronized
fun updateGUI(from: String, bgOnly: Boolean) {
val menuChartSettings = overviewMenus.setting
prepareGraphsIfNeeded(menuChartSettings.size)
aapsLogger.debug(LTag.UI, "updateGUI from: $from")
val pump = activePlugin.activePump
val profile = profileFunction.getProfile()
val lowLine = defaultValueHelper.determineLowLine()
val highLine = defaultValueHelper.determineHighLine()
lifecycleScope.launch(Dispatchers.Main) {
binding.noprofile.visibility = (profile == null).toVisibility()
profile ?: return@launch
if (destroyed) return@launch
binding.date.text = dateUtil.dateAndTimeString(start)
binding.zoom.text = rangeToDisplay.toString()
val graphData = GraphData(injector, binding.bggraph)
val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
// do preparation in different thread
withContext(Dispatchers.Default) {
val fromTime: Long = start + T.secs(100).msecs()
val toTime: Long = start + T.hours(rangeToDisplay.toLong()).msecs() + T.secs(100).msecs()
aapsLogger.debug(LTag.UI, "Period: " + dateUtil.dateAndTimeString(fromTime) + " - " + dateUtil.dateAndTimeString(toTime))
val pointer = System.currentTimeMillis()
// **** In range Area ****
graphData.addInRangeArea(fromTime, toTime, lowLine, highLine)
// **** BG ****
// graphData.addBgReadings(fromTime, toTime, highLine, null)
// if (buildHelper.isDev()) graphData.addBucketedData(fromTime, toTime)
// add target line
// graphData.addTargetLine(fromTime, toTime, profile, null)
// **** NOW line ****
graphData.addNowLine(pointer)
if (!bgOnly) {
// Treatments
// graphData.addTreatments(fromTime, toTime)
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
// graphData.addActivity(fromTime, toTime, false, 0.8)
// add basal data
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) {
// graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2)
}
// ------------------ 2nd graph
synchronized(graphLock) {
for (g in 0 until secondaryGraphs.size) {
val secondGraphData = GraphData(injector, secondaryGraphs[g])
var useIobForScale = false
var useCobForScale = false
var useDevForScale = false
var useRatioForScale = false
var useDSForScale = false
var useBGIForScale = false
var useABSForScale = false
when {
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
}
val alignIobScale = menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
// if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, toTime, useABSForScale, 1.0)
// if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, toTime, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal], alignIobScale)
// if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, toTime, useCobForScale, if (useCobForScale) 1.0 else 0.5)
// if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1.0, alignDevBgiScale)
// if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1.0)
// if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, toTime, useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale)
// if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, toTime, useDSForScale, 1.0)
// set manual x bounds to have nice steps
secondGraphData.formatAxis(fromTime, toTime)
secondGraphData.addNowLine(pointer)
secondaryGraphsData.add(secondGraphData)
}
}
}
// set manual x bounds to have nice steps
graphData.setNumVerticalLabels()
graphData.formatAxis(fromTime, toTime)
}
// finally enforce drawing of graphs in UI thread
graphData.performUpdate()
if (!bgOnly)
synchronized(graphLock) {
for (g in 0 until secondaryGraphs.size) {
secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1)
secondaryGraphs[g].visibility = (!bgOnly && (
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal]
)).toVisibility()
secondaryGraphsData[g].performUpdate()
}
}
}
}
}

View file

@ -1,42 +0,0 @@
package info.nightscout.androidaps.historyBrowser
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class IobCobCalculatorPluginHistory @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
aapsSchedulers: AapsSchedulers,
rxBus: RxBusWrapper,
sp: SP,
resourceHelper: ResourceHelper,
profileFunction: ProfileFunction,
activePlugin: ActivePlugin,
sensitivityOref1Plugin: SensitivityOref1Plugin,
sensitivityAAPSPlugin: SensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
fabricPrivacy: FabricPrivacy,
dateUtil: DateUtil,
repository: AppRepository
) : IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction,
activePlugin, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository) {
override fun onStart() { // do not attach to rxbus
}
}

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.aps.logger package info.nightscout.androidaps.plugins.aps.logger
import info.nightscout.androidaps.db.StaticInjector import info.nightscout.androidaps.di.StaticInjector
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import org.mozilla.javascript.ScriptableObject import org.mozilla.javascript.ScriptableObject

View file

@ -12,22 +12,24 @@ import androidx.core.app.NotificationCompat
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.* import info.nightscout.androidaps.*
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventNewBG import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.LoopInterface.LastRun import info.nightscout.androidaps.interfaces.Loop.LastRun
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
@ -44,7 +46,6 @@ import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAc
import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.commands.Command
import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.receivers.ReceiverStatusStore
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
@ -64,8 +65,9 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.math.abs import kotlin.math.abs
@OpenForTesting
@Singleton @Singleton
open class LoopPlugin @Inject constructor( class LoopPlugin @Inject constructor(
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
private val aapsSchedulers: AapsSchedulers, private val aapsSchedulers: AapsSchedulers,
@ -96,7 +98,7 @@ open class LoopPlugin @Inject constructor(
.enableByDefault(config.APS) .enableByDefault(config.APS)
.description(R.string.description_loop), .description(R.string.description_loop),
aapsLogger, resourceHelper, injector aapsLogger, resourceHelper, injector
), LoopInterface { ), Loop {
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private var lastBgTriggeredRun: Long = 0 private var lastBgTriggeredRun: Long = 0
@ -158,48 +160,21 @@ open class LoopPlugin @Inject constructor(
} }
} }
override fun suspendTo(endTime: Long) { override fun minutesToEndOfSuspend(): Int {
sp.putLong("loopSuspendedTill", endTime) val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet()
sp.putBoolean("isSuperBolus", false) return if (offlineEventWrapped is ValueWrapper.Existing) T.msecs(offlineEventWrapped.value.timestamp + offlineEventWrapped.value.duration - dateUtil.now()).mins().toInt()
sp.putBoolean("isDisconnected", false) else 0
} }
fun superBolusTo(endTime: Long) {
sp.putLong("loopSuspendedTill", endTime)
sp.putBoolean("isSuperBolus", true)
sp.putBoolean("isDisconnected", false)
}
private fun disconnectTo(endTime: Long) {
sp.putLong("loopSuspendedTill", endTime)
sp.putBoolean("isSuperBolus", false)
sp.putBoolean("isDisconnected", true)
}
fun minutesToEndOfSuspend(): Int {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
if (loopSuspendedTill == 0L) return 0
val now = System.currentTimeMillis()
val millisDiff = loopSuspendedTill - now
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return 0
}
return (millisDiff / 60.0 / 1000.0).toInt()
}
// time exceeded
override val isSuspended: Boolean override val isSuspended: Boolean
get() { get() = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
if (loopSuspendedTill == 0L) return false override var enabled: Boolean
val now = System.currentTimeMillis() get() = isEnabled()
if (loopSuspendedTill <= now) { // time exceeded set(value) {
suspendTo(0L) setPluginEnabled(PluginType.LOOP, value)
return false
}
return true
} }
val isLGS: Boolean val isLGS: Boolean
get() { get() {
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
@ -211,30 +186,16 @@ open class LoopPlugin @Inject constructor(
return isLGS return isLGS
} }
// time exceeded
val isSuperBolus: Boolean val isSuperBolus: Boolean
get() { get() {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet()
if (loopSuspendedTill == 0L) return false return offlineEventWrapped is ValueWrapper.Existing && offlineEventWrapped.value.reason == OfflineEvent.Reason.SUPER_BOLUS
val now = System.currentTimeMillis()
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return false
}
return sp.getBoolean("isSuperBolus", false)
} }
// time exceeded
val isDisconnected: Boolean val isDisconnected: Boolean
get() { get() {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet()
if (loopSuspendedTill == 0L) return false return offlineEventWrapped is ValueWrapper.Existing && offlineEventWrapped.value.reason == OfflineEvent.Reason.DISCONNECT_PUMP
val now = System.currentTimeMillis()
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return false
}
return sp.getBoolean("isDisconnected", false)
} }
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
@ -251,6 +212,17 @@ open class LoopPlugin @Inject constructor(
invoke(initiator, allowNotification, false) invoke(initiator, allowNotification, false)
} }
@Synchronized
fun isEmptyQueue(): Boolean {
val maxMinutes = 2L
val start = dateUtil.now()
while (start + T.mins(maxMinutes).msecs() > dateUtil.now()) {
if (commandQueue.size() == 0 && commandQueue.performing() == null) return true
SystemClock.sleep(100)
}
return false
}
@Synchronized @Synchronized
operator fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean) { operator fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean) {
try { try {
@ -289,6 +261,12 @@ open class LoopPlugin @Inject constructor(
return return
} else rxBus.send(EventLoopInvoked()) } else rxBus.send(EventLoopInvoked())
if (!isEmptyQueue()) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.pumpbusy))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpbusy)))
return
}
// Prepare for pumps using % basals // Prepare for pumps using % basals
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) { if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
apsResult.usePercent = true apsResult.usePercent = true
@ -353,15 +331,15 @@ open class LoopPlugin @Inject constructor(
if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
val intentAction5m = Intent(context, CarbSuggestionReceiver::class.java) val intentAction5m = Intent(context, CarbSuggestionReceiver::class.java)
intentAction5m.putExtra("ignoreDuration", 5) intentAction5m.putExtra("ignoreDuration", 5)
val pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT) val pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
val actionIgnore5m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m, "Ignore 5m"), pendingIntent5m) val actionIgnore5m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m, "Ignore 5m"), pendingIntent5m)
val intentAction15m = Intent(context, CarbSuggestionReceiver::class.java) val intentAction15m = Intent(context, CarbSuggestionReceiver::class.java)
intentAction15m.putExtra("ignoreDuration", 15) intentAction15m.putExtra("ignoreDuration", 15)
val pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT) val pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
val actionIgnore15m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m, "Ignore 15m"), pendingIntent15m) val actionIgnore15m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m, "Ignore 15m"), pendingIntent15m)
val intentAction30m = Intent(context, CarbSuggestionReceiver::class.java) val intentAction30m = Intent(context, CarbSuggestionReceiver::class.java)
intentAction30m.putExtra("ignoreDuration", 30) intentAction30m.putExtra("ignoreDuration", 30)
val pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT) val pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
val actionIgnore30m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m, "Ignore 30m"), pendingIntent30m) val actionIgnore30m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m, "Ignore 30m"), pendingIntent30m)
val builder = NotificationCompat.Builder(context, CHANNEL_ID) val builder = NotificationCompat.Builder(context, CHANNEL_ID)
builder.setSmallIcon(R.drawable.notif_icon) builder.setSmallIcon(R.drawable.notif_icon)
@ -399,8 +377,7 @@ open class LoopPlugin @Inject constructor(
} }
} }
if (resultAfterConstraints.isChangeRequested if (resultAfterConstraints.isChangeRequested
&& !commandQueue.bolusInQueue() && !commandQueue.bolusInQueue()) {
&& !commandQueue.isRunning(Command.CommandType.BOLUS)) {
val waiting = PumpEnactResult(injector) val waiting = PumpEnactResult(injector)
waiting.queued = true waiting.queued = true
if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting
@ -483,7 +460,7 @@ open class LoopPlugin @Inject constructor(
stackBuilder.addParentStack(MainActivity::class.java) stackBuilder.addParentStack(MainActivity::class.java)
// Adds the Intent that starts the Activity to the top of the stack // Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent) stackBuilder.addNextIntent(resultIntent)
val resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) val resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(resultPendingIntent) builder.setContentIntent(resultPendingIntent)
builder.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000)) builder.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -642,22 +619,28 @@ open class LoopPlugin @Inject constructor(
return virtualPumpPlugin.isEnabled(PluginType.PUMP) return virtualPumpPlugin.isEnabled(PluginType.PUMP)
} }
fun disconnectPump(durationInMinutes: Int, profile: Profile?) { override fun goToZeroTemp(durationInMinutes: Int, profile: Profile, reason: OfflineEvent.Reason) {
val pump = activePlugin.activePump val pump = activePlugin.activePump
disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), reason))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
} }
} }
}) })
} else { } else {
commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { commandQueue.tempBasalPercent(0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
} }
} }
}) })
@ -666,37 +649,28 @@ open class LoopPlugin @Inject constructor(
commandQueue.cancelExtended(object : Callback() { commandQueue.cancelExtended(object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.extendedbolusdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.extendedbolusdeliveryerror), R.raw.boluserror)
} }
} }
}) })
} }
createOfflineEvent(durationInMinutes)
} }
override fun suspendLoop(durationInMinutes: Int) { override fun suspendLoop(durationInMinutes: Int) {
suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), OfflineEvent.Reason.SUSPEND))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
} }
} }
}) })
createOfflineEvent(durationInMinutes)
}
override fun createOfflineEvent(durationInMinutes: Int) {
disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(
timestamp = dateUtil.now(),
type = TherapyEvent.Type.APS_OFFLINE,
duration = T.mins(durationInMinutes.toLong()).msecs(),
enteredBy = "openaps://" + "AndroidAPS",
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL
)).subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) }
)
} }
companion object { companion object {

View file

@ -17,7 +17,6 @@ import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONArray import org.json.JSONArray
@ -40,7 +39,6 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var openHumansUploader: OpenHumansUploader
private val mScriptReader: ScriptReader private val mScriptReader: ScriptReader
private var profile = JSONObject() private var profile = JSONObject()
@ -116,7 +114,6 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader
aapsLogger.debug(LTag.APS, "Result: $result") aapsLogger.debug(LTag.APS, "Result: $result")
try { try {
val resultJson = JSONObject(result) val resultJson = JSONObject(result)
openHumansUploader.enqueueAMAData(profile, glucoseStatus, iobData, mealData, currentTemp, autosensData, resultJson)
determineBasalResultAMA = DetermineBasalResultAMA(injector, jsResult, resultJson) determineBasalResultAMA = DetermineBasalResultAMA(injector, jsResult, resultJson)
} catch (e: JSONException) { } catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e) aapsLogger.error(LTag.APS, "Unhandled exception", e)

View file

@ -58,7 +58,6 @@ class OpenAPSAMAFragment : DaggerFragment() {
} }
@Synchronized @Synchronized
@kotlin.ExperimentalStdlibApi
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
@ -91,7 +90,6 @@ class OpenAPSAMAFragment : DaggerFragment() {
} }
@Synchronized @Synchronized
@kotlin.ExperimentalStdlibApi
private fun updateGUI() { private fun updateGUI() {
if (_binding == null) return if (_binding == null) return
openAPSAMAPlugin.lastAPSResult?.let { lastAPSResult -> openAPSAMAPlugin.lastAPSResult?.let { lastAPSResult ->

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.aps.openAPSAMA
import android.content.Context import android.content.Context
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
@ -27,8 +28,9 @@ import org.json.JSONException
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@OpenForTesting
@Singleton @Singleton
open class OpenAPSAMAPlugin @Inject constructor( class OpenAPSAMAPlugin @Inject constructor(
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
private val rxBus: RxBusWrapper, private val rxBus: RxBusWrapper,
@ -123,11 +125,11 @@ open class OpenAPSAMAPlugin @Inject constructor(
maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble()) maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble()) targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
} }
if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return if (!hardLimits.checkHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return if (!hardLimits.checkHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return if (!hardLimits.checkHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return if (!hardLimits.checkHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return if (!hardLimits.checkHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
startPart = System.currentTimeMillis() startPart = System.currentTimeMillis()
if (constraintChecker.isAutosensModeEnabled().value()) { if (constraintChecker.isAutosensModeEnabled().value()) {
val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin") val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin")

View file

@ -17,7 +17,6 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -41,7 +40,6 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var openHumansUploader: OpenHumansUploader
private var profile = JSONObject() private var profile = JSONObject()
private var mGlucoseStatus = JSONObject() private var mGlucoseStatus = JSONObject()
@ -129,7 +127,6 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
aapsLogger.debug(LTag.APS, "Result: $result") aapsLogger.debug(LTag.APS, "Result: $result")
try { try {
val resultJson = JSONObject(result) val resultJson = JSONObject(result)
openHumansUploader.enqueueSMBData(profile, mGlucoseStatus, iobData, mealData, currentTemp, autosensData, microBolusAllowed, smbAlwaysAllowed, resultJson)
determineBasalResultSMB = DetermineBasalResultSMB(injector, resultJson) determineBasalResultSMB = DetermineBasalResultSMB(injector, resultJson)
} catch (e: JSONException) { } catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e) aapsLogger.error(LTag.APS, "Unhandled exception", e)

View file

@ -59,7 +59,6 @@ class OpenAPSSMBFragment : DaggerFragment() {
} }
@Synchronized @Synchronized
@kotlin.ExperimentalStdlibApi
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
disposable += rxBus disposable += rxBus
@ -91,7 +90,6 @@ class OpenAPSSMBFragment : DaggerFragment() {
} }
@Synchronized @Synchronized
@kotlin.ExperimentalStdlibApi
fun updateGUI() { fun updateGUI() {
if (_binding == null) return if (_binding == null) return
openAPSSMBPlugin.lastAPSResult?.let { lastAPSResult -> openAPSSMBPlugin.lastAPSResult?.let { lastAPSResult ->

View file

@ -5,6 +5,7 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.extensions.target import info.nightscout.androidaps.extensions.target
@ -27,8 +28,9 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@OpenForTesting
@Singleton @Singleton
open class OpenAPSSMBPlugin @Inject constructor( class OpenAPSSMBPlugin @Inject constructor(
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
private val rxBus: RxBusWrapper, private val rxBus: RxBusWrapper,
@ -128,11 +130,11 @@ open class OpenAPSSMBPlugin @Inject constructor(
maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble()) maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble()) targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
} }
if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return if (!hardLimits.checkHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return if (!hardLimits.checkHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return if (!hardLimits.checkHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return if (!hardLimits.checkHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return if (!hardLimits.checkHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
startPart = System.currentTimeMillis() startPart = System.currentTimeMillis()
if (constraintChecker.isAutosensModeEnabled().value()) { if (constraintChecker.isAutosensModeEnabled().value()) {
val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin") val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin")

View file

@ -114,7 +114,6 @@ class ConfigBuilderFragment : DaggerFragment() {
createViewsForPlugins(R.string.configbuilder_loop, R.string.configbuilder_loop_description, PluginType.LOOP, activePlugin.getSpecificPluginsVisibleInList(PluginType.LOOP)) createViewsForPlugins(R.string.configbuilder_loop, R.string.configbuilder_loop_description, PluginType.LOOP, activePlugin.getSpecificPluginsVisibleInList(PluginType.LOOP))
createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, activePlugin.getSpecificPluginsVisibleInList(PluginType.CONSTRAINTS)) createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, activePlugin.getSpecificPluginsVisibleInList(PluginType.CONSTRAINTS))
} }
createViewsForPlugins(R.string.configbuilder_treatments, R.string.configbuilder_treatments_description, PluginType.TREATMENT, activePlugin.getSpecificPluginsVisibleInList(PluginType.TREATMENT))
createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, activePlugin.getSpecificPluginsVisibleInList(PluginType.GENERAL)) createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, activePlugin.getSpecificPluginsVisibleInList(PluginType.GENERAL))
} }
@ -139,22 +138,16 @@ class ConfigBuilderFragment : DaggerFragment() {
@Suppress("InflateParams") @Suppress("InflateParams")
val baseView: LinearLayout = fragment.layoutInflater.inflate(R.layout.configbuilder_single_plugin, null) as LinearLayout val baseView: LinearLayout = fragment.layoutInflater.inflate(R.layout.configbuilder_single_plugin, null) as LinearLayout
private val enabledExclusive: RadioButton private val enabledExclusive: RadioButton = baseView.findViewById(R.id.plugin_enabled_exclusive)
private val enabledInclusive: CheckBox private val enabledInclusive: CheckBox = baseView.findViewById(R.id.plugin_enabled_inclusive)
private val pluginIcon: ImageView private val pluginIcon: ImageView = baseView.findViewById(R.id.plugin_icon)
private val pluginName: TextView private val pluginIcon2: ImageView = baseView.findViewById(R.id.plugin_icon2)
private val pluginDescription: TextView private val pluginName: TextView = baseView.findViewById(R.id.plugin_name)
private val pluginPreferences: ImageButton private val pluginDescription: TextView = baseView.findViewById(R.id.plugin_description)
private val pluginVisibility: CheckBox private val pluginPreferences: ImageButton = baseView.findViewById(R.id.plugin_preferences)
private val pluginVisibility: CheckBox = baseView.findViewById(R.id.plugin_visibility)
init { init {
enabledExclusive = baseView.findViewById(R.id.plugin_enabled_exclusive)
enabledInclusive = baseView.findViewById(R.id.plugin_enabled_inclusive)
pluginIcon = baseView.findViewById(R.id.plugin_icon)
pluginName = baseView.findViewById(R.id.plugin_name)
pluginDescription = baseView.findViewById(R.id.plugin_description)
pluginPreferences = baseView.findViewById(R.id.plugin_preferences)
pluginVisibility = baseView.findViewById(R.id.plugin_visibility)
pluginVisibility.setOnClickListener { pluginVisibility.setOnClickListener {
plugin.setFragmentVisible(pluginType, pluginVisibility.isChecked) plugin.setFragmentVisible(pluginType, pluginVisibility.isChecked)
@ -192,6 +185,12 @@ class ConfigBuilderFragment : DaggerFragment() {
if (plugin.menuIcon != -1) { if (plugin.menuIcon != -1) {
pluginIcon.visibility = View.VISIBLE pluginIcon.visibility = View.VISIBLE
pluginIcon.setImageDrawable(context?.let { ContextCompat.getDrawable(it, plugin.menuIcon) }) pluginIcon.setImageDrawable(context?.let { ContextCompat.getDrawable(it, plugin.menuIcon) })
if (plugin.menuIcon2 != -1) {
pluginIcon2.visibility = View.VISIBLE
pluginIcon2.setImageDrawable(context?.let { ContextCompat.getDrawable(it, plugin.menuIcon2) })
} else {
pluginIcon2.visibility = View.GONE
}
} else { } else {
pluginIcon.visibility = View.GONE pluginIcon.visibility = View.GONE
} }

View file

@ -120,7 +120,6 @@ class ConfigBuilderPlugin @Inject constructor(
for (p in activePlugin.getPluginsList()) { for (p in activePlugin.getPluginsList()) {
aapsLogger.debug(LTag.CONFIGBUILDER, p.name + ":" + aapsLogger.debug(LTag.CONFIGBUILDER, p.name + ":" +
(if (p.isEnabled(PluginType.GENERAL)) " GENERAL" else "") + (if (p.isEnabled(PluginType.GENERAL)) " GENERAL" else "") +
(if (p.isEnabled(PluginType.TREATMENT)) " TREATMENT" else "") +
(if (p.isEnabled(PluginType.SENSITIVITY)) " SENSITIVITY" else "") + (if (p.isEnabled(PluginType.SENSITIVITY)) " SENSITIVITY" else "") +
(if (p.isEnabled(PluginType.PROFILE)) " PROFILE" else "") + (if (p.isEnabled(PluginType.PROFILE)) " PROFILE" else "") +
(if (p.isEnabled(PluginType.APS)) " APS" else "") + (if (p.isEnabled(PluginType.APS)) " APS" else "") +
@ -135,7 +134,12 @@ class ConfigBuilderPlugin @Inject constructor(
// Ask when switching to physical pump plugin // Ask when switching to physical pump plugin
fun switchAllowed(changedPlugin: PluginBase, newState: Boolean, activity: FragmentActivity?, type: PluginType) { fun switchAllowed(changedPlugin: PluginBase, newState: Boolean, activity: FragmentActivity?, type: PluginType) {
if (changedPlugin.getType() == PluginType.PUMP && changedPlugin.name != resourceHelper.gs(R.string.virtualpump)) confirmPumpPluginActivation(changedPlugin, newState, activity, type) else performPluginSwitch(changedPlugin, newState, type) if (changedPlugin.getType() == PluginType.PUMP && changedPlugin.name != resourceHelper.gs(R.string.virtualpump))
confirmPumpPluginActivation(changedPlugin, newState, activity, type)
else if (changedPlugin.getType() == PluginType.PUMP) {
performPluginSwitch(changedPlugin, newState, type)
pumpSync.connectNewPump()
} else performPluginSwitch(changedPlugin, newState, type)
} }
private fun confirmPumpPluginActivation(changedPlugin: PluginBase, newState: Boolean, activity: FragmentActivity?, type: PluginType) { private fun confirmPumpPluginActivation(changedPlugin: PluginBase, newState: Boolean, activity: FragmentActivity?, type: PluginType) {
@ -185,7 +189,6 @@ class ConfigBuilderPlugin @Inject constructor(
PluginType.APS -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(APS::class.java) PluginType.APS -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(APS::class.java)
PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileSource::class.java) PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileSource::class.java)
PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSource::class.java) PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSource::class.java)
PluginType.TREATMENT -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(TreatmentsInterface::class.java)
PluginType.PUMP -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Pump::class.java) PluginType.PUMP -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Pump::class.java)
else -> { else -> {

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.plugins.configBuilder package info.nightscout.androidaps.plugins.configBuilder
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
@ -21,7 +20,6 @@ class PluginStore @Inject constructor(
private var activeAPSStore: APS? = null private var activeAPSStore: APS? = null
private var activeInsulinStore: Insulin? = null private var activeInsulinStore: Insulin? = null
private var activeSensitivityStore: Sensitivity? = null private var activeSensitivityStore: Sensitivity? = null
private var activeTreatmentsStore: TreatmentsInterface? = null
fun loadDefaults() { fun loadDefaults() {
verifySelectionInCategories() verifySelectionInCategories()
@ -121,16 +119,6 @@ class PluginStore @Inject constructor(
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting PumpInterface") aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting PumpInterface")
} }
setFragmentVisibilities((activePumpStore as PluginBase).name, pluginsInCategory, PluginType.PUMP) setFragmentVisibilities((activePumpStore as PluginBase).name, pluginsInCategory, PluginType.PUMP)
// PluginType.TREATMENT
pluginsInCategory = getSpecificPluginsList(PluginType.TREATMENT)
activeTreatmentsStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.TREATMENT) as TreatmentsInterface?
if (activeTreatmentsStore == null) {
activeTreatmentsStore = getDefaultPlugin(PluginType.TREATMENT) as TreatmentsInterface
(activeTreatmentsStore as PluginBase).setPluginEnabled(PluginType.TREATMENT, true)
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting PumpInterface")
}
setFragmentVisibilities((activeTreatmentsStore as PluginBase).name, pluginsInCategory, PluginType.TREATMENT)
} }
private fun setFragmentVisibilities(activePluginName: String, pluginsInCategory: ArrayList<PluginBase>, private fun setFragmentVisibilities(activePluginName: String, pluginsInCategory: ArrayList<PluginBase>,
@ -175,13 +163,12 @@ class PluginStore @Inject constructor(
get() = activeSensitivityStore get() = activeSensitivityStore
?: checkNotNull(activeSensitivityStore) { "No sensitivity selected" } ?: checkNotNull(activeSensitivityStore) { "No sensitivity selected" }
override val activeTreatments: TreatmentsInterface
get() = activeTreatmentsStore
?: checkNotNull(activeTreatmentsStore) { "No treatments selected" }
override val activeOverview: Overview override val activeOverview: Overview
get() = getSpecificPluginsListByInterface(Overview::class.java).first() as Overview get() = getSpecificPluginsListByInterface(Overview::class.java).first() as Overview
override val activeSafety: Safety
get() = getSpecificPluginsListByInterface(Safety::class.java).first() as Safety
override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins) override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins)
} }

View file

@ -9,14 +9,12 @@ import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.ProfileSwitch import info.nightscout.androidaps.database.entities.ProfileSwitch
import info.nightscout.androidaps.database.transactions.InsertOrUpdateProfileSwitch import info.nightscout.androidaps.database.transactions.InsertOrUpdateProfileSwitch
import info.nightscout.androidaps.extensions.fromConstant import info.nightscout.androidaps.extensions.fromConstant
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.ProfileStore
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -30,10 +28,13 @@ import javax.inject.Singleton
class ProfileFunctionImplementation @Inject constructor( class ProfileFunctionImplementation @Inject constructor(
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
private val sp: SP, private val sp: SP,
private val rxBus: RxBusWrapper,
private val resourceHelper: ResourceHelper, private val resourceHelper: ResourceHelper,
private val activePlugin: ActivePlugin, private val activePlugin: ActivePlugin,
private val repository: AppRepository, private val repository: AppRepository,
private val dateUtil: DateUtil private val dateUtil: DateUtil,
private val config: Config,
private val hardLimits: HardLimits
) : ProfileFunction { ) : ProfileFunction {
val cache = LongSparseArray<Profile>() val cache = LongSparseArray<Profile>()
@ -67,14 +68,15 @@ class ProfileFunctionImplementation @Inject constructor(
override fun getProfile(): Profile? = override fun getProfile(): Profile? =
getProfile(dateUtil.now()) getProfile(dateUtil.now())
@Synchronized
override fun getProfile(time: Long): Profile? { override fun getProfile(time: Long): Profile? {
val rounded = time - time % 1000 val rounded = time - time % 1000
val cached = cache[rounded] val cached = cache[rounded]
if (cached != null) { if (cached != null) {
// aapsLogger.debug("XXXXXXXXXXXXXXX HIT getProfile for $time $rounded") // aapsLogger.debug("HIT getProfile for $time $rounded")
return cached return cached
} }
// aapsLogger.debug("XXXXXXXXXXXXXXX getProfile called for $time") // aapsLogger.debug("getProfile called for $time")
val ps = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet() val ps = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet()
if (ps is ValueWrapper.Existing) { if (ps is ValueWrapper.Existing) {
val sealed = ProfileSealed.EPS(ps.value) val sealed = ProfileSealed.EPS(ps.value)
@ -90,10 +92,10 @@ class ProfileFunctionImplementation @Inject constructor(
if (sp.getString(R.string.key_units, Constants.MGDL) == Constants.MGDL) GlucoseUnit.MGDL if (sp.getString(R.string.key_units, Constants.MGDL) == Constants.MGDL) GlucoseUnit.MGDL
else GlucoseUnit.MMOL else GlucoseUnit.MMOL
override fun createProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) { override fun buildProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) : ProfileSwitch {
val pureProfile = profileStore.getSpecificProfile(profileName) val pureProfile = profileStore.getSpecificProfile(profileName)
?: throw InvalidParameterSpecException(profileName) ?: throw InvalidParameterSpecException(profileName)
val ps = ProfileSwitch( return ProfileSwitch(
timestamp = timestamp, timestamp = timestamp,
basalBlocks = pureProfile.basalBlocks, basalBlocks = pureProfile.basalBlocks,
isfBlocks = pureProfile.isfBlocks, isfBlocks = pureProfile.isfBlocks,
@ -104,7 +106,14 @@ class ProfileFunctionImplementation @Inject constructor(
timeshift = T.hours(timeShiftInHours.toLong()).msecs(), timeshift = T.hours(timeShiftInHours.toLong()).msecs(),
percentage = percentage, percentage = percentage,
duration = T.mins(durationInMinutes.toLong()).msecs(), duration = T.mins(durationInMinutes.toLong()).msecs(),
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration) insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also {
it.insulinEndTime = (pureProfile.dia * 3600 * 1000).toLong()
}
)
}
override fun createProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) {
val ps = buildProfileSwitch(profileStore, profileName, durationInMinutes, percentage, timeShiftInHours, timestamp)
disposable += repository.runTransactionForResult(InsertOrUpdateProfileSwitch(ps)) disposable += repository.runTransactionForResult(InsertOrUpdateProfileSwitch(ps))
.subscribe({ result -> .subscribe({ result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") } result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") }
@ -114,10 +123,32 @@ class ProfileFunctionImplementation @Inject constructor(
}) })
} }
override fun createProfileSwitch(durationInMinutes: Int, percentage: Int, timeShiftInHours: Int) { override fun createProfileSwitch(durationInMinutes: Int, percentage: Int, timeShiftInHours: Int): Boolean {
val profileStore = activePlugin.activeProfileSource.profile ?: return val profile = repository.getPermanentProfileSwitch(dateUtil.now())
val profileName = activePlugin.activeProfileSource.profile?.getDefaultProfileName() ?: throw InvalidParameterSpecException("No active ProfileSwitch")
?: return val profileStore = activePlugin.activeProfileSource.profile ?: return false
createProfileSwitch(profileStore, profileName, durationInMinutes, percentage, timeShiftInHours, dateUtil.now()) val ps = buildProfileSwitch(profileStore, profile.profileName, durationInMinutes, percentage, 0, dateUtil.now())
val validity = ProfileSealed.PS(ps).isValid(
resourceHelper.gs(info.nightscout.androidaps.automation.R.string.careportal_profileswitch),
activePlugin.activePump,
config,
resourceHelper,
rxBus,
hardLimits
)
var returnValue = true
if (validity.isValid) {
repository.runTransactionForResult(InsertOrUpdateProfileSwitch(ps))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving ProfileSwitch", it)
returnValue = false
}
.blockingGet()
.also { result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated ProfileSwitch $it") }
}
} else returnValue = false
return returnValue
} }
} }

View file

@ -45,6 +45,7 @@ class ObjectivesPlugin @Inject constructor(
var objectives: MutableList<Objective> = ArrayList() var objectives: MutableList<Objective> = ArrayList()
companion object { companion object {
const val FIRST_OBJECTIVE = 0 const val FIRST_OBJECTIVE = 0
@Suppress("unused") const val USAGE_OBJECTIVE = 1 @Suppress("unused") const val USAGE_OBJECTIVE = 1
@Suppress("unused") const val EXAM_OBJECTIVE = 2 @Suppress("unused") const val EXAM_OBJECTIVE = 2
@ -53,40 +54,17 @@ class ObjectivesPlugin @Inject constructor(
const val MAXIOB_ZERO_CL_OBJECTIVE = 5 const val MAXIOB_ZERO_CL_OBJECTIVE = 5
@Suppress("unused") const val MAXIOB_OBJECTIVE = 6 @Suppress("unused") const val MAXIOB_OBJECTIVE = 6
const val AUTOSENS_OBJECTIVE = 7 const val AUTOSENS_OBJECTIVE = 7
const val AMA_OBJECTIVE = 8 const val SMB_OBJECTIVE = 8
const val SMB_OBJECTIVE = 9 const val AUTO_OBJECTIVE = 9
const val AUTO_OBJECTIVE = 10
} }
public override fun onStart() { public override fun onStart() {
super.onStart() super.onStart()
convertSP()
setupObjectives() setupObjectives()
} }
override fun specialEnableCondition(): Boolean { override fun specialEnableCondition(): Boolean =
return activePlugin.activePump.pumpDescription.isTempBasalCapable activePlugin.activePump.pumpDescription.isTempBasalCapable
}
// convert 2.3 SP version
private fun convertSP() {
doConvertSP(0, "config")
doConvertSP(1, "openloop")
doConvertSP(2, "maxbasal")
doConvertSP(3, "maxiobzero")
doConvertSP(4, "maxiob")
doConvertSP(5, "autosens")
doConvertSP(6, "ama")
doConvertSP(7, "smb")
}
private fun doConvertSP(number: Int, name: String) {
if (!sp.contains("Objectives_" + name + "_started")) {
sp.putLong("Objectives_" + name + "_started", sp.getLong("Objectives" + number + "started", 0L))
sp.putLong("Objectives_" + name + "_accomplished", sp.getLong("Objectives" + number + "accomplished", 0L))
}
// TODO: we can remove Objectives1accomplished sometimes later
}
private fun setupObjectives() { private fun setupObjectives() {
objectives.clear() objectives.clear()
@ -98,9 +76,9 @@ class ObjectivesPlugin @Inject constructor(
objectives.add(Objective5(injector)) objectives.add(Objective5(injector))
objectives.add(Objective6(injector)) objectives.add(Objective6(injector))
objectives.add(Objective7(injector)) objectives.add(Objective7(injector))
objectives.add(Objective8(injector))
objectives.add(Objective9(injector)) objectives.add(Objective9(injector))
objectives.add(Objective10(injector)) objectives.add(Objective10(injector))
// edit companion object if you remove/add Objective
} }
fun reset() { fun reset() {
@ -120,12 +98,11 @@ class ObjectivesPlugin @Inject constructor(
sp.putBoolean(R.string.key_objectiveusescale, false) sp.putBoolean(R.string.key_objectiveusescale, false)
} }
@kotlin.ExperimentalStdlibApi
fun completeObjectives(activity: FragmentActivity, request: String) { fun completeObjectives(activity: FragmentActivity, request: String) {
val requestCode = sp.getString(R.string.key_objectives_request_code, "") val requestCode = sp.getString(R.string.key_objectives_request_code, "")
var url = sp.getString(R.string.key_nsclientinternal_url, "").lowercase(Locale.getDefault()) var url = sp.getString(R.string.key_nsclientinternal_url, "").lowercase(Locale.getDefault())
if (!url.endsWith("/")) url = "$url/" if (!url.endsWith("/")) url = "$url/"
@Suppress("DEPRECATION") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString() @Suppress("DEPRECATION", "UnstableApiUsage") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString()
if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) { if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) {
sp.putLong("Objectives_" + "openloop" + "_started", dateUtil.now()) sp.putLong("Objectives_" + "openloop" + "_started", dateUtil.now())
sp.putLong("Objectives_" + "openloop" + "_accomplished", dateUtil.now()) sp.putLong("Objectives_" + "openloop" + "_accomplished", dateUtil.now())
@ -137,8 +114,6 @@ class ObjectivesPlugin @Inject constructor(
sp.putLong("Objectives_" + "maxiob" + "_accomplished", dateUtil.now()) sp.putLong("Objectives_" + "maxiob" + "_accomplished", dateUtil.now())
sp.putLong("Objectives_" + "autosens" + "_started", dateUtil.now()) sp.putLong("Objectives_" + "autosens" + "_started", dateUtil.now())
sp.putLong("Objectives_" + "autosens" + "_accomplished", dateUtil.now()) sp.putLong("Objectives_" + "autosens" + "_accomplished", dateUtil.now())
sp.putLong("Objectives_" + "ama" + "_started", dateUtil.now())
sp.putLong("Objectives_" + "ama" + "_accomplished", dateUtil.now())
sp.putLong("Objectives_" + "smb" + "_started", dateUtil.now()) sp.putLong("Objectives_" + "smb" + "_started", dateUtil.now())
sp.putLong("Objectives_" + "smb" + "_accomplished", dateUtil.now()) sp.putLong("Objectives_" + "smb" + "_accomplished", dateUtil.now())
sp.putLong("Objectives_" + "auto" + "_started", dateUtil.now()) sp.putLong("Objectives_" + "auto" + "_started", dateUtil.now())
@ -164,49 +139,43 @@ class ObjectivesPlugin @Inject constructor(
*/ */
override fun isLoopInvocationAllowed(value: Constraint<Boolean>): Constraint<Boolean> { override fun isLoopInvocationAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
if (!objectives[FIRST_OBJECTIVE].isStarted) if (!objectives[FIRST_OBJECTIVE].isStarted)
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), FIRST_OBJECTIVE + 1), this) value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, FIRST_OBJECTIVE + 1), this)
return value return value
} }
override fun isLgsAllowed(value: Constraint<Boolean>): Constraint<Boolean> { override fun isLgsAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
if (!objectives[MAXBASAL_OBJECTIVE].isStarted) if (!objectives[MAXBASAL_OBJECTIVE].isStarted)
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), MAXBASAL_OBJECTIVE + 1), this) value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, MAXBASAL_OBJECTIVE + 1), this)
return value return value
} }
override fun isClosedLoopAllowed(value: Constraint<Boolean>): Constraint<Boolean> { override fun isClosedLoopAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
if (!objectives[MAXIOB_ZERO_CL_OBJECTIVE].isStarted) if (!objectives[MAXIOB_ZERO_CL_OBJECTIVE].isStarted)
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), MAXIOB_ZERO_CL_OBJECTIVE + 1), this) value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, MAXIOB_ZERO_CL_OBJECTIVE + 1), this)
return value return value
} }
override fun isAutosensModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> { override fun isAutosensModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
if (!objectives[AUTOSENS_OBJECTIVE].isStarted) if (!objectives[AUTOSENS_OBJECTIVE].isStarted)
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), AUTOSENS_OBJECTIVE + 1), this) value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, AUTOSENS_OBJECTIVE + 1), this)
return value
}
override fun isAMAModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
if (!objectives[AMA_OBJECTIVE].isStarted)
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), AMA_OBJECTIVE + 1), this)
return value return value
} }
override fun isSMBModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> { override fun isSMBModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
if (!objectives[SMB_OBJECTIVE].isStarted) if (!objectives[SMB_OBJECTIVE].isStarted)
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), SMB_OBJECTIVE + 1), this) value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, SMB_OBJECTIVE + 1), this)
return value return value
} }
override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> { override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> {
if (objectives[MAXIOB_ZERO_CL_OBJECTIVE].isStarted && !objectives[MAXIOB_ZERO_CL_OBJECTIVE].isAccomplished) if (objectives[MAXIOB_ZERO_CL_OBJECTIVE].isStarted && !objectives[MAXIOB_ZERO_CL_OBJECTIVE].isAccomplished)
maxIob.set(aapsLogger, 0.0, String.format(resourceHelper.gs(R.string.objectivenotfinished), MAXIOB_ZERO_CL_OBJECTIVE + 1), this) maxIob.set(aapsLogger, 0.0, resourceHelper.gs(R.string.objectivenotfinished, MAXIOB_ZERO_CL_OBJECTIVE + 1), this)
return maxIob return maxIob
} }
override fun isAutomationEnabled(value: Constraint<Boolean>): Constraint<Boolean> { override fun isAutomationEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
if (!objectives[AUTO_OBJECTIVE].isStarted) if (!objectives[AUTO_OBJECTIVE].isStarted)
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), AUTO_OBJECTIVE + 1), this) value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, AUTO_OBJECTIVE + 1), this)
return value return value
} }
} }

View file

@ -29,7 +29,6 @@ class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(i
override fun specialActionEnabled(): Boolean = override fun specialActionEnabled(): Boolean =
nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true
@kotlin.ExperimentalStdlibApi
override fun specialAction(activity: FragmentActivity, input: String) { override fun specialAction(activity: FragmentActivity, input: String) {
objectivesPlugin.completeObjectives(activity, input) objectivesPlugin.completeObjectives(activity, input)
} }

View file

@ -1,12 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective8(injector: HasAndroidInjector) : Objective(injector, "ama", R.string.objectives_ama_objective, 0) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.constraints.safety
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
@ -20,6 +21,7 @@ import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONObject
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.math.floor import kotlin.math.floor
@ -49,7 +51,7 @@ class SafetyPlugin @Inject constructor(
.pluginName(R.string.safety) .pluginName(R.string.safety)
.preferencesId(R.xml.pref_safety), .preferencesId(R.xml.pref_safety),
aapsLogger, resourceHelper, injector aapsLogger, resourceHelper, injector
), Constraints { ), Constraints, Safety {
/** /**
* Constraints interface * Constraints interface
@ -186,11 +188,23 @@ class SafetyPlugin @Inject constructor(
override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> { override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> {
val apsMode = sp.getString(R.string.key_aps_mode, "open") val apsMode = sp.getString(R.string.key_aps_mode, "open")
val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled(PluginType.APS)) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string.key_openapsma_max_iob, 1.5) val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled()) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string.key_openapsma_max_iob, 1.5)
maxIob.setIfSmaller(aapsLogger, maxIobPref, String.format(resourceHelper.gs(R.string.limitingiob), maxIobPref, resourceHelper.gs(R.string.maxvalueinpreferences)), this) maxIob.setIfSmaller(aapsLogger, maxIobPref, String.format(resourceHelper.gs(R.string.limitingiob), maxIobPref, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
if (openAPSAMAPlugin.isEnabled(PluginType.APS)) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobAMA(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobAMA(), resourceHelper.gs(R.string.hardlimit)), this) if (openAPSAMAPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobAMA(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobAMA(), resourceHelper.gs(R.string.hardlimit)), this)
if (openAPSSMBPlugin.isEnabled(PluginType.APS)) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobSMB(), resourceHelper.gs(R.string.hardlimit)), this) if (openAPSSMBPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobSMB(), resourceHelper.gs(R.string.hardlimit)), this)
if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, HardLimits.MAX_IOB_LGS, String.format(resourceHelper.gs(R.string.limitingiob), HardLimits.MAX_IOB_LGS, resourceHelper.gs(R.string.lowglucosesuspend)), this) if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, HardLimits.MAX_IOB_LGS, String.format(resourceHelper.gs(R.string.limitingiob), HardLimits.MAX_IOB_LGS, resourceHelper.gs(R.string.lowglucosesuspend)), this)
return maxIob return maxIob
} }
override fun configuration(): JSONObject =
JSONObject()
.putString(R.string.key_age, sp, resourceHelper)
.putDouble(R.string.key_treatmentssafety_maxbolus, sp, resourceHelper)
.putDouble(R.string.key_treatmentssafety_maxcarbs, sp, resourceHelper)
override fun applyConfiguration(configuration: JSONObject) {
configuration.storeString(R.string.key_age, sp, resourceHelper)
configuration.storeDouble(R.string.key_treatmentssafety_maxbolus, sp, resourceHelper)
configuration.storeDouble(R.string.key_treatmentssafety_maxcarbs, sp, resourceHelper)
}
} }

View file

@ -152,7 +152,6 @@ class SignatureVerifierPlugin @Inject constructor(
return sb.toString() return sb.toString()
} }
@kotlin.ExperimentalStdlibApi
fun singleCharUnMap(shortHash: String): String { fun singleCharUnMap(shortHash: String): String {
val array = ByteArray(shortHash.length) val array = ByteArray(shortHash.length)
val sb = StringBuilder() val sb = StringBuilder()

View file

@ -5,6 +5,7 @@ import android.os.StatFs
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.Constraints
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
@ -20,8 +21,9 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@OpenForTesting
@Singleton @Singleton
open class StorageConstraintPlugin @Inject constructor( class StorageConstraintPlugin @Inject constructor(
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
resourceHelper: ResourceHelper, resourceHelper: ResourceHelper,
@ -48,7 +50,7 @@ open class StorageConstraintPlugin @Inject constructor(
return value return value
} }
open fun availableInternalMemorySize(): Long { fun availableInternalMemorySize(): Long {
val path = Environment.getDataDirectory() val path = Environment.getDataDirectory()
val stat = StatFs(path.path) val stat = StatFs(path.path)
val blockSize = stat.blockSizeLong val blockSize = stat.blockSizeLong

View file

@ -28,7 +28,8 @@ import info.nightscout.androidaps.events.EventTherapyEventChange
import info.nightscout.androidaps.extensions.toStringMedium import info.nightscout.androidaps.extensions.toStringMedium
import info.nightscout.androidaps.extensions.toStringShort import info.nightscout.androidaps.extensions.toStringShort
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity import info.nightscout.androidaps.activities.HistoryBrowseActivity
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
@ -114,7 +115,10 @@ class ActionsFragment : DaggerFragment() {
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View? {
//check screen width //check screen width
dm = DisplayMetrics() dm = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getMetrics(dm) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
activity?.display?.getRealMetrics(dm)
else
@Suppress("DEPRECATION") activity?.windowManager?.defaultDisplay?.getMetrics(dm)
val screenWidth = dm.widthPixels val screenWidth = dm.widthPixels
val screenHeight = dm.heightPixels val screenHeight = dm.heightPixels
@ -152,7 +156,7 @@ class ActionsFragment : DaggerFragment() {
pbLevelLabel = view.findViewById(R.id.pb_level_label) pbLevelLabel = view.findViewById(R.id.pb_level_label)
profileSwitch?.setOnClickListener { profileSwitch?.setOnClickListener {
ProfileSwitchDialog().show(childFragmentManager, "Actions") ProfileSwitchDialog().show(childFragmentManager, "ProfileSwitchDialog")
} }
tempTarget?.setOnClickListener { tempTarget?.setOnClickListener {
TempTargetDialog().show(childFragmentManager, "Actions") TempTargetDialog().show(childFragmentManager, "Actions")
@ -304,7 +308,11 @@ class ActionsFragment : DaggerFragment() {
val activeBgSource = activePlugin.activeBgSource val activeBgSource = activePlugin.activeBgSource
historyBrowser?.visibility = (profile != null).toVisibility() historyBrowser?.visibility = (profile != null).toVisibility()
fill?.visibility = (pump.pumpDescription.isRefillingCapable && pump.isInitialized() && !pump.isSuspended()).toVisibility() fill?.visibility = (pump.pumpDescription.isRefillingCapable && pump.isInitialized() && !pump.isSuspended()).toVisibility()
if(pump is DiaconnG8Plugin) {
pumpBatteryChange?.visibility = (pump.pumpDescription.isBatteryReplaceable && !pump.isBatteryChangeLoggingEnabled()).toVisibility()
} else {
pumpBatteryChange?.visibility = (pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)).toVisibility() pumpBatteryChange?.visibility = (pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)).toVisibility()
}
tempTarget?.visibility = (profile != null && config.APS).toVisibility() tempTarget?.visibility = (profile != null && config.APS).toVisibility()
tddStats?.visibility = pump.pumpDescription.supportsTDDs.toVisibility() tddStats?.visibility = pump.pumpDescription.supportsTDDs.toVisibility()

View file

@ -67,7 +67,6 @@ class FoodFragment : DaggerFragment() {
return binding.root return binding.root
} }
@kotlin.ExperimentalStdlibApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -129,7 +128,6 @@ class FoodFragment : DaggerFragment() {
} }
@Synchronized @Synchronized
@kotlin.ExperimentalStdlibApi
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
disposable.add(rxBus disposable.add(rxBus
@ -141,7 +139,6 @@ class FoodFragment : DaggerFragment() {
swapAdapter() swapAdapter()
} }
@kotlin.ExperimentalStdlibApi
private fun swapAdapter() { private fun swapAdapter() {
disposable += repository disposable += repository
.getFoodData() .getFoodData()
@ -202,7 +199,6 @@ class FoodFragment : DaggerFragment() {
} }
} }
@kotlin.ExperimentalStdlibApi
private fun filterData() { private fun filterData() {
val textFilter = binding.filter.text.toString() val textFilter = binding.filter.text.toString()
val categoryFilter = binding.category.selectedItem?.toString() val categoryFilter = binding.category.selectedItem?.toString()

View file

@ -363,7 +363,7 @@ class ImportExportPrefsImpl @Inject constructor(
override fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single<List<UserEntry>>) { override fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single<List<UserEntry>>) {
val entries = singleEntries.blockingGet() val entries = singleEntries.blockingGet()
prefFileList.ensureExportDirExists() prefFileList.ensureExportDirExists()
val newFile = prefFileList.newExportXmlFile() val newFile = prefFileList.newExportCsvFile()
try { try {
classicPrefsFormat.saveCsv(newFile, entries) classicPrefsFormat.saveCsv(newFile, entries)

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.plugins.general.maintenance package info.nightscout.androidaps.plugins.general.maintenance
import ch.qos.logback.classic.LoggerContext import ch.qos.logback.classic.LoggerContext
import info.nightscout.androidaps.annotations.OpenForTesting
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -8,6 +9,7 @@ import javax.inject.Singleton
/** /**
* This class provides several methods for log-handling (eg. sending logs as emails). * This class provides several methods for log-handling (eg. sending logs as emails).
*/ */
@OpenForTesting
@Singleton @Singleton
class LoggerUtils @Inject constructor() { class LoggerUtils @Inject constructor() {

View file

@ -16,11 +16,13 @@ import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.insight.database.InsightDatabase import info.nightscout.androidaps.insight.database.InsightDatabase
import info.nightscout.androidaps.interfaces.DataSyncSelector import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.ImportExportPrefs import info.nightscout.androidaps.interfaces.ImportExportPrefs
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.interfaces.PumpSync
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -44,6 +46,8 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var dataSyncSelector: DataSyncSelector @Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var pumpSync: PumpSync @Inject lateinit var pumpSync: PumpSync
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var overviewData: OverviewData
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
@ -63,7 +67,9 @@ class MaintenanceFragment : DaggerFragment() {
binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() } binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() }
binding.logDelete.setOnClickListener { binding.logDelete.setOnClickListener {
uel.log(Action.DELETE_LOGS, Sources.Maintenance) uel.log(Action.DELETE_LOGS, Sources.Maintenance)
maintenancePlugin.deleteLogs() Thread {
maintenancePlugin.deleteLogs(5)
}.start()
} }
binding.navResetdb.setOnClickListener { binding.navResetdb.setOnClickListener {
activity?.let { activity -> activity?.let { activity ->
@ -75,6 +81,9 @@ class MaintenanceFragment : DaggerFragment() {
insightDatabase.clearAllTables() insightDatabase.clearAllTables()
dataSyncSelector.resetToNextFullSync() dataSyncSelector.resetToNextFullSync()
pumpSync.connectNewPump() pumpSync.connectNewPump()
overviewData.reset()
iobCobCalculator.ads.reset()
iobCobCalculator.clearCache()
} }
.subscribeOn(aapsSchedulers.io) .subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)

View file

@ -8,8 +8,8 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
@ -35,8 +35,10 @@ class MaintenancePlugin @Inject constructor(
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
private val buildHelper: BuildHelper, private val buildHelper: BuildHelper,
private val config: Config, private val config: Config,
private val fileListProvider: PrefFileListProvider,
private val loggerUtils: LoggerUtils private val loggerUtils: LoggerUtils
) : PluginBase(PluginDescription() ) : PluginBase(
PluginDescription()
.mainType(PluginType.GENERAL) .mainType(PluginType.GENERAL)
.fragmentClass(MaintenanceFragment::class.java.name) .fragmentClass(MaintenanceFragment::class.java.name)
.alwaysVisible(false) .alwaysVisible(false)
@ -53,11 +55,12 @@ class MaintenancePlugin @Inject constructor(
val recipient = sp.getString(R.string.key_maintenance_logs_email, "logs@androidaps.org") val recipient = sp.getString(R.string.key_maintenance_logs_email, "logs@androidaps.org")
val amount = sp.getInt(R.string.key_maintenance_logs_amount, 2) val amount = sp.getInt(R.string.key_maintenance_logs_amount, 2)
val logs = getLogFiles(amount) val logs = getLogFiles(amount)
val zipDir = context.getExternalFilesDir("exports") val zipDir = fileListProvider.ensureTempDirExists()
val zipFile = File(zipDir, constructName()) val zipFile = File(zipDir, constructName())
aapsLogger.debug("zipFile: ${zipFile.absolutePath}") aapsLogger.debug("zipFile: ${zipFile.absolutePath}")
val zip = zipLogs(zipFile, logs) val zip = zipLogs(zipFile, logs)
val attachmentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", zip) val attachmentUri =
FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", zip)
val emailIntent: Intent = this.sendMail(attachmentUri, recipient, "Log Export") val emailIntent: Intent = this.sendMail(attachmentUri, recipient, "Log Export")
aapsLogger.debug("sending emailIntent") aapsLogger.debug("sending emailIntent")
context.startActivity(emailIntent) context.startActivity(emailIntent)
@ -65,14 +68,15 @@ class MaintenancePlugin @Inject constructor(
//todo replace this with a call on startup of the application, specifically to remove //todo replace this with a call on startup of the application, specifically to remove
// unnecessary garbage from the log exports // unnecessary garbage from the log exports
fun deleteLogs() { fun deleteLogs(keep: Int) {
val logDir = File(loggerUtils.logDirectory) val logDir = File(loggerUtils.logDirectory)
val files = logDir.listFiles { _: File?, name: String -> val files = logDir.listFiles { _: File?, name: String ->
(name.startsWith("AndroidAPS") && name.endsWith(".zip")) (name.startsWith("AndroidAPS") && name.endsWith(".zip"))
} }
Arrays.sort(files) { f1: File, f2: File -> f1.name.compareTo(f2.name) } if (files == null || files.isEmpty()) return
Arrays.sort(files) { f1: File, f2: File -> f2.name.compareTo(f1.name) }
var delFiles = listOf(*files) var delFiles = listOf(*files)
val amount = sp.getInt(R.string.key_logshipper_amount, 2) val amount = sp.getInt(R.string.key_logshipper_amount, keep)
val keepIndex = amount - 1 val keepIndex = amount - 1
if (keepIndex < delFiles.size) { if (keepIndex < delFiles.size) {
delFiles = delFiles.subList(keepIndex, delFiles.size) delFiles = delFiles.subList(keepIndex, delFiles.size)
@ -80,11 +84,10 @@ class MaintenancePlugin @Inject constructor(
file.delete() file.delete()
} }
} }
val exportDir = File(loggerUtils.logDirectory, "exports") val exportDir = fileListProvider.ensureTempDirExists()
if (exportDir.exists()) { if (exportDir.exists()) {
val expFiles = exportDir.listFiles() exportDir.listFiles()?.let { expFiles ->
for (file in expFiles) { for (file in expFiles) file.delete()
file.delete()
} }
exportDir.delete() exportDir.delete()
} }
@ -106,7 +109,7 @@ class MaintenancePlugin @Inject constructor(
(name.startsWith("AndroidAPS") (name.startsWith("AndroidAPS")
&& (name.endsWith(".log") && (name.endsWith(".log")
|| name.endsWith(".zip") && !name.endsWith(loggerUtils.suffix))) || name.endsWith(".zip") && !name.endsWith(loggerUtils.suffix)))
} } ?: emptyArray()
Arrays.sort(files) { f1: File, f2: File -> f2.name.compareTo(f1.name) } Arrays.sort(files) { f1: File, f2: File -> f2.name.compareTo(f1.name) }
val result = listOf(*files) val result = listOf(*files)
var toIndex = amount var toIndex = amount
@ -192,7 +195,12 @@ class MaintenancePlugin @Inject constructor(
* *
* @return * @return
*/ */
private fun sendMail(attachmentUri: Uri, recipient: String, subject: String, body: String): Intent { private fun sendMail(
attachmentUri: Uri,
recipient: String,
subject: String,
body: String
): Intent {
aapsLogger.debug("sending email to $recipient with subject $subject") aapsLogger.debug("sending email to $recipient with subject $subject")
val emailIntent = Intent(Intent.ACTION_SEND) val emailIntent = Intent(Intent.ACTION_SEND)
emailIntent.type = "text/plain" emailIntent.type = "text/plain"
@ -207,7 +215,8 @@ class MaintenancePlugin @Inject constructor(
override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) { override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) {
super.preprocessPreferences(preferenceFragment) super.preprocessPreferences(preferenceFragment)
val encryptSwitch = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_maintenance_encrypt_exported_prefs)) as SwitchPreference? val encryptSwitch =
preferenceFragment.findPreference(resourceHelper.gs(R.string.key_maintenance_encrypt_exported_prefs)) as SwitchPreference?
?: return ?: return
encryptSwitch.isVisible = buildHelper.isEngineeringMode() encryptSwitch.isVisible = buildHelper.isEngineeringMode()
encryptSwitch.isEnabled = buildHelper.isEngineeringMode() encryptSwitch.isEnabled = buildHelper.isEngineeringMode()

View file

@ -34,11 +34,13 @@ class DataSyncSelectorImplementation @Inject constructor(
processChangedTemporaryBasalsCompat() processChangedTemporaryBasalsCompat()
processChangedExtendedBolusesCompat() processChangedExtendedBolusesCompat()
processChangedProfileSwitchesCompat() processChangedProfileSwitchesCompat()
processChangedEffectiveProfileSwitchesCompat()
processChangedGlucoseValuesCompat() processChangedGlucoseValuesCompat()
processChangedTempTargetsCompat() processChangedTempTargetsCompat()
processChangedFoodsCompat() processChangedFoodsCompat()
processChangedTherapyEventsCompat() processChangedTherapyEventsCompat()
processChangedDeviceStatusesCompat() processChangedDeviceStatusesCompat()
processChangedOfflineEventsCompat()
processChangedProfileStore() processChangedProfileStore()
} }
} }
@ -55,6 +57,8 @@ class DataSyncSelectorImplementation @Inject constructor(
sp.remove(R.string.key_ns_extended_bolus_last_synced_id) sp.remove(R.string.key_ns_extended_bolus_last_synced_id)
sp.remove(R.string.key_ns_therapy_event_last_synced_id) sp.remove(R.string.key_ns_therapy_event_last_synced_id)
sp.remove(R.string.key_ns_profile_switch_last_synced_id) sp.remove(R.string.key_ns_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_effective_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_offline_event_last_synced_id)
sp.remove(R.string.key_ns_profile_store_last_synced_timestamp) sp.remove(R.string.key_ns_profile_store_last_synced_timestamp)
} }
@ -76,8 +80,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastBolusId = -1L @Volatile private var lastBolusId = -1L
private var lastBolusTime = -1L @Volatile private var lastBolusTime = -1L
override fun processChangedBolusesCompat(): Boolean { override fun processChangedBolusesCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastBolusIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastBolusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
@ -119,8 +123,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastCarbsId = -1L @Volatile private var lastCarbsId = -1L
private var lastCarbsTime = -1L @Volatile private var lastCarbsTime = -1L
override fun processChangedCarbsCompat(): Boolean { override fun processChangedCarbsCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastCarbsIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastCarbsIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
@ -162,8 +166,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastBcrId = -1L @Volatile private var lastBcrId = -1L
private var lastBcrTime = -1L @Volatile private var lastBcrTime = -1L
override fun processChangedBolusCalculatorResultsCompat(): Boolean { override fun processChangedBolusCalculatorResultsCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastBolusCalculatorResultIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastBolusCalculatorResultIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
@ -205,8 +209,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastTtId = -1L @Volatile private var lastTtId = -1L
private var lastTtTime = -1L @Volatile private var lastTtTime = -1L
override fun processChangedTempTargetsCompat(): Boolean { override fun processChangedTempTargetsCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastTempTargetIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastTempTargetIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
@ -248,8 +252,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastFoodId = -1L @Volatile private var lastFoodId = -1L
private var lastFoodTime = -1L @Volatile private var lastFoodTime = -1L
override fun processChangedFoodsCompat(): Boolean { override fun processChangedFoodsCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastFoodIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastFoodIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
@ -291,8 +295,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastGvId = -1L @Volatile private var lastGvId = -1L
private var lastGvTime = -1L @Volatile private var lastGvTime = -1L
override fun processChangedGlucoseValuesCompat(): Boolean { override fun processChangedGlucoseValuesCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastGlucoseValueIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastGlucoseValueIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
@ -340,8 +344,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastTeId = -1L @Volatile private var lastTeId = -1L
private var lastTeTime = -1L @Volatile private var lastTeTime = -1L
override fun processChangedTherapyEventsCompat(): Boolean { override fun processChangedTherapyEventsCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastTherapyEventIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastTherapyEventIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
@ -358,10 +362,10 @@ class DataSyncSelectorImplementation @Inject constructor(
when { when {
// without nsId = create new // without nsId = create new
te.first.interfaceIDs.nightscoutId == null -> te.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", te.first.toJson(true), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId") nsClientPlugin.nsClientService?.dbAdd("treatments", te.first.toJson(true, dateUtil), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId")
// nsId = update // nsId = update
te.first.interfaceIDs.nightscoutId != null -> te.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", te.first.interfaceIDs.nightscoutId, te.first.toJson(false), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId") nsClientPlugin.nsClientService?.dbUpdate("treatments", te.first.interfaceIDs.nightscoutId, te.first.toJson(false, dateUtil), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId")
} }
return true return true
} }
@ -382,8 +386,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastDsId = -1L @Volatile private var lastDsId = -1L
private var lastDsTime = -1L @Volatile private var lastDsTime = -1L
override fun processChangedDeviceStatusesCompat(): Boolean { override fun processChangedDeviceStatusesCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
@ -424,8 +428,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastTbrId = -1L @Volatile private var lastTbrId = -1L
private var lastTbrTime = -1L @Volatile private var lastTbrTime = -1L
override fun processChangedTemporaryBasalsCompat(): Boolean { override fun processChangedTemporaryBasalsCompat(): Boolean {
val useAbsolute = sp.getBoolean(R.string.key_ns_sync_use_absolute, false) val useAbsolute = sp.getBoolean(R.string.key_ns_sync_use_absolute, false)
val lastDbIdWrapped = appRepository.getLastTemporaryBasalIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastTemporaryBasalIdWrapped().blockingGet()
@ -475,8 +479,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastEbId = -1L @Volatile private var lastEbId = -1L
private var lastEbTime = -1L @Volatile private var lastEbTime = -1L
override fun processChangedExtendedBolusesCompat(): Boolean { override fun processChangedExtendedBolusesCompat(): Boolean {
val useAbsolute = sp.getBoolean(R.string.key_ns_sync_use_absolute, false) val useAbsolute = sp.getBoolean(R.string.key_ns_sync_use_absolute, false)
val lastDbIdWrapped = appRepository.getLastExtendedBolusIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastExtendedBolusIdWrapped().blockingGet()
@ -525,8 +529,8 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
} }
private var lastPsId = -1L @Volatile private var lastPsId = -1L
private var lastPsTime = -1L @Volatile private var lastPsTime = -1L
override fun processChangedProfileSwitchesCompat(): Boolean { override fun processChangedProfileSwitchesCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastProfileSwitchIdWrapped().blockingGet() val lastDbIdWrapped = appRepository.getLastProfileSwitchIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
@ -553,6 +557,91 @@ class DataSyncSelectorImplementation @Inject constructor(
return false return false
} }
override fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting EffectiveProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, lastSynced)
}
}
override fun changedEffectiveProfileSwitch(): List<EffectiveProfileSwitch> {
val startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
return appRepository.getModifiedEffectiveProfileSwitchDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading EffectiveProfileSwitch data for sync from $startId. Records ${it.size}")
}
}
@Volatile private var lastEpsId = -1L
@Volatile private var lastEpsTime = -1L
override fun processChangedEffectiveProfileSwitchesCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastEffectiveProfileSwitchIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
startId = 0
}
if (startId == lastEpsId && dateUtil.now() - lastEpsTime < 5000) return false
lastEpsId = startId
lastEpsTime = dateUtil.now()
appRepository.getNextSyncElementEffectiveProfileSwitch(startId).blockingGet()?.let { ps ->
aapsLogger.info(LTag.DATABASE, "Loading EffectiveProfileSwitch data Start: $startId ID: ${ps.first.id} HistoryID: ${ps.second} ")
when {
// without nsId = create new
ps.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", ps.first.toJson(true, dateUtil), DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second), "$startId/$lastDbId")
// with nsId = update
ps.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", ps.first.interfaceIDs.nightscoutId, ps.first.toJson(false, dateUtil), DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second), "$startId/$lastDbId")
}
return true
}
return false
}
override fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced")
sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedOfflineEvents(): List<OfflineEvent> {
val startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
return appRepository.getModifiedOfflineEventsDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading OfflineEvent data for sync from $startId. Records ${it.size}")
}
}
@Volatile private var lastOeId = -1L
@Volatile private var lastOeTime = -1L
override fun processChangedOfflineEventsCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastOfflineEventIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_offline_event_last_synced_id, 0)
startId = 0
}
if (startId == lastOeId && dateUtil.now() - lastOeTime < 5000) return false
lastOeId = startId
lastOeTime = dateUtil.now()
appRepository.getNextSyncElementOfflineEvent(startId).blockingGet()?.let { oe ->
aapsLogger.info(LTag.DATABASE, "Loading OfflineEvent data Start: $startId ID: ${oe.first.id} HistoryID: ${oe.second} ")
when {
// without nsId = create new
oe.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", oe.first.toJson(true, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second), "$startId/$lastDbId")
// existing with nsId = update
oe.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", oe.first.interfaceIDs.nightscoutId, oe.first.toJson(false, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second), "$startId/$lastDbId")
}
return true
}
return false
}
override fun confirmLastProfileStore(lastSynced: Long) { override fun confirmLastProfileStore(lastSynced: Long) {
sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced) sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced)
} }
@ -561,9 +650,9 @@ class DataSyncSelectorImplementation @Inject constructor(
val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0) val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0)
val lastChange = sp.getLong(R.string.key_local_profile_last_change, 0) val lastChange = sp.getLong(R.string.key_local_profile_last_change, 0)
if (lastChange == 0L) return if (lastChange == 0L) return
localProfilePlugin.createProfileStore() if (lastChange > lastSync) {
val profileJson = localProfilePlugin.profile?.data ?: return val profileJson = localProfilePlugin.profile?.data ?: return
if (lastChange > lastSync)
nsClientPlugin.nsClientService?.dbAdd("profile", profileJson, DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "") nsClientPlugin.nsClientService?.dbAdd("profile", profileJson, DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "")
} }
} }
}

View file

@ -228,6 +228,25 @@ class NSClientAddAckWorker(
dataSyncSelector.processChangedProfileSwitchesCompat() dataSyncSelector.processChangedProfileSwitchesCompat()
} }
is PairEffectiveProfileSwitch -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdEffectiveProfileSwitchTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of EffectiveProfileSwitch failed", error)
ret = Result.failure((workDataOf("Error" to error.toString())))
}
.doOnSuccess {
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
aapsLogger.debug(LTag.DATABASE, "Updated ns id of EffectiveProfileSwitch " + pair.value)
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked EffectiveProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedEffectiveProfileSwitchesCompat()
}
is DeviceStatus -> { is DeviceStatus -> {
val deviceStatus = ack.originalObject val deviceStatus = ack.originalObject
deviceStatus.interfaceIDs.nightscoutId = ack.id deviceStatus.interfaceIDs.nightscoutId = ack.id
@ -251,6 +270,26 @@ class NSClientAddAckWorker(
dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync) dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync)
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id)) rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id))
} }
is PairOfflineEvent -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdOfflineEventTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of OfflineEvent failed", error)
ret = Result.failure((workDataOf("Error" to error.toString())))
}
.doOnSuccess {
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
aapsLogger.debug(LTag.DATABASE, "Updated ns id of OfflineEvent " + pair.value)
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedOfflineEventsCompat()
}
} }
return ret return ret
} }

View file

@ -26,7 +26,6 @@ import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.JsonHelper.safeGetLong import info.nightscout.androidaps.utils.JsonHelper.safeGetLong
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -186,6 +185,32 @@ class NSClientAddUpdateWorker(
} }
} ?: aapsLogger.error("Error parsing TT json $json") } ?: aapsLogger.error("Error parsing TT json $json")
} }
eventType == TherapyEvent.Type.NOTE.text && json.isEffectiveProfileSwitch() -> // replace this by new Type when available in NS
if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) {
effectiveProfileSwitchFromJson(json, dateUtil)?.let { effectiveProfileSwitch ->
repository.runTransactionForResult(SyncNsEffectiveProfileSwitchTransaction(effectiveProfileSwitch, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving EffectiveProfileSwitch", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.inserted.forEach {
uel.log(Action.PROFILE_SWITCH, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp))
aapsLogger.debug(LTag.DATABASE, "Inserted EffectiveProfileSwitch $it")
}
result.invalidated.forEach {
uel.log(Action.PROFILE_SWITCH_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp))
aapsLogger.debug(LTag.DATABASE, "Invalidated EffectiveProfileSwitch $it")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId EffectiveProfileSwitch $it")
}
}
} ?: aapsLogger.error("Error parsing EffectiveProfileSwitch json $json")
}
eventType == TherapyEvent.Type.CANNULA_CHANGE.text || eventType == TherapyEvent.Type.CANNULA_CHANGE.text ||
eventType == TherapyEvent.Type.INSULIN_CHANGE.text || eventType == TherapyEvent.Type.INSULIN_CHANGE.text ||
eventType == TherapyEvent.Type.SENSOR_CHANGE.text || eventType == TherapyEvent.Type.SENSOR_CHANGE.text ||
@ -194,7 +219,7 @@ class NSClientAddUpdateWorker(
eventType == TherapyEvent.Type.ANNOUNCEMENT.text || eventType == TherapyEvent.Type.ANNOUNCEMENT.text ||
eventType == TherapyEvent.Type.QUESTION.text || eventType == TherapyEvent.Type.QUESTION.text ||
eventType == TherapyEvent.Type.EXERCISE.text || eventType == TherapyEvent.Type.EXERCISE.text ||
eventType == TherapyEvent.Type.APS_OFFLINE.text || eventType == TherapyEvent.Type.NOTE.text ||
eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text -> eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text ->
if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) { if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) {
therapyEventFromJson(json)?.let { therapyEvent -> therapyEventFromJson(json)?.let { therapyEvent ->
@ -341,6 +366,43 @@ class NSClientAddUpdateWorker(
} }
} ?: aapsLogger.error("Error parsing ProfileSwitch json $json") } ?: aapsLogger.error("Error parsing ProfileSwitch json $json")
} }
eventType == TherapyEvent.Type.APS_OFFLINE.text ->
if (sp.getBoolean(R.string.key_ns_receive_offline_event, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) {
offlineEventFromJson(json)?.let { offlineEvent ->
repository.runTransactionForResult(SyncNsOfflineEventTransaction(offlineEvent, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.inserted.forEach { oe ->
uel.log(Action.LOOP_CHANGE, Sources.NSClient,
ValueWithUnit.OfflineEventReason(oe.reason),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $oe")
}
result.invalidated.forEach { oe ->
uel.log(Action.LOOP_REMOVED, Sources.NSClient,
ValueWithUnit.OfflineEventReason(oe.reason),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Invalidated OfflineEvent $oe")
}
result.ended.forEach { oe ->
uel.log(Action.LOOP_CHANGE, Sources.NSClient,
ValueWithUnit.OfflineEventReason(oe.reason),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $oe")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId OfflineEvent $it")
}
}
} ?: aapsLogger.error("Error parsing OfflineEvent json $json")
}
} }
if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT)
if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) { if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) {

View file

@ -34,7 +34,7 @@ class NSClientMbgWorker(
var ret = Result.success() var ret = Result.success()
val acceptNSData = sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT val acceptNSData = sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT
if (!acceptNSData) return ret if (!acceptNSData) return Result.success(workDataOf("Result" to "Sync not enabled"))
val mbgArray = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1)) val mbgArray = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data")) ?: return Result.failure(workDataOf("Error" to "missing input data"))

View file

@ -125,6 +125,24 @@ class NSClientUpdateRemoveAckWorker(
dataSyncSelector.processChangedProfileSwitchesCompat() dataSyncSelector.processChangedProfileSwitchesCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairEffectiveProfileSwitch -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked EffectiveProfileSwitch " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedEffectiveProfileSwitchesCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairOfflineEvent -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked OfflineEvent" + ack._id))
// Send new if waiting
dataSyncSelector.processChangedOfflineEventsCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
} }
return ret return ret
} }

View file

@ -16,8 +16,8 @@ class NSAddAck(
) : Event(), Ack { ) : Event(), Ack {
var id: String? = null var id: String? = null
@JvmField var nsClientID: String? = null var nsClientID: String? = null
@JvmField var json: JSONObject? = null var json: JSONObject? = null
override fun call(vararg args: Any) { override fun call(vararg args: Any) {
// Regular response // Regular response
try { try {

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.general.nsclient.data
import android.content.Context import android.content.Context
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
@ -113,6 +114,7 @@ import javax.inject.Singleton
} }
*/ */
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
@OpenForTesting
@Singleton @Singleton
class NSSettingsStatus @Inject constructor( class NSSettingsStatus @Inject constructor(
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.general.nsclient.services
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ResolveInfo
import android.os.* import android.os.*
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import com.google.common.base.Charsets import com.google.common.base.Charsets
@ -16,7 +17,6 @@ import info.nightscout.androidaps.events.EventConfigBuilderChange
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.DataSyncSelector import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
@ -71,7 +71,6 @@ class NSClientService : DaggerService() {
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var nsSettingsStatus: NSSettingsStatus @Inject lateinit var nsSettingsStatus: NSSettingsStatus
@Inject lateinit var nsDeviceStatus: NSDeviceStatus @Inject lateinit var nsDeviceStatus: NSDeviceStatus
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@ -113,7 +112,6 @@ class NSClientService : DaggerService() {
var latestDateInReceivedData: Long = 0 var latestDateInReceivedData: Long = 0
@SuppressLint("WakelockTimeout") @SuppressLint("WakelockTimeout")
@kotlin.ExperimentalStdlibApi
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:NSClientService") wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:NSClientService")
@ -199,7 +197,7 @@ class NSClientService : DaggerService() {
.build()) .build())
} }
fun processAuthAck(ack: NSAuthAck) { private fun processAuthAck(ack: NSAuthAck) {
var connectionStatus = "Authenticated (" var connectionStatus = "Authenticated ("
if (ack.read) connectionStatus += "R" if (ack.read) connectionStatus += "R"
if (ack.write) connectionStatus += "W" if (ack.write) connectionStatus += "W"
@ -237,7 +235,6 @@ class NSClientService : DaggerService() {
return START_STICKY return START_STICKY
} }
@kotlin.ExperimentalStdlibApi
fun initialize() { fun initialize() {
dataCounter = 0 dataCounter = 0
readPreferences() readPreferences()
@ -488,7 +485,7 @@ class NSClientService : DaggerService() {
val intent = Intent(Intents.ACTION_NEW_PROFILE) val intent = Intent(Intents.ACTION_NEW_PROFILE)
intent.putExtras(bundle) intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
sendBroadcast(intent) broadcast(intent)
} }
} }
} }
@ -516,7 +513,7 @@ class NSClientService : DaggerService() {
val intent = Intent(Intents.ACTION_REMOVED_TREATMENT) val intent = Intent(Intents.ACTION_REMOVED_TREATMENT)
intent.putExtras(bundle) intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
sendBroadcast(intent) broadcast(intent)
} }
} }
if (addedOrUpdatedTreatments.length() > 0) { if (addedOrUpdatedTreatments.length() > 0) {
@ -533,7 +530,7 @@ class NSClientService : DaggerService() {
val intent = Intent(Intents.ACTION_CHANGED_TREATMENT) val intent = Intent(Intents.ACTION_CHANGED_TREATMENT)
intent.putExtras(bundle) intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
sendBroadcast(intent) broadcast(intent)
} }
} }
} }
@ -581,7 +578,7 @@ class NSClientService : DaggerService() {
val intent = Intent(Intents.ACTION_NEW_SGV) val intent = Intent(Intents.ACTION_NEW_SGV)
intent.putExtras(bundle) intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
sendBroadcast(intent) broadcast(intent)
} }
} }
} }
@ -652,7 +649,6 @@ class NSClientService : DaggerService() {
} }
} }
@kotlin.ExperimentalStdlibApi
fun restart() { fun restart() {
destroy() destroy()
initialize() initialize()
@ -721,6 +717,16 @@ class NSClientService : DaggerService() {
return ret return ret
} }
private fun broadcast(intent: Intent) {
val receivers: List<ResolveInfo> = packageManager.queryBroadcastReceivers(intent, 0)
for (resolveInfo in receivers)
resolveInfo.activityInfo.packageName?.let {
intent.setPackage(it)
sendBroadcast(intent)
aapsLogger.debug(LTag.CORE, "Sending broadcast " + intent.action + " to: " + it)
}
}
init { init {
if (handler == null) { if (handler == null) {
val handlerThread = HandlerThread(NSClientService::class.java.simpleName + "Handler") val handlerThread = HandlerThread(NSClientService::class.java.simpleName + "Handler")

View file

@ -1,68 +0,0 @@
package info.nightscout.androidaps.plugins.general.openhumans
import android.app.Notification
import android.content.Context
import android.net.wifi.WifiManager
import androidx.core.app.NotificationCompat
import androidx.work.ForegroundInfo
import androidx.work.RxWorker
import androidx.work.WorkerParameters
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader.Companion.NOTIFICATION_CHANNEL
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader.Companion.UPLOAD_NOTIFICATION_ID
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Single
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class OHUploadWorker(context: Context, workerParameters: WorkerParameters)
: RxWorker(context, workerParameters) {
@Inject
lateinit var sp: SP
@Inject
lateinit var openHumansUploader: OpenHumansUploader
@Inject
lateinit var resourceHelper: ResourceHelper
@kotlin.ExperimentalStdlibApi
override fun createWork(): Single<Result> = Single.defer {
// Here we inject every time we create work
// We could build our own WorkerFactory with dagger but this will create conflicts with other Workers
// (see https://medium.com/wonderquill/how-to-pass-custom-parameters-to-rxworker-worker-using-dagger-2-f4cfbc9892ba)
// This class will be replaced with new DB
(applicationContext as MainApp).androidInjector().inject(this)
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as? WifiManager
val wifiOnly = sp.getBoolean("key_oh_wifi_only", true)
val isConnectedToWifi = wifiManager?.isWifiEnabled ?: false && wifiManager?.connectionInfo?.networkId != -1
if (!wifiOnly || (wifiOnly && isConnectedToWifi)) {
setForegroundAsync(createForegroundInfo())
openHumansUploader.uploadDataSegmentally()
.andThen(Single.just(Result.success()))
.onErrorResumeNext { Single.just(Result.retry()) }
} else {
Single.just(Result.retry())
}
}
private fun createForegroundInfo(): ForegroundInfo {
val title = resourceHelper.gs(info.nightscout.androidaps.R.string.open_humans)
val notification: Notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL)
.setContentTitle(title)
.setTicker(title)
.setContentText(resourceHelper.gs(info.nightscout.androidaps.R.string.your_phone_is_upload_data))
.setSmallIcon(info.nightscout.androidaps.R.drawable.notif_icon)
.setOngoing(true)
.setProgress(0, 0 , true)
.build()
return ForegroundInfo(UPLOAD_NOTIFICATION_ID, notification)
}
}

View file

@ -1,191 +0,0 @@
package info.nightscout.androidaps.plugins.general.openhumans
import android.annotation.SuppressLint
import android.util.Base64
import io.reactivex.Completable
import io.reactivex.Single
import io.reactivex.disposables.Disposables
import okhttp3.*
import okio.BufferedSink
import org.json.JSONArray
import org.json.JSONObject
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*
class OpenHumansAPI(
private val baseUrl: String,
clientId: String,
clientSecret: String,
private val redirectUri: String
) {
private val authHeader = "Basic " + Base64.encodeToString("$clientId:$clientSecret".toByteArray(), Base64.NO_WRAP)
private val client = OkHttpClient()
fun exchangeAuthToken(code: String): Single<OAuthTokens> = sendTokenRequest(FormBody.Builder()
.add("grant_type", "authorization_code")
.add("redirect_uri", redirectUri)
.add("code", code)
.build())
fun refreshAccessToken(refreshToken: String): Single<OAuthTokens> = sendTokenRequest(FormBody.Builder()
.add("grant_type", "refresh_token")
.add("redirect_uri", redirectUri)
.add("refresh_token", refreshToken)
.build())
private fun sendTokenRequest(body: FormBody) = Request.Builder()
.url("$baseUrl/oauth2/token/")
.addHeader("Authorization", authHeader)
.post(body)
.build()
.toSingle()
.map { response ->
response.use { _ ->
val responseBody = response.body
val jsonObject = responseBody?.let { JSONObject(it.string()) }
if (!response.isSuccessful) throw OHHttpException(response.code, response.message, jsonObject?.getString("error"))
if (jsonObject == null) throw OHHttpException(response.code, response.message, "No body")
if (!jsonObject.has("expires_in")) throw OHMissingFieldException("expires_in")
OAuthTokens(
accessToken = jsonObject.getString("access_token")
?: throw OHMissingFieldException("access_token"),
refreshToken = jsonObject.getString("refresh_token")
?: throw OHMissingFieldException("refresh_token"),
expiresAt = response.sentRequestAtMillis + jsonObject.getInt("expires_in") * 1000L
)
}
}
fun getProjectMemberId(accessToken: String): Single<String> = Request.Builder()
.url("$baseUrl/api/direct-sharing/project/exchange-member/?access_token=$accessToken")
.get()
.build()
.toSingle()
.map {
it.jsonBody.getString("project_member_id")
?: throw OHMissingFieldException("project_member_id")
}
fun prepareFileUpload(accessToken: String, fileName: String, metadata: FileMetadata): Single<PreparedUpload> = Request.Builder()
.url("$baseUrl/api/direct-sharing/project/files/upload/direct/?access_token=$accessToken")
.post(FormBody.Builder()
.add("filename", fileName)
.add("metadata", metadata.toJSON().toString())
.build())
.build()
.toSingle()
.map {
val json = it.jsonBody
PreparedUpload(
fileId = json.getString("id") ?: throw OHMissingFieldException("id"),
uploadURL = json.getString("url") ?: throw OHMissingFieldException("url")
)
}
fun uploadFile(url: String, content: ByteArray): Completable = Request.Builder()
.url(url)
.put(object : RequestBody() {
override fun contentType(): MediaType? = null
override fun contentLength() = content.size.toLong()
override fun writeTo(sink: BufferedSink) {
sink.write(content)
}
})
.build()
.toSingle()
.doOnSuccess { response ->
response.use { _ ->
if (!response.isSuccessful) throw OHHttpException(response.code, response.message, null)
}
}
.ignoreElement()
fun completeFileUpload(accessToken: String, fileId: String): Completable = Request.Builder()
.url("$baseUrl/api/direct-sharing/project/files/upload/complete/?access_token=$accessToken")
.post(FormBody.Builder()
.add("file_id", fileId)
.build())
.build()
.toSingle()
.doOnSuccess { it.jsonBody }
.ignoreElement()
private fun Request.toSingle() = Single.create<Response> {
val call = client.newCall(this)
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
it.tryOnError(e)
}
override fun onResponse(call: Call, response: Response) {
it.onSuccess(response)
}
})
it.setDisposable(Disposables.fromRunnable { call.cancel() })
}
private val Response.jsonBody
get() = use { _ ->
val jsonObject = body?.let { JSONObject(it.string()) }
?: throw OHHttpException(code, message, null)
if (!isSuccessful) throw OHHttpException(code, message, jsonObject.getString("detail"))
jsonObject
}
data class OAuthTokens(
val accessToken: String,
val refreshToken: String,
val expiresAt: Long
)
data class FileMetadata(
val tags: List<String>,
val description: String,
val md5: String? = null,
val creationDate: Long? = null,
val startDate: Long? = null,
val endDate: Long? = null
) {
fun toJSON(): JSONObject {
val jsonObject = JSONObject()
jsonObject.put("tags", JSONArray().apply { tags.forEach { put(it) } })
jsonObject.put("description", description)
jsonObject.put("md5", md5)
creationDate?.let { jsonObject.put("creation_date", iso8601DateFormatter.format(Date(it))) }
startDate?.let { jsonObject.put("start_date", iso8601DateFormatter.format(Date(it))) }
endDate?.let { jsonObject.put("end_date", iso8601DateFormatter.format(Date(it))) }
return jsonObject
}
}
data class PreparedUpload(
val fileId: String,
val uploadURL: String
)
data class OHHttpException(
val code: Int,
val meaning: String,
val detail: String?
) : RuntimeException() {
override val message: String get() = toString()
}
data class OHMissingFieldException(
val name: String
) : RuntimeException() {
override val message: String get() = toString()
}
companion object {
@SuppressLint("SimpleDateFormat")
private val iso8601DateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
}
}

View file

@ -1,116 +0,0 @@
package info.nightscout.androidaps.plugins.general.openhumans
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.work.WorkManager
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.BackpressureStrategy
import io.reactivex.Single
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class OpenHumansFragment : DaggerFragment() {
private var viewsCreated = false
private var login: Button? = null
private var logout: Button? = null
private var memberId: TextView? = null
private var queueSize: TextView? = null
private var workerState: TextView? = null
private var queueSizeValue = 0L
private val compositeDisposable = CompositeDisposable()
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var openHumansUploader: OpenHumansUploader
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var databaseHelper: DatabaseHelperInterface
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
compositeDisposable += Single.fromCallable { databaseHelper.getOHQueueSize() }
.subscribeOn(aapsSchedulers.io)
.repeatWhen {
rxBus.toObservable(UpdateViewEvent::class.java)
.cast(Any::class.java)
.mergeWith(rxBus.toObservable(UpdateQueueEvent::class.java)
.throttleLatest(5, TimeUnit.SECONDS))
.toFlowable(BackpressureStrategy.LATEST)
}
.observeOn(aapsSchedulers.main)
.subscribe({
queueSizeValue = it
updateGUI()
}, {})
context?.applicationContext?.let { appContext ->
WorkManager.getInstance(appContext).getWorkInfosForUniqueWorkLiveData(OpenHumansUploader.WORK_NAME).observe(this, {
val workInfo = it.lastOrNull()
if (workInfo == null) {
workerState?.visibility = View.GONE
} else {
workerState?.visibility = View.VISIBLE
workerState?.text = getString(R.string.worker_state, workInfo.state.toString())
}
})
}
}
override fun onDestroy() {
compositeDisposable.clear()
super.onDestroy()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val view = inflater.inflate(R.layout.fragment_open_humans, container, false)
login = view.findViewById(R.id.login)
logout = view.findViewById(R.id.logout)
memberId = view.findViewById(R.id.member_id)
queueSize = view.findViewById(R.id.queue_size)
workerState = view.findViewById(R.id.worker_state)
login!!.setOnClickListener { startActivity(Intent(context, OpenHumansLoginActivity::class.java)) }
logout!!.setOnClickListener {
activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.oh_logout_confirmation)) { openHumansUploader.logout() } }
}
viewsCreated = true
updateGUI()
return view
}
override fun onDestroyView() {
viewsCreated = false
login = null
logout = null
memberId = null
queueSize = null
super.onDestroyView()
}
fun updateGUI() {
if (viewsCreated) {
queueSize!!.text = getString(R.string.queue_size, queueSizeValue)
val projectMemberId = openHumansUploader.projectMemberId
memberId!!.text = getString(R.string.project_member_id, projectMemberId
?: getString(R.string.not_logged_in))
login!!.visibility = if (projectMemberId == null) View.VISIBLE else View.GONE
logout!!.visibility = if (projectMemberId != null) View.VISIBLE else View.GONE
}
}
object UpdateViewEvent : Event()
object UpdateQueueEvent : Event()
}

View file

@ -1,138 +0,0 @@
package info.nightscout.androidaps.plugins.general.openhumans
import android.app.Dialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.Button
import android.widget.CheckBox
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.browser.customtabs.CustomTabsIntent
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.Disposable
import javax.inject.Inject
class OpenHumansLoginActivity : NoSplashAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_open_humans_login)
val button = findViewById<Button>(R.id.button)
val checkbox = findViewById<CheckBox>(R.id.checkbox)
button.setOnClickListener {
if (checkbox.isChecked) {
CustomTabsIntent.Builder().build().launchUrl(this, Uri.parse(OpenHumansUploader.AUTH_URL))
} else {
Toast.makeText(this, R.string.you_need_to_accept_the_of_use_first, Toast.LENGTH_SHORT).show()
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
val code = intent.data?.getQueryParameter("code")
if (supportFragmentManager.fragments.size == 0 && code != null) {
ExchangeAuthTokenDialog(code).show(supportFragmentManager, "ExchangeAuthTokenDialog")
}
}
class ExchangeAuthTokenDialog : DaggerDialogFragment() {
@Inject lateinit var openHumansUploader: OpenHumansUploader
@Inject lateinit var aapsSchedulers: AapsSchedulers
private var disposable: Disposable? = null
init {
isCancelable = false
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return AlertDialog.Builder(requireActivity())
.setTitle(R.string.completing_login)
.setMessage(R.string.please_wait)
.create()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
disposable = openHumansUploader.login(arguments?.getString("authToken")!!).subscribeOn(aapsSchedulers.io).subscribe({
dismiss()
SetupDoneDialog().show(parentFragmentManager, "SetupDoneDialog")
}, {
dismiss()
ErrorDialog(it.message).show(parentFragmentManager, "ErrorDialog")
})
}
override fun onDestroy() {
disposable?.dispose()
super.onDestroy()
}
companion object {
operator fun invoke(authToken: String): ExchangeAuthTokenDialog {
val dialog = ExchangeAuthTokenDialog()
val args = Bundle()
args.putString("authToken", authToken)
dialog.arguments = args
return dialog
}
}
}
class ErrorDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val message = arguments?.getString("message")
val shownMessage = if (message == null) getString(R.string.there_was_an_error)
else "${getString(R.string.there_was_an_error)}\n\n$message"
return AlertDialog.Builder(requireActivity())
.setTitle(R.string.error)
.setMessage(shownMessage)
.setPositiveButton(R.string.close, null)
.create()
}
companion object {
operator fun invoke(message: String?): ErrorDialog {
val dialog = ErrorDialog()
val args = Bundle()
args.putString("message", message)
dialog.arguments = args
return dialog
}
}
}
class SetupDoneDialog : DialogFragment() {
init {
isCancelable = false
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return AlertDialog.Builder(requireActivity())
.setTitle(R.string.successfully_logged_in)
.setMessage(R.string.setup_will_continue_in_background)
.setCancelable(false)
.setPositiveButton(R.string.close) { _, _ ->
requireActivity().run {
setResult(FragmentActivity.RESULT_OK)
requireActivity().finish()
}
}
.create()
}
}
}

View file

@ -1,652 +0,0 @@
package info.nightscout.androidaps.plugins.general.openhumans
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.PowerManager
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.work.*
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.db.*
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.extensions.toConstant
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.io.ByteArrayOutputStream
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class OpenHumansUploader @Inject constructor(
injector: HasAndroidInjector,
resourceHelper: ResourceHelper,
aapsLogger: AAPSLogger,
private val aapsSchedulers: AapsSchedulers,
private val sp: SP,
private val rxBus: RxBusWrapper,
private val context: Context,
private val databaseHelper: DatabaseHelperInterface,
val repository: AppRepository
) : PluginBase(
PluginDescription()
.mainType(PluginType.GENERAL)
.pluginIcon(R.drawable.open_humans_white)
.pluginName(R.string.open_humans)
.shortName(R.string.open_humans_short)
.description(R.string.donate_your_data_to_science)
.fragmentClass(OpenHumansFragment::class.qualifiedName)
.preferencesId(R.xml.pref_openhumans),
aapsLogger, resourceHelper, injector) {
companion object {
private const val OPEN_HUMANS_URL = "https://www.openhumans.org"
private const val CLIENT_ID = "oie6DvnaEOagTxSoD6BukkLPwDhVr6cMlN74Ihz1"
private const val CLIENT_SECRET = "jR0N8pkH1jOwtozHc7CsB1UPcJzFN95ldHcK4VGYIApecr8zGJox0v06xLwPLMASScngT12aIaIHXAVCJeKquEXAWG1XekZdbubSpccgNiQBmuVmIF8nc1xSKSNJltCf"
private const val REDIRECT_URL = "androidaps://setup-openhumans"
const val AUTH_URL = "https://www.openhumans.org/direct-sharing/projects/oauth2/authorize/?client_id=$CLIENT_ID&response_type=code"
const val WORK_NAME = "Open Humans"
const val NOTIFICATION_CHANNEL = "OpenHumans"
private const val COPY_NOTIFICATION_ID = 3122
private const val FAILURE_NOTIFICATION_ID = 3123
private const val SUCCESS_NOTIFICATION_ID = 3124
private const val SIGNED_OUT_NOTIFICATION_ID = 3125
const val UPLOAD_NOTIFICATION_ID = 3126
private const val UPLOAD_SEGMENT_SIZE = 10000L
}
private val openHumansAPI = OpenHumansAPI(OPEN_HUMANS_URL, CLIENT_ID, CLIENT_SECRET, REDIRECT_URL)
@Suppress("PrivatePropertyName")
private val FILE_NAME_DATE_FORMAT = SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.US).apply { timeZone = TimeZone.getTimeZone("UTC") }
private var isSetup
get() = sp.getBoolean("openhumans_is_setup", false)
set(value) = sp.putBoolean("openhumans_is_setup", value)
private var oAuthTokens: OpenHumansAPI.OAuthTokens?
get() {
return if (sp.contains("openhumans_access_token") && sp.contains("openhumans_refresh_token") && sp.contains("openhumans_expires_at")) {
OpenHumansAPI.OAuthTokens(
accessToken = sp.getStringOrNull("openhumans_access_token", null)!!,
refreshToken = sp.getStringOrNull("openhumans_refresh_token", null)!!,
expiresAt = sp.getLong("openhumans_expires_at", 0)
)
} else {
null
}
}
set(value) {
if (value != null) {
sp.putString("openhumans_access_token", value.accessToken)
sp.putString("openhumans_refresh_token", value.refreshToken)
sp.putLong("openhumans_expires_at", value.expiresAt)
} else {
sp.remove("openhumans_access_token")
sp.remove("openhumans_refresh_token")
sp.remove("openhumans_expires_at")
sp.remove("openhumans_expires_at")
}
}
var projectMemberId: String?
get() = sp.getStringOrNull("openhumans_project_member_id", null)
private set(value) {
if (value == null) sp.remove("openhumans_project_member_id")
else sp.putString("openhumans_project_member_id", value)
}
private var uploadCounter: Int
get() = sp.getInt("openhumans_counter", 1)
set(value) = sp.putInt("openhumans_counter", value)
private val appId: UUID
get() {
val id = sp.getStringOrNull("openhumans_appid", null)
return if (id == null) {
val generated = UUID.randomUUID()
sp.putString("openhumans_appid", generated.toString())
generated
} else {
UUID.fromString(id)
}
}
private var copyDisposable: Disposable? = null
private val wakeLock = (context.getSystemService(Context.POWER_SERVICE) as PowerManager)
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS::OpenHumans")
private val preferenceChangeDisposable = CompositeDisposable()
override fun onStart() {
super.onStart()
setupNotificationChannel()
if (isSetup) scheduleWorker(false)
preferenceChangeDisposable += rxBus.toObservable(EventPreferenceChange::class.java).subscribe {
onSharedPreferenceChanged(it)
}
}
override fun onStop() {
copyDisposable?.dispose()
cancelWorker()
preferenceChangeDisposable.clear()
super.onStop()
}
fun enqueueBGReading(glucoseValue: GlucoseValue?) = glucoseValue?.let {
insertQueueItem("BgReadings") {
put("date", glucoseValue.timestamp)
put("isValid", glucoseValue.isValid)
put("value", glucoseValue.value)
put("direction", glucoseValue.trendArrow)
put("raw", glucoseValue.raw)
put("source", glucoseValue.sourceSensor)
put("nsId", glucoseValue.interfaceIDs.nightscoutId)
}
}
// @JvmOverloads
// fun enqueueTreatment(treatment: Treatment?, deleted: Boolean = false) = treatment?.let {
// insertQueueItem("Treatments") {
// put("date", treatment.date)
// put("isValid", treatment.isValid)
// put("source", treatment.source)
// put("nsId", treatment._id)
// put("boluscalc", treatment.boluscalc)
// put("carbs", treatment.carbs)
// put("dia", treatment.dia)
// put("insulin", treatment.insulin)
// put("insulinInterfaceID", treatment.insulinInterfaceID)
// put("isSMB", treatment.isSMB)
// put("mealBolus", treatment.mealBolus)
// put("bolusCalcJson", treatment.getBoluscalc())
// put("isDeletion", deleted)
// }
// }
@JvmOverloads
fun enqueueTherapyEvent(therapyEvent: TherapyEvent, deleted: Boolean = false) = insertQueueItem("TherapyEvents") {
put("date", therapyEvent.timestamp)
put("isValid", therapyEvent.isValid)
put("nsId", therapyEvent.interfaceIDs.nightscoutId)
put("eventType", therapyEvent.type.text)
put("glucose", therapyEvent.glucose)
put("units", therapyEvent.glucoseUnit.toConstant())
put("glucoseType", therapyEvent.glucoseType?.text)
put("duration", therapyEvent.duration)
put("isDeletion", deleted)
}
// @JvmOverloads
// fun enqueueExtendedBolus(extendedBolus: ExtendedBolus, deleted: Boolean = false) = insertQueueItem("ExtendedBoluses") {
// put("date", extendedBolus.date)
// put("isValid", extendedBolus.isValid)
// put("source", extendedBolus.source)
// put("nsId", extendedBolus._id)
// put("pumpId", extendedBolus.pumpId)
// put("insulin", extendedBolus.insulin)
// put("durationInMinutes", extendedBolus.durationInMinutes)
// put("isDeletion", deleted)
// }
// @JvmOverloads
// fun enqueueProfileSwitch(profileSwitch: ProfileSwitch, deleted: Boolean = false) = insertQueueItem("ProfileSwitches") {
// put("date", profileSwitch.date)
// put("isValid", profileSwitch.isValid)
// put("source", profileSwitch.source)
// put("nsId", profileSwitch._id)
// put("isCPP", profileSwitch.isCPP)
// put("timeshift", profileSwitch.timeshift)
// put("percentage", profileSwitch.percentage)
// put("profile", JSONObject(profileSwitch.profileJson))
// put("profilePlugin", profileSwitch.profilePlugin)
// put("durationInMinutes", profileSwitch.durationInMinutes)
// put("isDeletion", deleted)
// }
// fun enqueueTotalDailyDose(tdd: TDD) = insertQueueItem("TotalDailyDoses") {
// put("double", tdd.date)
// put("double", tdd.bolus)
// put("double", tdd.basal)
// put("double", tdd.total)
// }
// @JvmOverloads
// fun enqueueTemporaryBasal(temporaryBasal: TemporaryBasal?, deleted: Boolean = false) = temporaryBasal?.let {
// insertQueueItem("TemporaryBasals") {
// put("date", temporaryBasal.date)
// put("isValid", temporaryBasal.isValid)
// put("source", temporaryBasal.source)
// put("nsId", temporaryBasal._id)
// put("pumpId", temporaryBasal.pumpId)
// put("durationInMinutes", temporaryBasal.durationInMinutes)
// put("durationInMinutes", temporaryBasal.durationInMinutes)
// put("isAbsolute", temporaryBasal.isAbsolute)
// put("percentRate", temporaryBasal.percentRate)
// put("absoluteRate", temporaryBasal.absoluteRate)
// put("isDeletion", deleted)
// }
// }
@JvmOverloads
fun enqueueTempTarget(tempTarget: TemporaryTarget?, deleted: Boolean = false) = tempTarget?.let {
insertQueueItem("TempTargets") {
put("date", tempTarget.timestamp)
put("isValid", tempTarget.isValid)
put("nsId", tempTarget.interfaceIDs_backing?.nightscoutId)
put("low", tempTarget.lowTarget)
put("high", tempTarget.highTarget)
put("reason", tempTarget.reason)
put("durationInMinutes", tempTarget.duration)
put("isDeletion", deleted)
}
}
fun enqueueSMBData(profile: JSONObject?, glucoseStatus: JSONObject?, iobData: JSONArray?, mealData: JSONObject?, currentTemp: JSONObject?, autosensData: JSONObject?, smbAllowed: Boolean, smbAlwaysAllowed: Boolean, result: JSONObject?) = insertQueueItem("APSData") {
put("algorithm", "SMB")
put("profile", profile)
put("glucoseStatus", glucoseStatus)
put("iobData", iobData)
put("mealData", mealData)
put("currentTemp", currentTemp)
put("autosensData", autosensData)
put("smbAllowed", smbAllowed)
put("smbAlwaysAllowed", smbAlwaysAllowed)
put("result", result)
}
fun enqueueAMAData(profile: JSONObject?, glucoseStatus: JSONObject?, iobData: JSONArray?, mealData: JSONObject?, currentTemp: JSONObject?, autosensData: JSONObject?, result: JSONObject?) = insertQueueItem("APSData") {
put("algorithm", "AMA")
put("profile", profile)
put("glucoseStatus", glucoseStatus)
put("iobData", iobData)
put("mealData", mealData)
put("currentTemp", currentTemp)
put("autosensData", autosensData)
put("result", result)
}
private fun insertQueueItem(file: String, structureVersion: Int = 1, generator: JSONObject.() -> Unit) {
if (oAuthTokens != null && this.isEnabled(PluginType.GENERAL)) {
try {
val jsonObject = JSONObject()
jsonObject.put("structureVersion", structureVersion)
jsonObject.put("queuedOn", System.currentTimeMillis())
generator(jsonObject)
val queueItem = OHQueueItem(
file = file,
content = jsonObject.toString()
)
databaseHelper.createOrUpdate(queueItem)
rxBus.send(OpenHumansFragment.UpdateQueueEvent)
} catch (e: JSONException) {
e.printStackTrace()
}
}
}
fun login(authCode: String): Completable =
openHumansAPI.exchangeAuthToken(authCode)
.doOnSuccess {
oAuthTokens = it
}
.flatMap { openHumansAPI.getProjectMemberId(it.accessToken) }
.doOnSuccess {
projectMemberId = it
copyExistingDataToQueue()
rxBus.send(OpenHumansFragment.UpdateViewEvent)
}
.doOnError {
aapsLogger.error("Failed to login to Open Humans", it)
}
.ignoreElement()
fun logout() {
cancelWorker()
copyDisposable?.dispose()
isSetup = false
oAuthTokens = null
projectMemberId = null
databaseHelper.clearOpenHumansQueue()
rxBus.send(OpenHumansFragment.UpdateViewEvent)
}
private fun copyExistingDataToQueue() {
copyDisposable?.dispose()
var currentProgress = 0L
var maxProgress = 0L
val increaseCounter = {
currentProgress++
//Updating the notification for every item drastically slows down the operation
if (currentProgress % 1000L == 0L) showOngoingNotification(maxProgress, currentProgress)
}
copyDisposable = Completable.fromCallable { databaseHelper.clearOpenHumansQueue() }
// .andThen(Single.defer { Single.just(databaseHelper.getCountOfAllRows() + treatmentsPlugin.service.count()) })
// .doOnSuccess { maxProgress = it }
// .flatMapObservable { Observable.defer { Observable.fromIterable(treatmentsPlugin.service.getTreatmentData()) } }
// .map { enqueueTreatment(it); increaseCounter() }
// .ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(repository.compatGetBgReadingsDataFromTime(0, true).blockingGet()) })
.map { enqueueBGReading(it); increaseCounter() }
.ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(repository.compatGetTherapyEventDataFromTime(0, true).blockingGet()) })
.map { enqueueTherapyEvent(it); increaseCounter() }
.ignoreElements()
// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllExtendedBoluses()) })
// .map { enqueueExtendedBolus(it); increaseCounter() }
// .ignoreElements()
// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllProfileSwitches()) })
// .map { enqueueProfileSwitch(it); increaseCounter() }
// .ignoreElements()
// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllTDDs()) })
// .map { enqueueTotalDailyDose(it); increaseCounter() }
// .ignoreElements()
// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllTemporaryBasals()) })
// .map { enqueueTemporaryBasal(it); increaseCounter() }
// .ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(repository.compatGetTemporaryTargetData().blockingGet()) })
.map { enqueueTempTarget(it); increaseCounter() }
.ignoreElements()
.doOnSubscribe {
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
showOngoingNotification()
}
.doOnComplete {
isSetup = true
scheduleWorker(false)
showSetupFinishedNotification()
}
.doOnError {
logout()
showSetupFailedNotification()
}
.doFinally {
copyDisposable = null
NotificationManagerCompat.from(context).cancel(COPY_NOTIFICATION_ID)
wakeLock.release()
}
.onErrorComplete()
.subscribeOn(aapsSchedulers.io)
.subscribe()
}
private fun showOngoingNotification(maxProgress: Long? = null, currentProgress: Long? = null) {
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
.setContentTitle(resourceHelper.gs(R.string.finishing_open_humans_setup))
.setContentText(resourceHelper.gs(R.string.this_may_take_a_while))
.setStyle(NotificationCompat.BigTextStyle())
.setProgress(maxProgress?.toInt() ?: 0, currentProgress?.toInt()
?: 0, maxProgress == null || currentProgress == null)
.setOngoing(true)
.setAutoCancel(false)
.setSmallIcon(R.drawable.notif_icon)
.build()
NotificationManagerCompat.from(context).notify(COPY_NOTIFICATION_ID, notification)
}
private fun showSetupFinishedNotification() {
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
.setContentTitle(resourceHelper.gs(R.string.setup_finished))
.setContentText(resourceHelper.gs(R.string.your_phone_will_upload_data))
.setStyle(NotificationCompat.BigTextStyle())
.setSmallIcon(R.drawable.notif_icon)
.build()
val notificationManager = NotificationManagerCompat.from(context)
notificationManager.notify(SUCCESS_NOTIFICATION_ID, notification)
}
private fun showSetupFailedNotification() {
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
.setContentTitle(resourceHelper.gs(R.string.setup_failed))
.setContentText(resourceHelper.gs(R.string.there_was_an_error))
.setStyle(NotificationCompat.BigTextStyle())
.setSmallIcon(R.drawable.notif_icon)
.build()
val notificationManager = NotificationManagerCompat.from(context)
notificationManager.notify(FAILURE_NOTIFICATION_ID, notification)
}
@kotlin.ExperimentalStdlibApi
fun uploadDataSegmentally(): Completable =
uploadData(UPLOAD_SEGMENT_SIZE)
.repeatUntil { databaseHelper.getOHQueueSize() == 0L }
.doOnSubscribe {
aapsLogger.info(LTag.OHUPLOADER, "Starting segmental upload")
}
.doOnComplete {
aapsLogger.info(LTag.OHUPLOADER, "Segmental upload successful")
}
.doOnError {
aapsLogger.error(LTag.OHUPLOADER, "Segmental upload exceptional", it)
}
@kotlin.ExperimentalStdlibApi
@Suppress("SameParameterValue")
private fun uploadData(maxEntries: Long): Completable = gatherData(maxEntries)
.flatMap { data -> refreshAccessTokensIfNeeded().map { accessToken -> accessToken to data } }
.flatMap { uploadFile(it.first, it.second).andThen(Single.just(it.second)) }
.flatMapCompletable {
if (it.highestQueueId != null) {
removeUploadedEntriesFromQueue(it.highestQueueId)
} else {
Completable.complete()
}
}
.doOnError {
if (it is OpenHumansAPI.OHHttpException && it.code == 401 && it.detail == "Invalid token.") {
handleSignOut()
}
aapsLogger.error("Error while uploading to Open Humans", it)
}
.doOnComplete {
aapsLogger.info(LTag.OHUPLOADER, "Upload successful")
rxBus.send(OpenHumansFragment.UpdateQueueEvent)
}
.doOnSubscribe {
aapsLogger.info(LTag.OHUPLOADER, "Starting upload")
}
private fun uploadFile(accessToken: String, uploadData: UploadData) = Completable.defer {
openHumansAPI.prepareFileUpload(accessToken, uploadData.fileName, uploadData.metadata)
.flatMap { openHumansAPI.uploadFile(it.uploadURL, uploadData.content).andThen(Single.just(it.fileId)) }
.flatMapCompletable { openHumansAPI.completeFileUpload(accessToken, it) }
}
private fun refreshAccessTokensIfNeeded() = Single.defer {
val oAuthTokens = this.oAuthTokens!!
if (oAuthTokens.expiresAt <= System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)) {
openHumansAPI.refreshAccessToken(oAuthTokens.refreshToken)
.doOnSuccess { this.oAuthTokens = it }
.map { it.accessToken }
} else {
Single.just(oAuthTokens.accessToken)
}
}
@kotlin.ExperimentalStdlibApi
private fun gatherData(maxEntries: Long) = Single.defer {
val items = databaseHelper.getAllOHQueueItems(maxEntries)
val baos = ByteArrayOutputStream()
val zos = ZipOutputStream(baos)
val tags = mutableListOf<String>()
items.groupBy { it.file }.forEach { entry ->
tags.add(entry.key)
val jsonArray = JSONArray()
entry.value.map { it.content }.forEach { jsonArray.put(JSONObject(it)) }
zos.writeFile("${entry.key}.json", jsonArray.toString().toByteArray())
}
val applicationInfo = JSONObject()
applicationInfo.put("versionName", BuildConfig.VERSION_NAME)
applicationInfo.put("versionCode", BuildConfig.VERSION_CODE)
val hasGitInfo = !BuildConfig.HEAD.endsWith("NoGitSystemAvailable", true)
val customRemote = !BuildConfig.REMOTE.equals("https://github.com/nightscout/AndroidAPS.git", true)
applicationInfo.put("hasGitInfo", hasGitInfo)
applicationInfo.put("customRemote", customRemote)
applicationInfo.put("applicationId", appId.toString())
zos.writeFile("ApplicationInfo.json", applicationInfo.toString().toByteArray())
tags.add("ApplicationInfo")
val preferences = JSONObject(sp.getAll().filterKeys { it.isAllowedKey() })
zos.writeFile("Preferences.json", preferences.toString().toByteArray())
tags.add("Preferences")
val deviceInfo = JSONObject()
deviceInfo.put("brand", Build.BRAND)
deviceInfo.put("device", Build.DEVICE)
deviceInfo.put("manufacturer", Build.MANUFACTURER)
deviceInfo.put("model", Build.MODEL)
deviceInfo.put("product", Build.PRODUCT)
zos.writeFile("DeviceInfo.json", deviceInfo.toString().toByteArray())
tags.add("DeviceInfo")
val displayMetrics = DisplayMetrics()
(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getMetrics(displayMetrics)
val displayInfo = JSONObject()
displayInfo.put("height", displayMetrics.heightPixels)
displayInfo.put("width", displayMetrics.widthPixels)
displayInfo.put("density", displayMetrics.density)
displayInfo.put("scaledDensity", displayMetrics.scaledDensity)
displayInfo.put("xdpi", displayMetrics.xdpi)
displayInfo.put("ydpi", displayMetrics.ydpi)
zos.writeFile("DisplayInfo.json", displayInfo.toString().toByteArray())
tags.add("DisplayInfo")
val uploadNumber = this.uploadCounter++
val uploadDate = Date()
val uploadInfo = JSONObject()
uploadInfo.put("fileVersion", 1)
uploadInfo.put("counter", uploadNumber)
uploadInfo.put("timestamp", uploadDate.time)
uploadInfo.put("utcOffset", TimeZone.getDefault().getOffset(uploadDate.time))
zos.writeFile("UploadInfo.json", uploadInfo.toString().toByteArray())
tags.add("UploadInfo")
zos.close()
val bytes = baos.toByteArray()
Single.just(UploadData(
fileName = "upload-num$uploadNumber-ver1-date${FILE_NAME_DATE_FORMAT.format(uploadDate)}-appid${appId.toString().replace("-", "")}.zip",
metadata = OpenHumansAPI.FileMetadata(
tags = tags,
description = "AndroidAPS Database Upload",
md5 = MessageDigest.getInstance("MD5").digest(bytes).toHexString(),
creationDate = uploadDate.time
),
content = bytes,
highestQueueId = items.map { it.id }.maxOrNull()
))
}
private fun ZipOutputStream.writeFile(name: String, bytes: ByteArray) {
putNextEntry(ZipEntry(name))
write(bytes)
closeEntry()
}
private fun removeUploadedEntriesFromQueue(highestId: Long) = Completable.fromCallable {
databaseHelper.removeAllOHQueueItemsWithIdSmallerThan(highestId)
}
private fun handleSignOut() {
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
.setContentTitle(resourceHelper.gs(R.string.you_have_been_signed_out_of_open_humans))
.setContentText(resourceHelper.gs(R.string.click_here_to_sign_in_again_if_this_wasnt_on_purpose))
.setStyle(NotificationCompat.BigTextStyle())
.setSmallIcon(R.drawable.notif_icon)
.setAutoCancel(true)
.setContentIntent(PendingIntent.getActivity(
context,
0,
Intent(context, OpenHumansLoginActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
},
0
))
.build()
NotificationManagerCompat.from(context).notify(SIGNED_OUT_NOTIFICATION_ID, notification)
logout()
}
private fun cancelWorker() {
WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME)
}
private fun scheduleWorker(replace: Boolean) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(sp.getBoolean("key_oh_charging_only", false))
.build()
val workRequest = PeriodicWorkRequestBuilder<OHUploadWorker>(1, TimeUnit.DAYS)
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.LINEAR, 20, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_NAME, if (replace) ExistingPeriodicWorkPolicy.REPLACE else ExistingPeriodicWorkPolicy.KEEP, workRequest)
}
private fun setupNotificationChannel() {
val notificationManagerCompat = NotificationManagerCompat.from(context)
notificationManagerCompat.createNotificationChannel(NotificationChannel(
NOTIFICATION_CHANNEL,
resourceHelper.gs(R.string.open_humans),
NotificationManager.IMPORTANCE_DEFAULT
))
}
private class UploadData(
val fileName: String,
val metadata: OpenHumansAPI.FileMetadata,
val content: ByteArray,
val highestQueueId: Long?
)
@Suppress("PrivatePropertyName")
private val HEX_DIGITS = "0123456789ABCDEF".toCharArray()
private fun ByteArray.toHexString(): String {
val stringBuilder = StringBuilder()
map { it.toInt() }.forEach {
stringBuilder.append(HEX_DIGITS[(it shr 4) and 0x0F])
stringBuilder.append(HEX_DIGITS[it and 0x0F])
}
return stringBuilder.toString()
}
private fun onSharedPreferenceChanged(event: EventPreferenceChange) {
if (event.changedKey == "key_oh_charging_only" && isSetup) scheduleWorker(true)
}
}

View file

@ -1,46 +1,56 @@
package info.nightscout.androidaps.plugins.general.overview package info.nightscout.androidaps.plugins.general.overview
import android.graphics.DashPathEffect
import android.graphics.Paint
import com.jjoe64.graphview.series.BarGraphSeries import com.jjoe64.graphview.series.BarGraphSeries
import com.jjoe64.graphview.series.DataPoint import com.jjoe64.graphview.series.DataPoint
import com.jjoe64.graphview.series.LineGraphSeries import com.jjoe64.graphview.series.LineGraphSeries
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.entities.ExtendedBolus import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.TemporaryBasal import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.extensions.convertedToPercent import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.extensions.toStringShort import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.extensions.valueToUnits import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.plugins.general.overview.graphExtensions.*
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.FixedLineGraphSeries
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
@Singleton @Singleton
class OverviewData @Inject constructor( class OverviewData @Inject constructor(
private val injector: HasAndroidInjector,
private val aapsLogger: AAPSLogger,
private val resourceHelper: ResourceHelper, private val resourceHelper: ResourceHelper,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
private val sp: SP, private val sp: SP,
private val activePlugin: ActivePlugin, private val activePlugin: ActivePlugin,
private val defaultValueHelper: DefaultValueHelper, private val defaultValueHelper: DefaultValueHelper,
private val profileFunction: ProfileFunction private val profileFunction: ProfileFunction,
private val config: Config,
private val loopPlugin: LoopPlugin,
private val nsDeviceStatus: NSDeviceStatus,
private val repository: AppRepository,
private val overviewMenus: OverviewMenus,
private val iobCobCalculator: IobCobCalculator,
private val translator: Translator
) { ) {
enum class Property { enum class Property {
@ -53,7 +63,8 @@ class OverviewData @Inject constructor(
BG, BG,
IOB_COB, IOB_COB,
SENSITIVITY, SENSITIVITY,
GRAPH GRAPH,
PUMPSTATUS
} }
var rangeToDisplay = 6 // for graph var rangeToDisplay = 6 // for graph
@ -61,6 +72,42 @@ class OverviewData @Inject constructor(
var fromTime: Long = 0 var fromTime: Long = 0
var endTime: Long = 0 var endTime: Long = 0
fun reset() {
pumpStatus = ""
calcProgress = ""
lastBg = null
temporaryBasal = null
extendedBolus = null
bolusIob = null
basalIob = null
cobInfo = null
lastCarbsTime = 0L
temporaryTarget = null
lastAutosensData = null
bgReadingsArray = ArrayList()
bucketedGraphSeries = PointsWithLabelGraphSeries()
bgReadingGraphSeries = PointsWithLabelGraphSeries()
predictionsGraphSeries = PointsWithLabelGraphSeries()
baseBasalGraphSeries = LineGraphSeries()
tempBasalGraphSeries = LineGraphSeries()
basalLineGraphSeries = LineGraphSeries()
absoluteBasalGraphSeries = LineGraphSeries()
activitySeries = FixedLineGraphSeries()
activityPredictionSeries = FixedLineGraphSeries()
iobSeries = FixedLineGraphSeries()
absIobSeries = FixedLineGraphSeries()
iobPredictions1Series = PointsWithLabelGraphSeries()
iobPredictions2Series = PointsWithLabelGraphSeries()
minusBgiSeries = FixedLineGraphSeries()
minusBgiHistSeries = FixedLineGraphSeries()
cobSeries = FixedLineGraphSeries()
cobMinFailOverSeries = PointsWithLabelGraphSeries()
deviationsSeries = BarGraphSeries()
ratioSeries = LineGraphSeries()
dsMaxSeries = LineGraphSeries()
dsMinSeries = LineGraphSeries()
}
fun initRange() { fun initRange() {
rangeToDisplay = sp.getInt(R.string.key_rangetodisplay, 6) rangeToDisplay = sp.getInt(R.string.key_rangetodisplay, 6)
@ -78,25 +125,10 @@ class OverviewData @Inject constructor(
} }
/* /*
* PROFILE * PUMP STATUS
*/ */
var profile: Profile? = null
var profileName: String? = null
var profileNameWithRemainingTime: String? = null
val profileBackgroudColor: Int var pumpStatus: String = ""
get() =
profile?.let { profile ->
if (profile.percentage != 100 || profile.timeshift != 0) resourceHelper.gc(R.color.ribbonWarning)
else resourceHelper.gc(R.color.ribbonDefault)
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
val profileTextColor: Int
get() =
profile?.let { profile ->
if (profile.percentage != 100 || profile.timeshift != 0) resourceHelper.gc(R.color.ribbonTextWarning)
else resourceHelper.gc(R.color.ribbonTextDefault)
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
/* /*
* CALC PROGRESS * CALC PROGRESS
@ -133,13 +165,14 @@ class OverviewData @Inject constructor(
val temporaryBasalText: String val temporaryBasalText: String
get() = get() =
profile?.let { profile -> profileFunction.getProfile()?.let { profile ->
if (temporaryBasal?.isInProgress == false) temporaryBasal = null
temporaryBasal?.let { "T:" + it.toStringShort() } temporaryBasal?.let { "T:" + it.toStringShort() }
?: resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal()) ?: resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal())
} ?: resourceHelper.gs(R.string.notavailable) } ?: resourceHelper.gs(R.string.notavailable)
val temporaryBasalDialogText: String val temporaryBasalDialogText: String
get() = profile?.let { profile -> get() = profileFunction.getProfile()?.let { profile ->
temporaryBasal?.let { temporaryBasal -> temporaryBasal?.let { temporaryBasal ->
"${resourceHelper.gs(R.string.basebasalrate_label)}: ${resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal())}" + "${resourceHelper.gs(R.string.basebasalrate_label)}: ${resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal())}" +
"\n" + resourceHelper.gs(R.string.tempbasal_label) + ": " + temporaryBasal.toStringFull(profile, dateUtil) "\n" + resourceHelper.gs(R.string.tempbasal_label) + ": " + temporaryBasal.toStringFull(profile, dateUtil)
@ -149,7 +182,7 @@ class OverviewData @Inject constructor(
val temporaryBasalIcon: Int val temporaryBasalIcon: Int
get() = get() =
profile?.let { profile -> profileFunction.getProfile()?.let { profile ->
temporaryBasal?.let { temporaryBasal -> temporaryBasal?.let { temporaryBasal ->
val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile) val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile)
when { when {
@ -173,7 +206,10 @@ class OverviewData @Inject constructor(
val extendedBolusText: String val extendedBolusText: String
get() = get() =
extendedBolus?.let { extendedBolus -> extendedBolus?.let { extendedBolus ->
if (activePlugin.activePump.isFakingTempsByExtendedBoluses) resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.rate) if (!extendedBolus.isInProgress(dateUtil)) {
this@OverviewData.extendedBolus = null
""
} else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.rate)
else "" else ""
} ?: "" } ?: ""
@ -211,7 +247,7 @@ class OverviewData @Inject constructor(
* TEMP TARGET * TEMP TARGET
*/ */
var temporarytarget: TemporaryTarget? = null var temporaryTarget: TemporaryTarget? = null
/* /*
* SENSITIVITY * SENSITIVITY
@ -278,4 +314,520 @@ class OverviewData @Inject constructor(
var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries() var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries() var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
@Synchronized
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
fun prepareBgData(from: String) {
// val start = dateUtil.now()
maxBgValue = Double.MIN_VALUE
bgReadingsArray = repository.compatGetBgReadingsDataFromTime(fromTime, toTime, false).blockingGet()
val bgListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
for (bg in bgReadingsArray) {
if (bg.timestamp < fromTime || bg.timestamp > toTime) continue
if (bg.value > maxBgValue) maxBgValue = bg.value
bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper))
}
bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, profileFunction.getUnits())
if (defaultValueHelper.determineHighLine() > maxBgValue) maxBgValue = defaultValueHelper.determineHighLine()
maxBgValue = addUpperChartMargin(maxBgValue)
// profiler.log(LTag.UI, "prepareBgData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun preparePredictions(from: String) {
// val start = dateUtil.now()
val apsResult = if (config.APS) loopPlugin.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
val predictionsAvailable = if (config.APS) loopPlugin.lastRun?.request?.hasPredictions == true else config.NSCLIENT
val menuChartSettings = overviewMenus.setting
// align to hours
val calendar = Calendar.getInstance().also {
it.timeInMillis = System.currentTimeMillis()
it[Calendar.MILLISECOND] = 0
it[Calendar.SECOND] = 0
it[Calendar.MINUTE] = 0
it.add(Calendar.HOUR, 1)
}
if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) {
var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt()
predictionHours = min(2, predictionHours)
predictionHours = max(0, predictionHours)
val hoursToFetch = rangeToDisplay - predictionHours
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
fromTime = toTime - T.hours(hoursToFetch.toLong()).msecs()
endTime = toTime + T.hours(predictionHours.toLong()).msecs()
} else {
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
fromTime = toTime - T.hours(rangeToDisplay.toLong()).msecs()
endTime = toTime
}
val bgListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val predictions: MutableList<GlucoseValueDataPoint>? = apsResult?.predictions
?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper) }
?.toMutableList()
if (predictions != null) {
predictions.sortWith { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) }
for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction)
}
predictionsGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
// profiler.log(LTag.UI, "preparePredictions() $from", start)
}
@Synchronized
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
fun prepareBucketedData(from: String) {
// val start = dateUtil.now()
val bucketedData = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return
if (bucketedData.isEmpty()) {
aapsLogger.debug("No bucketed data.")
return
}
val bucketedListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
for (inMemoryGlucoseValue in bucketedData) {
if (inMemoryGlucoseValue.timestamp < fromTime || inMemoryGlucoseValue.timestamp > toTime) continue
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, resourceHelper))
}
bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })
// profiler.log(LTag.UI, "prepareBucketedData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareBasalData(from: String) {
// val start = dateUtil.now()
maxBasalValueFound = 0.0
val baseBasalArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val tempBasalArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val basalLineArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val absoluteBasalLineArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
var lastLineBasal = 0.0
var lastAbsoluteLineBasal = -1.0
var lastBaseBasal = 0.0
var lastTempBasal = 0.0
var time = fromTime
while (time < toTime) {
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 60 * 1000L
continue
}
val basalData = iobCobCalculator.getBasalData(profile, time)
val baseBasalValue = basalData.basal
var absoluteLineValue = baseBasalValue
var tempBasalValue = 0.0
var basal = 0.0
if (basalData.isTempBasalRunning) {
tempBasalValue = basalData.tempBasalAbsolute
absoluteLineValue = tempBasalValue
if (tempBasalValue != lastTempBasal) {
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale))
tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, basalScale))
}
if (lastBaseBasal != 0.0) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale))
baseBasalArray.add(ScaledDataPoint(time, 0.0, basalScale))
lastBaseBasal = 0.0
}
} else {
if (baseBasalValue != lastBaseBasal) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale))
baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, basalScale))
lastBaseBasal = baseBasalValue
}
if (lastTempBasal != 0.0) {
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale))
tempBasalArray.add(ScaledDataPoint(time, 0.0, basalScale))
}
}
if (baseBasalValue != lastLineBasal) {
basalLineArray.add(ScaledDataPoint(time, lastLineBasal, basalScale))
basalLineArray.add(ScaledDataPoint(time, baseBasalValue, basalScale))
}
if (absoluteLineValue != lastAbsoluteLineBasal) {
absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(time, basal, basalScale))
}
lastAbsoluteLineBasal = absoluteLineValue
lastLineBasal = baseBasalValue
lastTempBasal = tempBasalValue
maxBasalValueFound = max(maxBasalValueFound, max(tempBasalValue, baseBasalValue))
time += 60 * 1000L
}
// final points
basalLineArray.add(ScaledDataPoint(toTime, lastLineBasal, basalScale))
baseBasalArray.add(ScaledDataPoint(toTime, lastBaseBasal, basalScale))
tempBasalArray.add(ScaledDataPoint(toTime, lastTempBasal, basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(toTime, lastAbsoluteLineBasal, basalScale))
// create series
baseBasalGraphSeries = LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = resourceHelper.gc(R.color.basebasal)
it.thickness = 0
}
tempBasalGraphSeries = LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = resourceHelper.gc(R.color.tempbasal)
it.thickness = 0
}
basalLineGraphSeries = LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
paint.color = resourceHelper.gc(R.color.basal)
})
}
absoluteBasalGraphSeries = LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
it.setCustomPaint(Paint().also { absolutePaint ->
absolutePaint.style = Paint.Style.STROKE
absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
absolutePaint.color = resourceHelper.gc(R.color.basal)
})
}
// profiler.log(LTag.UI, "prepareBasalData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareTemporaryTargetData(from: String) {
// val start = dateUtil.now()
val profile = profileFunction.getProfile() ?: return
val units = profileFunction.getUnits()
var toTime = toTime
val targetsSeriesArray: MutableList<DataPoint> = java.util.ArrayList()
var lastTarget = -1.0
loopPlugin.lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) }
var time = fromTime
while (time < toTime) {
val tt = repository.getTemporaryTargetActiveAt(time).blockingGet()
val value: Double = if (tt is ValueWrapper.Existing) {
Profile.fromMgdlToUnits(tt.value.target(), units)
} else {
Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units)
}
if (lastTarget != value) {
if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget))
targetsSeriesArray.add(DataPoint(time.toDouble(), value))
}
lastTarget = value
time += 5 * 60 * 1000L
}
// final point
targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget))
// create series
temporaryTargetSeries = LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
it.isDrawBackground = false
it.color = resourceHelper.gc(R.color.tempTargetBackground)
it.thickness = 2
}
// profiler.log(LTag.UI, "prepareTemporaryTargetData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareTreatmentsData(from: String) {
// val start = dateUtil.now()
maxTreatmentsValue = 0.0
val filteredTreatments: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
repository.getBolusesDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { BolusDataPoint(it, resourceHelper, activePlugin, defaultValueHelper) }
.filter { it.data.type == Bolus.Type.NORMAL || it.data.type == Bolus.Type.SMB }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
repository.getCarbsDataFromTimeToTimeExpanded(fromTime, endTime, true).blockingGet()
.map { CarbsDataPoint(it, resourceHelper) }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
// ProfileSwitch
repository.getEffectiveProfileSwitchDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { EffectiveProfileSwitchDataPoint(it) }
.forEach(filteredTreatments::add)
// OfflineEvent
repository.getOfflineEventDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { TherapyEventDataPoint(TherapyEvent(timestamp = it.timestamp, duration = it.duration, type = TherapyEvent.Type.APS_OFFLINE, glucoseUnit = TherapyEvent.GlucoseUnit.MMOL), resourceHelper, profileFunction, translator) }
.forEach(filteredTreatments::add)
// Extended bolus
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
repository.getExtendedBolusDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { ExtendedBolusDataPoint(it) }
.filter { it.duration != 0L }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
}
// Careportal
repository.compatGetTherapyEventDataFromToTime(fromTime - T.hours(6).msecs(), endTime).blockingGet()
.map { TherapyEventDataPoint(it, resourceHelper, profileFunction, translator) }
.filterTimeframe(fromTime, endTime)
.forEach {
if (it.y == 0.0) it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
// increase maxY if a treatment forces it's own height that's higher than a BG value
filteredTreatments.map { it.y }
.maxOrNull()
?.let(::addUpperChartMargin)
?.let { maxTreatmentsValue = maxOf(maxTreatmentsValue, it) }
treatmentsSeries = PointsWithLabelGraphSeries(filteredTreatments.toTypedArray())
// profiler.log(LTag.UI, "prepareTreatmentsData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareIobAutosensData(from: String) {
// val start = dateUtil.now()
val iobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val absIobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxIobValueFound = Double.MIN_VALUE
var lastIob = 0.0
var absLastIob = 0.0
var time = fromTime
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val cobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxCobValueFound = Double.MIN_VALUE
var lastCob = 0
val actArrayHist: MutableList<ScaledDataPoint> = java.util.ArrayList()
val actArrayPrediction: MutableList<ScaledDataPoint> = java.util.ArrayList()
val now = dateUtil.now().toDouble()
maxIAValue = 0.0
val bgiArrayHist: MutableList<ScaledDataPoint> = java.util.ArrayList()
val bgiArrayPrediction: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxBGIValue = Double.MIN_VALUE
val devArray: MutableList<OverviewPlugin.DeviationDataPoint> = java.util.ArrayList()
maxDevValueFound = Double.MIN_VALUE
val ratioArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
minRatioValueFound = -5.0
val dsMaxArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val dsMinArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxFromMaxValueFound = Double.MIN_VALUE
maxFromMinValueFound = Double.MIN_VALUE
val adsData = iobCobCalculator.ads.clone()
while (time <= toTime) {
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 5 * 60 * 1000L
continue
}
// IOB
val iob = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
val baseBasalIob = iobCobCalculator.calculateAbsoluteIobFromBaseBasals(time)
val absIob = IobTotal.combine(iob, baseBasalIob)
val autosensData = adsData.getAutosensDataAtTime(time)
if (abs(lastIob - iob.iob) > 0.02) {
if (abs(lastIob - iob.iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
iobArray.add(ScaledDataPoint(time, iob.iob, iobScale))
maxIobValueFound = maxOf(maxIobValueFound, abs(iob.iob))
lastIob = iob.iob
}
if (abs(absLastIob - absIob.iob) > 0.02) {
if (abs(absLastIob - absIob.iob) > 0.2) absIobArray.add(ScaledDataPoint(time, absLastIob, iobScale))
absIobArray.add(ScaledDataPoint(time, absIob.iob, iobScale))
maxIobValueFound = maxOf(maxIobValueFound, abs(absIob.iob))
absLastIob = absIob.iob
}
// COB
if (autosensData != null) {
val cob = autosensData.cob.toInt()
if (cob != lastCob) {
if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), cobScale))
cobArray.add(ScaledDataPoint(time, cob.toDouble(), cobScale))
maxCobValueFound = max(maxCobValueFound, cob.toDouble())
lastCob = cob
}
if (autosensData.failoverToMinAbsorbtionRate) {
autosensData.setScale(cobScale)
autosensData.setChartTime(time)
minFailOverActiveList.add(autosensData)
}
}
// ACTIVITY
if (time <= now) actArrayHist.add(ScaledDataPoint(time, iob.activity, actScale))
else actArrayPrediction.add(ScaledDataPoint(time, iob.activity, actScale))
maxIAValue = max(maxIAValue, abs(iob.activity))
// BGI
val devBgiScale = overviewMenus.isEnabledIn(OverviewMenus.CharType.DEV) == overviewMenus.isEnabledIn(OverviewMenus.CharType.BGI)
val deviation = if (devBgiScale) autosensData?.deviation ?: 0.0 else 0.0
val bgi: Double = iob.activity * profile.getIsfMgdl(time) * 5.0
if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, bgiScale))
else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, bgiScale))
maxBGIValue = max(maxBGIValue, max(abs(bgi), deviation))
// DEVIATIONS
if (autosensData != null) {
var color = resourceHelper.gc(R.color.deviationblack) // "="
if (autosensData.type == "" || autosensData.type == "non-meal") {
if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey)
if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen)
if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred)
} else if (autosensData.type == "uam") {
color = resourceHelper.gc(R.color.uam)
} else if (autosensData.type == "csf") {
color = resourceHelper.gc(R.color.deviationgrey)
}
devArray.add(OverviewPlugin.DeviationDataPoint(time.toDouble(), autosensData.deviation, color, devScale))
maxDevValueFound = maxOf(maxDevValueFound, abs(autosensData.deviation), abs(bgi))
}
// RATIO
if (autosensData != null) {
ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1), ratioScale))
maxRatioValueFound = max(maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
minRatioValueFound = min(minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
}
// DEV SLOPE
if (autosensData != null) {
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale))
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale))
maxFromMaxValueFound = max(maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
maxFromMinValueFound = max(maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation))
}
time += 5 * 60 * 1000L
}
// IOB
iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
it.color = resourceHelper.gc(R.color.iob)
it.thickness = 3
}
absIobSeries = FixedLineGraphSeries(Array(absIobArray.size) { i -> absIobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
it.color = resourceHelper.gc(R.color.iob)
it.thickness = 3
}
if (overviewMenus.setting[0][OverviewMenus.CharType.PRE.ordinal]) {
val autosensData = adsData.getLastAutosensData("GraphData", aapsLogger, dateUtil)
val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult()
val isTempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
val iobPrediction: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val iobPredictionArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray) {
iobPrediction.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
iobPredictions1Series = PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] })
val iobPrediction2: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val iobPredictionArray2 = iobCobCalculator.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray2) {
iobPrediction2.add(i.setColor(resourceHelper.gc(R.color.iobPred)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
iobPredictions2Series = PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] })
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray))
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray2))
} else {
iobPredictions1Series = PointsWithLabelGraphSeries()
iobPredictions2Series = PointsWithLabelGraphSeries()
}
// COB
cobSeries = FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50%
it.color = resourceHelper.gc(R.color.cob)
it.thickness = 3
}
cobMinFailOverSeries = PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })
// ACTIVITY
activitySeries = FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
it.isDrawBackground = false
it.color = resourceHelper.gc(R.color.activity)
it.thickness = 3
}
activityPredictionSeries = FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
paint.color = resourceHelper.gc(R.color.activity)
})
}
// BGI
minusBgiSeries = FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
it.isDrawBackground = false
it.color = resourceHelper.gc(R.color.bgi)
it.thickness = 3
}
minusBgiHistSeries = FixedLineGraphSeries(Array(bgiArrayPrediction.size) { i -> bgiArrayPrediction[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
paint.color = resourceHelper.gc(R.color.bgi)
})
}
// DEVIATIONS
deviationsSeries = BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
it.setValueDependentColor { data: OverviewPlugin.DeviationDataPoint -> data.color }
}
// RATIO
ratioSeries = LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
it.color = resourceHelper.gc(R.color.ratio)
it.thickness = 3
}
// DEV SLOPE
dsMaxSeries = LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
it.color = resourceHelper.gc(R.color.devslopepos)
it.thickness = 3
}
dsMinSeries = LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
it.color = resourceHelper.gc(R.color.devslopeneg)
it.thickness = 3
}
// profiler.log(LTag.UI, "prepareIobAutosensData() $from", start)
}
private fun addUpperChartMargin(maxBgValue: Double) =
if (profileFunction.getUnits() == GlucoseUnit.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4
private fun getNearestBg(date: Long): Double {
bgReadingsArray.let { bgReadingsArray ->
for (reading in bgReadingsArray) {
if (reading.timestamp > date) continue
return Profile.fromMgdlToUnits(reading.value, profileFunction.getUnits())
}
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, profileFunction.getUnits())
else Profile.fromMgdlToUnits(100.0, profileFunction.getUnits())
}
}
private fun <E : DataPointWithLabelInterface> List<E>.filterTimeframe(fromTime: Long, endTime: Long): List<E> =
filter { it.x + it.duration >= fromTime && it.x <= endTime }
} }

View file

@ -26,6 +26,7 @@ import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
@ -38,6 +39,7 @@ import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventPumpStatusChanged import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.directionToIcon import info.nightscout.androidaps.extensions.directionToIcon
import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.extensions.valueToUnitsString
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
@ -57,10 +59,13 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProv
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.source.DexcomPlugin import info.nightscout.androidaps.plugins.source.DexcomPlugin
import info.nightscout.androidaps.plugins.source.XdripPlugin import info.nightscout.androidaps.plugins.source.XdripPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.CommandQueue import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.skins.SkinProvider import info.nightscout.androidaps.skins.SkinProvider
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.TrendCalculator
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
@ -72,6 +77,7 @@ import info.nightscout.androidaps.utils.wizard.QuickWizard
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.plusAssign
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.abs import kotlin.math.abs
@ -92,7 +98,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Inject lateinit var nsDeviceStatus: NSDeviceStatus @Inject lateinit var nsDeviceStatus: NSDeviceStatus
@Inject lateinit var loopPlugin: LoopPlugin @Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var dexcomPlugin: DexcomPlugin @Inject lateinit var dexcomPlugin: DexcomPlugin
@Inject lateinit var dexcomMediator: DexcomPlugin.DexcomMediator @Inject lateinit var dexcomMediator: DexcomPlugin.DexcomMediator
@ -108,7 +113,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Inject lateinit var trendCalculator: TrendCalculator @Inject lateinit var trendCalculator: TrendCalculator
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider @Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
@ -121,7 +125,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
private var smallHeight = false private var smallHeight = false
private lateinit var dm: DisplayMetrics private lateinit var dm: DisplayMetrics
private var axisWidth: Int = 0 private var axisWidth: Int = 0
private var refreshLoop: Runnable? = null private lateinit var refreshLoop: Runnable
private lateinit var handler: Handler private lateinit var handler: Handler
private val secondaryGraphs = ArrayList<GraphView>() private val secondaryGraphs = ArrayList<GraphView>()
@ -140,7 +144,10 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
_binding = it _binding = it
//check screen width //check screen width
dm = DisplayMetrics() dm = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getMetrics(dm) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
activity?.display?.getRealMetrics(dm)
else
@Suppress("DEPRECATION") activity?.windowManager?.defaultDisplay?.getMetrics(dm)
}.root }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -157,8 +164,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
skinProvider.activeSkin().preProcessLandscapeOverviewLayout(dm, view, landscape, resourceHelper.gb(R.bool.isTablet), smallHeight) skinProvider.activeSkin().preProcessLandscapeOverviewLayout(dm, view, landscape, resourceHelper.gb(R.bool.isTablet), smallHeight)
binding.nsclientLayout.visibility = config.NSCLIENT.toVisibility() binding.nsclientLayout.visibility = config.NSCLIENT.toVisibility()
binding.loopPumpStatusLayout.pumpStatus.setBackgroundColor(resourceHelper.gc(R.color.colorInitializingBorder))
binding.notifications.setHasFixedSize(false) binding.notifications.setHasFixedSize(false)
binding.notifications.layoutManager = LinearLayoutManager(view.context) binding.notifications.layoutManager = LinearLayoutManager(view.context)
axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80 axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80
@ -177,6 +182,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else overviewData.rangeToDisplay overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else overviewData.rangeToDisplay
sp.putInt(R.string.key_rangetodisplay, overviewData.rangeToDisplay) sp.putInt(R.string.key_rangetodisplay, overviewData.rangeToDisplay)
overviewData.initRange() overviewData.initRange()
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
updateGUI("rangeChange", OverviewData.Property.GRAPH) updateGUI("rangeChange", OverviewData.Property.GRAPH)
rxBus.send(EventPreferenceChange(resourceHelper, R.string.key_rangetodisplay)) rxBus.send(EventPreferenceChange(resourceHelper, R.string.key_rangetodisplay))
sp.putBoolean(R.string.key_objectiveusescale, true) sp.putBoolean(R.string.key_objectiveusescale, true)
@ -185,10 +192,10 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
prepareGraphsIfNeeded(overviewMenus.setting.size) prepareGraphsIfNeeded(overviewMenus.setting.size)
overviewMenus.setupChartMenu(binding.graphsLayout.chartMenuButton) overviewMenus.setupChartMenu(binding.graphsLayout.chartMenuButton)
binding.loopPumpStatusLayout.activeProfile.setOnClickListener(this) binding.activeProfile.setOnClickListener(this)
binding.loopPumpStatusLayout.activeProfile.setOnLongClickListener(this) binding.activeProfile.setOnLongClickListener(this)
binding.loopPumpStatusLayout.tempTarget.setOnClickListener(this) binding.tempTarget.setOnClickListener(this)
binding.loopPumpStatusLayout.tempTarget.setOnLongClickListener(this) binding.tempTarget.setOnLongClickListener(this)
binding.buttonsLayout.acceptTempButton.setOnClickListener(this) binding.buttonsLayout.acceptTempButton.setOnClickListener(this)
binding.buttonsLayout.treatmentButton.setOnClickListener(this) binding.buttonsLayout.treatmentButton.setOnClickListener(this)
binding.buttonsLayout.wizardButton.setOnClickListener(this) binding.buttonsLayout.wizardButton.setOnClickListener(this)
@ -200,7 +207,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.buttonsLayout.quickWizardButton.setOnLongClickListener(this) binding.buttonsLayout.quickWizardButton.setOnLongClickListener(this)
binding.infoLayout.apsMode.setOnClickListener(this) binding.infoLayout.apsMode.setOnClickListener(this)
binding.infoLayout.apsMode.setOnLongClickListener(this) binding.infoLayout.apsMode.setOnLongClickListener(this)
binding.loopPumpStatusLayout.activeProfile.setOnLongClickListener(this) binding.activeProfile.setOnLongClickListener(this)
} }
@Synchronized @Synchronized
@ -241,10 +248,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
.toObservable(EventNewOpenLoopNotification::class.java) .toObservable(EventNewOpenLoopNotification::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventNewOpenLoopNotification") }, fabricPrivacy::logException)) .subscribe({ scheduleUpdateGUI("EventNewOpenLoopNotification") }, fabricPrivacy::logException))
disposable.add(rxBus disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java) .toObservable(EventPumpStatusChanged::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updatePumpStatus(it) }, fabricPrivacy::logException)) .delay (30, TimeUnit.MILLISECONDS, aapsSchedulers.main)
.subscribe({
overviewData.pumpStatus = it.getStatus(resourceHelper)
updateGUI("EventPumpStatusChanged", OverviewData.Property.PUMPSTATUS)
}, fabricPrivacy::logException)
refreshLoop = Runnable { refreshLoop = Runnable {
overviewPlugin.refreshLoop("refreshLoop") overviewPlugin.refreshLoop("refreshLoop")
@ -374,7 +385,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
R.id.temp_target -> v.performClick() R.id.temp_target -> v.performClick()
R.id.active_profile -> activity?.let { activity -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { ProfileSwitchDialog().show(childFragmentManager, "Overview") }) } R.id.active_profile -> activity?.let { activity -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { ProfileSwitchDialog().show(childFragmentManager, "ProfileSwitchDialog") }) }
} }
return false return false
@ -402,18 +413,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
} }
private fun updatePumpStatus(event: EventPumpStatusChanged) {
val status = event.getStatus(resourceHelper)
if (status != "") {
binding.loopPumpStatusLayout.pumpStatus.text = status
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.VISIBLE
binding.loopPumpStatusLayout.loopLayout.visibility = View.GONE
} else {
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.GONE
binding.loopPumpStatusLayout.loopLayout.visibility = View.VISIBLE
}
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun processButtonsVisibility() { private fun processButtonsVisibility() {
val lastBG = iobCobCalculator.ads.lastBg() val lastBG = iobCobCalculator.ads.lastBg()
@ -585,9 +584,9 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
task = null task = null
} }
} }
handler.removeCallbacks(task) task?.let { handler.removeCallbacks(it) }
task = UpdateRunnable() task = UpdateRunnable()
handler.postDelayed(task, 500) task?.let { handler.postDelayed(it, 500) }
} }
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
@ -595,15 +594,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
fun updateGUI(from: String, what: OverviewData.Property) { fun updateGUI(from: String, what: OverviewData.Property) {
// if (what != OverviewData.Property.CALC_PROGRESS) // if (what != OverviewData.Property.CALC_PROGRESS)
// aapsLogger.debug(LTag.UI, "UpdateGui $from $what") // aapsLogger.debug(LTag.UI, "UpdateGui $from $what")
if (overviewData.profile == null) { if (profileFunction.getProfile() == null) {
binding.loopPumpStatusLayout.pumpStatus.setText(R.string.noprofileset) binding.activeProfile.setText(R.string.noprofileset)
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.VISIBLE binding.activeProfile.setBackgroundColor(resourceHelper.gc(R.color.errorAlertBackground))
binding.loopPumpStatusLayout.loopLayout.visibility = View.GONE
return return
} }
binding.notifications.let { notificationStore.updateNotifications(it) } binding.notifications.let { notificationStore.updateNotifications(it) }
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.GONE
binding.loopPumpStatusLayout.loopLayout.visibility = View.VISIBLE
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val pump = activePlugin.activePump val pump = activePlugin.activePump
@ -623,7 +619,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.infoLayout.avgDelta.text = Profile.toSignedUnitsString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units) binding.infoLayout.avgDelta.text = Profile.toSignedUnitsString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units)
binding.infoLayout.longAvgDelta.text = Profile.toSignedUnitsString(glucoseStatus.longAvgDelta, glucoseStatus.longAvgDelta * Constants.MGDL_TO_MMOLL, units) binding.infoLayout.longAvgDelta.text = Profile.toSignedUnitsString(glucoseStatus.longAvgDelta, glucoseStatus.longAvgDelta * Constants.MGDL_TO_MMOLL, units)
} else { } else {
binding.infoLayout.deltaLarge.text = "Δ " + resourceHelper.gs(R.string.notavailable) binding.infoLayout.deltaLarge.text = ""
binding.infoLayout.delta.text = "Δ " + resourceHelper.gs(R.string.notavailable) binding.infoLayout.delta.text = "Δ " + resourceHelper.gs(R.string.notavailable)
binding.infoLayout.avgDelta.text = "" binding.infoLayout.avgDelta.text = ""
binding.infoLayout.longAvgDelta.text = "" binding.infoLayout.longAvgDelta.text = ""
@ -638,10 +634,25 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
OverviewData.Property.PROFILE -> { OverviewData.Property.PROFILE -> {
binding.loopPumpStatusLayout.activeProfile.text = overviewData.profileNameWithRemainingTime val profileBackgroundColor =
?: "" profileFunction.getProfile()?.let {
binding.loopPumpStatusLayout.activeProfile.setBackgroundColor(overviewData.profileBackgroudColor) val profile = (it as ProfileSealed.EPS).value
binding.loopPumpStatusLayout.activeProfile.setTextColor(overviewData.profileTextColor) if (profile.originalPercentage != 100 || profile.originalTimeshift != 0L || profile.originalDuration != 0L)
resourceHelper.gc(R.color.ribbonWarning)
else resourceHelper.gc(R.color.ribbonDefault)
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
val profileTextColor =
profileFunction.getProfile()?.let {
val profile = (it as ProfileSealed.EPS).value
if (profile.originalPercentage != 100 || profile.originalTimeshift != 0L || profile.originalDuration != 0L)
resourceHelper.gc(R.color.ribbonTextWarning)
else resourceHelper.gc(R.color.ribbonTextDefault)
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
binding.activeProfile.text = profileFunction.getProfileNameWithRemainingTime()
binding.activeProfile.setBackgroundColor(profileBackgroundColor)
binding.activeProfile.setTextColor(profileTextColor)
} }
OverviewData.Property.TEMPORARY_BASAL -> { OverviewData.Property.TEMPORARY_BASAL -> {
@ -655,7 +666,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
OverviewData.Property.EXTENDED_BOLUS -> { OverviewData.Property.EXTENDED_BOLUS -> {
binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText
binding.infoLayout.extendedBolus.setOnClickListener { binding.infoLayout.extendedLayout.setOnClickListener {
activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.extended_bolus), overviewData.extendedBolusDialogText) } activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.extended_bolus), overviewData.extendedBolusDialogText) }
} }
binding.infoLayout.extendedLayout.visibility = (overviewData.extendedBolus != null && !pump.isFakingTempsByExtendedBoluses).toVisibility() binding.infoLayout.extendedLayout.visibility = (overviewData.extendedBolus != null && !pump.isFakingTempsByExtendedBoluses).toVisibility()
@ -677,14 +688,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.iob), overviewData.iobDialogText) } activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.iob), overviewData.iobDialogText) }
} }
// cob // cob
var cobText: String = resourceHelper.gs(R.string.value_unavailable_short) var cobText = overviewData.cobInfo?.displayText(resourceHelper, dateUtil, buildHelper.isDev()) ?: resourceHelper.gs(R.string.value_unavailable_short)
overviewData.cobInfo?.let { cobInfo ->
if (cobInfo.displayCob != null) {
cobText = resourceHelper.gs(R.string.format_carbs, cobInfo.displayCob!!.toInt())
if (cobInfo.futureCarbs > 0) cobText += "(" + DecimalFormatter.to0Decimal(cobInfo.futureCarbs) + ")"
}
}
binding.infoLayout.cob.text = cobText
val constraintsProcessed = loopPlugin.lastRun?.constraintsProcessed val constraintsProcessed = loopPlugin.lastRun?.constraintsProcessed
val lastRun = loopPlugin.lastRun val lastRun = loopPlugin.lastRun
@ -692,7 +696,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (constraintsProcessed.carbsReq > 0) { if (constraintsProcessed.carbsReq > 0) {
//only display carbsreq when carbs have not been entered recently //only display carbsreq when carbs have not been entered recently
if (overviewData.lastCarbsTime < lastRun.lastAPSRun) { if (overviewData.lastCarbsTime < lastRun.lastAPSRun) {
cobText = cobText + " | " + constraintsProcessed.carbsReq + " " + resourceHelper.gs(R.string.required) cobText += " | " + constraintsProcessed.carbsReq + " " + resourceHelper.gs(R.string.required)
} }
if (carbAnimation?.isRunning == false) if (carbAnimation?.isRunning == false)
carbAnimation?.start() carbAnimation?.start()
@ -701,36 +705,38 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
carbAnimation?.selectDrawable(0) carbAnimation?.selectDrawable(0)
} }
} }
binding.infoLayout.cob.text = cobText
} }
OverviewData.Property.TEMPORARY_TARGET -> { OverviewData.Property.TEMPORARY_TARGET -> {
// temp target // temp target
val tempTarget = overviewData.temporarytarget if (overviewData.temporaryTarget?.isInProgress(dateUtil) == false) overviewData.temporaryTarget = null
val tempTarget = overviewData.temporaryTarget
if (tempTarget != null) { if (tempTarget != null) {
binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) binding.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning))
binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) binding.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning))
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(tempTarget.lowTarget, tempTarget.highTarget, GlucoseUnit.MGDL, units) + " " + dateUtil.untilString(tempTarget.end, resourceHelper) binding.tempTarget.text = Profile.toTargetRangeString(tempTarget.lowTarget, tempTarget.highTarget, GlucoseUnit.MGDL, units) + " " + dateUtil.untilString(tempTarget.end, resourceHelper)
} else { } else {
// If the target is not the same as set in the profile then oref has overridden it // If the target is not the same as set in the profile then oref has overridden it
overviewData.profile?.let { profile -> profileFunction.getProfile()?.let { profile ->
val targetUsed = loopPlugin.lastRun?.constraintsProcessed?.targetBG ?: 0.0 val targetUsed = loopPlugin.lastRun?.constraintsProcessed?.targetBG ?: 0.0
if (targetUsed != 0.0 && abs(profile.getTargetMgdl() - targetUsed) > 0.01) { if (targetUsed != 0.0 && abs(profile.getTargetMgdl() - targetUsed) > 0.01) {
aapsLogger.debug("Adjusted target. Profile: ${profile.getTargetMgdl()} APS: $targetUsed") aapsLogger.debug("Adjusted target. Profile: ${profile.getTargetMgdl()} APS: $targetUsed")
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(targetUsed, targetUsed, GlucoseUnit.MGDL, units) binding.tempTarget.text = Profile.toTargetRangeString(targetUsed, targetUsed, GlucoseUnit.MGDL, units)
binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) binding.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning))
binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.tempTargetBackground)) binding.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.tempTargetBackground))
} else { } else {
binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) binding.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault))
binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) binding.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault))
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units) binding.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units)
} }
} }
} }
} }
OverviewData.Property.GRAPH -> { OverviewData.Property.GRAPH -> {
val graphData = GraphData(injector, binding.graphsLayout.bgGraph) val graphData = GraphData(injector, binding.graphsLayout.bgGraph, overviewData)
val menuChartSettings = overviewMenus.setting val menuChartSettings = overviewMenus.setting
graphData.addInRangeArea(overviewData.fromTime, overviewData.endTime, defaultValueHelper.determineLowLine(), defaultValueHelper.determineHighLine()) graphData.addInRangeArea(overviewData.fromTime, overviewData.endTime, defaultValueHelper.determineLowLine(), defaultValueHelper.determineHighLine())
graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
@ -738,7 +744,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
graphData.addTreatments() graphData.addTreatments()
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal]) if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
graphData.addActivity(0.8) graphData.addActivity(0.8)
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) if ((pump.pumpDescription.isTempBasalCapable || config.NSCLIENT) && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
graphData.addBasals() graphData.addBasals()
graphData.addTargetLine() graphData.addTargetLine()
graphData.addNowLine(dateUtil.now()) graphData.addNowLine(dateUtil.now())
@ -755,7 +761,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) { for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
val secondGraphData = GraphData(injector, secondaryGraphs[g]) val secondGraphData = GraphData(injector, secondaryGraphs[g], overviewData)
var useABSForScale = false var useABSForScale = false
var useIobForScale = false var useIobForScale = false
var useCobForScale = false var useCobForScale = false
@ -780,7 +786,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(useDevForScale, 1.0) if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(useDevForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8) if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8)
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(useRatioForScale, if (useRatioForScale) 1.0 else 0.8) if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(useRatioForScale, if (useRatioForScale) 1.0 else 0.8)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(useDSForScale, 1.0) if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(useDSForScale, if(useDSForScale) 1.0 else 0.8, useRatioForScale)
// set manual x bounds to have nice steps // set manual x bounds to have nice steps
secondGraphData.formatAxis(overviewData.fromTime, overviewData.endTime) secondGraphData.formatAxis(overviewData.fromTime, overviewData.endTime)
@ -818,6 +824,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100) String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
} ?: "" } ?: ""
} }
OverviewData.Property.PUMPSTATUS -> {
val status = overviewData.pumpStatus
binding.pumpStatus.text = status
binding.pumpStatusLayout.visibility = (status != "").toVisibility()
}
} }
} }
} }

View file

@ -113,6 +113,9 @@ class OverviewMenus @Inject constructor(
if (m == CharType.PRE) insert = predictionsAvailable if (m == CharType.PRE) insert = predictionsAvailable
if (m == CharType.DEVSLOPE) insert = buildHelper.isDev() if (m == CharType.DEVSLOPE) insert = buildHelper.isDev()
if (used.contains(m.ordinal)) insert = false if (used.contains(m.ordinal)) insert = false
for (g2 in g + 1 until numOfGraphs) {
if (settingsCopy[g2][m.ordinal]) insert = false
}
if (insert) { if (insert) {
val item = popup.menu.add(Menu.NONE, m.ordinal + 100 * (g + 1), Menu.NONE, resourceHelper.gs(m.nameId)) val item = popup.menu.add(Menu.NONE, m.ordinal + 100 * (g + 1), Menu.NONE, resourceHelper.gs(m.nameId))
val title = item.title val title = item.title

View file

@ -1,50 +1,36 @@
package info.nightscout.androidaps.plugins.general.overview package info.nightscout.androidaps.plugins.general.overview
import android.graphics.DashPathEffect
import android.graphics.Paint
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import com.jjoe64.graphview.series.BarGraphSeries
import com.jjoe64.graphview.series.DataPoint
import com.jjoe64.graphview.series.LineGraphSeries
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.events.* import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.extensions.* import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverview import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverview
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.* import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.plusAssign
import org.json.JSONObject import org.json.JSONObject
import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
@Singleton @Singleton
class OverviewPlugin @Inject constructor( class OverviewPlugin @Inject constructor(
@ -58,15 +44,9 @@ class OverviewPlugin @Inject constructor(
resourceHelper: ResourceHelper, resourceHelper: ResourceHelper,
private val config: Config, private val config: Config,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
private val translator: Translator,
// private val profiler: Profiler,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val iobCobCalculator: IobCobCalculator, private val iobCobCalculator: IobCobCalculator,
private val repository: AppRepository, private val repository: AppRepository,
private val defaultValueHelper: DefaultValueHelper,
private val loopPlugin: LoopPlugin,
private val activePlugin: ActivePlugin,
private val nsDeviceStatus: NSDeviceStatus,
private val overviewData: OverviewData, private val overviewData: OverviewData,
private val overviewMenus: OverviewMenus private val overviewMenus: OverviewMenus
) : PluginBase(PluginDescription() ) : PluginBase(PluginDescription()
@ -110,7 +90,7 @@ class OverviewPlugin @Inject constructor(
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java) .toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.io)
.subscribe({ overviewData.calcProgress = it.progress; overviewBus.send(EventUpdateOverview("EventIobCalculationProgress", OverviewData.Property.CALC_PROGRESS)) }, fabricPrivacy::logException) .subscribe({ overviewData.calcProgress = it.progress; overviewBus.send(EventUpdateOverview("EventIobCalculationProgress", OverviewData.Property.CALC_PROGRESS)) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventTempBasalChange::class.java) .toObservable(EventTempBasalChange::class.java)
@ -133,36 +113,44 @@ class OverviewPlugin @Inject constructor(
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ .subscribe({
loadIobCobResults("EventTreatmentChange") loadIobCobResults("EventTreatmentChange")
prepareTreatmentsData("EventTreatmentChange") overviewData.prepareTreatmentsData("EventTreatmentChange")
overviewBus.send(EventUpdateOverview("EventTreatmentChange", OverviewData.Property.GRAPH)) overviewBus.send(EventUpdateOverview("EventTreatmentChange", OverviewData.Property.GRAPH))
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventTherapyEventChange::class.java) .toObservable(EventTherapyEventChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ .subscribe({
prepareTreatmentsData("EventTherapyEventChange") overviewData.prepareTreatmentsData("EventTherapyEventChange")
overviewBus.send(EventUpdateOverview("EventTherapyEventChange", OverviewData.Property.GRAPH)) overviewBus.send(EventUpdateOverview("EventTherapyEventChange", OverviewData.Property.GRAPH))
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventBucketedDataCreated::class.java) .toObservable(EventBucketedDataCreated::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ .subscribe({
prepareBucketedData("EventBucketedDataCreated") overviewData.prepareBucketedData("EventBucketedDataCreated")
prepareBgData("EventBucketedDataCreated") overviewData.prepareBgData("EventBucketedDataCreated")
overviewBus.send(EventUpdateOverview("EventBucketedDataCreated", OverviewData.Property.GRAPH)) overviewBus.send(EventUpdateOverview("EventBucketedDataCreated", OverviewData.Property.GRAPH))
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventLoopInvoked::class.java) .toObservable(EventLoopInvoked::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException) .subscribe({ overviewData.preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException)
disposable.add(rxBus disposable += rxBus
.toObservable(EventProfileSwitchChanged::class.java) .toObservable(EventNewBasalProfile::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ loadProfile("EventProfileSwitchChanged") }, fabricPrivacy::logException)) .subscribe({ loadProfile("EventNewBasalProfile") }, fabricPrivacy::logException)
disposable.add(rxBus disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ refreshLoop("EventAutosensCalculationFinished") }, fabricPrivacy::logException)) .subscribe({
if (it.cause !is EventCustomCalculationFinished) refreshLoop("EventAutosensCalculationFinished")
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
overviewData.pumpStatus = it.getStatus(resourceHelper)
}, fabricPrivacy::logException)
Thread { loadAll("onResume") }.start() Thread { loadAll("onResume") }.start()
} }
@ -188,6 +176,7 @@ class OverviewPlugin @Inject constructor(
override fun configuration(): JSONObject = override fun configuration(): JSONObject =
JSONObject() JSONObject()
.putInt(R.string.key_units, sp, resourceHelper)
.putString(R.string.key_quickwizard, sp, resourceHelper) .putString(R.string.key_quickwizard, sp, resourceHelper)
.putInt(R.string.key_eatingsoon_duration, sp, resourceHelper) .putInt(R.string.key_eatingsoon_duration, sp, resourceHelper)
.putDouble(R.string.key_eatingsoon_target, sp, resourceHelper) .putDouble(R.string.key_eatingsoon_target, sp, resourceHelper)
@ -214,6 +203,7 @@ class OverviewPlugin @Inject constructor(
override fun applyConfiguration(configuration: JSONObject) { override fun applyConfiguration(configuration: JSONObject) {
configuration configuration
.storeInt(R.string.key_units, sp, resourceHelper)
.storeString(R.string.key_quickwizard, sp, resourceHelper) .storeString(R.string.key_quickwizard, sp, resourceHelper)
.storeInt(R.string.key_eatingsoon_duration, sp, resourceHelper) .storeInt(R.string.key_eatingsoon_duration, sp, resourceHelper)
.storeDouble(R.string.key_eatingsoon_target, sp, resourceHelper) .storeDouble(R.string.key_eatingsoon_target, sp, resourceHelper)
@ -239,25 +229,27 @@ class OverviewPlugin @Inject constructor(
.storeDouble(R.string.key_statuslights_bat_critical, sp, resourceHelper) .storeDouble(R.string.key_statuslights_bat_critical, sp, resourceHelper)
} }
@Volatile var runningRefresh = false @Volatile
var runningRefresh = false
override fun refreshLoop(from: String) { override fun refreshLoop(from: String) {
if (runningRefresh) return if (runningRefresh) return
runningRefresh = true runningRefresh = true
loadIobCobResults(from) loadIobCobResults(from)
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.PROFILE))
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.BG)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.BG))
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TIME)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TIME))
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_BASAL)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_BASAL))
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.EXTENDED_BOLUS)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.EXTENDED_BOLUS))
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB))
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET))
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.SENSITIVITY)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.SENSITIVITY))
loadAsData(from) loadAsData(from)
preparePredictions(from) overviewData.preparePredictions(from)
prepareBasalData(from) overviewData.prepareBasalData(from)
prepareTemporaryTargetData(from) overviewData.prepareTemporaryTargetData(from)
prepareTreatmentsData(from) overviewData.prepareTreatmentsData(from)
prepareIobAutosensData(from) overviewData.prepareIobAutosensData(from)
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH))
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB))
aapsLogger.debug(LTag.UI, "refreshLoop finished") aapsLogger.debug(LTag.UI, "refreshLoop finished")
runningRefresh = false runningRefresh = false
} }
@ -271,9 +263,9 @@ class OverviewPlugin @Inject constructor(
loadTemporaryTarget(from) loadTemporaryTarget(from)
loadIobCobResults(from) loadIobCobResults(from)
loadAsData(from) loadAsData(from)
prepareBasalData(from) overviewData.prepareBasalData(from)
prepareTemporaryTargetData(from) overviewData.prepareTemporaryTargetData(from)
prepareTreatmentsData(from) overviewData.prepareTreatmentsData(from)
// prepareIobAutosensData(from) // prepareIobAutosensData(from)
// preparePredictions(from) // preparePredictions(from)
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH))
@ -281,9 +273,6 @@ class OverviewPlugin @Inject constructor(
} }
private fun loadProfile(from: String) { private fun loadProfile(from: String) {
overviewData.profile = profileFunction.getProfile()
overviewData.profileName = profileFunction.getProfileName()
overviewData.profileNameWithRemainingTime = profileFunction.getProfileNameWithRemainingTime()
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.PROFILE)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.PROFILE))
} }
@ -299,8 +288,8 @@ class OverviewPlugin @Inject constructor(
private fun loadTemporaryTarget(from: String) { private fun loadTemporaryTarget(from: String) {
val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
if (tempTarget is ValueWrapper.Existing) overviewData.temporarytarget = tempTarget.value if (tempTarget is ValueWrapper.Existing) overviewData.temporaryTarget = tempTarget.value
else overviewData.temporarytarget = null else overviewData.temporaryTarget = null
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET))
} }
@ -319,521 +308,11 @@ class OverviewPlugin @Inject constructor(
private fun loadIobCobResults(from: String) { private fun loadIobCobResults(from: String) {
overviewData.bolusIob = iobCobCalculator.calculateIobFromBolus().round() overviewData.bolusIob = iobCobCalculator.calculateIobFromBolus().round()
overviewData.basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() overviewData.basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
overviewData.cobInfo = iobCobCalculator.getCobInfo(false, "Overview COB") overviewData.cobInfo = iobCobCalculator.getCobInfo(true, "Overview COB")
val lastCarbs = repository.getLastCarbsRecordWrapped().blockingGet() val lastCarbs = repository.getLastCarbsRecordWrapped().blockingGet()
overviewData.lastCarbsTime = if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L overviewData.lastCarbsTime = if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB)) overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB))
} }
@Synchronized
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
private fun prepareBgData(from: String) {
// val start = dateUtil.now()
var maxBgValue = Double.MIN_VALUE
overviewData.bgReadingsArray = repository.compatGetBgReadingsDataFromTime(overviewData.fromTime, overviewData.toTime, false).blockingGet()
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
for (bg in overviewData.bgReadingsArray) {
if (bg.timestamp < overviewData.fromTime || bg.timestamp > overviewData.toTime) continue
if (bg.value > maxBgValue) maxBgValue = bg.value
bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper))
}
overviewData.bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
overviewData.maxBgValue = Profile.fromMgdlToUnits(maxBgValue, profileFunction.getUnits())
if (defaultValueHelper.determineHighLine() > maxBgValue) overviewData.maxBgValue = defaultValueHelper.determineHighLine()
overviewData.maxBgValue = addUpperChartMargin(overviewData.maxBgValue)
// profiler.log(LTag.UI, "prepareBgData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
private fun preparePredictions(from: String) {
// val start = dateUtil.now()
val apsResult = if (config.APS) loopPlugin.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
val predictionsAvailable = if (config.APS) loopPlugin.lastRun?.request?.hasPredictions == true else config.NSCLIENT
val menuChartSettings = overviewMenus.setting
// align to hours
val calendar = Calendar.getInstance().also {
it.timeInMillis = System.currentTimeMillis()
it[Calendar.MILLISECOND] = 0
it[Calendar.SECOND] = 0
it[Calendar.MINUTE] = 0
it.add(Calendar.HOUR, 1)
}
if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) {
var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt()
predictionHours = min(2, predictionHours)
predictionHours = max(0, predictionHours)
val hoursToFetch = overviewData.rangeToDisplay - predictionHours
overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
overviewData.fromTime = overviewData.toTime - T.hours(hoursToFetch.toLong()).msecs()
overviewData.endTime = overviewData.toTime + T.hours(predictionHours.toLong()).msecs()
} else {
overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
overviewData.fromTime = overviewData.toTime - T.hours(overviewData.rangeToDisplay.toLong()).msecs()
overviewData.endTime = overviewData.toTime
}
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
val predictions: MutableList<GlucoseValueDataPoint>? = apsResult?.predictions
?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper) }
?.toMutableList()
if (predictions != null) {
predictions.sortWith { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) }
for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction)
}
overviewData.predictionsGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
// profiler.log(LTag.UI, "preparePredictions() $from", start)
}
@Synchronized
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
private fun prepareBucketedData(from: String) {
// val start = dateUtil.now()
val bucketedData = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return
if (bucketedData.isEmpty()) {
aapsLogger.debug("No bucketed data.")
return
}
val bucketedListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
for (inMemoryGlucoseValue in bucketedData) {
if (inMemoryGlucoseValue.timestamp < overviewData.fromTime || inMemoryGlucoseValue.timestamp > overviewData.toTime) continue
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, resourceHelper))
}
overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })
// profiler.log(LTag.UI, "prepareBucketedData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
private fun prepareBasalData(from: String) {
// val start = dateUtil.now()
overviewData.maxBasalValueFound = 0.0
val baseBasalArray: MutableList<ScaledDataPoint> = ArrayList()
val tempBasalArray: MutableList<ScaledDataPoint> = ArrayList()
val basalLineArray: MutableList<ScaledDataPoint> = ArrayList()
val absoluteBasalLineArray: MutableList<ScaledDataPoint> = ArrayList()
var lastLineBasal = 0.0
var lastAbsoluteLineBasal = -1.0
var lastBaseBasal = 0.0
var lastTempBasal = 0.0
var time = overviewData.fromTime
while (time < overviewData.toTime) {
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 60 * 1000L
continue
}
val basalData = iobCobCalculator.getBasalData(profile, time)
val baseBasalValue = basalData.basal
var absoluteLineValue = baseBasalValue
var tempBasalValue = 0.0
var basal = 0.0
if (basalData.isTempBasalRunning) {
tempBasalValue = basalData.tempBasalAbsolute
absoluteLineValue = tempBasalValue
if (tempBasalValue != lastTempBasal) {
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, overviewData.basalScale))
tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, overviewData.basalScale))
}
if (lastBaseBasal != 0.0) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, overviewData.basalScale))
baseBasalArray.add(ScaledDataPoint(time, 0.0, overviewData.basalScale))
lastBaseBasal = 0.0
}
} else {
if (baseBasalValue != lastBaseBasal) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, overviewData.basalScale))
baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, overviewData.basalScale))
lastBaseBasal = baseBasalValue
}
if (lastTempBasal != 0.0) {
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, overviewData.basalScale))
tempBasalArray.add(ScaledDataPoint(time, 0.0, overviewData.basalScale))
}
}
if (baseBasalValue != lastLineBasal) {
basalLineArray.add(ScaledDataPoint(time, lastLineBasal, overviewData.basalScale))
basalLineArray.add(ScaledDataPoint(time, baseBasalValue, overviewData.basalScale))
}
if (absoluteLineValue != lastAbsoluteLineBasal) {
absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, overviewData.basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(time, basal, overviewData.basalScale))
}
lastAbsoluteLineBasal = absoluteLineValue
lastLineBasal = baseBasalValue
lastTempBasal = tempBasalValue
overviewData.maxBasalValueFound = max(overviewData.maxBasalValueFound, max(tempBasalValue, baseBasalValue))
time += 60 * 1000L
}
// final points
basalLineArray.add(ScaledDataPoint(overviewData.toTime, lastLineBasal, overviewData.basalScale))
baseBasalArray.add(ScaledDataPoint(overviewData.toTime, lastBaseBasal, overviewData.basalScale))
tempBasalArray.add(ScaledDataPoint(overviewData.toTime, lastTempBasal, overviewData.basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(overviewData.toTime, lastAbsoluteLineBasal, overviewData.basalScale))
// create series
overviewData.baseBasalGraphSeries = LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = resourceHelper.gc(R.color.basebasal)
it.thickness = 0
}
overviewData.tempBasalGraphSeries = LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = resourceHelper.gc(R.color.tempbasal)
it.thickness = 0
}
overviewData.basalLineGraphSeries = LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
paint.color = resourceHelper.gc(R.color.basal)
})
}
overviewData.absoluteBasalGraphSeries = LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
it.setCustomPaint(Paint().also { absolutePaint ->
absolutePaint.style = Paint.Style.STROKE
absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
absolutePaint.color = resourceHelper.gc(R.color.basal)
})
}
// profiler.log(LTag.UI, "prepareBasalData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
private fun prepareTemporaryTargetData(from: String) {
// val start = dateUtil.now()
val profile = overviewData.profile ?: return
val units = profileFunction.getUnits()
var toTime = overviewData.toTime
val targetsSeriesArray: MutableList<DataPoint> = ArrayList()
var lastTarget = -1.0
loopPlugin.lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) }
var time = overviewData.fromTime
while (time < toTime) {
val tt = repository.getTemporaryTargetActiveAt(time).blockingGet()
val value: Double = if (tt is ValueWrapper.Existing) {
Profile.fromMgdlToUnits(tt.value.target(), units)
} else {
Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units)
}
if (lastTarget != value) {
if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget))
targetsSeriesArray.add(DataPoint(time.toDouble(), value))
}
lastTarget = value
time += 5 * 60 * 1000L
}
// final point
targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget))
// create series
overviewData.temporaryTargetSeries = LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
it.isDrawBackground = false
it.color = resourceHelper.gc(R.color.tempTargetBackground)
it.thickness = 2
}
// profiler.log(LTag.UI, "prepareTemporaryTargetData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
private fun prepareTreatmentsData(from: String) {
// val start = dateUtil.now()
overviewData.maxTreatmentsValue = 0.0
val filteredTreatments: MutableList<DataPointWithLabelInterface> = ArrayList()
repository.getBolusesIncludingInvalidFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet()
.map { BolusDataPoint(it, resourceHelper, activePlugin, defaultValueHelper) }
.filter { it.data.type != Bolus.Type.SMB || it.data.isValid }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
repository.getCarbsIncludingInvalidFromTimeToTimeExpanded(overviewData.fromTime, overviewData.endTime, true).blockingGet()
.map { CarbsDataPoint(it, resourceHelper) }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
// ProfileSwitch
repository.getEffectiveProfileSwitchDataFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet()
.map { EffectiveProfileSwitchDataPoint(it) }
.forEach(filteredTreatments::add)
// Extended bolus
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
repository.getExtendedBolusDataFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet()
.map { ExtendedBolusDataPoint(it) }
.filter { it.duration != 0L }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
}
// Careportal
repository.compatGetTherapyEventDataFromToTime(overviewData.fromTime - T.hours(6).msecs(), overviewData.endTime).blockingGet()
.map { TherapyEventDataPoint(it, resourceHelper, profileFunction, translator) }
.filterTimeframe(overviewData.fromTime, overviewData.endTime)
.forEach {
if (it.y == 0.0) it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
// increase maxY if a treatment forces it's own height that's higher than a BG value
filteredTreatments.map { it.y }
.maxOrNull()
?.let(::addUpperChartMargin)
?.let { overviewData.maxTreatmentsValue = maxOf(overviewData.maxTreatmentsValue, it) }
overviewData.treatmentsSeries = PointsWithLabelGraphSeries(filteredTreatments.toTypedArray())
// profiler.log(LTag.UI, "prepareTreatmentsData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
private fun prepareIobAutosensData(from: String) {
// val start = dateUtil.now()
val iobArray: MutableList<ScaledDataPoint> = ArrayList()
val absIobArray: MutableList<ScaledDataPoint> = ArrayList()
overviewData.maxIobValueFound = Double.MIN_VALUE
var lastIob = 0.0
var absLastIob = 0.0
var time = overviewData.fromTime
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = ArrayList()
val cobArray: MutableList<ScaledDataPoint> = ArrayList()
overviewData.maxCobValueFound = Double.MIN_VALUE
var lastCob = 0
val actArrayHist: MutableList<ScaledDataPoint> = ArrayList()
val actArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
val now = dateUtil.now().toDouble()
overviewData.maxIAValue = 0.0
val bgiArrayHist: MutableList<ScaledDataPoint> = ArrayList()
val bgiArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
overviewData.maxBGIValue = Double.MIN_VALUE
val devArray: MutableList<DeviationDataPoint> = ArrayList()
overviewData.maxDevValueFound = Double.MIN_VALUE
val ratioArray: MutableList<ScaledDataPoint> = ArrayList()
overviewData.maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
overviewData.minRatioValueFound = -5.0
val dsMaxArray: MutableList<ScaledDataPoint> = ArrayList()
val dsMinArray: MutableList<ScaledDataPoint> = ArrayList()
overviewData.maxFromMaxValueFound = Double.MIN_VALUE
overviewData.maxFromMinValueFound = Double.MIN_VALUE
val adsData = iobCobCalculator.ads.clone()
while (time <= overviewData.toTime) {
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 5 * 60 * 1000L
continue
}
// IOB
val iob = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
val baseBasalIob = iobCobCalculator.calculateAbsoluteIobFromBaseBasals(time)
val absIob = IobTotal.combine(iob, baseBasalIob)
val autosensData = adsData.getAutosensDataAtTime(time)
if (abs(lastIob - iob.iob) > 0.02) {
if (abs(lastIob - iob.iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, overviewData.iobScale))
iobArray.add(ScaledDataPoint(time, iob.iob, overviewData.iobScale))
overviewData.maxIobValueFound = maxOf(overviewData.maxIobValueFound, abs(iob.iob))
lastIob = iob.iob
}
if (abs(absLastIob - absIob.iob) > 0.02) {
if (abs(absLastIob - absIob.iob) > 0.2) absIobArray.add(ScaledDataPoint(time, absLastIob, overviewData.iobScale))
absIobArray.add(ScaledDataPoint(time, absIob.iob, overviewData.iobScale))
overviewData.maxIobValueFound = maxOf(overviewData.maxIobValueFound, abs(absIob.iob))
absLastIob = absIob.iob
}
// COB
if (autosensData != null) {
val cob = autosensData.cob.toInt()
if (cob != lastCob) {
if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), overviewData.cobScale))
cobArray.add(ScaledDataPoint(time, cob.toDouble(), overviewData.cobScale))
overviewData.maxCobValueFound = max(overviewData.maxCobValueFound, cob.toDouble())
lastCob = cob
}
if (autosensData.failoverToMinAbsorbtionRate) {
autosensData.setScale(overviewData.cobScale)
autosensData.setChartTime(time)
minFailOverActiveList.add(autosensData)
}
}
// ACTIVITY
if (time <= now) actArrayHist.add(ScaledDataPoint(time, iob.activity, overviewData.actScale))
else actArrayPrediction.add(ScaledDataPoint(time, iob.activity, overviewData.actScale))
overviewData.maxIAValue = max(overviewData.maxIAValue, abs(iob.activity))
// BGI
val devBgiScale = overviewMenus.isEnabledIn(OverviewMenus.CharType.DEV) == overviewMenus.isEnabledIn(OverviewMenus.CharType.BGI)
val deviation = if (devBgiScale) autosensData?.deviation ?: 0.0 else 0.0
val bgi: Double = iob.activity * profile.getIsfMgdl(time) * 5.0
if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, overviewData.bgiScale))
else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, overviewData.bgiScale))
overviewData.maxBGIValue = max(overviewData.maxBGIValue, max(abs(bgi), deviation))
// DEVIATIONS
if (autosensData != null) {
var color = resourceHelper.gc(R.color.deviationblack) // "="
if (autosensData.type == "" || autosensData.type == "non-meal") {
if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey)
if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen)
if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred)
} else if (autosensData.type == "uam") {
color = resourceHelper.gc(R.color.uam)
} else if (autosensData.type == "csf") {
color = resourceHelper.gc(R.color.deviationgrey)
}
devArray.add(DeviationDataPoint(time.toDouble(), autosensData.deviation, color, overviewData.devScale))
overviewData.maxDevValueFound = maxOf(overviewData.maxDevValueFound, abs(autosensData.deviation), abs(bgi))
}
// RATIO
if (autosensData != null) {
ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1), overviewData.ratioScale))
overviewData.maxRatioValueFound = max(overviewData.maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
overviewData.minRatioValueFound = min(overviewData.minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
}
// DEV SLOPE
if (autosensData != null) {
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, overviewData.dsMaxScale))
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, overviewData.dsMinScale))
overviewData.maxFromMaxValueFound = max(overviewData.maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
overviewData.maxFromMinValueFound = max(overviewData.maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation))
}
time += 5 * 60 * 1000L
}
// IOB
overviewData.iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
it.color = resourceHelper.gc(R.color.iob)
it.thickness = 3
}
overviewData.absIobSeries = FixedLineGraphSeries(Array(absIobArray.size) { i -> absIobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
it.color = resourceHelper.gc(R.color.iob)
it.thickness = 3
}
if (overviewMenus.setting[0][OverviewMenus.CharType.PRE.ordinal]) {
val autosensData = adsData.getLastAutosensData("GraphData", aapsLogger, dateUtil)
val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult()
val isTempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
val iobPrediction: MutableList<DataPointWithLabelInterface> = ArrayList()
val iobPredictionArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray) {
iobPrediction.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)))
overviewData.maxIobValueFound = max(overviewData.maxIobValueFound, abs(i.iob))
}
overviewData.iobPredictions1Series = PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] })
val iobPrediction2: MutableList<DataPointWithLabelInterface> = ArrayList()
val iobPredictionArray2 = iobCobCalculator.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray2) {
iobPrediction2.add(i.setColor(resourceHelper.gc(R.color.iobPred)))
overviewData.maxIobValueFound = max(overviewData.maxIobValueFound, abs(i.iob))
}
overviewData.iobPredictions2Series = PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] })
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray))
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray2))
} else {
overviewData.iobPredictions1Series = PointsWithLabelGraphSeries()
overviewData.iobPredictions2Series = PointsWithLabelGraphSeries()
}
// COB
overviewData.cobSeries = FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50%
it.color = resourceHelper.gc(R.color.cob)
it.thickness = 3
}
overviewData.cobMinFailOverSeries = PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })
// ACTIVITY
overviewData.activitySeries = FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
it.isDrawBackground = false
it.color = resourceHelper.gc(R.color.activity)
it.thickness = 3
}
overviewData.activityPredictionSeries = FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
paint.color = resourceHelper.gc(R.color.activity)
})
}
// BGI
overviewData.minusBgiSeries = FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
it.isDrawBackground = false
it.color = resourceHelper.gc(R.color.bgi)
it.thickness = 3
}
overviewData.minusBgiHistSeries = FixedLineGraphSeries(Array(bgiArrayPrediction.size) { i -> bgiArrayPrediction[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
paint.color = resourceHelper.gc(R.color.bgi)
})
}
// DEVIATIONS
overviewData.deviationsSeries = BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
it.setValueDependentColor { data: DeviationDataPoint -> data.color }
}
// RATIO
overviewData.ratioSeries = LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
it.color = resourceHelper.gc(R.color.ratio)
it.thickness = 3
}
// DEV SLOPE
overviewData.dsMaxSeries = LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
it.color = resourceHelper.gc(R.color.devslopepos)
it.thickness = 3
}
overviewData.dsMinSeries = LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
it.color = resourceHelper.gc(R.color.devslopeneg)
it.thickness = 3
}
// profiler.log(LTag.UI, "prepareIobAutosensData() $from", start)
}
private fun addUpperChartMargin(maxBgValue: Double) =
if (profileFunction.getUnits() == GlucoseUnit.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4
private fun getNearestBg(date: Long): Double {
overviewData.bgReadingsArray.let { bgReadingsArray ->
for (reading in bgReadingsArray) {
if (reading.timestamp > date) continue
return Profile.fromMgdlToUnits(reading.value, profileFunction.getUnits())
}
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, profileFunction.getUnits())
else Profile.fromMgdlToUnits(100.0, profileFunction.getUnits())
}
}
private fun <E : DataPointWithLabelInterface> List<E>.filterTimeframe(fromTime: Long, endTime: Long): List<E> =
filter { it.x + it.duration >= fromTime && it.x <= endTime }
} }

View file

@ -74,7 +74,7 @@ class StatusLightHandler @Inject constructor(
private fun handleAge(view: TextView?, type: TherapyEvent.Type, @StringRes warnSettings: Int, defaultWarnThreshold: Double, @StringRes urgentSettings: Int, defaultUrgentThreshold: Double) { private fun handleAge(view: TextView?, type: TherapyEvent.Type, @StringRes warnSettings: Int, defaultWarnThreshold: Double, @StringRes urgentSettings: Int, defaultUrgentThreshold: Double) {
val warn = sp.getDouble(warnSettings, defaultWarnThreshold) val warn = sp.getDouble(warnSettings, defaultWarnThreshold)
val urgent = sp.getDouble(urgentSettings, defaultUrgentThreshold) val urgent = sp.getDouble(urgentSettings, defaultUrgentThreshold)
val therapyEvent = repository.getLastTherapyRecord(type).blockingGet() val therapyEvent = repository.getLastTherapyRecordUpToNow(type).blockingGet()
if (therapyEvent is ValueWrapper.Existing) { if (therapyEvent is ValueWrapper.Existing) {
warnColors.setColorByAge(view, therapyEvent.value, warn, urgent) warnColors.setColorByAge(view, therapyEvent.value, warn, urgent)
view?.text = therapyEvent.value.age(resourceHelper.shortTextMode(), resourceHelper, dateUtil) view?.text = therapyEvent.value.age(resourceHelper.shortTextMode(), resourceHelper, dateUtil)

View file

@ -64,12 +64,12 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() {
val manager = fragmentManager val manager = fragmentManager
val editQuickWizardDialog = EditQuickWizardDialog() val editQuickWizardDialog = EditQuickWizardDialog()
val bundle = Bundle() val bundle = Bundle()
bundle.putInt("position", adapterPosition) bundle.putInt("position", bindingAdapterPosition)
editQuickWizardDialog.arguments = bundle editQuickWizardDialog.arguments = bundle
editQuickWizardDialog.show(manager, "EditQuickWizardDialog") editQuickWizardDialog.show(manager, "EditQuickWizardDialog")
} }
removeButton.setOnClickListener { removeButton.setOnClickListener {
quickWizard.remove(adapterPosition) quickWizard.remove(bindingAdapterPosition)
rxBus.send(EventQuickWizardChange()) rxBus.send(EventQuickWizardChange())
} }
} }

View file

@ -26,16 +26,16 @@ import kotlin.math.max
class GraphData( class GraphData(
injector: HasAndroidInjector, injector: HasAndroidInjector,
private val graph: GraphView private val graph: GraphView,
private val overviewData: OverviewData
) { ) {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var overviewData: OverviewData
var maxY = Double.MIN_VALUE private var maxY = Double.MIN_VALUE
private var minY = Double.MAX_VALUE private var minY = Double.MAX_VALUE
private val units: GlucoseUnit private val units: GlucoseUnit
private val series: MutableList<Series<*>> = ArrayList() private val series: MutableList<Series<*>> = ArrayList()
@ -76,7 +76,7 @@ class GraphData(
addSeries(overviewData.tempBasalGraphSeries) addSeries(overviewData.tempBasalGraphSeries)
addSeries(overviewData.basalLineGraphSeries) addSeries(overviewData.basalLineGraphSeries)
addSeries(overviewData.absoluteBasalGraphSeries) addSeries(overviewData.absoluteBasalGraphSeries)
overviewData.basalScale.setMultiplier(maxY * scale / overviewData.maxBasalValueFound) overviewData.basalScale.multiplier = maxY * scale / overviewData.maxBasalValueFound
} }
fun addTargetLine() { fun addTargetLine() {
@ -91,7 +91,7 @@ class GraphData(
fun addActivity(scale: Double) { fun addActivity(scale: Double) {
addSeries(overviewData.activitySeries) addSeries(overviewData.activitySeries)
addSeries(overviewData.activityPredictionSeries) addSeries(overviewData.activityPredictionSeries)
overviewData.actScale.setMultiplier(maxY * scale / overviewData.maxIAValue) overviewData.actScale.multiplier = maxY * scale / overviewData.maxIAValue
} }
//Function below show -BGI to be able to compare curves with deviations //Function below show -BGI to be able to compare curves with deviations
@ -100,7 +100,7 @@ class GraphData(
maxY = overviewData.maxBGIValue maxY = overviewData.maxBGIValue
minY = -overviewData.maxBGIValue minY = -overviewData.maxBGIValue
} }
overviewData.bgiScale.setMultiplier(maxY * scale / overviewData.maxBGIValue) overviewData.bgiScale.multiplier = maxY * scale / overviewData.maxBGIValue
addSeries(overviewData.minusBgiSeries) addSeries(overviewData.minusBgiSeries)
addSeries(overviewData.minusBgiHistSeries) addSeries(overviewData.minusBgiHistSeries)
} }
@ -111,7 +111,7 @@ class GraphData(
maxY = overviewData.maxIobValueFound maxY = overviewData.maxIobValueFound
minY = -overviewData.maxIobValueFound minY = -overviewData.maxIobValueFound
} }
overviewData.iobScale.setMultiplier(maxY * scale / overviewData.maxIobValueFound) overviewData.iobScale.multiplier = maxY * scale / overviewData.maxIobValueFound
addSeries(overviewData.iobSeries) addSeries(overviewData.iobSeries)
addSeries(overviewData.iobPredictions1Series) addSeries(overviewData.iobPredictions1Series)
addSeries(overviewData.iobPredictions2Series) addSeries(overviewData.iobPredictions2Series)
@ -123,7 +123,7 @@ class GraphData(
maxY = overviewData.maxIobValueFound maxY = overviewData.maxIobValueFound
minY = -overviewData.maxIobValueFound minY = -overviewData.maxIobValueFound
} }
overviewData.iobScale.setMultiplier(maxY * scale / overviewData.maxIobValueFound) overviewData.iobScale.multiplier = maxY * scale / overviewData.maxIobValueFound
addSeries(overviewData.absIobSeries) addSeries(overviewData.absIobSeries)
} }
@ -133,7 +133,7 @@ class GraphData(
maxY = overviewData.maxCobValueFound maxY = overviewData.maxCobValueFound
minY = 0.0 minY = 0.0
} }
overviewData.cobScale.setMultiplier(maxY * scale / overviewData.maxCobValueFound) overviewData.cobScale.multiplier = maxY * scale / overviewData.maxCobValueFound
addSeries(overviewData.cobSeries) addSeries(overviewData.cobSeries)
addSeries(overviewData.cobMinFailOverSeries) addSeries(overviewData.cobMinFailOverSeries)
} }
@ -144,7 +144,7 @@ class GraphData(
maxY = overviewData.maxDevValueFound maxY = overviewData.maxDevValueFound
minY = -maxY minY = -maxY
} }
overviewData.devScale.setMultiplier(maxY * scale / overviewData.maxDevValueFound) overviewData.devScale.multiplier = maxY * scale / overviewData.maxDevValueFound
addSeries(overviewData.deviationsSeries) addSeries(overviewData.deviationsSeries)
} }
@ -153,20 +153,32 @@ class GraphData(
if (useForScale) { if (useForScale) {
maxY = 100.0 + max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound)) maxY = 100.0 + max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))
minY = 100.0 - max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound)) minY = 100.0 - max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))
overviewData.ratioScale.setMultiplier(1.0) overviewData.ratioScale.multiplier = 1.0
} else overviewData.ratioScale.shift = 100.0
overviewData.ratioScale.setMultiplier(maxY * scale / max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))) } else {
overviewData.ratioScale.multiplier = maxY * scale / max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))
overviewData.ratioScale.shift = 0.0
}
addSeries(overviewData.ratioSeries) addSeries(overviewData.ratioSeries)
} }
// scale in % of vertical size (like 0.3) // scale in % of vertical size (like 0.3)
fun addDeviationSlope(useForScale: Boolean, scale: Double) { fun addDeviationSlope(useForScale: Boolean, scale: Double, isRatioScale: Boolean = false) {
if (useForScale) { if (useForScale) {
maxY = max(overviewData.maxFromMaxValueFound, overviewData.maxFromMinValueFound) maxY = max(overviewData.maxFromMaxValueFound, overviewData.maxFromMinValueFound)
minY = -maxY minY = -maxY
} }
overviewData.dsMaxScale.setMultiplier(maxY * scale / overviewData.maxFromMaxValueFound) var graphMaxY = maxY
overviewData.dsMinScale.setMultiplier(maxY * scale / overviewData.maxFromMinValueFound) if (isRatioScale) {
graphMaxY = maxY - 100.0
overviewData.dsMinScale.shift = 100.0
overviewData.dsMaxScale.shift = 100.0
} else {
overviewData.dsMinScale.shift = 0.0
overviewData.dsMaxScale.shift = 0.0
}
overviewData.dsMaxScale.multiplier = graphMaxY * scale / overviewData.maxFromMaxValueFound
overviewData.dsMinScale.multiplier = graphMaxY * scale / overviewData.maxFromMinValueFound
addSeries(overviewData.dsMaxSeries) addSeries(overviewData.dsMaxSeries)
addSeries(overviewData.dsMinSeries) addSeries(overviewData.dsMinSeries)
} }
@ -201,7 +213,7 @@ class GraphData(
graph.gridLabelRenderer.numHorizontalLabels = 7 // only 7 because of the space graph.gridLabelRenderer.numHorizontalLabels = 7 // only 7 because of the space
} }
internal fun addSeries(s: Series<*>) = series.add(s) private fun addSeries(s: Series<*>) = series.add(s)
fun performUpdate() { fun performUpdate() {
// clear old data // clear old data

View file

@ -3,10 +3,8 @@ package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.graphics.Color import android.graphics.Color
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.core.R import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.Interval
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -17,7 +15,7 @@ class TherapyEventDataPoint @Inject constructor(
private val resourceHelper: ResourceHelper, private val resourceHelper: ResourceHelper,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val translator: Translator private val translator: Translator
) : DataPointWithLabelInterface, Interval { ) : DataPointWithLabelInterface {
private var yValue = 0.0 private var yValue = 0.0
@ -49,10 +47,10 @@ class TherapyEventDataPoint @Inject constructor(
} }
override fun getLabel(): String? = override fun getLabel(): String? =
if (data.note != null) data.note if (data.note.isNullOrBlank().not()) data.note
else translator.translate(data.type) else translator.translate(data.type)
override fun getDuration(): Long = end() - start() override fun getDuration(): Long = data.duration
override fun getShape(): PointsWithLabelGraphSeries.Shape = override fun getShape(): PointsWithLabelGraphSeries.Shape =
when { when {
data.type == TherapyEvent.Type.NS_MBG -> PointsWithLabelGraphSeries.Shape.MBG data.type == TherapyEvent.Type.NS_MBG -> PointsWithLabelGraphSeries.Shape.MBG
@ -74,22 +72,4 @@ class TherapyEventDataPoint @Inject constructor(
TherapyEvent.Type.APS_OFFLINE -> Color.GRAY and -0x7f000001 TherapyEvent.Type.APS_OFFLINE -> Color.GRAY and -0x7f000001
else -> Color.GRAY else -> Color.GRAY
} }
// Interval interface
private var cutEnd: Long? = null
override fun durationInMsec(): Long = data.duration
override fun start(): Long = data.timestamp
override fun originalEnd(): Long = data.timestamp + durationInMsec()
override fun end(): Long = cutEnd ?: originalEnd()
override fun cutEndTo(end: Long) {
cutEnd = end
}
override fun match(time: Long): Boolean = start() <= time && end() >= time
override fun before(time: Long): Boolean = end() < time
override fun after(time: Long): Boolean = start() > time
override val isInProgress: Boolean get() = match(System.currentTimeMillis())
override val isEndingEvent: Boolean get() = durationInMsec() == 0L
override val isValid: Boolean get() = data.type == TherapyEvent.Type.APS_OFFLINE
} }

View file

@ -128,7 +128,7 @@ class NotificationStore @Inject constructor(
private fun deleteIntent(id: Int): PendingIntent { private fun deleteIntent(id: Int): PendingIntent {
val intent = Intent(context, DismissNotificationService::class.java) val intent = Intent(context, DismissNotificationService::class.java)
intent.putExtra("alertID", id) intent.putExtra("alertID", id)
return PendingIntent.getService(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT) return PendingIntent.getService(context, id, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
} }
fun createNotificationChannel() { fun createNotificationChannel() {

View file

@ -171,7 +171,7 @@ class PersistentNotificationPlugin @Inject constructor(
val msgReadPendingIntent = PendingIntent.getBroadcast(context, val msgReadPendingIntent = PendingIntent.getBroadcast(context,
notificationHolder.notificationID, notificationHolder.notificationID,
msgReadIntent, msgReadIntent,
PendingIntent.FLAG_UPDATE_CURRENT) PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
val msgReplyIntent = Intent() val msgReplyIntent = Intent()
.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
.setAction(REPLY_ACTION) .setAction(REPLY_ACTION)
@ -181,7 +181,7 @@ class PersistentNotificationPlugin @Inject constructor(
context, context,
notificationHolder.notificationID, notificationHolder.notificationID,
msgReplyIntent, msgReplyIntent,
PendingIntent.FLAG_UPDATE_CURRENT) PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
// Build a RemoteInput for receiving voice input from devices // Build a RemoteInput for receiving voice input from devices
val remoteInput = RemoteInput.Builder(EXTRA_VOICE_REPLY).build() val remoteInput = RemoteInput.Builder(EXTRA_VOICE_REPLY).build()
// Create the UnreadConversation // Create the UnreadConversation

View file

@ -1,13 +1,16 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator package info.nightscout.androidaps.plugins.general.smsCommunicator
import android.os.SystemClock
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePasswordValidationResult import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePasswordValidationResult
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject import javax.inject.Inject
@ -23,6 +26,7 @@ class AuthRequest internal constructor(
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var otp: OneTimePassword @Inject lateinit var otp: OneTimePassword
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var commandQueue: CommandQueueProvider
private var date = 0L private var date = 0L
private var processed = false private var processed = false
@ -49,6 +53,19 @@ class AuthRequest internal constructor(
} }
if (dateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) { if (dateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) {
processed = true processed = true
if (action.pumpCommand) {
val start = dateUtil.now()
//wait for empty queue
while (start + T.mins(3).msecs() > dateUtil.now()) {
if (commandQueue.size() == 0) break
SystemClock.sleep(100)
}
if (commandQueue.size() != 0) {
aapsLogger.debug(LTag.SMS, "Command timed out: " + requester.text)
smsCommunicatorPlugin.sendSMS(Sms(requester.phoneNumber, resourceHelper.gs(R.string.sms_timeout_while_wating)))
return
}
}
aapsLogger.debug(LTag.SMS, "Processing confirmed SMS: " + requester.text) aapsLogger.debug(LTag.SMS, "Processing confirmed SMS: " + requester.text)
action.run() action.run()
return return

View file

@ -1,37 +1,37 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator package info.nightscout.androidaps.plugins.general.smsCommunicator
abstract class SmsAction : Runnable { abstract class SmsAction(val pumpCommand: Boolean) : Runnable {
var aDouble: Double? = null var aDouble: Double? = null
var anInteger: Int? = null var anInteger: Int? = null
var secondInteger: Int? = null var secondInteger: Int? = null
var secondLong: Long? = null var secondLong: Long? = null
var aString: String? = null var aString: String? = null
internal constructor() internal constructor(pumpCommand: Boolean, aDouble: Double) : this(pumpCommand) {
internal constructor(aDouble: Double) {
this.aDouble = aDouble this.aDouble = aDouble
} }
internal constructor(aDouble: Double, secondInteger: Int) { internal constructor(pumpCommand: Boolean, aDouble: Double, secondInteger: Int) : this(pumpCommand) {
this.aDouble = aDouble this.aDouble = aDouble
this.secondInteger = secondInteger this.secondInteger = secondInteger
} }
internal constructor(aString: String, secondInteger: Int) { internal constructor(pumpCommand: Boolean, aString: String, secondInteger: Int) : this(pumpCommand) {
this.aString = aString this.aString = aString
this.secondInteger = secondInteger this.secondInteger = secondInteger
} }
internal constructor(anInteger: Int) { internal constructor(pumpCommand: Boolean, anInteger: Int) : this(pumpCommand) {
this.anInteger = anInteger this.anInteger = anInteger
} }
internal constructor(anInteger: Int, secondInteger: Int) { internal constructor(pumpCommand: Boolean, anInteger: Int, secondInteger: Int) : this(pumpCommand) {
this.anInteger = anInteger this.anInteger = anInteger
this.secondInteger = secondInteger this.secondInteger = secondInteger
} }
internal constructor(anInteger: Int, secondLong: Long) { internal constructor(pumpCommand: Boolean, anInteger: Int, secondLong: Long) : this(pumpCommand) {
this.anInteger = anInteger this.anInteger = anInteger
this.secondLong = secondLong this.secondLong = secondLong
} }

View file

@ -11,25 +11,27 @@ import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import androidx.work.workDataOf import androidx.work.workDataOf
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.valueToUnitsString
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
@ -41,7 +43,6 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProv
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.extensions.valueToUnitsString
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -59,11 +60,13 @@ import javax.inject.Singleton
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@OpenForTesting
@Singleton @Singleton
class SmsCommunicatorPlugin @Inject constructor( class SmsCommunicatorPlugin @Inject constructor(
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
resourceHelper: ResourceHelper, resourceHelper: ResourceHelper,
private val smsManager: SmsManager,
private val aapsSchedulers: AapsSchedulers, private val aapsSchedulers: AapsSchedulers,
private val sp: SP, private val sp: SP,
private val constraintChecker: ConstraintChecker, private val constraintChecker: ConstraintChecker,
@ -72,7 +75,7 @@ class SmsCommunicatorPlugin @Inject constructor(
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val activePlugin: ActivePlugin, private val activePlugin: ActivePlugin,
private val commandQueue: CommandQueueProvider, private val commandQueue: CommandQueueProvider,
private val loopPlugin: LoopPlugin, private val loop: Loop,
private val iobCobCalculator: IobCobCalculator, private val iobCobCalculator: IobCobCalculator,
private val xdripCalibrations: XdripCalibrations, private val xdripCalibrations: XdripCalibrations,
private var otp: OneTimePassword, private var otp: OneTimePassword,
@ -94,8 +97,8 @@ class SmsCommunicatorPlugin @Inject constructor(
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
var allowedNumbers: MutableList<String> = ArrayList() var allowedNumbers: MutableList<String> = ArrayList()
var messageToConfirm: AuthRequest? = null @Volatile var messageToConfirm: AuthRequest? = null
var lastRemoteBolusTime: Long = 0 @Volatile var lastRemoteBolusTime: Long = 0
var messages = ArrayList<Sms>() var messages = ArrayList<Sms>()
val commands = mapOf( val commands = mapOf(
@ -181,7 +184,6 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
@kotlin.ExperimentalStdlibApi
override fun doWork(): Result { override fun doWork(): Result {
val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1)) val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data")) ?: return Result.failure(workDataOf("Error" to "missing input data"))
@ -224,7 +226,6 @@ class SmsCommunicatorPlugin @Inject constructor(
return false return false
} }
@kotlin.ExperimentalStdlibApi
fun processSms(receivedSms: Sms) { fun processSms(receivedSms: Sms) {
if (!isEnabled(PluginType.GENERAL)) { if (!isEnabled(PluginType.GENERAL)) {
aapsLogger.debug(LTag.SMS, "Ignoring SMS. Plugin disabled.") aapsLogger.debug(LTag.SMS, "Ignoring SMS. Plugin disabled.")
@ -278,6 +279,7 @@ class SmsCommunicatorPlugin @Inject constructor(
else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat)))
"BOLUS" -> "BOLUS" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed)))
else if (commandQueue.bolusInQueue()) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_another_bolus_in_queue)))
else if (divided.size == 2 && dateUtil.now() - lastRemoteBolusTime < minDistance) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotebolusnotallowed))) else if (divided.size == 2 && dateUtil.now() - lastRemoteBolusTime < minDistance) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotebolusnotallowed)))
else if (divided.size == 2 && pump.isSuspended()) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.pumpsuspended))) else if (divided.size == 2 && pump.isSuspended()) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.pumpsuspended)))
else if (divided.size == 2 || divided.size == 3) processBOLUS(divided, receivedSms) else if (divided.size == 2 || divided.size == 3) processBOLUS(divided, receivedSms)
@ -303,9 +305,13 @@ class SmsCommunicatorPlugin @Inject constructor(
else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat)))
else -> else ->
if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) { if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) {
messageToConfirm?.action(divided[0]) val execute = messageToConfirm
messageToConfirm = null messageToConfirm = null
} else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_unknowncommand))) execute?.action(divided[0])
} else {
messageToConfirm = null
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_unknowncommand)))
}
} }
} }
rxBus.send(EventSmsCommunicatorUpdateGui()) rxBus.send(EventSmsCommunicatorUpdateGui())
@ -336,18 +342,17 @@ class SmsCommunicatorPlugin @Inject constructor(
receivedSms.processed = true receivedSms.processed = true
} }
@kotlin.ExperimentalStdlibApi
private fun processLOOP(divided: Array<String>, receivedSms: Sms) { private fun processLOOP(divided: Array<String>, receivedSms: Sms) {
when (divided[1].uppercase(Locale.getDefault())) { when (divided[1].uppercase(Locale.getDefault())) {
"DISABLE", "STOP" -> { "DISABLE", "STOP" -> {
if (loopPlugin.isEnabled(PluginType.LOOP)) { if (loop.enabled) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopdisablereplywithcode), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopdisablereplywithcode), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
uel.log(Action.LOOP_DISABLED, Sources.SMS) uel.log(Action.LOOP_DISABLED, Sources.SMS)
loopPlugin.setPluginEnabled(PluginType.LOOP, false) loop.enabled = false
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
rxBus.send(EventRefreshOverview("SMS_LOOP_STOP")) rxBus.send(EventRefreshOverview("SMS_LOOP_STOP"))
@ -364,14 +369,14 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
"ENABLE", "START" -> { "ENABLE", "START" -> {
if (!loopPlugin.isEnabled(PluginType.LOOP)) { if (!loop.enabled) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopenablereplywithcode), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopenablereplywithcode), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
uel.log(Action.LOOP_ENABLED, Sources.SMS) uel.log(Action.LOOP_ENABLED, Sources.SMS)
loopPlugin.setPluginEnabled(PluginType.LOOP, true) loop.enabled = true
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loophasbeenenabled))) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loophasbeenenabled)))
rxBus.send(EventRefreshOverview("SMS_LOOP_START")) rxBus.send(EventRefreshOverview("SMS_LOOP_START"))
} }
@ -382,8 +387,8 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
"STATUS" -> { "STATUS" -> {
val reply = if (loopPlugin.isEnabled(PluginType.LOOP)) { val reply = if (loop.enabled) {
if (loopPlugin.isSuspended) String.format(resourceHelper.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) if (loop.isSuspended) String.format(resourceHelper.gs(R.string.loopsuspendedfor), loop.minutesToEndOfSuspend())
else resourceHelper.gs(R.string.smscommunicator_loopisenabled) else resourceHelper.gs(R.string.smscommunicator_loopisenabled)
} else } else
resourceHelper.gs(R.string.loopisdisabled) resourceHelper.gs(R.string.loopisdisabled)
@ -395,10 +400,15 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopresumereplywithcode), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopresumereplywithcode), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
uel.log(Action.RESUME, Sources.SMS) uel.log(Action.RESUME, Sources.SMS)
loopPlugin.suspendTo(0L) disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("SMS_LOOP_RESUME")) rxBus.send(EventRefreshOverview("SMS_LOOP_RESUME"))
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
@ -409,7 +419,6 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
} }
}) })
loopPlugin.createOfflineEvent(0)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loopresumed))) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loopresumed)))
} }
}) })
@ -428,14 +437,19 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(duration) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, duration) {
override fun run() { override fun run() {
uel.log(Action.SUSPEND, Sources.SMS) uel.log(Action.SUSPEND, Sources.SMS)
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (result.success) { if (result.success) {
loopPlugin.suspendTo(dateUtil.now() + anInteger() * 60L * 1000) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(anInteger().toLong()).msecs(), OfflineEvent.Reason.SUSPEND))
loopPlugin.createOfflineEvent(anInteger() * 60) .subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("SMS_LOOP_SUSPENDED")) rxBus.send(EventRefreshOverview("SMS_LOOP_SUSPENDED"))
val replyText = resourceHelper.gs(R.string.smscommunicator_loopsuspended) + " " + val replyText = resourceHelper.gs(R.string.smscommunicator_loopsuspended) + " " +
resourceHelper.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed) resourceHelper.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed)
@ -456,7 +470,6 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
} }
@kotlin.ExperimentalStdlibApi
private fun processNSCLIENT(divided: Array<String>, receivedSms: Sms) { private fun processNSCLIENT(divided: Array<String>, receivedSms: Sms) {
if (divided[1].uppercase(Locale.getDefault()) == "RESTART") { if (divided[1].uppercase(Locale.getDefault()) == "RESTART") {
rxBus.send(EventNSClientRestart()) rxBus.send(EventNSClientRestart())
@ -466,7 +479,6 @@ class SmsCommunicatorPlugin @Inject constructor(
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat)))
} }
@kotlin.ExperimentalStdlibApi
private fun processHELP(divided: Array<String>, receivedSms: Sms) { private fun processHELP(divided: Array<String>, receivedSms: Sms) {
when { when {
divided.size == 1 -> { divided.size == 1 -> {
@ -504,7 +516,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_pumpconnectwithcode), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_pumpconnectwithcode), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
uel.log(Action.RECONNECT, Sources.SMS) uel.log(Action.RECONNECT, Sources.SMS)
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
@ -512,10 +524,14 @@ class SmsCommunicatorPlugin @Inject constructor(
if (!result.success) { if (!result.success) {
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpconnectfail))) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpconnectfail)))
} else { } else {
loopPlugin.suspendTo(0L) disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_reconnect))) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_reconnect)))
rxBus.send(EventRefreshOverview("SMS_PUMP_START")) rxBus.send(EventRefreshOverview("SMS_PUMP_START"))
loopPlugin.createOfflineEvent(0)
} }
} }
}) })
@ -533,11 +549,11 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_pumpdisconnectwithcode), duration, passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_pumpdisconnectwithcode), duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
uel.log(Action.DISCONNECT, Sources.SMS) uel.log(Action.DISCONNECT, Sources.SMS)
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile() ?: return
loopPlugin.disconnectPump(duration, profile) loop.goToZeroTemp(duration, profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("SMS_PUMP_DISCONNECT")) rxBus.send(EventRefreshOverview("SMS_PUMP_DISCONNECT"))
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpdisconnected))) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpdisconnected)))
} }
@ -549,7 +565,6 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
} }
@kotlin.ExperimentalStdlibApi
private fun processPROFILE(divided: Array<String>, receivedSms: Sms) { // load profiles private fun processPROFILE(divided: Array<String>, receivedSms: Sms) { // load profiles
val anInterface = activePlugin.activeProfileSource val anInterface = activePlugin.activeProfileSource
val store = anInterface.profile val store = anInterface.profile
@ -588,7 +603,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_profilereplywithcode), list[pIndex - 1], percentage, passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_profilereplywithcode), list[pIndex - 1], percentage, passCode)
receivedSms.processed = true receivedSms.processed = true
val finalPercentage = percentage val finalPercentage = percentage
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(list[pIndex - 1] as String, finalPercentage) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, list[pIndex - 1] as String, finalPercentage) {
override fun run() { override fun run() {
profileFunction.createProfileSwitch(store, list[pIndex - 1] as String, 0, finalPercentage, 0, dateUtil.now()) profileFunction.createProfileSwitch(store, list[pIndex - 1] as String, 0, finalPercentage, 0, dateUtil.now())
val replyText = resourceHelper.gs(R.string.profileswitchcreated) val replyText = resourceHelper.gs(R.string.profileswitchcreated)
@ -603,13 +618,12 @@ class SmsCommunicatorPlugin @Inject constructor(
receivedSms.processed = true receivedSms.processed = true
} }
@kotlin.ExperimentalStdlibApi
private fun processBASAL(divided: Array<String>, receivedSms: Sms) { private fun processBASAL(divided: Array<String>, receivedSms: Sms) {
if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") { if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalstopreplywithcode), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalstopreplywithcode), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
@ -644,7 +658,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasalPct, duration) {
override fun run() { override fun run() {
commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() {
override fun run() { override fun run() {
@ -688,7 +702,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasal, duration) {
override fun run() { override fun run() {
commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() {
override fun run() { override fun run() {
@ -720,13 +734,12 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
} }
@kotlin.ExperimentalStdlibApi
private fun processEXTENDED(divided: Array<String>, receivedSms: Sms) { private fun processEXTENDED(divided: Array<String>, receivedSms: Sms) {
if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") { if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
commandQueue.cancelExtended(object : Callback() { commandQueue.cancelExtended(object : Callback() {
override fun run() { override fun run() {
@ -756,7 +769,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(extended, duration) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, extended, duration) {
override fun run() { override fun run() {
commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() { commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() {
override fun run() { override fun run() {
@ -802,7 +815,7 @@ class SmsCommunicatorPlugin @Inject constructor(
else else
String.format(resourceHelper.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode) String.format(resourceHelper.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(bolus) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, bolus) {
override fun run() { override fun run() {
val detailedBolusInfo = DetailedBolusInfo() val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.insulin = aDouble() detailedBolusInfo.insulin = aDouble()
@ -832,7 +845,7 @@ class SmsCommunicatorPlugin @Inject constructor(
currentProfile.units == GlucoseUnit.MMOL -> Constants.defaultEatingSoonTTmmol currentProfile.units == GlucoseUnit.MMOL -> Constants.defaultEatingSoonTTmmol
else -> Constants.defaultEatingSoonTTmgdl else -> Constants.defaultEatingSoonTTmgdl
} }
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = dateUtil.now(), timestamp = dateUtil.now(),
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON, reason = TemporaryTarget.Reason.EATING_SOON,
@ -887,7 +900,6 @@ class SmsCommunicatorPlugin @Inject constructor(
return retVal return retVal
} }
@kotlin.ExperimentalStdlibApi
private fun processCARBS(divided: Array<String>, receivedSms: Sms) { private fun processCARBS(divided: Array<String>, receivedSms: Sms) {
var grams = SafeParse.stringToInt(divided[1]) var grams = SafeParse.stringToInt(divided[1])
var time = dateUtil.now() var time = dateUtil.now()
@ -904,7 +916,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_carbsreplywithcode), grams, dateUtil.timeString(time), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_carbsreplywithcode), grams, dateUtil.timeString(time), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(grams, time) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, grams, time) {
override fun run() { override fun run() {
val detailedBolusInfo = DetailedBolusInfo() val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.carbs = anInteger().toDouble() detailedBolusInfo.carbs = anInteger().toDouble()
@ -931,7 +943,6 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
} }
@kotlin.ExperimentalStdlibApi
private fun processTARGET(divided: Array<String>, receivedSms: Sms) { private fun processTARGET(divided: Array<String>, receivedSms: Sms) {
val isMeal = divided[1].equals("MEAL", ignoreCase = true) val isMeal = divided[1].equals("MEAL", ignoreCase = true)
val isActivity = divided[1].equals("ACTIVITY", ignoreCase = true) val isActivity = divided[1].equals("ACTIVITY", ignoreCase = true)
@ -941,7 +952,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_temptargetwithcode), divided[1].uppercase(Locale.getDefault()), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_temptargetwithcode), divided[1].uppercase(Locale.getDefault()), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
var keyDuration = 0 var keyDuration = 0
@ -979,7 +990,7 @@ class SmsCommunicatorPlugin @Inject constructor(
var tt = sp.getDouble(keyTarget, if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL) var tt = sp.getDouble(keyTarget, if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL)
tt = Profile.toCurrentUnits(profileFunction, tt) tt = Profile.toCurrentUnits(profileFunction, tt)
tt = if (tt > 0) tt else if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL tt = if (tt > 0) tt else if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = dateUtil.now(), timestamp = dateUtil.now(),
duration = TimeUnit.MINUTES.toMillis(ttDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(ttDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON, reason = TemporaryTarget.Reason.EATING_SOON,
@ -1003,7 +1014,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_temptargetcancel), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_temptargetcancel), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(dateUtil.now())) disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(dateUtil.now()))
.subscribe({ result -> .subscribe({ result ->
@ -1028,7 +1039,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_stopsmswithcode), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_stopsmswithcode), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
sp.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false) sp.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)
val replyText = String.format(resourceHelper.gs(R.string.smscommunicator_stoppedsms)) val replyText = String.format(resourceHelper.gs(R.string.smscommunicator_stoppedsms))
@ -1046,7 +1057,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(cal) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false, cal) {
override fun run() { override fun run() {
val result = xdripCalibrations.sendIntent(aDouble!!) val result = xdripCalibrations.sendIntent(aDouble!!)
val replyText = val replyText =
@ -1080,7 +1091,6 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
fun sendSMS(sms: Sms): Boolean { fun sendSMS(sms: Sms): Boolean {
val smsManager = SmsManager.getDefault()
sms.text = stripAccents(sms.text) sms.text = stripAccents(sms.text)
try { try {
aapsLogger.debug(LTag.SMS, "Sending SMS to " + sms.phoneNumber + ": " + sms.text) aapsLogger.debug(LTag.SMS, "Sending SMS to " + sms.phoneNumber + ": " + sms.text)

View file

@ -88,7 +88,7 @@ class SmsCommunicatorOtpActivity : NoSplashAppCompatActivity() {
Runnable { Runnable {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("OTP Secret", otp.provisioningSecret()) val clip = ClipData.newPlainText("OTP Secret", otp.provisioningSecret())
clipboard.primaryClip = clip clipboard.setPrimaryClip(clip)
ToastUtils.Long.infoToast(this, resourceHelper.gs(R.string.smscommunicator_otp_export_successful)) ToastUtils.Long.infoToast(this, resourceHelper.gs(R.string.smscommunicator_otp_export_successful))
uel.log(Action.OTP_EXPORT, Sources.SMS) uel.log(Action.OTP_EXPORT, Sources.SMS)
}) })

View file

@ -5,6 +5,7 @@ import com.eatthepath.otp.HmacOneTimePasswordGenerator
import com.google.common.io.BaseEncoding import com.google.common.io.BaseEncoding
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -15,6 +16,7 @@ import javax.crypto.spec.SecretKeySpec
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@OpenForTesting
@Singleton @Singleton
class OneTimePassword @Inject constructor( class OneTimePassword @Inject constructor(
private val sp: SP, private val sp: SP,

View file

@ -20,7 +20,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.extensions.total import info.nightscout.androidaps.extensions.total
import info.nightscout.androidaps.extensions.valueToUnits import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
@ -572,7 +572,7 @@ class ActionStringHandler @Inject constructor(
private fun generateTempTarget(duration: Int, low: Double, high: Double) { private fun generateTempTarget(duration: Int, low: Double, high: Double) {
if (duration != 0) { if (duration != 0) {
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(duration.toLong()), duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
reason = TemporaryTarget.Reason.WEAR, reason = TemporaryTarget.Reason.WEAR,

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