Merge pull request #579 from nightscout/dev

AndroidAPS 3.0.0
This commit is contained in:
Milos Kozak 2022-01-31 22:05:22 +01:00 committed by GitHub
commit 23207a275f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4034 changed files with 216960 additions and 110473 deletions

73
.circleci/config.yml Normal file
View file

@ -0,0 +1,73 @@
# Use the latest 2.1 version of CircleCI pipeline process engine.
# See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1
# Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.
orbs:
android: circleci/android@1.0.3
codecov: codecov/codecov@1.2.0
jobs:
# Below is the definition of your job to build and test your app, you can rename and customize it as you want.
build-and-test:
# These next lines define the Android machine image executor: https://circleci.com/docs/2.0/executor-types/
executor:
name: android/android-machine
steps:
# Checkout the code as the first step.
- checkout
# The next step will run the unit tests
- android/run-tests:
test-command: ./gradlew -Pcoverage -PfirebaseDisable testFullDebugUnitTest jacocoTestFullDebugUnitTestReport
# Then start the emulator and run the Instrumentation tests!
# - android/start-emulator-and-run-tests:
# test-command: ./gradlew connectedDebugAndroidTest
# system-image: system-images;android-25;google_apis;x86
# And finally run the release build
# - run:
# name: Assemble release build
# command: |
# ./gradlew assembleRelease
- codecov/upload:
file: './app/build/jacoco/jacoco.xml'
- codecov/upload:
file: './automation/build/jacoco/jacoco.xml'
- codecov/upload:
file: './combo/build/jacoco/jacoco.xml'
- codecov/upload:
file: './core/build/jacoco/jacoco.xml'
- codecov/upload:
file: './dana/build/jacoco/jacoco.xml'
- codecov/upload:
file: './danar/build/jacoco/jacoco.xml'
- codecov/upload:
file: './danars/build/jacoco/jacoco.xml'
- codecov/upload:
file: './database/build/jacoco/jacoco.xml'
- codecov/upload:
file: './insight/build/jacoco/jacoco.xml'
- codecov/upload:
file: './medtronic/build/jacoco/jacoco.xml'
- codecov/upload:
file: './omnipod-common/build/jacoco/jacoco.xml'
- codecov/upload:
file: './omnipod-dash/build/jacoco/jacoco.xml'
- codecov/upload:
file: './omnipod-eros/build/jacoco/jacoco.xml'
- codecov/upload:
file: './rileylink/build/jacoco/jacoco.xml'
- codecov/upload:
file: './wear/build/jacoco/jacoco.xml'
workflows:
# Below is the definition of your workflow.
# 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.
# For more details on extending your workflow, see the configuration docs: https://circleci.com/docs/2.0/configuration-reference/#workflows
dotests:
jobs:
- build-and-test

5
.editorconfig Normal file
View file

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

19
.github/ISSUE_TEMPLATE/custom.md vendored Normal file
View file

@ -0,0 +1,19 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---
Reporting bugs
--------------
- **Note the precise time the problem occurred** and describe the circumstances and steps that caused
the problem
- Note the Build version (found in the About dialog in the app, when pressing the three dots in the
upper-right corner).
- Obtain the app's log files, which can be found on the phone in
_/storage/emulated/0/Android/data/info.nightscout.androidaps/_
See https://androidaps.readthedocs.io/en/latest/Usage/Accessing-logfiles.html
- Open an issue at https://github.com/nightscout/AndroidAPS/issues/new

4
.gitignore vendored
View file

@ -2,15 +2,15 @@
.gradle .gradle
/local.properties /local.properties
.DS_Store .DS_Store
*/jacoco.exec
/build /build
/captures /captures
*.apk *.apk
build/ build/
!.idea/dictionaries/project-dictionary.xml
.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,27 +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="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="java.util" alias="false" withSubpackages="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<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" />
@ -140,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="200" />
<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

@ -0,0 +1,108 @@
<component name="ProjectDictionaryState">
<dictionary name="project-dictionary">
<words>
<w>aaps</w>
<w>abcdef</w>
<w>acked</w>
<w>actionstring</w>
<w>allowednumbers</w>
<w>androidaps</w>
<w>autosens</w>
<w>autosensdata</w>
<w>autosense</w>
<w>bage</w>
<w>basaliob</w>
<w>basals</w>
<w>bgcheck</w>
<w>bgsource</w>
<w>boluscalc</w>
<w>bolusing</w>
<w>carb</w>
<w>carbratio</w>
<w>carbs</w>
<w>carbsreq</w>
<w>careportal</w>
<w>cellnovo</w>
<w>crashlytics</w>
<w>danar</w>
<w>danars</w>
<w>dataset</w>
<w>datasets</w>
<w>dexcom</w>
<w>dexdrip</w>
<w>enteredby</w>
<w>enteredinsulin</w>
<w>eveningoutpost</w>
<w>eversense</w>
<w>extendedbolus</w>
<w>fileprovider</w>
<w>firebase</w>
<w>glimp</w>
<w>gson</w>
<w>hmac</w>
<w>iage</w>
<w>insulet</w>
<w>iobtotal</w>
<w>libre</w>
<w>listdelimiter</w>
<w>localprofile</w>
<w>medtronic</w>
<w>mgdl</w>
<w>mmol</w>
<w>motol</w>
<w>multiwave</w>
<w>netinsulin</w>
<w>netratio</w>
<w>nightscout</w>
<w>notif</w>
<w>nsclient</w>
<w>okcancel</w>
<w>omnipod</w>
<w>openaps</w>
<w>openhumans</w>
<w>oref</w>
<w>otpauth</w>
<w>passcode</w>
<w>pdus</w>
<w>philoul</w>
<w>poctech</w>
<w>pred</w>
<w>profileswitch</w>
<w>pumpbtcomm</w>
<w>quickwizard</w>
<w>readstatus</w>
<w>realduration</w>
<w>refresheventsfromnightscout</w>
<w>rileylink</w>
<w>roboelectric</w>
<w>sgvs</w>
<w>sitechange</w>
<w>smscommunicator</w>
<w>sntp</w>
<w>sooil</w>
<w>soundid</w>
<w>splitted</w>
<w>ssid</w>
<w>superbolus</w>
<w>targethigh</w>
<w>targetlow</w>
<w>tbrs</w>
<w>tdds</w>
<w>tempbasal</w>
<w>tempbasals</w>
<w>temptarget</w>
<w>textl</w>
<w>tidepool</w>
<w>timeshift</w>
<w>tirs</w>
<w>totp</w>
<w>uart</w>
<w>wizzardpage</w>
<w>xdrip</w>
<w>xstream</w>
<w>ypso</w>
<w>ypsomed</w>
<w>ypsopump</w>
</words>
</dictionary>
</component>

View file

@ -1,5 +1,5 @@
language: android language: android
jdk: oraclejdk8 jdk: openjdk11
dist: trusty dist: trusty
env: env:
matrix: matrix:

View file

@ -1,33 +1,24 @@
buildscript {
repositories {
jcenter()
maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2
}
dependencies {
//classpath 'com.dicedmelon.gradle:jacoco-android:0.1.4'
classpath 'com.hiya:jacoco-android:0.2'
}
}
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
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: 'jacoco-android'
apply plugin: 'com.hiya.jacoco-android' apply plugin: 'com.hiya.jacoco-android'
apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'com.google.firebase.crashlytics'
jacoco { apply from: "${project.rootDir}/gradle/android_dependencies.gradle"
toolVersion = "0.8.3" apply from: "${project.rootDir}/gradle/jacoco_global.gradle"
}
repositories { repositories {
jcenter { url "https://jcenter.bintray.com/" }
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 {
@ -108,57 +99,27 @@ def allCommitted = { ->
tasks.matching { it instanceof Test }.all { tasks.matching { it instanceof Test }.all {
testLogging.events = ["failed", "skipped", "started"] testLogging.events = ["failed", "skipped", "started"]
// testLogging.events = ["failed", "skipped", "started", "standard_out"] use to display stdout in travis
testLogging.exceptionFormat = "full" testLogging.exceptionFormat = "full"
} }
android { android {
compileSdkVersion 28
ndkVersion "21.1.6352462" ndkVersion "21.1.6352462"
defaultConfig { defaultConfig {
minSdkVersion 26
targetSdkVersion 28
multiDexEnabled true multiDexEnabled true
versionCode 1500 versionCode 1500
version "2.8.2.1" version "3.0.0"
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() + '"'
buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"' buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"'
buildConfigField "String", "COMMITTED", '"' + allCommitted() + '"' buildConfigField "String", "COMMITTED", '"' + allCommitted() + '"'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// if you change minSdkVersion to less than 11, you need to change executeTask for wear
ndk {
moduleName "BleCommandUtil"
} }
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
lintOptions {
checkReleaseBuilds false
disable 'MissingTranslation'
disable 'ExtraTranslation'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
testCoverageEnabled(project.hasProperty('coverage'))
}
firebaseDisable {
System.setProperty("disableFirebase", "true")
ext.enableCrashlytics = false
}
}
productFlavors {
flavorDimensions "standard" flavorDimensions "standard"
productFlavors {
full { full {
applicationId "info.nightscout.androidaps" applicationId "info.nightscout.androidaps"
dimension "standard" dimension "standard"
@ -200,28 +161,8 @@ android {
] ]
} }
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions {
unitTests {
returnDefaultValues = true
includeAndroidResources = true
all {
maxParallelForks = 10
forkEvery = 20
}
}
}
useLibrary "org.apache.http.legacy" useLibrary "org.apache.http.legacy"
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
}
} }
allprojects { allprojects {
@ -235,51 +176,40 @@ allprojects {
dependencies { dependencies {
wearApp project(':wear') wearApp project(':wear')
implementation project(':shared')
implementation project(':core') implementation project(':core')
implementation project(':automation')
implementation project(':combo')
implementation project(':database')
implementation project(':dana') implementation project(':dana')
implementation project(':danars') implementation project(':danars')
implementation project(':danar') implementation project(':danar')
implementation project(':insight')
implementation project(':pump-common')
implementation project(':rileylink') implementation project(':rileylink')
implementation project(':medtronic') implementation project(':medtronic')
implementation project(':omnipod') implementation project(':omnipod-common')
implementation project(':omnipod-eros')
implementation project(':omnipod-dash')
implementation project(':diaconn')
implementation project(':openhumans')
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation "junit:junit:$junit_version"
testImplementation 'org.json:json:20200518'
testImplementation "org.mockito:mockito-core:${mockitoVersion}"
testImplementation "org.powermock:powermock-api-mockito2:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4-rule-agent:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}"
testImplementation 'joda-time:joda-time:2.10.6'
testImplementation('com.google.truth:truth:1.0.1') {
exclude group: "com.google.guava", module: "guava"
exclude group: "com.google.code.findbugs", module: "jsr305"
}
testImplementation "org.skyscreamer:jsonassert:1.5.0"
testImplementation "org.hamcrest:hamcrest-all:1.3"
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha03'
androidTestImplementation "androidx.test.ext:junit:$androidx_junit"
androidTestImplementation "androidx.test:rules:$androidx_rules"
androidTestImplementation 'com.google.code.findbugs:jsr305:3.0.2'
/* 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"
/* Dagger2 - default dependency */ /* Dagger2 - default dependency */
kapt "com.google.dagger:dagger-compiler:$dagger_version" kapt "com.google.dagger:dagger-compiler:$dagger_version"
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
} }
apply from: "${project.rootDir}/gradle/test_dependencies.gradle"
/* /*
// Run 'adb' shell command to clear application data of main app for 'debug' variant // Run 'adb' shell command to clear application data of main app for 'debug' variant
task clearMainAppData(type: Exec) { task clearMainAppData(type: Exec) {

Binary file not shown.

Binary file not shown.

View file

@ -1,40 +1,13 @@
package info.nightscout.androidaps package info.nightscout.androidaps
import android.os.SystemClock
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.data.Profile
import info.nightscout.androidaps.interfaces.PluginBase
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.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin
import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlugin
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.source.RandomBgPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.extensions.isRunningTest
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONObject
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
import javax.inject.Inject
@LargeTest @LargeTest
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class RealPumpTest { class RealPumpTest {
/*
companion object { companion object {
const val R_PASSWORD = 1234 const val R_PASSWORD = 1234
const val R_SERIAL = "PBB00013LR_P" const val R_SERIAL = "PBB00013LR_P"
@ -89,7 +62,7 @@ class RealPumpTest {
localProfilePlugin.numOfProfiles = 0 localProfilePlugin.numOfProfiles = 0
val singleProfile = LocalProfilePlugin.SingleProfile().copyFrom(localProfilePlugin.rawProfile, profile, "TestProfile") val singleProfile = LocalProfilePlugin.SingleProfile().copyFrom(localProfilePlugin.rawProfile, profile, "TestProfile")
localProfilePlugin.addProfile(singleProfile) localProfilePlugin.addProfile(singleProfile)
val profileSwitch = profileFunction.prepareProfileSwitch(localProfilePlugin.createProfileStore(), "TestProfile", 0, 100, 0, DateUtil.now()) val profileSwitch = profileFunction.prepareProfileSwitch(localProfilePlugin.createProfileStore(), "TestProfile", 0, 100, 0, dateUtil._now())
treatmentsPlugin.addToHistoryProfileSwitch(profileSwitch) treatmentsPlugin.addToHistoryProfileSwitch(profileSwitch)
// Insulin // Insulin
configBuilderPlugin.performPluginSwitch(insulinOrefUltraRapidActingPlugin, true, PluginType.INSULIN) configBuilderPlugin.performPluginSwitch(insulinOrefUltraRapidActingPlugin, true, PluginType.INSULIN)
@ -125,4 +98,5 @@ class RealPumpTest {
SystemClock.sleep(1000) SystemClock.sleep(1000)
} }
} }
*/
} }

View file

@ -1,48 +1,13 @@
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
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class SetupWizardActivityTest { class SetupWizardActivityTest {
/*
@Rule @Rule
@JvmField @JvmField
var mActivityTestRule = ActivityTestRule(SetupWizardActivity::class.java) var mActivityTestRule = ActivityTestRule(SetupWizardActivity::class.java)
@ -226,4 +191,5 @@ adb shell settings put global animator_duration_scale 0 &
} }
} }
} }
*/
} }

View file

@ -3,10 +3,11 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="info.nightscout.androidaps"> package="info.nightscout.androidaps">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@ -26,6 +27,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="com.dexcom.cgm.EXTERNAL_PERMISSION" /> <uses-permission android:name="com.dexcom.cgm.EXTERNAL_PERMISSION" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" /> <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
@ -52,7 +54,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 +63,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,15 +72,20 @@
</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.SurveyActivity" /> <activity android:name=".activities.SurveyActivity" />
<activity android:name=".activities.ProfileHelperActivity" <activity android:name=".activities.ProfileHelperActivity"
android:theme="@style/ProfileHelperAppTheme" /> android:theme="@style/ProfileHelperAppTheme" />
<activity android:name=".activities.StatsActivity" /> <activity android:name=".activities.StatsActivity" />
<activity <activity
android:name="com.google.firebase.auth.internal.FederatedSignInActivity" android:name="com.google.firebase.auth.internal.FederatedSignInActivity"
tools:replace="android:launchMode" android:excludeFromRecents="true"
android:launchMode="standard" /> android:exported="true"
android:launchMode="singleInstance"
android:permission="com.google.firebase.auth.api.gms.permission.LAUNCH_FEDERATED_SIGN_IN"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
tools:replace="android:launchMode" />
<!-- Receive new BG readings from other local apps --> <!-- Receive new BG readings from other local apps -->
<receiver <receiver
@ -114,7 +123,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
@ -136,13 +145,6 @@
android:resource="@xml/filepaths" /> android:resource="@xml/filepaths" />
</provider> </provider>
<!-- Service processing incomming data -->
<service
android:name=".services.DataService"
android:exported="false" />
<service
android:name=".services.LocationService"
android:exported="false" />
<service <service
android:name=".plugins.general.wear.wearintegration.WatchUpdaterService" android:name=".plugins.general.wear.wearintegration.WatchUpdaterService"
android:exported="true"> android:exported="true">
@ -219,8 +221,6 @@
android:exported="false" /> android:exported="false" />
<service android:name=".plugins.general.persistentNotification.DummyService" /> <service android:name=".plugins.general.persistentNotification.DummyService" />
<service android:name=".plugins.pump.insight.connection_service.InsightConnectionService" />
<service android:name=".plugins.pump.insight.InsightAlertService" />
<meta-data <meta-data
android:name="io.fabric.ApiKey" android:name="io.fabric.ApiKey"
@ -236,20 +236,6 @@
android:name=".activities.SingleFragmentActivity" android:name=".activities.SingleFragmentActivity"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity android:name=".plugins.general.maintenance.activities.LogSettingActivity" /> <activity android:name=".plugins.general.maintenance.activities.LogSettingActivity" />
<activity
android:name=".plugins.pump.insight.activities.InsightPairingActivity"
android:label="@string/insight_pairing"
android:theme="@style/AppTheme" />
<activity
android:name=".plugins.pump.insight.activities.InsightAlertActivity"
android:label="@string/pump_alert"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:theme="@style/InsightAlertDialog" />
<activity
android:name=".plugins.pump.insight.activities.InsightPairingInformationActivity"
android:label="@string/pairing_information"
android:theme="@style/AppTheme" />
<activity android:name=".activities.RequestDexcomPermissionActivity" /> <activity android:name=".activities.RequestDexcomPermissionActivity" />
<activity android:name=".plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity"> <activity android:name=".plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity">
<intent-filter> <intent-filter>
@ -259,27 +245,6 @@
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Medtronic service and activities -->
<service
android:name=".plugins.pump.medtronic.service.RileyLinkMedtronicService"
android:enabled="true"
android:exported="true" />
<activity android:name=".plugins.pump.medtronic.dialog.MedtronicHistoryActivity" />
<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

@ -4,4 +4,7 @@
55:5D:70:C9:BE:10:41:7E:4B:01:A9:C4:C6:44:4A:F8:69:71:35:25:ED:95:23:16:C7:15:E8:EB:C6:08:FC:B1 55:5D:70:C9:BE:10:41:7E:4B:01:A9:C4:C6:44:4A:F8:69:71:35:25:ED:95:23:16:C7:15:E8:EB:C6:08:FC:B1
# àqΣnΖ`ZϼγwÛ/τàΒϳ9Φ'$ΑϵžλUΛ`ÆÌΣЃA # àqΣnΖ`ZϼγwÛ/τàΒϳ9Φ'$ΑϵžλUΛ`ÆÌΣЃA
E0:71:A3:6E:96:60:5A:FC:B3:77:DB:2F:C4:E0:92:F3:39:A6:27:24:91:F5:7E:BB:55:9B:60:C6:CC:A3:03:41 E0:71:A3:6E:96:60:5A:FC:B3:77:DB:2F:C4:E0:92:F3:39:A6:27:24:91:F5:7E:BB:55:9B:60:C6:CC:A3:03:41
# 2ΙšÄΠΒϨÒÇeЄtЄЗž-*Ж*ZcHijЊÄœ<|x"Ε
32:99:61:C4:A0:92:E8:D2:C7:65:04:74:04:17:7E:2D:2A:16:2A:5A:63:48:69:6A:0A:C4:53:3C:7C:78:22:95 32:99:61:C4:A0:92:E8:D2:C7:65:04:74:04:17:7E:2D:2A:16:2A:5A:63:48:69:6A:0A:C4:53:3C:7C:78:22:95
# mςRr¡ЇζΛLφK&ĐΨ5CnЎϴPDñЍŒkϼ{ΨδwВb
6D:C2:52:72:A1:07:B6:9B:4C:C6:4B:26:10:A8:35:43:6E:0E:F4:50:44:F1:0D:52:6B:FC:7B:A8:B4:77:12:62

View file

@ -1,21 +0,0 @@
package info.nightscout.androidaps
import android.os.Build
import info.nightscout.androidaps.interfaces.ConfigInterface
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class Config @Inject constructor() : ConfigInterface {
override val SUPPORTEDNSVERSION = 1002 // 0.10.00
override val APS = BuildConfig.FLAVOR == "full"
override val NSCLIENT = BuildConfig.FLAVOR == "nsclient" || BuildConfig.FLAVOR == "nsclient2"
override val PUMPCONTROL = BuildConfig.FLAVOR == "pumpcontrol"
override val PUMPDRIVERS = BuildConfig.FLAVOR == "full" || BuildConfig.FLAVOR == "pumpcontrol"
override val FLAVOR = BuildConfig.FLAVOR
override val VERSION_NAME = BuildConfig.VERSION_NAME
override val currentDeviceModelString =
Build.MANUFACTURER + " " + Build.MODEL + " (" + Build.DEVICE + ")"
}

View file

@ -21,28 +21,23 @@ import android.widget.TextView
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.joanzapata.iconify.Iconify import com.joanzapata.iconify.Iconify
import com.joanzapata.iconify.fonts.FontAwesomeModule import com.joanzapata.iconify.fonts.FontAwesomeModule
import dev.doubledot.doki.ui.DokiActivity import dev.doubledot.doki.ui.DokiActivity
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.activities.*
import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.activities.ProfileHelperActivity import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.activities.StatsActivity
import info.nightscout.androidaps.databinding.ActivityMainBinding 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.interfaces.*
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.plugins.bus.RxBus
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.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils
@ -56,12 +51,13 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.isRunningRealPumpTest import info.nightscout.androidaps.utils.extensions.isRunningRealPumpTest
import info.nightscout.androidaps.utils.locale.LocaleHelper import info.nightscout.androidaps.utils.locale.LocaleHelper
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.IconsProvider import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.tabs.TabPageAdapter import info.nightscout.androidaps.utils.tabs.TabPageAdapter
import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.ui.UIRunnable
import io.reactivex.android.schedulers.AndroidSchedulers import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -70,26 +66,29 @@ class MainActivity : NoSplashAppCompatActivity() {
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBus
@Inject lateinit var androidPermission: AndroidPermission @Inject lateinit var androidPermission: AndroidPermission
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var versionCheckerUtils: VersionCheckerUtils @Inject lateinit var versionCheckerUtils: VersionCheckerUtils
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var loopPlugin: LoopPlugin @Inject lateinit var loop: Loop
@Inject lateinit var nsSettingsStatus: NSSettingsStatus @Inject lateinit var nsSettingsStatus: NSSettingsStatus
@Inject lateinit var buildHelper: BuildHelper @Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var protectionCheck: ProtectionCheck @Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var iconsProvider: IconsProvider @Inject lateinit var iconsProvider: IconsProvider
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin @Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var profileFunction: ProfileFunction
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
private var pluginPreferencesMenuItem: MenuItem? = null private var pluginPreferencesMenuItem: MenuItem? = null
private var menu: Menu? = null private var menu: Menu? = null
private var menuOpen = false
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
@ -109,7 +108,7 @@ class MainActivity : NoSplashAppCompatActivity() {
} }
// initialize screen wake lock // initialize screen wake lock
processPreferenceChange(EventPreferenceChange(resourceHelper.gs(R.string.key_keep_screen_on))) processPreferenceChange(EventPreferenceChange(rh.gs(R.string.key_keep_screen_on)))
binding.mainPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { binding.mainPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
@ -120,34 +119,33 @@ class MainActivity : NoSplashAppCompatActivity() {
}) })
//Check here if loop plugin is disabled. Else check via constraints //Check here if loop plugin is disabled. Else check via constraints
if (!loopPlugin.isEnabled(PluginType.LOOP)) versionCheckerUtils.triggerCheckVersion() if (!(loop as PluginBase).isEnabled()) versionCheckerUtils.triggerCheckVersion()
setUserStats() setUserStats()
setupViews() setupViews()
disposable.add(rxBus disposable += rxBus
.toObservable(EventRebuildTabs::class.java) .toObservable(EventRebuildTabs::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
if (it.recreate) recreate() if (it.recreate) recreate()
else setupViews() else setupViews()
setWakeLock() setWakeLock()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
) disposable += rxBus
disposable.add(rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException) .subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException)
) if (startWizard() && !isRunningRealPumpTest()) {
if (!sp.getBoolean(R.string.key_setupwizard_processed, false) && !isRunningRealPumpTest()) {
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, { protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
startActivity(Intent(this, SetupWizardActivity::class.java)) startActivity(Intent(this, SetupWizardActivity::class.java))
}) })
} }
androidPermission.notifyForStoragePermission(this) androidPermission.notifyForStoragePermission(this)
androidPermission.notifyForBatteryOptimizationPermission(this) androidPermission.notifyForBatteryOptimizationPermission(this)
androidPermission.notifyForLocationPermissions(this) if (!config.NSCLIENT) androidPermission.notifyForLocationPermissions(this)
if (config.PUMPDRIVERS) { if (config.PUMPDRIVERS) {
androidPermission.notifyForSMSPermissions(this, smsCommunicatorPlugin) androidPermission.notifyForSMSPermissions(this, smsCommunicatorPlugin)
androidPermission.notifyForSystemWindowPermissions(this) androidPermission.notifyForSystemWindowPermissions(this)
androidPermission.notifyForBtConnectPermission(this)
} }
} }
@ -155,12 +153,15 @@ class MainActivity : NoSplashAppCompatActivity() {
if (viewPager.currentItem >= 0) pluginPreferencesMenuItem?.isEnabled = (viewPager.adapter as TabPageAdapter).getPluginAt(viewPager.currentItem).preferencesId != -1 if (viewPager.currentItem >= 0) pluginPreferencesMenuItem?.isEnabled = (viewPager.adapter as TabPageAdapter).getPluginAt(viewPager.currentItem).preferencesId != -1
} }
private fun startWizard(): Boolean =
!sp.getBoolean(R.string.key_setupwizard_processed, false)
override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) { override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onPostCreate(savedInstanceState, persistentState) super.onPostCreate(savedInstanceState, persistentState)
actionBarDrawerToggle.syncState() actionBarDrawerToggle.syncState()
} }
public override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
disposable.clear() disposable.clear()
} }
@ -168,8 +169,8 @@ class MainActivity : NoSplashAppCompatActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, null, protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, null,
UIRunnable { OKDialog.show(this, "", resourceHelper.gs(R.string.authorizationfailed)) { finish() } }, UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { finish() } },
UIRunnable { OKDialog.show(this, "", resourceHelper.gs(R.string.authorizationfailed)) { finish() } } UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { finish() } }
) )
} }
@ -179,8 +180,8 @@ class MainActivity : NoSplashAppCompatActivity() {
} }
private fun processPreferenceChange(ev: EventPreferenceChange) { private fun processPreferenceChange(ev: EventPreferenceChange) {
if (ev.isChanged(resourceHelper, R.string.key_keep_screen_on)) setWakeLock() if (ev.isChanged(rh, R.string.key_keep_screen_on)) setWakeLock()
if (ev.isChanged(resourceHelper, R.string.key_skin)) recreate() if (ev.isChanged(rh, R.string.key_skin)) recreate()
} }
private fun setupViews() { private fun setupViews() {
@ -224,8 +225,10 @@ class MainActivity : NoSplashAppCompatActivity() {
binding.tabsCompact.visibility = View.GONE binding.tabsCompact.visibility = View.GONE
val typedValue = TypedValue() val typedValue = TypedValue()
if (theme.resolveAttribute(R.attr.actionBarSize, typedValue, true)) { if (theme.resolveAttribute(R.attr.actionBarSize, typedValue, true)) {
binding.toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, binding.toolbar.layoutParams = LinearLayout.LayoutParams(
TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)) Toolbar.LayoutParams.MATCH_PARENT,
TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)
)
} }
TabLayoutMediator(binding.tabsNormal, binding.mainPager) { tab, position -> TabLayoutMediator(binding.tabsNormal, binding.mainPager) { tab, position ->
tab.text = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(position).name tab.text = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(position).name
@ -252,10 +255,25 @@ class MainActivity : NoSplashAppCompatActivity() {
private fun setPluginPreferenceMenuName() { private fun setPluginPreferenceMenuName() {
if (binding.mainPager.currentItem >= 0) { if (binding.mainPager.currentItem >= 0) {
val plugin = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(binding.mainPager.currentItem) val plugin = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(binding.mainPager.currentItem)
this.menu?.findItem(R.id.nav_plugin_preferences)?.title = resourceHelper.gs(R.string.nav_preferences_plugin, plugin.name) this.menu?.findItem(R.id.nav_plugin_preferences)?.title = rh.gs(R.string.nav_preferences_plugin, plugin.name)
} }
} }
override fun onMenuOpened(featureId: Int, menu: Menu): Boolean {
menuOpen = true
if (binding.mainDrawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.mainDrawerLayout.closeDrawers()
}
val result = super.onMenuOpened(featureId, menu)
menu.findItem(R.id.nav_treatments)?.isEnabled = profileFunction.getProfile() != null
return result
}
override fun onPanelClosed(featureId: Int, menu: Menu) {
menuOpen = false;
super.onPanelClosed(featureId, menu)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
this.menu = menu this.menu = menu
menuInflater.inflate(R.menu.menu_main, menu) menuInflater.inflate(R.menu.menu_main, menu)
@ -281,6 +299,11 @@ class MainActivity : NoSplashAppCompatActivity() {
return true return true
} }
R.id.nav_treatments -> {
startActivity(Intent(this, TreatmentsActivity::class.java))
return true
}
R.id.nav_setupwizard -> { R.id.nav_setupwizard -> {
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, { protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
startActivity(Intent(this, SetupWizardActivity::class.java)) startActivity(Intent(this, SetupWizardActivity::class.java))
@ -291,18 +314,18 @@ class MainActivity : NoSplashAppCompatActivity() {
R.id.nav_about -> { R.id.nav_about -> {
var message = "Build: ${BuildConfig.BUILDVERSION}\n" var message = "Build: ${BuildConfig.BUILDVERSION}\n"
message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n" message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n"
message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.nightscoutVersionName}" message += "${rh.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.getVersion()}"
if (buildHelper.isEngineeringMode()) message += "\n${resourceHelper.gs(R.string.engineering_mode_enabled)}" if (buildHelper.isEngineeringMode()) message += "\n${rh.gs(R.string.engineering_mode_enabled)}"
if (!fabricPrivacy.fabricEnabled()) message += "\n${resourceHelper.gs(R.string.fabric_upload_disabled)}" if (!fabricPrivacy.fabricEnabled()) message += "\n${rh.gs(R.string.fabric_upload_disabled)}"
message += resourceHelper.gs(R.string.about_link_urls) message += rh.gs(R.string.about_link_urls)
val messageSpanned = SpannableString(message) val messageSpanned = SpannableString(message)
Linkify.addLinks(messageSpanned, Linkify.WEB_URLS) Linkify.addLinks(messageSpanned, Linkify.WEB_URLS)
AlertDialog.Builder(this) AlertDialog.Builder(this)
.setTitle(resourceHelper.gs(R.string.app_name) + " " + BuildConfig.VERSION) .setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION)
.setIcon(iconsProvider.getIcon()) .setIcon(iconsProvider.getIcon())
.setMessage(messageSpanned) .setMessage(messageSpanned)
.setPositiveButton(resourceHelper.gs(R.string.ok), null) .setPositiveButton(rh.gs(R.string.ok), null)
.setNeutralButton(resourceHelper.gs(R.string.cta_dont_kill_my_app_info)) { _, _ -> DokiActivity.start(context = this@MainActivity) } .setNeutralButton(rh.gs(R.string.cta_dont_kill_my_app_info)) { _, _ -> DokiActivity.start(context = this@MainActivity) }
.create().apply { .create().apply {
show() show()
findViewById<TextView>(android.R.id.message)?.movementMethod = LinkMovementMethod.getInstance() findViewById<TextView>(android.R.id.message)?.movementMethod = LinkMovementMethod.getInstance()
@ -312,6 +335,7 @@ class MainActivity : NoSplashAppCompatActivity() {
R.id.nav_exit -> { R.id.nav_exit -> {
aapsLogger.debug(LTag.CORE, "Exiting") aapsLogger.debug(LTag.CORE, "Exiting")
uel.log(Action.EXIT_AAPS, Sources.Aaps)
rxBus.send(EventAppExit()) rxBus.send(EventAppExit())
finish() finish()
System.runFinalization() System.runFinalization()
@ -346,6 +370,22 @@ class MainActivity : NoSplashAppCompatActivity() {
return actionBarDrawerToggle.onOptionsItemSelected(item) return actionBarDrawerToggle.onOptionsItemSelected(item)
} }
override fun onBackPressed() {
if (binding.mainDrawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.mainDrawerLayout.closeDrawers()
return
}
if (menuOpen) {
this.menu?.close()
return
}
if (binding.mainPager.currentItem != 0) {
binding.mainPager.currentItem = 0
return
}
super.onBackPressed()
}
// 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
@ -353,7 +393,7 @@ class MainActivity : NoSplashAppCompatActivity() {
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"
// Size is limited to 36 chars // Size is limited to 36 chars
val remote = BuildConfig.REMOTE.toLowerCase(Locale.getDefault()) val remote = BuildConfig.REMOTE.lowercase(Locale.getDefault())
.replace("https://", "") .replace("https://", "")
.replace("http://", "") .replace("http://", "")
.replace(".git", "") .replace(".git", "")
@ -371,7 +411,7 @@ class MainActivity : NoSplashAppCompatActivity() {
if (!config.NSCLIENT && !config.PUMPCONTROL) if (!config.NSCLIENT && !config.PUMPCONTROL)
activePlugin.activeAPS.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Aps", it::class.java.simpleName) } activePlugin.activeAPS.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Aps", it::class.java.simpleName) }
activePlugin.activeBgSource.let { fabricPrivacy.firebaseAnalytics.setUserProperty("BgSource", it::class.java.simpleName) } activePlugin.activeBgSource.let { fabricPrivacy.firebaseAnalytics.setUserProperty("BgSource", it::class.java.simpleName) }
fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileInterface.javaClass.simpleName) fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileSource.javaClass.simpleName)
activePlugin.activeSensitivity.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) } activePlugin.activeSensitivity.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) }
activePlugin.activeInsulin.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Insulin", it::class.java.simpleName) } activePlugin.activeInsulin.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Insulin", it::class.java.simpleName) }
// Add to crash log too // Add to crash log too
@ -380,6 +420,7 @@ class MainActivity : NoSplashAppCompatActivity() {
FirebaseCrashlytics.getInstance().setCustomKey("Remote", remote) FirebaseCrashlytics.getInstance().setCustomKey("Remote", remote)
FirebaseCrashlytics.getInstance().setCustomKey("Committed", BuildConfig.COMMITTED) FirebaseCrashlytics.getInstance().setCustomKey("Committed", BuildConfig.COMMITTED)
FirebaseCrashlytics.getInstance().setCustomKey("Hash", hashes[0]) FirebaseCrashlytics.getInstance().setCustomKey("Hash", hashes[0])
FirebaseCrashlytics.getInstance().setCustomKey("Email", sp.getString(R.string.key_email_for_crash_report, ""))
} }
} }

View file

@ -1,170 +0,0 @@
package info.nightscout.androidaps;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.j256.ormlite.android.apptools.OpenHelperManager;
import net.danlew.android.joda.JodaTimeAndroid;
import java.util.List;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.DaggerApplication;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.StaticInjector;
import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.configBuilder.PluginStore;
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.receivers.BTReceiver;
import info.nightscout.androidaps.receivers.ChargingStateReceiver;
import info.nightscout.androidaps.receivers.DataReceiver;
import info.nightscout.androidaps.receivers.KeepAliveReceiver;
import info.nightscout.androidaps.receivers.NetworkChangeReceiver;
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver;
import info.nightscout.androidaps.services.Intents;
import info.nightscout.androidaps.utils.ActivityMonitor;
import info.nightscout.androidaps.utils.locale.LocaleHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class MainApp extends DaggerApplication {
static MainApp sInstance;
private static Resources sResources;
static DatabaseHelper sDatabaseHelper = null;
@Inject PluginStore pluginStore;
@Inject AAPSLogger aapsLogger;
@Inject ActivityMonitor activityMonitor;
@Inject VersionCheckerUtils versionCheckersUtils;
@Inject SP sp;
@Inject NSUpload nsUpload;
@Inject Config config;
@Inject ConfigBuilderPlugin configBuilderPlugin;
@Inject KeepAliveReceiver.KeepAliveManager keepAliveManager;
@Inject List<PluginBase> plugins;
@Inject StaticInjector staticInjector; // TODO avoid , here fake only to initialize
@Override
public void onCreate() {
super.onCreate();
aapsLogger.debug("onCreate");
sInstance = this;
sResources = getResources();
LocaleHelper.INSTANCE.update(this);
sDatabaseHelper = OpenHelperManager.getHelper(sInstance, DatabaseHelper.class);
/*
Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> {
if (ex instanceof InternalError) {
// usually the app trying to spawn a thread while being killed
return;
}
aapsLogger.error("Uncaught exception crashing app", ex);
});
*/
registerActivityLifecycleCallbacks(activityMonitor);
JodaTimeAndroid.init(this);
aapsLogger.debug("Version: " + BuildConfig.VERSION_NAME);
aapsLogger.debug("BuildVersion: " + BuildConfig.BUILDVERSION);
aapsLogger.debug("Remote: " + BuildConfig.REMOTE);
registerLocalBroadcastReceiver();
//trigger here to see the new version on app start after an update
versionCheckersUtils.triggerCheckVersion();
// Register all tabs in app here
pluginStore.setPlugins(plugins);
configBuilderPlugin.initialize();
nsUpload.uploadAppStart();
new Thread(() -> keepAliveManager.setAlarm(this)).start();
doMigrations();
}
private void doMigrations() {
// set values for different builds
if (!sp.contains(R.string.key_ns_alarms))
sp.putBoolean(R.string.key_ns_alarms, config.getNSCLIENT());
if (!sp.contains(R.string.key_ns_announcements))
sp.putBoolean(R.string.key_ns_announcements, config.getNSCLIENT());
if (!sp.contains(R.string.key_language))
sp.putString(R.string.key_language, "default");
}
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent
.builder()
.application(this)
.build();
}
@SuppressWarnings("deprecation")
private void registerLocalBroadcastReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intents.ACTION_NEW_TREATMENT);
filter.addAction(Intents.ACTION_CHANGED_TREATMENT);
filter.addAction(Intents.ACTION_REMOVED_TREATMENT);
filter.addAction(Intents.ACTION_NEW_SGV);
filter.addAction(Intents.ACTION_NEW_PROFILE);
filter.addAction(Intents.ACTION_NEW_MBG);
filter.addAction(Intents.ACTION_NEW_CAL);
LocalBroadcastManager.getInstance(this).registerReceiver(new DataReceiver(), filter);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
registerReceiver(new TimeDateOrTZChangeReceiver(), filter);
filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
registerReceiver(new NetworkChangeReceiver(), filter);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(new ChargingStateReceiver(), filter);
filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
registerReceiver(new BTReceiver(), filter);
}
public static DatabaseHelper getDbHelper() {
return sDatabaseHelper;
}
@Override
public void onTerminate() {
aapsLogger.debug(LTag.CORE, "onTerminate");
unregisterActivityLifecycleCallbacks(activityMonitor);
keepAliveManager.cancelAlarm(this);
super.onTerminate();
}
}

View file

@ -0,0 +1,193 @@
package info.nightscout.androidaps
import android.bluetooth.BluetoothDevice
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.wifi.WifiManager
import android.os.Build
import com.uber.rxdogtag.RxDogTag
import dagger.android.AndroidInjector
import dagger.android.DaggerApplication
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction
import info.nightscout.androidaps.database.transactions.VersionChangeTransaction
import info.nightscout.androidaps.db.CompatDBHelper
import info.nightscout.androidaps.di.DaggerAppComponent
import info.nightscout.androidaps.di.StaticInjector
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.ConfigBuilder
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
import info.nightscout.androidaps.receivers.BTReceiver
import info.nightscout.androidaps.receivers.ChargingStateReceiver
import info.nightscout.androidaps.receivers.KeepAliveReceiver.KeepAliveManager
import info.nightscout.androidaps.receivers.NetworkChangeReceiver
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver
import info.nightscout.androidaps.services.AlarmSoundServiceHelper
import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.locale.LocaleHelper
import info.nightscout.androidaps.utils.protection.PasswordCheck
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.exceptions.UndeliverableException
import io.reactivex.plugins.RxJavaPlugins
import io.reactivex.rxkotlin.plusAssign
import net.danlew.android.joda.JodaTimeAndroid
import java.io.IOException
import java.net.SocketException
import javax.inject.Inject
class MainApp : DaggerApplication() {
private val disposable = CompositeDisposable()
@Inject lateinit var pluginStore: PluginStore
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var versionCheckersUtils: VersionCheckerUtils
@Inject lateinit var sp: SP
@Inject lateinit var config: Config
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var keepAliveManager: KeepAliveManager
@Inject lateinit var plugins: List<@JvmSuppressWildcards PluginBase>
@Inject lateinit var compatDBHelper: CompatDBHelper
@Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var passwordCheck: PasswordCheck
@Inject lateinit var alarmSoundServiceHelper: AlarmSoundServiceHelper
@Inject lateinit var notificationStore: NotificationStore
override fun onCreate() {
super.onCreate()
aapsLogger.debug("onCreate")
RxDogTag.install()
setRxErrorHandler()
LocaleHelper.update(this)
var gitRemote: String? = BuildConfig.REMOTE
var commitHash: String? = BuildConfig.HEAD
if (gitRemote?.contains("NoGitSystemAvailable") == true) {
gitRemote = null
commitHash = null
}
disposable += repository.runTransaction(VersionChangeTransaction(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, gitRemote, commitHash)).subscribe()
if (sp.getBoolean(R.string.key_ns_logappstartedevent, config.APS))
disposable += repository
.runTransaction(
InsertIfNewByTimestampTherapyEventTransaction(
timestamp = dateUtil.now(),
type = TherapyEvent.Type.NOTE,
note = getString(info.nightscout.androidaps.core.R.string.androidaps_start) + " - " + Build.MANUFACTURER + " " + Build.MODEL,
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL
)
)
.subscribe()
disposable += compatDBHelper.dbChangeDisposable()
registerActivityLifecycleCallbacks(activityMonitor)
JodaTimeAndroid.init(this)
aapsLogger.debug("Version: " + BuildConfig.VERSION_NAME)
aapsLogger.debug("BuildVersion: " + BuildConfig.BUILDVERSION)
aapsLogger.debug("Remote: " + BuildConfig.REMOTE)
registerLocalBroadcastReceiver()
// trigger here to see the new version on app start after an update
versionCheckersUtils.triggerCheckVersion()
// check if identification is set
if (buildHelper.isDev() && sp.getStringOrNull(R.string.key_email_for_crash_report, null).isNullOrBlank())
notificationStore.add(Notification(Notification.IDENTIFICATION_NOT_SET, getString(R.string.identification_not_set), Notification.INFO))
// Register all tabs in app here
pluginStore.plugins = plugins
configBuilder.initialize()
keepAliveManager.setAlarm(this)
doMigrations()
uel.log(UserEntry.Action.START_AAPS, UserEntry.Sources.Aaps)
passwordCheck.passwordResetCheck(this)
}
private fun setRxErrorHandler() {
RxJavaPlugins.setErrorHandler { t: Throwable ->
var e = t
if (e is UndeliverableException) {
e = e.cause!!
}
if (e is IOException || e is SocketException) {
// fine, irrelevant network problem or API that throws on cancellation
return@setErrorHandler
}
if (e is InterruptedException) {
// fine, some blocking code was interrupted by a dispose call
return@setErrorHandler
}
if (e is NullPointerException || e is IllegalArgumentException) {
// that's likely a bug in the application
Thread.currentThread().uncaughtExceptionHandler?.uncaughtException(Thread.currentThread(), e)
return@setErrorHandler
}
if (e is IllegalStateException) {
// that's a bug in RxJava or in a custom operator
Thread.currentThread().uncaughtExceptionHandler?.uncaughtException(Thread.currentThread(), e)
return@setErrorHandler
}
aapsLogger.warn(LTag.CORE, "Undeliverable exception received, not sure what to do", e)
}
}
private fun doMigrations() {
// set values for different builds
if (!sp.contains(R.string.key_ns_alarms)) sp.putBoolean(R.string.key_ns_alarms, config.NSCLIENT)
if (!sp.contains(R.string.key_ns_announcements)) sp.putBoolean(R.string.key_ns_announcements, config.NSCLIENT)
if (!sp.contains(R.string.key_language)) sp.putString(R.string.key_language, "default")
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent
.builder()
.application(this)
.build()
}
private fun registerLocalBroadcastReceiver() {
var filter = IntentFilter()
filter.addAction(Intent.ACTION_TIME_CHANGED)
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
registerReceiver(TimeDateOrTZChangeReceiver(), filter)
filter = IntentFilter()
@Suppress("DEPRECATION")
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION)
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION)
registerReceiver(NetworkChangeReceiver(), filter)
filter = IntentFilter()
filter.addAction(Intent.ACTION_POWER_CONNECTED)
filter.addAction(Intent.ACTION_POWER_DISCONNECTED)
filter.addAction(Intent.ACTION_BATTERY_CHANGED)
registerReceiver(ChargingStateReceiver(), filter)
filter = IntentFilter()
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
registerReceiver(BTReceiver(), filter)
}
override fun onTerminate() {
aapsLogger.debug(LTag.CORE, "onTerminate")
unregisterActivityLifecycleCallbacks(activityMonitor)
keepAliveManager.cancelAlarm(this)
alarmSoundServiceHelper.stopService(this)
super.onTerminate()
}
}

View file

@ -0,0 +1,439 @@
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.Loop
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBus
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.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.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 aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBus
@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 loop: Loop
@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,
rh,
profileFunction,
activePlugin,
sensitivityOref1Plugin,
sensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin,
fabricPrivacy,
dateUtil,
repository
)
overviewData =
OverviewData(
injector,
aapsLogger,
rh,
dateUtil,
sp,
activePlugin,
defaultValueHelper,
profileFunction,
config,
loop,
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 = rh.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 += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
// catch only events from iobCobCalculator
if (it.cause is EventCustomCalculationFinished)
refreshLoop("EventAutosensCalculationFinished")
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({
if (it.cause is EventCustomCalculationFinished)
binding.overviewIobcalculationprogess.text = it.progress
}, fabricPrivacy::logException)
disposable += 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, rh.dpToPx(100)).also { it.setMargins(0, rh.dpToPx(15), 0, rh.dpToPx(10)) }
graph.gridLabelRenderer?.gridColor = rh.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(rh.dpToPx(30), rh.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

@ -8,28 +8,29 @@ import android.os.Bundle
import androidx.annotation.XmlRes import androidx.annotation.XmlRes
import androidx.preference.* import androidx.preference.*
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import info.nightscout.androidaps.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin 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.data.Profile import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
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
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.PluginStore import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin 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
@ -47,12 +48,13 @@ import info.nightscout.androidaps.plugins.source.EversensePlugin
import info.nightscout.androidaps.plugins.source.GlimpPlugin import info.nightscout.androidaps.plugins.source.GlimpPlugin
import info.nightscout.androidaps.plugins.source.PoctechPlugin import info.nightscout.androidaps.plugins.source.PoctechPlugin
import info.nightscout.androidaps.plugins.source.TomatoPlugin import info.nightscout.androidaps.plugins.source.TomatoPlugin
import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.plugins.source.GlunovoPlugin
import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show
import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.androidaps.utils.protection.PasswordCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import javax.inject.Inject import javax.inject.Inject
class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListener { class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListener {
@ -60,8 +62,8 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
private var pluginId = -1 private var pluginId = -1
private var filter = "" private var filter = ""
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBus
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var pluginStore: PluginStore @Inject lateinit var pluginStore: PluginStore
@ -89,6 +91,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
@Inject lateinit var glimpPlugin: GlimpPlugin @Inject lateinit var glimpPlugin: GlimpPlugin
@Inject lateinit var poctechPlugin: PoctechPlugin @Inject lateinit var poctechPlugin: PoctechPlugin
@Inject lateinit var tomatoPlugin: TomatoPlugin @Inject lateinit var tomatoPlugin: TomatoPlugin
@Inject lateinit var glunovoPlugin: GlunovoPlugin
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var statusLinePlugin: StatusLinePlugin @Inject lateinit var statusLinePlugin: StatusLinePlugin
@Inject lateinit var tidepoolPlugin: TidepoolPlugin @Inject lateinit var tidepoolPlugin: TidepoolPlugin
@ -99,6 +102,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)
@ -158,6 +162,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
addPreferencesFromResourceIfEnabled(eversensePlugin, rootKey) addPreferencesFromResourceIfEnabled(eversensePlugin, rootKey)
addPreferencesFromResourceIfEnabled(dexcomPlugin, rootKey) addPreferencesFromResourceIfEnabled(dexcomPlugin, rootKey)
addPreferencesFromResourceIfEnabled(tomatoPlugin, rootKey) addPreferencesFromResourceIfEnabled(tomatoPlugin, rootKey)
addPreferencesFromResourceIfEnabled(glunovoPlugin, rootKey)
addPreferencesFromResourceIfEnabled(poctechPlugin, rootKey) addPreferencesFromResourceIfEnabled(poctechPlugin, rootKey)
addPreferencesFromResourceIfEnabled(glimpPlugin, rootKey) addPreferencesFromResourceIfEnabled(glimpPlugin, rootKey)
addPreferencesFromResourceIfEnabled(loopPlugin, rootKey, config.APS) addPreferencesFromResourceIfEnabled(loopPlugin, rootKey, config.APS)
@ -173,6 +178,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 +188,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)
@ -194,21 +200,21 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
rxBus.send(EventPreferenceChange(key)) rxBus.send(EventPreferenceChange(key))
if (key == resourceHelper.gs(R.string.key_language)) { if (key == rh.gs(R.string.key_language)) {
rxBus.send(EventRebuildTabs(true)) rxBus.send(EventRebuildTabs(true))
//recreate() does not update language so better close settings //recreate() does not update language so better close settings
activity?.finish() activity?.finish()
} }
if (key == resourceHelper.gs(R.string.key_short_tabtitles)) { if (key == rh.gs(R.string.key_short_tabtitles)) {
rxBus.send(EventRebuildTabs()) rxBus.send(EventRebuildTabs())
} }
if (key == resourceHelper.gs(R.string.key_units)) { if (key == rh.gs(R.string.key_units)) {
activity?.recreate() activity?.recreate()
return return
} }
if (key == resourceHelper.gs(R.string.key_openapsama_useautosens) && sp.getBoolean(R.string.key_openapsama_useautosens, false)) { if (key == rh.gs(R.string.key_openapsama_useautosens) && sp.getBoolean(R.string.key_openapsama_useautosens, false)) {
activity?.let { activity?.let {
show(it, resourceHelper.gs(R.string.configbuilder_sensitivity), resourceHelper.gs(R.string.sensitivity_warning)) show(it, rh.gs(R.string.configbuilder_sensitivity), rh.gs(R.string.sensitivity_warning))
} }
} }
checkForBiometricFallback(key) checkForBiometricFallback(key)
@ -225,15 +231,15 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
private fun checkForBiometricFallback(key: String) { private fun checkForBiometricFallback(key: String) {
// Biometric protection activated without set master password // Biometric protection activated without set master password
if ((resourceHelper.gs(R.string.key_settings_protection) == key || if ((rh.gs(R.string.key_settings_protection) == key ||
resourceHelper.gs(R.string.key_application_protection) == key || rh.gs(R.string.key_application_protection) == key ||
resourceHelper.gs(R.string.key_bolus_protection) == key) && rh.gs(R.string.key_bolus_protection) == key) &&
sp.getString(R.string.key_master_password, "") == "" && sp.getString(R.string.key_master_password, "") == "" &&
sp.getInt(key, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal sp.getInt(key, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal
) { ) {
activity?.let { activity?.let {
val title = resourceHelper.gs(R.string.unsecure_fallback_biometric) val title = rh.gs(R.string.unsecure_fallback_biometric)
val message = resourceHelper.gs(R.string.master_password_missing, resourceHelper.gs(R.string.configbuilder_general), resourceHelper.gs(R.string.protection)) val message = rh.gs(R.string.master_password_missing, rh.gs(R.string.configbuilder_general), rh.gs(R.string.protection))
show(it, title = title, message = message) show(it, title = title, message = message)
} }
} }
@ -242,10 +248,10 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
val isBiometricActivated = sp.getInt(R.string.key_settings_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal || val isBiometricActivated = sp.getInt(R.string.key_settings_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal ||
sp.getInt(R.string.key_application_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal || sp.getInt(R.string.key_application_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal ||
sp.getInt(R.string.key_bolus_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal sp.getInt(R.string.key_bolus_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal
if (resourceHelper.gs(R.string.key_master_password) == key && sp.getString(key, "") == "" && isBiometricActivated) { if (rh.gs(R.string.key_master_password) == key && sp.getString(key, "") == "" && isBiometricActivated) {
activity?.let { activity?.let {
val title = resourceHelper.gs(R.string.unsecure_fallback_biometric) val title = rh.gs(R.string.unsecure_fallback_biometric)
val message = resourceHelper.gs(R.string.unsecure_fallback_descriotion_biometric) val message = rh.gs(R.string.unsecure_fallback_descriotion_biometric)
show(it, title = title, message = message) show(it, title = title, message = message)
} }
} }
@ -275,11 +281,11 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
private fun adjustUnitDependentPrefs(pref: Preference) { // convert preferences values to current units private fun adjustUnitDependentPrefs(pref: Preference) { // convert preferences values to current units
val unitDependent = arrayOf( val unitDependent = arrayOf(
resourceHelper.gs(R.string.key_hypo_target), rh.gs(R.string.key_hypo_target),
resourceHelper.gs(R.string.key_activity_target), rh.gs(R.string.key_activity_target),
resourceHelper.gs(R.string.key_eatingsoon_target), rh.gs(R.string.key_eatingsoon_target),
resourceHelper.gs(R.string.key_high_mark), rh.gs(R.string.key_high_mark),
resourceHelper.gs(R.string.key_low_mark) rh.gs(R.string.key_low_mark)
) )
if (unitDependent.toList().contains(pref.key) && pref is EditTextPreference) { if (unitDependent.toList().contains(pref.key) && pref is EditTextPreference) {
val converted = Profile.toCurrentUnits(profileFunction, SafeParse.stringToDouble(pref.text)) val converted = Profile.toCurrentUnits(profileFunction, SafeParse.stringToDouble(pref.text))
@ -319,20 +325,20 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
pref.setSummary(pref.entry) pref.setSummary(pref.entry)
// Preferences // Preferences
// Preferences // Preferences
if (pref.getKey() == resourceHelper.gs(R.string.key_settings_protection)) { if (pref.getKey() == rh.gs(R.string.key_settings_protection)) {
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_settings_password)) val pass: Preference? = findPreference(rh.gs(R.string.key_settings_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString() if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
} }
// Application // Application
// Application // Application
if (pref.getKey() == resourceHelper.gs(R.string.key_application_protection)) { if (pref.getKey() == rh.gs(R.string.key_application_protection)) {
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_application_password)) val pass: Preference? = findPreference(rh.gs(R.string.key_application_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString() if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
} }
// Bolus // Bolus
// Bolus // Bolus
if (pref.getKey() == resourceHelper.gs(R.string.key_bolus_protection)) { if (pref.getKey() == rh.gs(R.string.key_bolus_protection)) {
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_bolus_password)) val pass: Preference? = findPreference(rh.gs(R.string.key_bolus_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString() if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
} }
} }
@ -350,10 +356,10 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
} }
val hmacPasswords = arrayOf( val hmacPasswords = arrayOf(
resourceHelper.gs(R.string.key_bolus_password), rh.gs(R.string.key_bolus_password),
resourceHelper.gs(R.string.key_master_password), rh.gs(R.string.key_master_password),
resourceHelper.gs(R.string.key_application_password), rh.gs(R.string.key_application_password),
resourceHelper.gs(R.string.key_settings_password) rh.gs(R.string.key_settings_password)
) )
if (pref is Preference) { if (pref is Preference) {
@ -361,7 +367,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (sp.getString(pref.key, "").startsWith("hmac:")) { if (sp.getString(pref.key, "").startsWith("hmac:")) {
pref.summary = "******" pref.summary = "******"
} else { } else {
pref.summary = resourceHelper.gs(R.string.password_not_set) pref.summary = rh.gs(R.string.password_not_set)
} }
} }
} }
@ -390,26 +396,26 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
override fun onPreferenceTreeClick(preference: Preference?): Boolean { override fun onPreferenceTreeClick(preference: Preference?): Boolean {
context?.let { context -> context?.let { context ->
if (preference != null) { if (preference != null) {
if (preference.key == resourceHelper.gs(R.string.key_master_password)) { if (preference.key == rh.gs(R.string.key_master_password)) {
passwordCheck.queryPassword(context, R.string.current_master_password, R.string.key_master_password, { passwordCheck.queryPassword(context, R.string.current_master_password, R.string.key_master_password, {
passwordCheck.setPassword(context, R.string.master_password, R.string.key_master_password) passwordCheck.setPassword(context, R.string.master_password, R.string.key_master_password)
}) })
return true return true
} }
if (preference.key == resourceHelper.gs(R.string.key_settings_password)) { if (preference.key == rh.gs(R.string.key_settings_password)) {
passwordCheck.setPassword(context, R.string.settings_password, R.string.key_settings_password) passwordCheck.setPassword(context, R.string.settings_password, R.string.key_settings_password)
return true return true
} }
if (preference.key == resourceHelper.gs(R.string.key_bolus_password)) { if (preference.key == rh.gs(R.string.key_bolus_password)) {
passwordCheck.setPassword(context, R.string.bolus_password, R.string.key_bolus_password) passwordCheck.setPassword(context, R.string.bolus_password, R.string.key_bolus_password)
return true return true
} }
if (preference.key == resourceHelper.gs(R.string.key_application_password)) { if (preference.key == rh.gs(R.string.key_application_password)) {
passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password) passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password)
return true return true
} }
// NSClient copy settings // NSClient copy settings
if (preference.key == resourceHelper.gs(R.string.key_statuslights_copy_ns)) { if (preference.key == rh.gs(R.string.key_statuslights_copy_ns)) {
nsSettingStatus.copyStatusLightsNsSettings(context) nsSettingStatus.copyStatusLightsNsSettings(context)
return true return true
} }

View file

@ -31,7 +31,7 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa
override fun afterTextChanged(s: Editable) {} override fun afterTextChanged(s: Editable) {}
}) })
title = resourceHelper.gs(R.string.nav_preferences) title = rh.gs(R.string.nav_preferences)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
myPreferenceFragment = MyPreferenceFragment() myPreferenceFragment = MyPreferenceFragment()

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.activities package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
@ -7,40 +8,39 @@ import android.text.TextWatcher
import android.view.Menu import android.view.Menu
import android.widget.PopupMenu import android.widget.PopupMenu
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.data.defaultProfile.DefaultProfile import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.data.defaultProfile.DefaultProfileDPV import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch
import info.nightscout.androidaps.databinding.ActivityProfilehelperBinding import info.nightscout.androidaps.databinding.ActivityProfilehelperBinding
import info.nightscout.androidaps.db.ProfileSwitch
import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
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.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
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.utils.extensions.toVisibility import info.nightscout.androidaps.utils.defaultProfile.DefaultProfile
import info.nightscout.androidaps.utils.defaultProfile.DefaultProfileDPV
import info.nightscout.androidaps.utils.stats.TddCalculator import info.nightscout.androidaps.utils.stats.TddCalculator
import java.text.DecimalFormat import java.text.DecimalFormat
import javax.inject.Inject import javax.inject.Inject
class ProfileHelperActivity : NoSplashAppCompatActivity() { class ProfileHelperActivity : NoSplashAppCompatActivity() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultProfile: DefaultProfile @Inject lateinit var defaultProfile: DefaultProfile
@Inject lateinit var defaultProfileDPV: DefaultProfileDPV @Inject lateinit var defaultProfileDPV: DefaultProfileDPV
@Inject lateinit var localProfilePlugin: LocalProfilePlugin @Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBus
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var databaseHelper: DatabaseHelperInterface @Inject lateinit var repository: AppRepository
enum class ProfileType { enum class ProfileType {
MOTOL_DEFAULT, MOTOL_DEFAULT,
@ -61,7 +61,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
private lateinit var profileList: ArrayList<CharSequence> private lateinit var profileList: ArrayList<CharSequence>
private val profileUsed = arrayOf(0, 0) private val profileUsed = arrayOf(0, 0)
private lateinit var profileSwitch: List<ProfileSwitch> private lateinit var profileSwitch: List<EffectiveProfileSwitch>
private val profileSwitchUsed = arrayOf(0, 0) private val profileSwitchUsed = arrayOf(0, 0)
private lateinit var binding: ActivityProfilehelperBinding private lateinit var binding: ActivityProfilehelperBinding
@ -98,7 +98,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
} }
// Active profile // Active profile
profileList = activePlugin.activeProfileInterface.profile?.getProfileList() ?: ArrayList() profileList = activePlugin.activeProfileSource.profile?.getProfileList() ?: ArrayList()
binding.availableProfileList.setOnClickListener { binding.availableProfileList.setOnClickListener {
PopupMenu(this, binding.availableProfileList).apply { PopupMenu(this, binding.availableProfileList).apply {
@ -114,12 +114,12 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
} }
// Profile switch // Profile switch
profileSwitch = databaseHelper.getProfileSwitchData(dateUtil._now() - T.months(2).msecs(), true) profileSwitch = repository.getEffectiveProfileSwitchDataFromTime(dateUtil.now() - T.months(2).msecs(), true).blockingGet()
binding.profileswitchList.setOnClickListener { binding.profileswitchList.setOnClickListener {
PopupMenu(this, binding.profileswitchList).apply { PopupMenu(this, binding.profileswitchList).apply {
var order = 0 var order = 0
for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.customizedName) for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.originalCustomizedName)
setOnMenuItemClickListener { item -> setOnMenuItemClickListener { item ->
binding.profileswitchList.setText(item.title) binding.profileswitchList.setText(item.title)
profileSwitchUsed[tabSelected] = item.itemId profileSwitchUsed[tabSelected] = item.itemId
@ -131,6 +131,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
// Default profile // Default profile
binding.copytolocalprofile.setOnClickListener { binding.copytolocalprofile.setOnClickListener {
storeValues()
val age = ageUsed[tabSelected] val age = ageUsed[tabSelected]
val weight = weightUsed[tabSelected] val weight = weightUsed[tabSelected]
val tdd = tddUsed[tabSelected] val tdd = tddUsed[tabSelected]
@ -138,8 +139,14 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
val profile = if (typeSelected[tabSelected] == ProfileType.MOTOL_DEFAULT) defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) val profile = if (typeSelected[tabSelected] == ProfileType.MOTOL_DEFAULT) defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())
else defaultProfileDPV.profile(age, tdd, pct / 100.0, profileFunction.getUnits()) else defaultProfileDPV.profile(age, tdd, pct / 100.0, profileFunction.getUnits())
profile?.let { profile?.let {
OKDialog.showConfirmation(this, resourceHelper.gs(R.string.careportal_profileswitch), resourceHelper.gs(R.string.copytolocalprofile), Runnable { OKDialog.showConfirmation(this, rh.gs(R.string.careportal_profileswitch), rh.gs(R.string.copytolocalprofile), Runnable {
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(it, "DefaultProfile" + dateUtil.dateAndTimeAndSecondsString(dateUtil._now()))) localProfilePlugin.addProfile(
localProfilePlugin.copyFrom(
it, "DefaultProfile " +
dateUtil.dateAndTimeAndSecondsString(dateUtil.now())
.replace(".", "/")
)
)
rxBus.send(EventLocalProfileChanged()) rxBus.send(EventLocalProfileChanged())
}) })
} }
@ -163,7 +170,12 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
binding.basalpctfromtdd.setParams(32.0, 32.0, 37.0, 1.0, DecimalFormat("0"), false, null) binding.basalpctfromtdd.setParams(32.0, 32.0, 37.0, 1.0, DecimalFormat("0"), false, null)
binding.tdds.text = tddCalculator.stats() @SuppressLint("SetTextI18n")
binding.tdds.text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress)
Thread {
val tdds = tddCalculator.stats()
runOnUiThread { binding.tdds.text = tdds }
}.start()
// Current profile // Current profile
binding.currentProfileText.text = profileFunction.getProfileName() binding.currentProfileText.text = profileFunction.getProfileName()
@ -206,12 +218,20 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
getProfile(ageUsed[1], tddUsed[1], weightUsed[1], pctUsed[1] / 100.0, 1)?.let { profile1 -> getProfile(ageUsed[1], tddUsed[1], weightUsed[1], pctUsed[1] / 100.0, 1)?.let { profile1 ->
ProfileViewerDialog().also { pvd -> ProfileViewerDialog().also { pvd ->
pvd.arguments = Bundle().also { pvd.arguments = Bundle().also {
it.putLong("time", DateUtil.now()) it.putLong("time", dateUtil.now())
it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal) it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal)
it.putString("customProfile", profile0.data.toString()) it.putString("customProfile", profile0.jsonObject.toString())
it.putString("customProfile2", profile1.data.toString()) it.putString("customProfile2", profile1.jsonObject.toString())
it.putString("customProfileUnits", profileFunction.getUnits()) it.putString(
it.putString("customProfileName", getProfileName(ageUsed[0], tddUsed[0], weightUsed[0], pctUsed[0] / 100.0, 0) + "\n" + getProfileName(ageUsed[1], tddUsed[1], weightUsed[1], pctUsed[1] / 100.0, 1)) "customProfileName",
getProfileName(ageUsed[0], tddUsed[0], weightUsed[0], pctUsed[0] / 100.0, 0) + "\n" + getProfileName(
ageUsed[1],
tddUsed[1],
weightUsed[1],
pctUsed[1] / 100.0,
1
)
)
} }
}.show(supportFragmentManager, "ProfileViewDialog") }.show(supportFragmentManager, "ProfileViewDialog")
return@setOnClickListener return@setOnClickListener
@ -223,14 +243,14 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
switchTab(0, typeSelected[0], false) switchTab(0, typeSelected[0], false)
} }
private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): Profile? = private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): PureProfile? =
try { // profile must not exist try { // profile must not exist
when (typeSelected[tab]) { when (typeSelected[tab]) {
ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())
ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits()) ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits())
ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile() ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile(dateUtil)
ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileInterface.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString()) ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileSource.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString())
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].profileObject?.convertToNonCustomizedProfile() ProfileType.PROFILE_SWITCH -> ProfileSealed.EPS(profileSwitch[profileSwitchUsed[tab]]).convertToNonCustomizedProfile(dateUtil)
} }
} catch (e: Exception) { } catch (e: Exception) {
null null
@ -238,11 +258,11 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
private fun getProfileName(age: Double, tdd: Double, weight: Double, basalSumPct: Double, tab: Int): String = private fun getProfileName(age: Double, tdd: Double, weight: Double, basalSumPct: Double, tab: Int): String =
when (typeSelected[tab]) { when (typeSelected[tab]) {
ProfileType.MOTOL_DEFAULT -> if (tdd > 0) resourceHelper.gs(R.string.formatwithtdd, age, tdd) else resourceHelper.gs(R.string.formatwithweight, age, weight) ProfileType.MOTOL_DEFAULT -> if (tdd > 0) rh.gs(R.string.formatwithtdd, age, tdd) else rh.gs(R.string.formatwithweight, age, weight)
ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt()) ProfileType.DPV_DEFAULT -> rh.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt())
ProfileType.CURRENT -> profileFunction.getProfileName() ProfileType.CURRENT -> profileFunction.getProfileName()
ProfileType.AVAILABLE_PROFILE -> profileList[profileUsed[tab]].toString() ProfileType.AVAILABLE_PROFILE -> profileList[profileUsed[tab]].toString()
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].customizedName ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].originalCustomizedName
} }
private fun storeValues() { private fun storeValues() {
@ -259,17 +279,18 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
tabSelected = tab tabSelected = tab
typeSelected[tabSelected] = newContent typeSelected[tabSelected] = newContent
binding.profiletypeTitle.defaultHintTextColor = ColorStateList.valueOf(resourceHelper.gc(if (tab == 0) R.color.tabBgColorSelected else R.color.examinedProfile)) binding.profiletypeTitle.defaultHintTextColor = ColorStateList.valueOf(rh.gc(if (tab == 0) R.color.tabBgColorSelected else R.color.examinedProfile))
// show new content // show new content
binding.profiletype.setText( binding.profiletype.setText(
when (typeSelected[tabSelected]) { when (typeSelected[tabSelected]) {
ProfileType.MOTOL_DEFAULT -> resourceHelper.gs(R.string.motoldefaultprofile) ProfileType.MOTOL_DEFAULT -> rh.gs(R.string.motoldefaultprofile)
ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.dpvdefaultprofile) ProfileType.DPV_DEFAULT -> rh.gs(R.string.dpvdefaultprofile)
ProfileType.CURRENT -> resourceHelper.gs(R.string.currentprofile) ProfileType.CURRENT -> rh.gs(R.string.currentprofile)
ProfileType.AVAILABLE_PROFILE -> resourceHelper.gs(R.string.availableprofile) ProfileType.AVAILABLE_PROFILE -> rh.gs(R.string.availableprofile)
ProfileType.PROFILE_SWITCH -> resourceHelper.gs(R.string.careportal_profileswitch) ProfileType.PROFILE_SWITCH -> rh.gs(R.string.careportal_profileswitch)
}) }
)
binding.defaultProfile.visibility = (newContent == ProfileType.MOTOL_DEFAULT || newContent == ProfileType.DPV_DEFAULT).toVisibility() binding.defaultProfile.visibility = (newContent == ProfileType.MOTOL_DEFAULT || newContent == ProfileType.DPV_DEFAULT).toVisibility()
binding.currentProfile.visibility = (newContent == ProfileType.CURRENT).toVisibility() binding.currentProfile.visibility = (newContent == ProfileType.CURRENT).toVisibility()
binding.availableProfile.visibility = (newContent == ProfileType.AVAILABLE_PROFILE).toVisibility() binding.availableProfile.visibility = (newContent == ProfileType.AVAILABLE_PROFILE).toVisibility()
@ -285,11 +306,11 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
if (profileList.isNotEmpty()) if (profileList.isNotEmpty())
binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString()) binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString())
if (profileSwitch.isNotEmpty()) if (profileSwitch.isNotEmpty())
binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].customizedName) binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].originalCustomizedName)
} }
private fun setBackgroundColorOnSelected(tab: Int) { private fun setBackgroundColorOnSelected(tab: Int) {
binding.menu1.setBackgroundColor(resourceHelper.gc(if (tab == 1) R.color.defaultbackground else R.color.tabBgColorSelected)) binding.menu1.setBackgroundColor(rh.gc(if (tab == 1) R.color.defaultbackground else R.color.tempbasal))
binding.menu2.setBackgroundColor(resourceHelper.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile)) binding.menu2.setBackgroundColor(rh.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile))
} }
} }

View file

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

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,10 +1,15 @@
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.Sources
import info.nightscout.androidaps.databinding.ActivityStatsBinding import info.nightscout.androidaps.databinding.ActivityStatsBinding
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.utils.ActivityMonitor import info.nightscout.androidaps.utils.ActivityMonitor
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.stats.TddCalculator import info.nightscout.androidaps.utils.stats.TddCalculator
import info.nightscout.androidaps.utils.stats.TirCalculator import info.nightscout.androidaps.utils.stats.TirCalculator
import javax.inject.Inject import javax.inject.Inject
@ -14,21 +19,37 @@ class StatsActivity : NoSplashAppCompatActivity() {
@Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var tirCalculator: TirCalculator @Inject lateinit var tirCalculator: TirCalculator
@Inject lateinit var activityMonitor: ActivityMonitor @Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var uel: UserEntryLogger
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) + ": " + rh.gs(R.string.calculation_in_progress)
binding.tir.text = tirCalculator.stats() binding.tir.text = getString(R.string.tir) + ": " + rh.gs(R.string.calculation_in_progress)
binding.activity.text = activityMonitor.stats() binding.activity.text = rh.gs(R.string.activitymonitor) + ": " + rh.gs(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 {
OKDialog.showConfirmation(this, resourceHelper.gs(R.string.doyouwantresetstats)) { OKDialog.showConfirmation(this, rh.gs(R.string.doyouwantresetstats)) {
uel.log(Action.STAT_RESET, Sources.Stats)
activityMonitor.reset() activityMonitor.reset()
recreate() recreate()
} }

View file

@ -5,31 +5,30 @@ import android.widget.ArrayAdapter
import com.google.firebase.auth.FirebaseAuth import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.FirebaseDatabase import com.google.firebase.database.FirebaseDatabase
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.defaultProfile.DefaultProfile
import info.nightscout.androidaps.databinding.ActivitySurveyBinding import info.nightscout.androidaps.databinding.ActivitySurveyBinding
import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
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.InstanceId import info.nightscout.androidaps.utils.InstanceId
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.defaultProfile.DefaultProfile
import info.nightscout.androidaps.utils.stats.TddCalculator import info.nightscout.androidaps.utils.stats.TddCalculator
import info.nightscout.androidaps.utils.stats.TirCalculator import info.nightscout.androidaps.utils.stats.TirCalculator
import info.nightscout.shared.SafeParse
import info.nightscout.shared.logging.LTag
import javax.inject.Inject import javax.inject.Inject
class SurveyActivity : NoSplashAppCompatActivity() { class SurveyActivity : NoSplashAppCompatActivity() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var tirCalculator: TirCalculator @Inject lateinit var tirCalculator: TirCalculator
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var activityMonitor: ActivityMonitor @Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var defaultProfile: DefaultProfile @Inject lateinit var defaultProfile: DefaultProfile
@Inject lateinit var dateUtil: DateUtil
private lateinit var binding: ActivitySurveyBinding private lateinit var binding: ActivitySurveyBinding
@ -40,7 +39,7 @@ class SurveyActivity : NoSplashAppCompatActivity() {
binding.id.text = InstanceId.instanceId() binding.id.text = InstanceId.instanceId()
val profileStore = activePlugin.activeProfileInterface.profile val profileStore = activePlugin.activeProfileSource.profile
val profileList = profileStore?.getProfileList() ?: return val profileList = profileStore?.getProfileList() ?: return
binding.spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList) binding.spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList)
@ -68,11 +67,10 @@ class SurveyActivity : NoSplashAppCompatActivity() {
defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())?.let { profile -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())?.let { profile ->
ProfileViewerDialog().also { pvd -> ProfileViewerDialog().also { pvd ->
pvd.arguments = Bundle().also { pvd.arguments = Bundle().also {
it.putLong("time", DateUtil.now()) it.putLong("time", dateUtil.now())
it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal) it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal)
it.putString("customProfile", runningProfile.data.toString()) it.putString("customProfile", runningProfile.toPureNsJson(dateUtil).toString())
it.putString("customProfile2", profile.data.toString()) it.putString("customProfile2", profile.jsonObject.toString())
it.putString("customProfileUnits", profile.units)
it.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight") it.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight")
} }
}.show(supportFragmentManager, "ProfileViewDialog") }.show(supportFragmentManager, "ProfileViewDialog")

View file

@ -0,0 +1,80 @@
package info.nightscout.androidaps.activities
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.fragments.*
import info.nightscout.androidaps.databinding.TreatmentsFragmentBinding
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import javax.inject.Inject
class TreatmentsActivity : NoSplashAppCompatActivity() {
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var activePlugin: ActivePlugin
private lateinit var binding: TreatmentsFragmentBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = TreatmentsFragmentBinding.inflate(layoutInflater)
setContentView(binding.root)
//binding.tempBasals.visibility = buildHelper.isEngineeringMode().toVisibility()
//binding.extendedBoluses.visibility = (buildHelper.isEngineeringMode() && !activePlugin.activePump.isFakingTempsByExtendedBoluses).toVisibility()
binding.treatments.setOnClickListener {
setFragment(TreatmentsBolusCarbsFragment())
setBackgroundColorOnSelected(it)
}
binding.extendedBoluses.setOnClickListener {
setFragment(TreatmentsExtendedBolusesFragment())
setBackgroundColorOnSelected(it)
}
binding.tempBasals.setOnClickListener {
setFragment(TreatmentsTemporaryBasalsFragment())
setBackgroundColorOnSelected(it)
}
binding.tempTargets.setOnClickListener {
setFragment(TreatmentsTempTargetFragment())
setBackgroundColorOnSelected(it)
}
binding.profileSwitches.setOnClickListener {
setFragment(TreatmentsProfileSwitchFragment())
setBackgroundColorOnSelected(it)
}
binding.careportal.setOnClickListener {
setFragment(TreatmentsCareportalFragment())
setBackgroundColorOnSelected(it)
}
binding.userentry.setOnClickListener {
setFragment(TreatmentsUserEntryFragment())
setBackgroundColorOnSelected(it)
}
setFragment(TreatmentsBolusCarbsFragment())
setBackgroundColorOnSelected(binding.treatments)
}
private fun setFragment(selectedFragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, selectedFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit()
}
private fun setBackgroundColorOnSelected(selected: View) {
binding.treatments.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.extendedBoluses.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.tempBasals.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.tempTargets.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.profileSwitches.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.careportal.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.userentry.setBackgroundColor(rh.gc(R.color.defaultbackground))
selected.setBackgroundColor(rh.gc(R.color.tabBgColorSelected))
}
}

View file

@ -0,0 +1,400 @@
package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint
import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.BolusCalculatorResult
import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.CutCarbsTransaction
import info.nightscout.androidaps.database.transactions.InvalidateBolusCalculatorResultTransaction
import info.nightscout.androidaps.database.transactions.InvalidateBolusTransaction
import info.nightscout.androidaps.database.transactions.InvalidateCarbsTransaction
import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsItemBinding
import info.nightscout.androidaps.dialogs.WizardInfoDialog
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTreatmentChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class TreatmentsBolusCarbsFragment : DaggerFragment() {
@Inject lateinit var rxBus: RxBus
@Inject lateinit var sp: SP
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var activePlugin: ActivePlugin
class MealLink(
val bolus: Bolus? = null,
val carbs: Carbs? = null,
val bolusCalculatorResult: BolusCalculatorResult? = null
)
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
private var _binding: TreatmentsBolusCarbsFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("CheckResult")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.refreshFromNightscout.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") {
uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments)
disposable +=
Completable.fromAction {
repository.deleteAllBolusCalculatorResults()
repository.deleteAllBoluses()
repository.deleteAllCarbs()
}
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) },
onComplete = {
rxBus.send(EventTreatmentChange())
rxBus.send(EventNewHistoryData(0, false))
}
)
rxBus.send(EventNSClientRestart())
}
}
}
binding.deleteFutureTreatments.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), rh.gs(R.string.deletefuturetreatments) + "?", Runnable {
uel.log(Action.DELETE_FUTURE_TREATMENTS, Sources.Treatments)
repository
.getBolusesDataFromTime(dateUtil.now(), false)
.observeOn(aapsSchedulers.main)
.subscribe { list ->
list.forEach { bolus ->
disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) }
)
}
}
repository
.getCarbsDataFromTimeNotExpanded(dateUtil.now(), false)
.observeOn(aapsSchedulers.main)
.subscribe { list ->
list.forEach { carb ->
if (carb.duration == 0L)
disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
)
else {
disposable += repository.runTransactionForResult(CutCarbsTransaction(carb.id, dateUtil.now()))
.subscribe(
{ result ->
result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated (cut end) carbs $it") }
},
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
)
}
}
}
repository
.getBolusCalculatorResultsDataFromTime(dateUtil.now(), false)
.observeOn(aapsSchedulers.main)
.subscribe { list ->
list.forEach { bolusCalc ->
disposable += repository.runTransactionForResult(InvalidateBolusCalculatorResultTransaction(bolusCalc.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolusCalculatorResult $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolusCalculatorResult", it) }
)
}
}
binding.deleteFutureTreatments.visibility = View.GONE
})
}
}
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_insulin, false) || !sp.getBoolean(R.string.key_ns_receive_carbs, false) || !buildHelper.isEngineeringMode()
if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}
}
private fun bolusMealLinksWithInvalid(now: Long) = repository
.getBolusesIncludingInvalidFromTime(now - millsToThePast, false)
.map { bolus -> bolus.map { MealLink(bolus = it) } }
private fun carbsMealLinksWithInvalid(now: Long) = repository
.getCarbsIncludingInvalidFromTime(now - millsToThePast, false)
.map { carb -> carb.map { MealLink(carbs = it) } }
private fun calcResultMealLinksWithInvalid(now: Long) = repository
.getBolusCalculatorResultsIncludingInvalidFromTime(now - millsToThePast, false)
.map { calc -> calc.map { MealLink(bolusCalculatorResult = it) } }
private fun bolusMealLinks(now: Long) = repository
.getBolusesDataFromTime(now - millsToThePast, false)
.map { bolus -> bolus.map { MealLink(bolus = it) } }
private fun carbsMealLinks(now: Long) = repository
.getCarbsDataFromTime(now - millsToThePast, false)
.map { carb -> carb.map { MealLink(carbs = it) } }
private fun calcResultMealLinks(now: Long) = repository
.getBolusCalculatorResultsDataFromTime(now - millsToThePast, false)
.map { calc -> calc.map { MealLink(bolusCalculatorResult = it) } }
fun swapAdapter() {
val now = System.currentTimeMillis()
if (binding.showInvalidated.isChecked)
disposable += carbsMealLinksWithInvalid(now)
.zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second }
.zipWith(calcResultMealLinksWithInvalid(now)) { first, second -> first + second }
.map { ml ->
ml.sortedByDescending {
it.carbs?.timestamp ?: it.bolus?.timestamp
?: it.bolusCalculatorResult?.timestamp
}
}
.observeOn(aapsSchedulers.main)
.subscribe { list ->
binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true)
binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility()
}
else
disposable += carbsMealLinks(now)
.zipWith(bolusMealLinks(now)) { first, second -> first + second }
.zipWith(calcResultMealLinks(now)) { first, second -> first + second }
.map { ml ->
ml.sortedByDescending {
it.carbs?.timestamp ?: it.bolus?.timestamp
?: it.bolusCalculatorResult?.timestamp
}
}
.observeOn(aapsSchedulers.main)
.subscribe { list ->
binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true)
binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility()
}
}
@Synchronized
override fun onResume() {
super.onResume()
swapAdapter()
disposable += rxBus
.toObservable(EventTreatmentChange::class.java)
.observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
inner class RecyclerViewAdapter internal constructor(var mealLinks: List<MealLink>) : RecyclerView.Adapter<RecyclerViewAdapter.MealLinkLoadedViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): MealLinkLoadedViewHolder =
MealLinkLoadedViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_bolus_carbs_item, viewGroup, false))
override fun onBindViewHolder(holder: MealLinkLoadedViewHolder, position: Int) {
val profile = profileFunction.getProfile() ?: return
val ml = mealLinks[position]
// Metadata
holder.binding.metadataLayout.visibility = (ml.bolusCalculatorResult != null && (ml.bolusCalculatorResult.isValid || binding.showInvalidated.isChecked)).toVisibility()
ml.bolusCalculatorResult?.let { bolusCalculatorResult ->
holder.binding.date.text = dateUtil.dateAndTimeString(bolusCalculatorResult.timestamp)
}
// Bolus
holder.binding.bolusLayout.visibility = (ml.bolus != null && (ml.bolus.isValid || binding.showInvalidated.isChecked)).toVisibility()
ml.bolus?.let { bolus ->
holder.binding.bolusDate.text = dateUtil.timeString(bolus.timestamp)
holder.binding.insulin.text = rh.gs(R.string.formatinsulinunits, bolus.amount)
holder.binding.bolusNs.visibility = (bolus.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.bolusPump.visibility = (bolus.interfaceIDs.pumpId != null).toVisibility()
holder.binding.bolusInvalid.visibility = bolus.isValid.not().toVisibility()
val iob = bolus.iobCalc(activePlugin, System.currentTimeMillis(), profile.dia)
if (iob.iobContrib > 0.01) {
holder.binding.iob.setTextColor(rh.gc(R.color.colorActive))
holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iobContrib)
holder.binding.iobLabel.visibility = View.VISIBLE
holder.binding.iob.visibility = View.VISIBLE
} else {
holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, 0.0)
holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor)
holder.binding.iobLabel.visibility = View.GONE
holder.binding.iob.visibility = View.GONE
}
if (bolus.timestamp > dateUtil.now()) holder.binding.date.setTextColor(rh.gc(R.color.colorScheduled)) else holder.binding.date.setTextColor(holder.binding.carbs.currentTextColor)
holder.binding.mealOrCorrection.text =
when (ml.bolus.type) {
Bolus.Type.SMB -> "SMB"
Bolus.Type.NORMAL -> rh.gs(R.string.mealbolus)
Bolus.Type.PRIMING -> rh.gs(R.string.prime)
}
}
// Carbs
holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || binding.showInvalidated.isChecked)).toVisibility()
ml.carbs?.let { carbs ->
holder.binding.carbsDate.text = dateUtil.timeString(carbs.timestamp)
holder.binding.carbs.text = rh.gs(R.string.format_carbs, carbs.amount.toInt())
holder.binding.carbsDuration.text = if (carbs.duration > 0) rh.gs(R.string.format_mins, T.msecs(carbs.duration).mins().toInt()) else ""
holder.binding.carbsNs.visibility = (carbs.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility()
holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility()
}
holder.binding.bolusRemove.visibility = (ml.bolus?.isValid == true).toVisibility()
holder.binding.carbsRemove.visibility = (ml.carbs?.isValid == true).toVisibility()
holder.binding.bolusRemove.tag = ml
holder.binding.carbsRemove.tag = ml
holder.binding.calculation.tag = ml
}
override fun getItemCount(): Int {
return mealLinks.size
}
inner class MealLinkLoadedViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) {
val binding = TreatmentsBolusCarbsItemBinding.bind(view)
init {
binding.calculation.setOnClickListener {
val mealLinkLoaded = it.tag as MealLink? ?: return@setOnClickListener
mealLinkLoaded.bolusCalculatorResult?.let { bolusCalculatorResult ->
WizardInfoDialog().also { wizardDialog ->
wizardDialog.setData(bolusCalculatorResult)
wizardDialog.show(childFragmentManager, "WizardInfoDialog")
}
}
}
binding.calculation.paintFlags = binding.calculation.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.bolusRemove.setOnClickListener { ml ->
val bolus = (ml.tag as MealLink?)?.bolus ?: return@setOnClickListener
activity?.let { activity ->
val text = rh.gs(R.string.configbuilder_insulin) + ": " +
rh.gs(R.string.formatinsulinunits, bolus.amount) + "\n" +
rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(bolus.timestamp)
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable {
uel.log(
Action.BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(bolus.timestamp),
ValueWithUnit.Insulin(bolus.amount)
//ValueWithUnit.Gram(mealLinkLoaded.carbs.toInt())
)
disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) }
)
})
}
}
binding.bolusRemove.paintFlags = binding.bolusRemove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.carbsRemove.setOnClickListener { ml ->
val carb = (ml.tag as MealLink?)?.carbs ?: return@setOnClickListener
activity?.let { activity ->
val text = rh.gs(R.string.carbs) + ": " +
rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carb.amount.toInt()) + "\n" +
rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(carb.timestamp)
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable {
uel.log(
Action.CARBS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(carb.timestamp),
ValueWithUnit.Gram(carb.amount.toInt())
)
disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
)
})
}
}
binding.carbsRemove.paintFlags = binding.carbsRemove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
}
}
}

View file

@ -0,0 +1,206 @@
package info.nightscout.androidaps.activities.fragments
import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.InvalidateAAPSStartedTherapyEventTransaction
import info.nightscout.androidaps.database.transactions.InvalidateTherapyEventTransaction
import info.nightscout.androidaps.databinding.TreatmentsCareportalFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsCareportalItemBinding
import info.nightscout.androidaps.events.EventTherapyEventChange
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.activities.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class TreatmentsCareportalFragment : DaggerFragment() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBus
@Inject lateinit var sp: SP
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var translator: Translator
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
private var _binding: TreatmentsCareportalFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsCareportalFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.refreshFromNightscout.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.refresheventsfromnightscout) + " ?", Runnable {
uel.log(Action.CAREPORTAL_NS_REFRESH, Sources.Treatments)
disposable += Completable.fromAction { repository.deleteAllTherapyEventsEntries() }
.subscribeOn(aapsSchedulers.io)
.subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) },
onComplete = { rxBus.send(EventTherapyEventChange()) }
)
rxBus.send(EventNSClientRestart())
})
}
}
binding.removeAndroidapsStartedEvents.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.careportal_removestartedevents), Runnable {
uel.log(Action.RESTART_EVENTS_REMOVED, Sources.Treatments)
repository.runTransactionForResult(InvalidateAAPSStartedTherapyEventTransaction(rh.gs(R.string.androidaps_start)))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) }
)
}, null)
}
}
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode()
if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}
}
fun swapAdapter() {
val now = System.currentTimeMillis()
disposable +=
if (binding.showInvalidated.isChecked)
repository
.getTherapyEventDataIncludingInvalidFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
else
repository
.getTherapyEventDataFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
}
@Synchronized
override fun onResume() {
super.onResume()
swapAdapter()
disposable += rxBus
.toObservable(EventTherapyEventChange::class.java)
.observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
inner class RecyclerViewAdapter internal constructor(private var list: List<TherapyEvent>) : RecyclerView.Adapter<TherapyEventsViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TherapyEventsViewHolder {
val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_careportal_item, viewGroup, false)
return TherapyEventsViewHolder(v)
}
override fun onBindViewHolder(holder: TherapyEventsViewHolder, position: Int) {
val therapyEvent = list[position]
holder.binding.ns.visibility = (therapyEvent.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = therapyEvent.isValid.not().toVisibility()
holder.binding.date.text = dateUtil.dateAndTimeString(therapyEvent.timestamp)
holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh)
holder.binding.note.text = therapyEvent.note
holder.binding.type.text = translator.translate(therapyEvent.type)
holder.binding.remove.tag = therapyEvent
}
override fun getItemCount(): Int {
return list.size
}
inner class TherapyEventsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = TreatmentsCareportalItemBinding.bind(view)
init {
binding.remove.setOnClickListener { v: View ->
val therapyEvent = v.tag as TherapyEvent
activity?.let { activity ->
val text = rh.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type) + "\n" +
rh.gs(R.string.notes_label) + ": " + (therapyEvent.note
?: "") + "\n" +
rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(therapyEvent.timestamp)
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable {
uel.log(Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note ,
ValueWithUnit.Timestamp(therapyEvent.timestamp),
ValueWithUnit.TherapyEventType(therapyEvent.type))
disposable += repository.runTransactionForResult(InvalidateTherapyEventTransaction(therapyEvent.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) }
)
}, null)
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
}
}
}

View file

@ -0,0 +1,178 @@
package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ExtendedBolus
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.database.transactions.InvalidateExtendedBolusTransaction
import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusItemBinding
import info.nightscout.androidaps.events.EventExtendedBolusChange
import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.activities.fragments.TreatmentsExtendedBolusesFragment.RecyclerViewAdapter.ExtendedBolusesViewHolder
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class TreatmentsExtendedBolusesFragment : DaggerFragment() {
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var rxBus: RxBus
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private var _binding: TreatmentsExtendedbolusFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View =
TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
}
fun swapAdapter() {
val now = System.currentTimeMillis()
if (binding.showInvalidated.isChecked)
repository
.getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
else
repository
.getExtendedBolusDataFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
}
@Synchronized
override fun onResume() {
super.onResume()
swapAdapter()
disposable += rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
private inner class RecyclerViewAdapter(private var extendedBolusList: List<ExtendedBolus>) : RecyclerView.Adapter<ExtendedBolusesViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ExtendedBolusesViewHolder {
val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_extendedbolus_item, viewGroup, false)
return ExtendedBolusesViewHolder(v)
}
override fun onBindViewHolder(holder: ExtendedBolusesViewHolder, position: Int) {
val extendedBolus = extendedBolusList[position]
holder.binding.ns.visibility = (extendedBolus.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.ph.visibility = (extendedBolus.interfaceIDs.pumpId != null).toVisibility()
holder.binding.invalid.visibility = extendedBolus.isValid.not().toVisibility()
@SuppressLint("SetTextI18n")
if (extendedBolus.isInProgress(dateUtil)) {
holder.binding.date.text = dateUtil.dateAndTimeString(extendedBolus.timestamp)
holder.binding.date.setTextColor(rh.gc(R.color.colorActive))
} else {
holder.binding.date.text = dateUtil.dateAndTimeString(extendedBolus.timestamp) + " - " + dateUtil.timeString(extendedBolus.end)
holder.binding.date.setTextColor(holder.binding.insulin.currentTextColor)
}
val profile = profileFunction.getProfile(extendedBolus.timestamp) ?: return
holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(extendedBolus.duration).mins())
holder.binding.insulin.text = rh.gs(R.string.formatinsulinunits, extendedBolus.amount)
val iob = extendedBolus.iobCalc(System.currentTimeMillis(), profile, activePlugin.activeInsulin)
holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iob)
holder.binding.ratio.text = rh.gs(R.string.pump_basebasalrate, extendedBolus.rate)
if (iob.iob != 0.0) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor)
holder.binding.remove.tag = extendedBolus
}
override fun getItemCount(): Int = extendedBolusList.size
inner class ExtendedBolusesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = TreatmentsExtendedbolusItemBinding.bind(itemView)
init {
binding.remove.setOnClickListener { v: View ->
val extendedBolus = v.tag as ExtendedBolus
context?.let { context ->
OKDialog.showConfirmation(context, rh.gs(R.string.removerecord),
"""
${rh.gs(R.string.extended_bolus)}
${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(extendedBolus.timestamp)}
""".trimIndent(), { _: DialogInterface, _: Int ->
uel.log(Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(extendedBolus.timestamp),
ValueWithUnit.Insulin(extendedBolus.amount),
ValueWithUnit.UnitPerHour(extendedBolus.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt()))
disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) })
}, null)
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
}
}
}

View file

@ -0,0 +1,264 @@
package info.nightscout.androidaps.activities.fragments
import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InvalidateProfileSwitchTransaction
import info.nightscout.androidaps.databinding.TreatmentsProfileswitchFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsProfileswitchItemBinding
import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.extensions.getCustomizedName
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.activities.fragments.TreatmentsProfileSwitchFragment.RecyclerProfileViewAdapter.ProfileSwitchViewHolder
import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy
import javax.inject.Inject
class TreatmentsProfileSwitchFragment : DaggerFragment() {
@Inject lateinit var rxBus: RxBus
@Inject lateinit var sp: SP
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger
private var _binding: TreatmentsProfileswitchFragmentBinding? = null
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsProfileswitchFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.refreshFromNightscout.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") {
uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments)
disposable +=
Completable.fromAction {
repository.deleteAllEffectiveProfileSwitches()
repository.deleteAllProfileSwitches()
}
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) },
onComplete = {
rxBus.send(EventProfileSwitchChanged())
rxBus.send(EventEffectiveProfileSwitchChanged(0L))
rxBus.send(EventNewHistoryData(0, false))
}
)
rxBus.send(EventNSClientRestart())
}
}
}
if (!sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode()) binding.refreshFromNightscout.visibility = View.GONE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}
}
private fun profileSwitchWithInvalid(now: Long) = repository
.getProfileSwitchDataIncludingInvalidFromTime(now - millsToThePast, false)
.map { bolus -> bolus.map { ProfileSealed.PS(it) } }
private fun effectiveProfileSwitchWithInvalid(now: Long) = repository
.getEffectiveProfileSwitchDataIncludingInvalidFromTime(now - millsToThePast, false)
.map { carb -> carb.map { ProfileSealed.EPS(it) } }
private fun profileSwitches(now: Long) = repository
.getProfileSwitchDataFromTime(now - millsToThePast, false)
.map { bolus -> bolus.map { ProfileSealed.PS(it) } }
private fun effectiveProfileSwitches(now: Long) = repository
.getEffectiveProfileSwitchDataFromTime(now - millsToThePast, false)
.map { carb -> carb.map { ProfileSealed.EPS(it) } }
fun swapAdapter() {
val now = System.currentTimeMillis()
if (binding.showInvalidated.isChecked)
disposable += profileSwitchWithInvalid(now)
.zipWith(effectiveProfileSwitchWithInvalid(now)) { first, second -> first + second }
.map { ml -> ml.sortedByDescending { it.timestamp } }
.observeOn(aapsSchedulers.main)
.subscribe { list ->
binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true)
}
else
disposable += profileSwitches(now)
.zipWith(effectiveProfileSwitches(now)) { first, second -> first + second }
.map { ml -> ml.sortedByDescending { it.timestamp } }
.observeOn(aapsSchedulers.main)
.subscribe { list ->
binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true)
}
}
@Synchronized
override fun onResume() {
super.onResume()
swapAdapter()
disposable += rxBus
.toObservable(EventProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
inner class RecyclerProfileViewAdapter(private var profileSwitchList: List<ProfileSealed>) : RecyclerView.Adapter<ProfileSwitchViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ProfileSwitchViewHolder =
ProfileSwitchViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_profileswitch_item, viewGroup, false))
override fun onBindViewHolder(holder: ProfileSwitchViewHolder, position: Int) {
val profileSwitch = profileSwitchList[position]
holder.binding.ph.visibility = (profileSwitch is ProfileSealed.EPS).toVisibility()
holder.binding.ns.visibility = (profileSwitch.interfaceIDs_backing?.nightscoutId != null).toVisibility()
holder.binding.date.text = dateUtil.dateAndTimeString(profileSwitch.timestamp)
holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(profileSwitch.duration ?: 0L).mins())
holder.binding.name.text = if (profileSwitch is ProfileSealed.PS) profileSwitch.value.getCustomizedName() else if (profileSwitch is ProfileSealed.EPS) profileSwitch.value.originalCustomizedName else ""
if (profileSwitch.isInProgress(dateUtil)) holder.binding.date.setTextColor(rh.gc(R.color.colorActive))
else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor)
holder.binding.remove.tag = profileSwitch
holder.binding.clone.tag = profileSwitch
holder.binding.name.tag = profileSwitch
holder.binding.date.tag = profileSwitch
holder.binding.invalid.visibility = profileSwitch.isValid.not().toVisibility()
holder.binding.duration.visibility = (profileSwitch.duration != 0L && profileSwitch.duration != null).toVisibility()
holder.binding.remove.visibility = (profileSwitch is ProfileSealed.PS).toVisibility()
holder.binding.clone.visibility = (profileSwitch is ProfileSealed.PS).toVisibility()
holder.binding.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility()
holder.binding.root.setBackgroundColor(rh.gc(if (profileSwitch is ProfileSealed.PS) R.color.defaultbackground else R.color.list_delimiter))
}
override fun getItemCount(): Int {
return profileSwitchList.size
}
inner class ProfileSwitchViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = TreatmentsProfileswitchItemBinding.bind(itemView)
init {
binding.remove.setOnClickListener { view ->
val profileSwitch = view.tag as ProfileSealed.PS
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord),
rh.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName +
"\n" + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.timestamp), Runnable {
uel.log(Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName,
ValueWithUnit.Timestamp(profileSwitch.timestamp))
disposable += repository.runTransactionForResult(InvalidateProfileSwitchTransaction(profileSwitch.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) }
)
})
}
}
binding.clone.setOnClickListener {
activity?.let { activity ->
val profileSwitch = (it.tag as ProfileSealed.PS).value
val profileSealed = it.tag as ProfileSealed
OKDialog.showConfirmation(activity, rh.gs(R.string.careportal_profileswitch), rh.gs(R.string.copytolocalprofile) + "\n" + profileSwitch.getCustomizedName() + "\n" + dateUtil.dateAndTimeString(profileSwitch.timestamp), Runnable {
uel.log(Action.PROFILE_SWITCH_CLONED, Sources.Treatments,
profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_"),
ValueWithUnit.Timestamp(profileSwitch.timestamp),
ValueWithUnit.SimpleString(profileSwitch.profileName))
val nonCustomized = profileSealed.convertToNonCustomizedProfile(dateUtil)
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(nonCustomized, profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_")))
rxBus.send(EventLocalProfileChanged())
})
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.clone.paintFlags = binding.clone.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.name.setOnClickListener {
ProfileViewerDialog().also { pvd ->
pvd.arguments = Bundle().also { args ->
args.putLong("time", (it.tag as ProfileSealed).timestamp)
args.putInt("mode", ProfileViewerDialog.Mode.RUNNING_PROFILE.ordinal)
}
pvd.show(childFragmentManager, "ProfileViewDialog")
}
}
binding.date.setOnClickListener {
ProfileViewerDialog().also { pvd ->
pvd.arguments = Bundle().also { args ->
args.putLong("time", (it.tag as ProfileSealed).timestamp)
args.putInt("mode", ProfileViewerDialog.Mode.RUNNING_PROFILE.ordinal)
}
pvd.show(childFragmentManager, "ProfileViewDialog")
}
}
}
}
}
}

View file

@ -0,0 +1,214 @@
package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.database.transactions.InvalidateTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.TreatmentsTemptargetFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsTemptargetItemBinding
import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.extensions.friendlyDescription
import info.nightscout.androidaps.extensions.highValueToUnitsToString
import info.nightscout.androidaps.extensions.lowValueToUnitsToString
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class TreatmentsTempTargetFragment : DaggerFragment() {
@Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBus
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var translator: Translator
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
private var _binding: TreatmentsTemptargetFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsTemptargetFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.refreshFromNightscout.setOnClickListener {
context?.let { context ->
OKDialog.showConfirmation(context, rh.gs(R.string.refresheventsfromnightscout) + " ?", {
uel.log(Action.TT_NS_REFRESH, Sources.Treatments)
disposable += Completable.fromAction { repository.deleteAllTempTargetEntries() }
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribeBy(
onError = { aapsLogger.error("Error removing entries", it) },
onComplete = { rxBus.send(EventTempTargetChange()) }
)
rxBus.send(EventNSClientRestart())
})
}
}
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode()
if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.INVISIBLE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}
}
fun swapAdapter() {
val now = System.currentTimeMillis()
if (binding.showInvalidated.isChecked)
repository
.getTemporaryTargetDataIncludingInvalidFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
else
repository
.getTemporaryTargetDataFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
}
@Synchronized
override fun onResume() {
super.onResume()
swapAdapter()
disposable += rxBus
.toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
private inner class RecyclerViewAdapter(private var tempTargetList: List<TemporaryTarget>) : RecyclerView.Adapter<TempTargetsViewHolder>() {
private val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
private val currentlyActiveTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TempTargetsViewHolder =
TempTargetsViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_temptarget_item, viewGroup, false))
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: TempTargetsViewHolder, position: Int) {
val units = profileFunction.getUnits()
val tempTarget = tempTargetList[position]
holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility()
holder.binding.remove.visibility = tempTarget.isValid.toVisibility()
holder.binding.date.text = dateUtil.dateAndTimeString(tempTarget.timestamp) + " - " + dateUtil.timeString(tempTarget.end)
holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(tempTarget.duration).mins())
holder.binding.low.text = tempTarget.lowValueToUnitsToString(units)
holder.binding.high.text = tempTarget.highValueToUnitsToString(units)
holder.binding.reason.text = translator.translate(tempTarget.reason)
holder.binding.date.setTextColor(
when {
tempTarget.id == currentlyActiveTarget?.id -> rh.gc(R.color.colorActive)
tempTarget.timestamp > dateUtil.now() -> rh.gc(R.color.colorScheduled)
else -> holder.binding.reasonColon.currentTextColor
})
holder.binding.remove.tag = tempTarget
}
override fun getItemCount(): Int = tempTargetList.size
inner class TempTargetsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = TreatmentsTemptargetItemBinding.bind(view)
init {
binding.remove.setOnClickListener { v: View ->
val tempTarget = v.tag as TemporaryTarget
context?.let { context ->
OKDialog.showConfirmation(context, rh.gs(R.string.removerecord),
"""
${rh.gs(R.string.careportal_temporarytarget)}: ${tempTarget.friendlyDescription(profileFunction.getUnits(), rh)}
${dateUtil.dateAndTimeString(tempTarget.timestamp)}
""".trimIndent(),
{ _: DialogInterface?, _: Int ->
uel.log(Action.TT_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempTarget.timestamp),
ValueWithUnit.TherapyEventTTReason(tempTarget.reason),
ValueWithUnit.Mgdl(tempTarget.lowTarget),
ValueWithUnit.Mgdl(tempTarget.highTarget).takeIf { tempTarget.lowTarget != tempTarget.highTarget },
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tempTarget.duration).toInt()))
disposable += repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(tempTarget.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed temp target $tempTarget") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) })
}, null)
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
}
}
}

View file

@ -0,0 +1,243 @@
package info.nightscout.androidaps.activities.fragments
import android.content.DialogInterface
import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.ExtendedBolus
import info.nightscout.androidaps.database.entities.TemporaryBasal
import info.nightscout.androidaps.database.entities.UserEntry.*
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.database.transactions.InvalidateExtendedBolusTransaction
import info.nightscout.androidaps.database.transactions.InvalidateTemporaryBasalTransaction
import info.nightscout.androidaps.databinding.TreatmentsTempbasalsFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsTempbasalsItemBinding
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.androidaps.extensions.toTemporaryBasal
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.math.abs
class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
private val disposable = CompositeDisposable()
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBus
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private var _binding: TreatmentsTempbasalsFragmentBinding? = null
private val millsToThePast = T.days(30).msecs()
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsTempbasalsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
}
private fun tempBasalsWithInvalid(now: Long) = repository
.getTemporaryBasalsDataIncludingInvalidFromTime(now - millsToThePast, false)
private fun tempBasals(now: Long) = repository
.getTemporaryBasalsDataFromTime(now - millsToThePast, false)
private fun extendedBolusesWithInvalid(now: Long) = repository
.getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false)
.map { eb -> eb.map { profileFunction.getProfile(it.timestamp)?.let { profile -> it.toTemporaryBasal(profile) } } }
private fun extendedBoluses(now: Long) = repository
.getExtendedBolusDataFromTime(now - millsToThePast, false)
.map { eb -> eb.map { profileFunction.getProfile(it.timestamp)?.let { profile -> it.toTemporaryBasal(profile) } } }
fun swapAdapter() {
val now = System.currentTimeMillis()
disposable +=
if (activePlugin.activePump.isFakingTempsByExtendedBoluses) {
if (binding.showInvalidated.isChecked)
tempBasalsWithInvalid(now)
.zipWith(extendedBolusesWithInvalid(now)) { first, second -> first + second }
.map { list -> list.filterNotNull() }
.map { list -> list.sortedByDescending { it.timestamp } }
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
else
tempBasals(now)
.zipWith(extendedBoluses(now)) { first, second -> first + second }
.map { list -> list.filterNotNull() }
.map { list -> list.sortedByDescending { it.timestamp } }
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
} else {
if (binding.showInvalidated.isChecked)
tempBasalsWithInvalid(now)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
else
tempBasals(now)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
}
}
@Synchronized
override fun onResume() {
super.onResume()
swapAdapter()
disposable += rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
inner class RecyclerViewAdapter internal constructor(private var tempBasalList: List<TemporaryBasal>) : RecyclerView.Adapter<TempBasalsViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TempBasalsViewHolder =
TempBasalsViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_tempbasals_item, viewGroup, false))
override fun onBindViewHolder(holder: TempBasalsViewHolder, position: Int) {
val tempBasal = tempBasalList[position]
holder.binding.ns.visibility = (tempBasal.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = tempBasal.isValid.not().toVisibility()
holder.binding.ph.visibility = (tempBasal.interfaceIDs.pumpId != null).toVisibility()
if (tempBasal.isInProgress) {
holder.binding.date.text = dateUtil.dateAndTimeString(tempBasal.timestamp)
holder.binding.date.setTextColor(rh.gc(R.color.colorActive))
} else {
holder.binding.date.text = dateUtil.dateAndTimeRangeString(tempBasal.timestamp, tempBasal.end)
holder.binding.date.setTextColor(holder.binding.duration.currentTextColor)
}
holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(tempBasal.duration).mins())
if (tempBasal.isAbsolute) holder.binding.rate.text = rh.gs(R.string.pump_basebasalrate, tempBasal.rate)
else holder.binding.rate.text = rh.gs(R.string.format_percent, tempBasal.rate.toInt())
val now = dateUtil.now()
var iob = IobTotal(now)
val profile = profileFunction.getProfile(now)
if (profile != null) iob = tempBasal.iobCalc(now, profile, activePlugin.activeInsulin)
holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.basaliob)
holder.binding.extendedFlag.visibility = (tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED).toVisibility()
holder.binding.suspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.PUMP_SUSPEND).toVisibility()
holder.binding.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).toVisibility()
holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility()
if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor)
holder.binding.remove.tag = tempBasal
}
override fun getItemCount(): Int = tempBasalList.size
inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = TreatmentsTempbasalsItemBinding.bind(itemView)
init {
binding.remove.setOnClickListener { v: View ->
val tempBasal = v.tag as TemporaryBasal
var extendedBolus: ExtendedBolus? = null
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
if (isFakeExtended) {
val eb = repository.getExtendedBolusActiveAt(tempBasal.timestamp).blockingGet()
extendedBolus = if (eb is ValueWrapper.Existing) eb.value else null
}
val profile = profileFunction.getProfile(dateUtil.now())
?: return@setOnClickListener
context?.let {
OKDialog.showConfirmation(it, rh.gs(R.string.removerecord),
"""
${if (isFakeExtended) rh.gs(R.string.extended_bolus) else rh.gs(R.string.tempbasal_label)}: ${tempBasal.toStringFull(profile, dateUtil)}
${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(tempBasal.timestamp)}
""".trimIndent(),
{ _: DialogInterface?, _: Int ->
if (isFakeExtended && extendedBolus != null) {
uel.log(Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(extendedBolus.timestamp),
ValueWithUnit.Insulin(extendedBolus.amount),
ValueWithUnit.UnitPerHour(extendedBolus.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt()))
disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) })
} else if (!isFakeExtended) {
uel.log(Action.TEMP_BASAL_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempBasal.timestamp),
if (tempBasal.isAbsolute) ValueWithUnit.UnitPerHour(tempBasal.rate) else ValueWithUnit.Percent(tempBasal.rate.toInt()),
ValueWithUnit.Minute(T.msecs(tempBasal.duration).mins().toInt()))
disposable += repository.runTransactionForResult(InvalidateTemporaryBasalTransaction(tempBasal.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed temporary basal $tempBasal") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) })
}
}, null)
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
}
}
}

View file

@ -0,0 +1,152 @@
package info.nightscout.androidaps.activities.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.TreatmentsUserEntryFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsUserEntryItemBinding
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.ImportExportPrefs
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class TreatmentsUserEntryFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var rxBus: RxBus
@Inject lateinit var translator: Translator
@Inject lateinit var importExportPrefs: ImportExportPrefs
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var userEntryPresentationHelper: UserEntryPresentationHelper
private val disposable = CompositeDisposable()
private val millsToThePastFiltered = T.days(30).msecs()
private val millsToThePastUnFiltered = T.days(3).msecs()
private var _binding: TreatmentsUserEntryFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsUserEntryFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.ueExportToXml.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.ue_export_to_csv) + "?") {
uel.log(Action.EXPORT_CSV, Sources.Treatments)
importExportPrefs.exportUserEntriesCsv(activity)
}
}
}
binding.showLoop.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}
}
fun swapAdapter() {
val now = System.currentTimeMillis()
if (binding.showLoop.isChecked)
disposable.add( repository
.getUserEntryDataFromTime(now - millsToThePastUnFiltered)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) }
)
else
disposable.add( repository
.getUserEntryFilteredDataFromTime(now - millsToThePastFiltered)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) }
)
}
@Synchronized
override fun onResume() {
super.onResume()
swapAdapter()
disposable.add(rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ swapAdapter() }, fabricPrivacy::logException))
disposable.add(rxBus
.toObservable(EventTreatmentUpdateGui::class.java)
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException))
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
_binding = null
}
inner class UserEntryAdapter internal constructor(var entries: List<UserEntry>) : RecyclerView.Adapter<UserEntryAdapter.UserEntryViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserEntryViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(R.layout.treatments_user_entry_item, parent, false)
return UserEntryViewHolder(view)
}
override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) {
val current = entries[position]
holder.binding.date.text = dateUtil.dateAndTimeAndSecondsString(current.timestamp)
holder.binding.action.text = userEntryPresentationHelper.actionToColoredString(current.action)
holder.binding.s.text = current.note
holder.binding.s.visibility = if (current.note != "") View.VISIBLE else View.GONE
holder.binding.iconSource.setImageResource(userEntryPresentationHelper.iconId(current.source))
holder.binding.iconSource.visibility = View.VISIBLE
holder.binding.values.text = userEntryPresentationHelper.listToPresentationString(current.values)
holder.binding.values.visibility = if (holder.binding.values.text != "") View.VISIBLE else View.GONE
}
inner class UserEntryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = TreatmentsUserEntryItemBinding.bind(itemView)
}
override fun getItemCount(): Int = entries.size
}
}

View file

@ -0,0 +1,88 @@
package info.nightscout.androidaps.db
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.events.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import io.reactivex.disposables.Disposable
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class CompatDBHelper @Inject constructor(
val aapsLogger: AAPSLogger,
val repository: AppRepository,
val rxBus: RxBus
) {
fun dbChangeDisposable(): Disposable = repository
.changeObservable()
.doOnSubscribe {
rxBus.send(EventNewBG(null))
}
.subscribe {
/**
* GlucoseValues can come in batch
* oldest one should be used for invalidation, newest one for for triggering Loop.
* Thus we need to collect both
*
*/
var newestGlucoseValue: GlucoseValue? = null
it.filterIsInstance<GlucoseValue>().lastOrNull()?.let { gv ->
aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg $gv")
rxBus.send(EventNewBG(gv))
newestGlucoseValue = gv
}
it.filterIsInstance<GlucoseValue>().map { gv -> gv.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventNewHistoryData $newestGlucoseValue")
rxBus.send(EventNewHistoryData(timestamp, true, newestGlucoseValue))
}
it.filterIsInstance<Carbs>().map { t -> t.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTreatmentChange $timestamp")
rxBus.send(EventTreatmentChange())
rxBus.send(EventNewHistoryData(timestamp, false))
}
it.filterIsInstance<Bolus>().map { t -> t.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTreatmentChange $timestamp")
rxBus.send(EventTreatmentChange())
rxBus.send(EventNewHistoryData(timestamp, false))
}
it.filterIsInstance<TemporaryBasal>().map { t -> t.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTempBasalChange $timestamp")
rxBus.send(EventTempBasalChange())
rxBus.send(EventNewHistoryData(timestamp, false))
}
it.filterIsInstance<ExtendedBolus>().map { t -> t.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventExtendedBolusChange $timestamp")
rxBus.send(EventExtendedBolusChange())
rxBus.send(EventNewHistoryData(timestamp, false))
}
it.filterIsInstance<TemporaryTarget>().firstOrNull()?.let { tt ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange $tt")
rxBus.send(EventTempTargetChange())
}
it.filterIsInstance<TherapyEvent>().firstOrNull()?.let { te ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTherapyEventChange $te")
rxBus.send(EventTherapyEventChange())
}
it.filterIsInstance<Food>().firstOrNull()?.let { food ->
aapsLogger.debug(LTag.DATABASE, "Firing EventFoodDatabaseChanged $food")
rxBus.send(EventFoodDatabaseChanged())
}
it.filterIsInstance<ProfileSwitch>().firstOrNull()?.let { ps ->
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged $ps")
rxBus.send(EventProfileSwitchChanged())
}
it.filterIsInstance<EffectiveProfileSwitch>().firstOrNull()?.let { eps ->
aapsLogger.debug(LTag.DATABASE, "Firing EventEffectiveProfileSwitchChanged $eps")
rxBus.send(EventEffectiveProfileSwitchChanged(eps))
}
it.filterIsInstance<OfflineEvent>().firstOrNull()?.let { oe ->
aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange $oe")
rxBus.send(EventOfflineChange())
}
}
}

View file

@ -1,115 +0,0 @@
package info.nightscout.androidaps.db;
import com.j256.ormlite.dao.CloseableIterator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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;
@Singleton
public class DatabaseHelperProvider implements DatabaseHelperInterface {
@Inject DatabaseHelperProvider() {
}
@NotNull @Override public List<BgReading> getAllBgreadingsDataFromTime(long mills, boolean ascending) {
return MainApp.getDbHelper().getAllBgreadingsDataFromTime(mills, ascending);
}
@Override public void createOrUpdate(@NotNull CareportalEvent careportalEvent) {
MainApp.getDbHelper().createOrUpdate(careportalEvent);
}
@Override public void createOrUpdate(@NotNull DanaRHistoryRecord record) {
MainApp.getDbHelper().createOrUpdate(record);
}
@Override public void createOrUpdate(@NotNull OmnipodHistoryRecord record) {
MainApp.getDbHelper().createOrUpdate(record);
}
@NotNull @Override public List<DanaRHistoryRecord> getDanaRHistoryRecordsByType(byte type) {
return MainApp.getDbHelper().getDanaRHistoryRecordsByType(type);
}
@NotNull @Override public List<TDD> getTDDs() {
return MainApp.getDbHelper().getTDDs();
}
@Override public long size(@NotNull String table) {
return MainApp.getDbHelper().size(table);
}
@Override public void create(@NotNull DbRequest record) {
try {
MainApp.getDbHelper().create(record);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override public void deleteAllDbRequests() {
MainApp.getDbHelper().deleteAllDbRequests();
}
@Override public int deleteDbRequest(@NotNull String id) {
return MainApp.getDbHelper().deleteDbRequest(id);
}
@Override public void deleteDbRequestbyMongoId(@NotNull String action, @NotNull String _id) {
MainApp.getDbHelper().deleteDbRequestbyMongoId(action, _id);
}
@NotNull @Override public CloseableIterator<DbRequest> getDbRequestInterator() {
return MainApp.getDbHelper().getDbRequestInterator();
}
@Override public long roundDateToSec(long date) {
return MainApp.getDbHelper().roundDateToSec(date);
}
@Override public void createOrUpdateTDD(@NotNull TDD record) {
MainApp.getDbHelper().createOrUpdateTDD(record);
}
@Override public void createOrUpdate(@NotNull TemporaryBasal tempBasal) {
MainApp.getDbHelper().createOrUpdate(tempBasal);
}
@NotNull @Override public TemporaryBasal findTempBasalByPumpId(long id) {
return MainApp.getDbHelper().findTempBasalByPumpId(id);
}
@NotNull @Override public List<TemporaryBasal> getTemporaryBasalsDataFromTime(long mills, boolean ascending) {
return MainApp.getDbHelper().getTemporaryBasalsDataFromTime(mills, ascending);
}
@Override public CareportalEvent getCareportalEventFromTimestamp(long timestamp) {
return MainApp.getDbHelper().getCareportalEventFromTimestamp(timestamp);
}
@NotNull @Override public List<OmnipodHistoryRecord> getAllOmnipodHistoryRecordsFromTimestamp(long timestamp, boolean ascending) {
return MainApp.getDbHelper().getAllOmnipodHistoryRecordsFromTimeStamp(timestamp, ascending);
}
@Nullable @Override public OmnipodHistoryRecord findOmnipodHistoryRecordByPumpId(long pumpId) {
return MainApp.getDbHelper().findOmnipodHistoryRecordByPumpId(pumpId);
}
@NotNull @Override public List<TDD> getTDDsForLastXDays(int days) {
return MainApp.getDbHelper().getTDDsForLastXDays(days);
}
@NotNull @Override public List<ProfileSwitch> getProfileSwitchData(long from, boolean ascending) {
return MainApp.getDbHelper().getProfileSwitchData(from, ascending);
}
}

View file

@ -1,14 +0,0 @@
package info.nightscout.androidaps.db
import com.j256.ormlite.field.DatabaseField
import com.j256.ormlite.table.DatabaseTable
@DatabaseTable(tableName = DatabaseHelper.DATABASE_OPEN_HUMANS_QUEUE)
data class OHQueueItem @JvmOverloads constructor(
@DatabaseField(generatedId = true)
val id: Long = 0,
@DatabaseField
val file: String = "",
@DatabaseField
val content: String = ""
)

View file

@ -1,44 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.MainActivity
import info.nightscout.androidaps.activities.*
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity
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.smsCommunicator.activities.SmsCommunicatorOtpActivity
import info.nightscout.androidaps.plugins.pump.common.dialog.RileyLinkBLEConfigActivity
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusActivity
import info.nightscout.androidaps.plugins.pump.insight.activities.InsightAlertActivity
import info.nightscout.androidaps.plugins.pump.insight.activities.InsightPairingActivity
import info.nightscout.androidaps.plugins.pump.insight.activities.InsightPairingInformationActivity
import info.nightscout.androidaps.plugins.pump.medtronic.dialog.MedtronicHistoryActivity
import info.nightscout.androidaps.setupwizard.SetupWizardActivity
@Module
@Suppress("unused")
abstract class ActivitiesModule {
@ContributesAndroidInjector abstract fun contributesHistoryBrowseActivity(): HistoryBrowseActivity
@ContributesAndroidInjector abstract fun contributesInsightAlertActivity(): InsightAlertActivity
@ContributesAndroidInjector abstract fun contributesInsightPairingActivity(): InsightPairingActivity
@ContributesAndroidInjector abstract fun contributesInsightPairingInformationActivity(): InsightPairingInformationActivity
@ContributesAndroidInjector abstract fun contributesLogSettingActivity(): LogSettingActivity
@ContributesAndroidInjector abstract fun contributeMainActivity(): MainActivity
@ContributesAndroidInjector abstract fun contributesMedtronicHistoryActivity(): MedtronicHistoryActivity
@ContributesAndroidInjector abstract fun contributesPreferencesActivity(): PreferencesActivity
@ContributesAndroidInjector abstract fun contributesQuickWizardListActivity(): QuickWizardListActivity
@ContributesAndroidInjector abstract fun contributesRequestDexcomPermissionActivity(): RequestDexcomPermissionActivity
@ContributesAndroidInjector abstract fun contributesRileyLinkStatusActivity(): RileyLinkStatusActivity
@ContributesAndroidInjector abstract fun contributesRileyLinkBLEConfigActivity(): RileyLinkBLEConfigActivity
@ContributesAndroidInjector abstract fun contributesSetupWizardActivity(): SetupWizardActivity
@ContributesAndroidInjector abstract fun contributesSingleFragmentActivity(): SingleFragmentActivity
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorOtpActivity(): SmsCommunicatorOtpActivity
@ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity
@ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity
@ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity
@ContributesAndroidInjector abstract fun contributesOpenHumansLoginActivity(): OpenHumansLoginActivity
}

View file

@ -1,57 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import dagger.BindsInstance
import dagger.Component
import dagger.android.AndroidInjectionModule
import dagger.android.AndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.core.di.CoreModule
import info.nightscout.androidaps.dana.di.DanaModule
import info.nightscout.androidaps.danar.di.DanaRModule
import info.nightscout.androidaps.danars.di.DanaRSModule
import info.nightscout.androidaps.plugins.pump.common.dagger.RileyLinkModule
import info.nightscout.androidaps.plugins.pump.omnipod.dagger.OmnipodModule
import javax.inject.Singleton
@Singleton
@Component(
modules = [
AndroidInjectionModule::class,
PluginsModule::class,
SkinsModule::class,
ActivitiesModule::class,
FragmentsModule::class,
AppModule::class,
ReceiversModule::class,
ServicesModule::class,
AutomationModule::class,
CommandQueueModule::class,
ObjectivesModule::class,
WizardModule::class,
RileyLinkModule::class,
MedtronicModule::class,
OmnipodModule::class,
APSModule::class,
PreferencesModule::class,
OverviewModule::class,
DataClassesModule::class,
SMSModule::class,
UIModule::class,
CoreModule::class,
DanaModule::class,
DanaRModule::class,
DanaRSModule::class,
OHUploaderModule::class
]
)
interface AppComponent : AndroidInjector<MainApp> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(mainApp: MainApp): Builder
fun build(): AppComponent
}
}

View file

@ -1,64 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import android.content.Context
import dagger.Binds
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.db.DatabaseHelperProvider
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.utils.androidNotification.NotificationHolder
import info.nightscout.androidaps.utils.storage.FileStorage
import info.nightscout.androidaps.utils.storage.Storage
import javax.inject.Singleton
@Module(includes = [
AppModule.AppBindings::class
])
open class AppModule {
@Provides
fun providesPlugins(configInterface: ConfigInterface,
@PluginsModule.AllConfigs allConfigs: Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>,
@PluginsModule.PumpDriver pumpDrivers: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.NotNSClient notNsClient: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.APS aps: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>)
: List<@JvmSuppressWildcards PluginBase> {
val plugins = allConfigs.toMutableMap()
if (configInterface.PUMPDRIVERS) plugins += pumpDrivers.get()
if (configInterface.APS) plugins += aps.get()
if (!configInterface.NSCLIENT) plugins += notNsClient.get()
return plugins.toList().sortedBy { it.first }.map { it.second }
}
@Provides
@Singleton
fun provideStorage(): Storage {
return FileStorage()
}
@Module
interface AppBindings {
@Binds fun bindContext(mainApp: MainApp): Context
@Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector
@Binds fun bindActivePluginProvider(pluginStore: PluginStore): ActivePluginProvider
@Binds fun bindCommandQueueProvider(commandQueue: CommandQueue): CommandQueueProvider
@Binds fun bindConfigInterface(config: Config): ConfigInterface
@Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilderInterface
@Binds fun bindTreatmentInterface(treatmentsPlugin: TreatmentsPlugin): TreatmentsInterface
@Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface
@Binds fun bindUploadQueueInterface(uploadQueue: UploadQueue): UploadQueueInterface
@Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolder): NotificationHolderInterface
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefs): ImportExportPrefsInterface
}
}

View file

@ -1,72 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.automation.AutomationEvent
import info.nightscout.androidaps.plugins.general.automation.actions.*
import info.nightscout.androidaps.plugins.general.automation.elements.*
import info.nightscout.androidaps.plugins.general.automation.triggers.*
import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.queue.commands.*
@Module
@Suppress("unused")
abstract class AutomationModule {
@ContributesAndroidInjector abstract fun automationEventInjector(): AutomationEvent
@ContributesAndroidInjector abstract fun triggerInjector(): Trigger
@ContributesAndroidInjector abstract fun triggerAutosensValueInjector(): TriggerAutosensValue
@ContributesAndroidInjector abstract fun triggerBgInjector(): TriggerBg
@ContributesAndroidInjector abstract fun triggerBolusAgoInjector(): TriggerBolusAgo
@ContributesAndroidInjector abstract fun triggerCOBInjector(): TriggerCOB
@ContributesAndroidInjector abstract fun triggerConnectorInjector(): TriggerConnector
@ContributesAndroidInjector abstract fun triggerDeltaInjector(): TriggerDelta
@ContributesAndroidInjector abstract fun triggerDummyInjector(): TriggerDummy
@ContributesAndroidInjector abstract fun triggerIobInjector(): TriggerIob
@ContributesAndroidInjector abstract fun triggerLocationInjector(): TriggerLocation
@ContributesAndroidInjector abstract fun triggerProfilePercentInjector(): TriggerProfilePercent
@ContributesAndroidInjector abstract fun triggerPumpLastConnectionInjector(): TriggerPumpLastConnection
@ContributesAndroidInjector abstract fun triggerBTDeviceInjector(): TriggerBTDevice
@ContributesAndroidInjector abstract fun triggerRecurringTimeInjector(): TriggerRecurringTime
@ContributesAndroidInjector abstract fun triggerTempTargetInjector(): TriggerTempTarget
@ContributesAndroidInjector abstract fun triggerTime(): TriggerTime
@ContributesAndroidInjector abstract fun triggerTimeRangeInjector(): TriggerTimeRange
@ContributesAndroidInjector abstract fun triggerWifiSsidInjector(): TriggerWifiSsid
@ContributesAndroidInjector abstract fun actionInjector(): Action
@ContributesAndroidInjector abstract fun actionLoopDisableInjector(): ActionLoopDisable
@ContributesAndroidInjector abstract fun actionLoopEnableInjector(): ActionLoopEnable
@ContributesAndroidInjector abstract fun actionLoopResumeInjector(): ActionLoopResume
@ContributesAndroidInjector abstract fun actionLoopSuspendInjector(): ActionLoopSuspend
@ContributesAndroidInjector abstract fun actionNotificationInjector(): ActionNotification
@ContributesAndroidInjector abstract fun actionAlarmInjector(): ActionAlarm
@ContributesAndroidInjector abstract fun actionProfileSwitchInjector(): ActionProfileSwitch
@ContributesAndroidInjector abstract fun actionProfileSwitchPercentInjector(): ActionProfileSwitchPercent
@ContributesAndroidInjector abstract fun actionSendSMSInjector(): ActionSendSMS
@ContributesAndroidInjector abstract fun actionStartTempTargetInjector(): ActionStartTempTarget
@ContributesAndroidInjector abstract fun actionStopTempTargetInjector(): ActionStopTempTarget
@ContributesAndroidInjector abstract fun actionDummyInjector(): ActionDummy
@ContributesAndroidInjector abstract fun elementInjector(): Element
@ContributesAndroidInjector abstract fun inputBgInjector(): InputBg
@ContributesAndroidInjector abstract fun inputButtonInjector(): InputButton
@ContributesAndroidInjector abstract fun comparatorInjector(): Comparator
@ContributesAndroidInjector abstract fun comparatorConnectInjector(): ComparatorConnect
@ContributesAndroidInjector abstract fun comparatorExistsInjector(): ComparatorExists
@ContributesAndroidInjector abstract fun inputDateTimeInjector(): InputDateTime
@ContributesAndroidInjector abstract fun inputDeltaInjector(): InputDelta
@ContributesAndroidInjector abstract fun inputDoubleInjector(): InputDouble
@ContributesAndroidInjector abstract fun inputDropdownMenuInjector(): InputDropdownMenu
@ContributesAndroidInjector abstract fun inputDurationInjector(): InputDuration
@ContributesAndroidInjector abstract fun inputInsulinInjector(): InputInsulin
@ContributesAndroidInjector abstract fun inputLocationModeInjector(): InputLocationMode
@ContributesAndroidInjector abstract fun inputPercentInjector(): InputPercent
@ContributesAndroidInjector abstract fun inputProfileNameInjector(): InputProfileName
@ContributesAndroidInjector abstract fun inputStringInjector(): InputString
@ContributesAndroidInjector abstract fun inputTempTargetInjector(): InputTempTarget
@ContributesAndroidInjector abstract fun inputTimeRangeInjector(): InputTimeRange
@ContributesAndroidInjector abstract fun inputTimeInjector(): InputTime
@ContributesAndroidInjector abstract fun inputWeekDayInjector(): InputWeekDay
@ContributesAndroidInjector abstract fun labelWithElementInjector(): LabelWithElement
@ContributesAndroidInjector abstract fun staticLabelInjector(): StaticLabel
}

View file

@ -1,26 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.*
import info.nightscout.androidaps.interfaces.ProfileStore
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.general.food.FoodService
import info.nightscout.androidaps.plugins.treatments.TreatmentService
import info.nightscout.androidaps.utils.wizard.BolusWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
@Module
@Suppress("unused")
abstract class DataClassesModule {
@ContributesAndroidInjector abstract fun glucoseStatusInjector(): GlucoseStatus
@ContributesAndroidInjector abstract fun DatabaseHelperInjector(): DatabaseHelper
@ContributesAndroidInjector abstract fun treatmentServiceInjector(): TreatmentService
@ContributesAndroidInjector abstract fun foodServiceInjector(): FoodService
@ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard
@ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry
}

View file

@ -1,132 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.activities.MyPreferenceFragment
import info.nightscout.androidaps.dialogs.*
import info.nightscout.androidaps.plugins.aps.loop.LoopFragment
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAFragment
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBFragment
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment
import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment
import info.nightscout.androidaps.plugins.general.automation.AutomationFragment
import info.nightscout.androidaps.plugins.general.automation.dialogs.ChooseActionDialog
import info.nightscout.androidaps.plugins.general.automation.dialogs.ChooseTriggerDialog
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditActionDialog
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditTriggerDialog
import info.nightscout.androidaps.plugins.general.food.FoodFragment
import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment
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.dialogs.EditQuickWizardDialog
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorFragment
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment
import info.nightscout.androidaps.plugins.general.wear.WearFragment
import info.nightscout.androidaps.plugins.insulin.InsulinFragment
import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment
import info.nightscout.androidaps.plugins.pump.combo.ComboFragment
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusGeneralFragment
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusHistoryFragment
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightFragment
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicFragment
import info.nightscout.androidaps.plugins.pump.medtronic.dialog.RileyLinkStatusDeviceMedtronic
import info.nightscout.androidaps.plugins.pump.omnipod.ui.OmnipodOverviewFragment
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment
import info.nightscout.androidaps.plugins.source.BGSourceFragment
import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment
import info.nightscout.androidaps.plugins.treatments.fragments.*
import info.nightscout.androidaps.utils.protection.PasswordCheck
@Module
@Suppress("unused")
abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesPreferencesFragment(): MyPreferenceFragment
@ContributesAndroidInjector abstract fun contributesActionsFragment(): ActionsFragment
@ContributesAndroidInjector abstract fun contributesAutomationFragment(): AutomationFragment
@ContributesAndroidInjector abstract fun contributesBGSourceFragment(): BGSourceFragment
@ContributesAndroidInjector abstract fun contributesComboFragment(): ComboFragment
@ContributesAndroidInjector
abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment
@ContributesAndroidInjector abstract fun contributesFoodFragment(): FoodFragment
@ContributesAndroidInjector abstract fun contributesInsulinFragment(): InsulinFragment
@ContributesAndroidInjector abstract fun contributesLocalProfileFragment(): LocalProfileFragment
@ContributesAndroidInjector abstract fun contributesObjectivesFragment(): ObjectivesFragment
@ContributesAndroidInjector abstract fun contributesOpenAPSAMAFragment(): OpenAPSAMAFragment
@ContributesAndroidInjector abstract fun contributesOpenAPSSMBFragment(): OpenAPSSMBFragment
@ContributesAndroidInjector abstract fun contributesOverviewFragment(): OverviewFragment
@ContributesAndroidInjector abstract fun contributesLocalInsightFragment(): LocalInsightFragment
@ContributesAndroidInjector abstract fun contributesLoopFragment(): LoopFragment
@ContributesAndroidInjector abstract fun contributesMaintenanceFragment(): MaintenanceFragment
@ContributesAndroidInjector abstract fun contributesMedtronicFragment(): MedtronicFragment
@ContributesAndroidInjector abstract fun contributesOmnipodFragment(): OmnipodOverviewFragment
@ContributesAndroidInjector abstract fun contributesNSProfileFragment(): NSProfileFragment
@ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment
@ContributesAndroidInjector
abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment
@ContributesAndroidInjector abstract fun contributesWearFragment(): WearFragment
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsCareportalFragment(): TreatmentsCareportalFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsProfileSwitchFragment(): TreatmentsProfileSwitchFragment
@ContributesAndroidInjector abstract fun contributesVirtualPumpFragment(): VirtualPumpFragment
@ContributesAndroidInjector abstract fun contributesOpenHumansFragment(): OpenHumansFragment
@ContributesAndroidInjector abstract fun contributesCalibrationDialog(): CalibrationDialog
@ContributesAndroidInjector abstract fun contributesCarbsDialog(): CarbsDialog
@ContributesAndroidInjector abstract fun contributesCareDialog(): CareDialog
@ContributesAndroidInjector abstract fun contributesEditActionDialog(): EditActionDialog
@ContributesAndroidInjector abstract fun contributesEditEventDialog(): EditEventDialog
@ContributesAndroidInjector abstract fun contributesEditTriggerDialog(): EditTriggerDialog
@ContributesAndroidInjector
abstract fun contributesEditQuickWizardDialog(): EditQuickWizardDialog
@ContributesAndroidInjector abstract fun contributesExtendedBolusDialog(): ExtendedBolusDialog
@ContributesAndroidInjector abstract fun contributesFillDialog(): FillDialog
@ContributesAndroidInjector abstract fun contributesChooseActionDialog(): ChooseActionDialog
@ContributesAndroidInjector abstract fun contributesChooseTriggerDialog(): ChooseTriggerDialog
@ContributesAndroidInjector abstract fun contributesInsulinDialog(): InsulinDialog
@ContributesAndroidInjector abstract fun contributesLoopDialog(): LoopDialog
@ContributesAndroidInjector abstract fun contributesObjectivesExamDialog(): ObjectivesExamDialog
@ContributesAndroidInjector abstract fun contributesProfileSwitchDialog(): ProfileSwitchDialog
@ContributesAndroidInjector abstract fun contributesTempBasalDialog(): TempBasalDialog
@ContributesAndroidInjector abstract fun contributesTempTargetDialog(): TempTargetDialog
@ContributesAndroidInjector abstract fun contributesTreatmentDialog(): TreatmentDialog
@ContributesAndroidInjector abstract fun contributesWizardDialog(): WizardDialog
@ContributesAndroidInjector abstract fun contributesWizardInfoDialog(): WizardInfoDialog
@ContributesAndroidInjector
abstract fun contributesExchangeAuthTokenDialot(): OpenHumansLoginActivity.ExchangeAuthTokenDialog
@ContributesAndroidInjector abstract fun contributesPasswordCheck(): PasswordCheck
@ContributesAndroidInjector
abstract fun contributesRileyLinkStatusGeneral(): RileyLinkStatusGeneralFragment
@ContributesAndroidInjector
abstract fun contributesRileyLinkStatusHistoryFragment(): RileyLinkStatusHistoryFragment
@ContributesAndroidInjector
abstract fun contributesRileyLinkStatusDeviceMedtronic(): RileyLinkStatusDeviceMedtronic
}

View file

@ -1,13 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager
import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUITask
@Module
@Suppress("unused")
abstract class MedtronicModule {
@ContributesAndroidInjector abstract fun medtronicCommunicationManagerProvider(): MedtronicCommunicationManager
@ContributesAndroidInjector abstract fun medtronicUITaskProvider(): MedtronicUITask
}

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

@ -1,18 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
import info.nightscout.androidaps.plugins.general.maintenance.formats.ClassicPrefsFormat
import info.nightscout.androidaps.plugins.general.maintenance.formats.EncryptedPrefsFormat
import info.nightscout.androidaps.utils.CryptoUtil
@Module
@Suppress("unused")
abstract class PreferencesModule {
@ContributesAndroidInjector abstract fun cryptoUtilInjector(): CryptoUtil
@ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat
@ContributesAndroidInjector abstract fun classicPrefsFormatInjector(): ClassicPrefsFormat
@ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider
}

View file

@ -1,34 +0,0 @@
package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService
import info.nightscout.androidaps.plugins.general.overview.notifications.DismissNotificationService
import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService
import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkService
import info.nightscout.androidaps.plugins.pump.insight.InsightAlertService
import info.nightscout.androidaps.plugins.pump.insight.connection_service.InsightConnectionService
import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.service.RileyLinkOmnipodService
import info.nightscout.androidaps.services.AlarmSoundService
import info.nightscout.androidaps.services.DataService
import info.nightscout.androidaps.services.LocationService
@Module
@Suppress("unused")
abstract class ServicesModule {
@ContributesAndroidInjector abstract fun contributesAlarmSoundService(): AlarmSoundService
@ContributesAndroidInjector abstract fun contributesDataService(): DataService
@ContributesAndroidInjector abstract fun contributesDismissNotificationService(): DismissNotificationService
@ContributesAndroidInjector abstract fun contributesDummyService(): DummyService
@ContributesAndroidInjector abstract fun contributesLocationService(): LocationService
@ContributesAndroidInjector abstract fun contributesNSClientService(): NSClientService
@ContributesAndroidInjector abstract fun contributesWatchUpdaterService(): WatchUpdaterService
@ContributesAndroidInjector abstract fun contributesInsightAlertService(): InsightAlertService
@ContributesAndroidInjector abstract fun contributesInsightConnectionService(): InsightConnectionService
@ContributesAndroidInjector abstract fun contributesRileyLinkService(): RileyLinkService
@ContributesAndroidInjector abstract fun contributesRileyLinkMedtronicService(): RileyLinkMedtronicService
@ContributesAndroidInjector abstract fun contributesRileyLinkOmnipodService(): RileyLinkOmnipodService
}

View file

@ -1,14 +1,12 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback
import info.nightscout.androidaps.plugins.aps.loop.APSResult
import info.nightscout.androidaps.plugins.aps.openAPSAMA.DetermineBasalAdapterAMAJS import info.nightscout.androidaps.plugins.aps.openAPSAMA.DetermineBasalAdapterAMAJS
import info.nightscout.androidaps.plugins.aps.openAPSAMA.DetermineBasalResultAMA import info.nightscout.androidaps.plugins.aps.openAPSAMA.DetermineBasalResultAMA
import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS
import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOref1Thread import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOref1Thread
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobThread import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobThread

View file

@ -0,0 +1,31 @@
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.MainActivity
import info.nightscout.androidaps.activities.*
import info.nightscout.androidaps.activities.HistoryBrowseActivity
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity
import info.nightscout.androidaps.plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity
import info.nightscout.androidaps.setupwizard.SetupWizardActivity
@Module
@Suppress("unused")
abstract class ActivitiesModule {
@ContributesAndroidInjector abstract fun contributesTreatmentsActivity(): TreatmentsActivity
@ContributesAndroidInjector abstract fun contributesHistoryBrowseActivity(): HistoryBrowseActivity
@ContributesAndroidInjector abstract fun contributesLogSettingActivity(): LogSettingActivity
@ContributesAndroidInjector abstract fun contributeMainActivity(): MainActivity
@ContributesAndroidInjector abstract fun contributesPreferencesActivity(): PreferencesActivity
@ContributesAndroidInjector abstract fun contributesQuickWizardListActivity(): QuickWizardListActivity
@ContributesAndroidInjector abstract fun contributesRequestDexcomPermissionActivity(): RequestDexcomPermissionActivity
@ContributesAndroidInjector abstract fun contributesSetupWizardActivity(): SetupWizardActivity
@ContributesAndroidInjector abstract fun contributesSingleFragmentActivity(): SingleFragmentActivity
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorOtpActivity(): SmsCommunicatorOtpActivity
@ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity
@ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity
@ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity
}

View file

@ -0,0 +1,78 @@
package info.nightscout.androidaps.di
import dagger.BindsInstance
import dagger.Component
import dagger.android.AndroidInjectionModule
import dagger.android.AndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.automation.di.AutomationModule
import info.nightscout.androidaps.combo.di.ComboModule
import info.nightscout.androidaps.dana.di.DanaHistoryModule
import info.nightscout.androidaps.dana.di.DanaModule
import info.nightscout.androidaps.danar.di.DanaRModule
import info.nightscout.androidaps.danars.di.DanaRSModule
import info.nightscout.androidaps.database.DatabaseModule
import info.nightscout.androidaps.diaconn.di.DiaconnG8Module
import info.nightscout.androidaps.insight.di.InsightDatabaseModule
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.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.shared.di.SharedModule
import javax.inject.Singleton
@Singleton
@Component(
modules = [
AndroidInjectionModule::class,
DatabaseModule::class,
PluginsModule::class,
SkinsModule::class,
ActivitiesModule::class,
FragmentsModule::class,
AppModule::class,
ReceiversModule::class,
ServicesModule::class,
AutomationModule::class,
CommandQueueModule::class,
ObjectivesModule::class,
WizardModule::class,
PumpCommonModule::class,
RileyLinkModule::class,
MedtronicModule::class,
OmnipodDashModule::class,
OmnipodErosModule::class,
APSModule::class,
PreferencesModule::class,
OverviewModule::class,
DataClassesModule::class,
SMSModule::class,
UIModule::class,
CoreModule::class,
DanaModule::class,
DanaHistoryModule::class,
DanaRModule::class,
DanaRSModule::class,
ComboModule::class,
InsightModule::class,
InsightDatabaseModule::class,
WorkersModule::class,
DiaconnG8Module::class,
OpenHumansModule::class,
SharedModule::class
]
)
interface AppComponent : AndroidInjector<MainApp> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(mainApp: MainApp): Builder
fun build(): AppComponent
}
}

View file

@ -0,0 +1,108 @@
package info.nightscout.androidaps.di
import android.content.Context
import dagger.Binds
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImplementation
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.data.DeviceStatusData
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.PumpSyncImplementation
import info.nightscout.androidaps.queue.CommandQueueImplementation
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits
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.resources.IconsProviderImplementation
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.rx.DefaultAapsSchedulers
import info.nightscout.androidaps.utils.storage.FileStorage
import info.nightscout.androidaps.utils.storage.Storage
import info.nightscout.shared.sharedPreferences.SP
import javax.inject.Singleton
@Suppress("unused")
@Module(includes = [
AppModule.AppBindings::class
])
open class AppModule {
@Provides
fun providesPlugins(config: Config,
@PluginsModule.AllConfigs allConfigs: Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>,
@PluginsModule.PumpDriver pumpDrivers: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.NotNSClient notNsClient: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.APS aps: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>)
: List<@JvmSuppressWildcards PluginBase> {
val plugins = allConfigs.toMutableMap()
if (config.PUMPDRIVERS) plugins += pumpDrivers.get()
if (config.APS) plugins += aps.get()
if (!config.NSCLIENT) plugins += notNsClient.get()
return plugins.toList().sortedBy { it.first }.map { it.second }
}
@Provides
@Singleton
fun provideStorage(): Storage = FileStorage()
@Provides
@Singleton
fun provideBuildHelper(config: Config, fileListProvider: PrefFileListProvider): BuildHelper = BuildHelperImpl(config, fileListProvider)
@Provides
@Singleton
internal fun provideSchedulers(): AapsSchedulers = DefaultAapsSchedulers()
@Provides
@Singleton
fun provideProfileFunction(
aapsLogger: AAPSLogger, sp: SP, rxBus: RxBus, rh:
ResourceHelper, activePlugin:
ActivePlugin, repository: AppRepository, dateUtil: DateUtil, config: Config, hardLimits: HardLimits,
aapsSchedulers: AapsSchedulers, fabricPrivacy: FabricPrivacy, deviceStatusData: DeviceStatusData
): ProfileFunction =
ProfileFunctionImplementation(
aapsLogger, sp, rxBus, rh, activePlugin, repository, dateUtil,
config, hardLimits, aapsSchedulers, fabricPrivacy, deviceStatusData
)
@Module
interface AppBindings {
@Binds fun bindContext(mainApp: MainApp): Context
@Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector
@Binds fun bindActivePlugin(pluginStore: PluginStore): ActivePlugin
@Binds fun bindCommandQueue(commandQueue: CommandQueueImplementation): CommandQueue
@Binds fun bindConfigInterface(config: ConfigImpl): Config
@Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilder
@Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs
@Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider
@Binds fun bindLoopInterface(loopPlugin: LoopPlugin): Loop
@Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
@Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
@Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync
}
}

View file

@ -1,15 +1,15 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.queue.CommandQueue import info.nightscout.androidaps.queue.CommandQueueImplementation
import info.nightscout.androidaps.queue.commands.* import info.nightscout.androidaps.queue.commands.*
@Module @Module
@Suppress("unused") @Suppress("unused")
abstract class CommandQueueModule { abstract class CommandQueueModule {
@ContributesAndroidInjector abstract fun commandQueueInjector(): CommandQueue @ContributesAndroidInjector abstract fun commandQueueInjector(): CommandQueueImplementation
@ContributesAndroidInjector abstract fun commandBolusInjector(): CommandBolus @ContributesAndroidInjector abstract fun commandBolusInjector(): CommandBolus
@ContributesAndroidInjector abstract fun commandCancelExtendedBolusInjector(): CommandCancelExtendedBolus @ContributesAndroidInjector abstract fun commandCancelExtendedBolusInjector(): CommandCancelExtendedBolus
@ContributesAndroidInjector abstract fun commandCancelTempBasalInjector(): CommandCancelTempBasal @ContributesAndroidInjector abstract fun commandCancelTempBasalInjector(): CommandCancelTempBasal

View file

@ -0,0 +1,17 @@
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.utils.wizard.BolusWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
@Module
@Suppress("unused")
abstract class DataClassesModule {
@ContributesAndroidInjector abstract fun glucoseStatusInjector(): GlucoseStatus
@ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard
@ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry
}

View file

@ -0,0 +1,92 @@
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.activities.MyPreferenceFragment
import info.nightscout.androidaps.dialogs.*
import info.nightscout.androidaps.plugins.aps.loop.LoopFragment
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAFragment
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBFragment
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment
import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment
import info.nightscout.androidaps.plugins.general.automation.AutomationFragment
import info.nightscout.androidaps.plugins.general.food.FoodFragment
import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment
import info.nightscout.androidaps.plugins.general.nsclient.NSClientFragment
import info.nightscout.androidaps.plugins.general.overview.OverviewFragment
import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorFragment
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment
import info.nightscout.androidaps.plugins.general.wear.WearFragment
import info.nightscout.androidaps.plugins.insulin.InsulinFragment
import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment
import info.nightscout.androidaps.plugins.source.BGSourceFragment
import info.nightscout.androidaps.activities.fragments.*
import info.nightscout.androidaps.plugins.general.automation.dialogs.*
import info.nightscout.androidaps.utils.protection.PasswordCheck
@Module
@Suppress("unused")
abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesPreferencesFragment(): MyPreferenceFragment
@ContributesAndroidInjector abstract fun contributesActionsFragment(): ActionsFragment
@ContributesAndroidInjector abstract fun contributesAutomationFragment(): AutomationFragment
@ContributesAndroidInjector abstract fun contributesBGSourceFragment(): BGSourceFragment
@ContributesAndroidInjector abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment
@ContributesAndroidInjector abstract fun contributesFoodFragment(): FoodFragment
@ContributesAndroidInjector abstract fun contributesInsulinFragment(): InsulinFragment
@ContributesAndroidInjector abstract fun contributesLocalProfileFragment(): LocalProfileFragment
@ContributesAndroidInjector abstract fun contributesObjectivesFragment(): ObjectivesFragment
@ContributesAndroidInjector abstract fun contributesOpenAPSAMAFragment(): OpenAPSAMAFragment
@ContributesAndroidInjector abstract fun contributesOpenAPSSMBFragment(): OpenAPSSMBFragment
@ContributesAndroidInjector abstract fun contributesOverviewFragment(): OverviewFragment
@ContributesAndroidInjector abstract fun contributesLoopFragment(): LoopFragment
@ContributesAndroidInjector abstract fun contributesMaintenanceFragment(): MaintenanceFragment
@ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment
@ContributesAndroidInjector abstract fun contributesWearFragment(): WearFragment
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusCarbsFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsCareportalFragment(): TreatmentsCareportalFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsProfileSwitchFragment(): TreatmentsProfileSwitchFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsUserEntryFragment(): TreatmentsUserEntryFragment
@ContributesAndroidInjector abstract fun contributesVirtualPumpFragment(): VirtualPumpFragment
@ContributesAndroidInjector abstract fun contributesCalibrationDialog(): CalibrationDialog
@ContributesAndroidInjector abstract fun contributesCarbsDialog(): CarbsDialog
@ContributesAndroidInjector abstract fun contributesCareDialog(): CareDialog
@ContributesAndroidInjector abstract fun contributesEditActionDialog(): EditActionDialog
@ContributesAndroidInjector abstract fun contributesEditEventDialog(): EditEventDialog
@ContributesAndroidInjector abstract fun contributesEditTriggerDialog(): EditTriggerDialog
@ContributesAndroidInjector abstract fun contributesEditQuickWizardDialog(): EditQuickWizardDialog
@ContributesAndroidInjector abstract fun contributesExtendedBolusDialog(): ExtendedBolusDialog
@ContributesAndroidInjector abstract fun contributesFillDialog(): FillDialog
@ContributesAndroidInjector abstract fun contributesChooseActionDialog(): ChooseActionDialog
@ContributesAndroidInjector abstract fun contributesChooseTriggerDialog(): ChooseTriggerDialog
@ContributesAndroidInjector abstract fun contributesChooseOperationDialog(): ChooseOperationDialog
@ContributesAndroidInjector abstract fun contributesInsulinDialog(): InsulinDialog
@ContributesAndroidInjector abstract fun contributesLoopDialog(): LoopDialog
@ContributesAndroidInjector abstract fun contributesObjectivesExamDialog(): ObjectivesExamDialog
@ContributesAndroidInjector abstract fun contributesProfileSwitchDialog(): ProfileSwitchDialog
@ContributesAndroidInjector abstract fun contributesTempBasalDialog(): TempBasalDialog
@ContributesAndroidInjector abstract fun contributesTempTargetDialog(): TempTargetDialog
@ContributesAndroidInjector abstract fun contributesTreatmentDialog(): TreatmentDialog
@ContributesAndroidInjector abstract fun contributesWizardDialog(): WizardDialog
@ContributesAndroidInjector abstract fun contributesWizardInfoDialog(): WizardInfoDialog
@ContributesAndroidInjector abstract fun contributesPasswordCheck(): PasswordCheck
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
@ -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

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
@ -8,11 +8,14 @@ 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
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.constraints.bgQualityCheck.BgQualityCheckPlugin
import info.nightscout.androidaps.plugins.constraints.dstHelper.DstHelperPlugin import info.nightscout.androidaps.plugins.constraints.dstHelper.DstHelperPlugin
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
@ -25,7 +28,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
@ -37,20 +39,20 @@ import info.nightscout.androidaps.plugins.insulin.InsulinOrefRapidActingPlugin
import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlugin import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin 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.OmnipodPumpPlugin import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin
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
@Suppress("unused")
@Module @Module
abstract class PluginsModule { abstract class PluginsModule {
@ -58,6 +60,12 @@ abstract class PluginsModule {
@AllConfigs @AllConfigs
@IntoMap @IntoMap
@IntKey(0) @IntKey(0)
abstract fun bindPersistentNotificationPlugin(plugin: PersistentNotificationPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(5)
abstract fun bindOverviewPlugin(plugin: OverviewPlugin): PluginBase abstract fun bindOverviewPlugin(plugin: OverviewPlugin): PluginBase
@Binds @Binds
@ -150,6 +158,18 @@ abstract class PluginsModule {
@IntKey(140) @IntKey(140)
abstract fun bindComboPlugin(plugin: ComboPlugin): PluginBase abstract fun bindComboPlugin(plugin: ComboPlugin): PluginBase
@Binds
@PumpDriver
@IntoMap
@IntKey(145)
abstract fun bindOmnipodErosPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase
@Binds
@PumpDriver
@IntoMap
@IntKey(148)
abstract fun bindOmnipodDashPumpPlugin(plugin: OmnipodDashPumpPlugin): PluginBase
@Binds @Binds
@PumpDriver @PumpDriver
@IntoMap @IntoMap
@ -160,7 +180,7 @@ abstract class PluginsModule {
@PumpDriver @PumpDriver
@IntoMap @IntoMap
@IntKey(155) @IntKey(155)
abstract fun bindOmnipodPumpPlugin(plugin: OmnipodPumpPlugin): PluginBase abstract fun bindDiaconnG8Plugin(plugin: DiaconnG8Plugin): PluginBase
@Binds @Binds
@NotNSClient @NotNSClient
@ -195,12 +215,6 @@ abstract class PluginsModule {
@Binds @Binds
@AllConfigs @AllConfigs
@IntoMap @IntoMap
@IntKey(230)
abstract fun bindNSProfilePlugin(plugin: NSProfilePlugin): PluginBase
@Binds
@NotNSClient
@IntoMap
@IntKey(240) @IntKey(240)
abstract fun bindLocalProfilePlugin(plugin: LocalProfilePlugin): PluginBase abstract fun bindLocalProfilePlugin(plugin: LocalProfilePlugin): PluginBase
@ -210,12 +224,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
@ -270,12 +278,6 @@ abstract class PluginsModule {
@IntKey(340) @IntKey(340)
abstract fun bindStatusLinePlugin(plugin: StatusLinePlugin): PluginBase abstract fun bindStatusLinePlugin(plugin: StatusLinePlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(350)
abstract fun bindPersistentNotificationPlugin(plugin: PersistentNotificationPlugin): PluginBase
@Binds @Binds
@AllConfigs @AllConfigs
@IntoMap @IntoMap
@ -294,6 +296,12 @@ abstract class PluginsModule {
@IntKey(380) @IntKey(380)
abstract fun bindDstHelperPlugin(plugin: DstHelperPlugin): PluginBase abstract fun bindDstHelperPlugin(plugin: DstHelperPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(381)
abstract fun bindBgQualityCheckPlugin(plugin: BgQualityCheckPlugin): PluginBase
@Binds @Binds
@AllConfigs @AllConfigs
@IntoMap @IntoMap
@ -346,13 +354,25 @@ abstract class PluginsModule {
@AllConfigs @AllConfigs
@IntoMap @IntoMap
@IntKey(470) @IntKey(470)
abstract fun bindGlunovoPlugin(plugin: GlunovoPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(475)
abstract fun bindRandomBgPlugin(plugin: RandomBgPlugin): PluginBase abstract fun bindRandomBgPlugin(plugin: RandomBgPlugin): PluginBase
// @Binds
// @NotNSClient
// @IntoMap
// @IntKey(480)
// abstract fun bindOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase
@Binds @Binds
@NotNSClient @NotNSClient
@IntoMap @IntoMap
@IntKey(480) @IntKey(480)
abstract fun bindOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase abstract fun bindsOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase
@Binds @Binds
@AllConfigs @AllConfigs

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
import info.nightscout.androidaps.plugins.general.maintenance.formats.EncryptedPrefsFormat
import info.nightscout.androidaps.utils.CryptoUtil
@Module
@Suppress("unused")
abstract class PreferencesModule {
@ContributesAndroidInjector abstract fun cryptoUtilInjector(): CryptoUtil
@ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat
@ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
@ -17,6 +17,7 @@ abstract class ReceiversModule {
@ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver @ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver
@ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver @ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver @ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveWorker(): KeepAliveReceiver.KeepAliveWorker
@ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver @ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver
@ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver @ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver
@ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver @ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector

View file

@ -0,0 +1,22 @@
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService
import info.nightscout.androidaps.plugins.general.overview.notifications.DismissNotificationService
import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService
import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService
import info.nightscout.androidaps.services.AlarmSoundService
import info.nightscout.androidaps.services.LocationService
@Module
@Suppress("unused")
abstract class ServicesModule {
@ContributesAndroidInjector abstract fun contributesAlarmSoundService(): AlarmSoundService
@ContributesAndroidInjector abstract fun contributesDismissNotificationService(): DismissNotificationService
@ContributesAndroidInjector abstract fun contributesDummyService(): DummyService
@ContributesAndroidInjector abstract fun contributesLocationService(): LocationService
@ContributesAndroidInjector abstract fun contributesNSClientService(): NSClientService
@ContributesAndroidInjector abstract fun contributesWatchUpdaterService(): WatchUpdaterService
}

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.multibindings.IntKey import dagger.multibindings.IntKey

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
@ -13,12 +13,15 @@ abstract class WizardModule {
@ContributesAndroidInjector abstract fun swBreakInjector(): SWBreak @ContributesAndroidInjector abstract fun swBreakInjector(): SWBreak
@ContributesAndroidInjector abstract fun swButtonInjector(): SWButton @ContributesAndroidInjector abstract fun swButtonInjector(): SWButton
@ContributesAndroidInjector abstract fun swEditNumberWithUnitsInjector(): SWEditNumberWithUnits @ContributesAndroidInjector abstract fun swEditNumberWithUnitsInjector(): SWEditNumberWithUnits
@ContributesAndroidInjector abstract fun swEditNumberInjector(): SWEditNumber
@ContributesAndroidInjector abstract fun swEditIntNumberInjector(): SWEditIntNumber
@ContributesAndroidInjector abstract fun swEditStringInjector(): SWEditString @ContributesAndroidInjector abstract fun swEditStringInjector(): SWEditString
@ContributesAndroidInjector abstract fun swEditEncryptedPasswordInjector(): SWEditEncryptedPassword @ContributesAndroidInjector abstract fun swEditEncryptedPasswordInjector(): SWEditEncryptedPassword
@ContributesAndroidInjector abstract fun swEditUrlInjector(): SWEditUrl @ContributesAndroidInjector abstract fun swEditUrlInjector(): SWEditUrl
@ContributesAndroidInjector abstract fun swFragmentInjector(): SWFragment @ContributesAndroidInjector abstract fun swFragmentInjector(): SWFragment
@ContributesAndroidInjector abstract fun swPreferenceInjector(): SWPreference
@ContributesAndroidInjector abstract fun swHtmlLinkInjector(): SWHtmlLink @ContributesAndroidInjector abstract fun swHtmlLinkInjector(): SWHtmlLink
@ContributesAndroidInjector abstract fun swInfotextInjector(): SWInfotext @ContributesAndroidInjector abstract fun swInfotextInjector(): SWInfoText
@ContributesAndroidInjector abstract fun swItemInjector(): SWItem @ContributesAndroidInjector abstract fun swItemInjector(): SWItem
@ContributesAndroidInjector abstract fun swPluginInjector(): SWPlugin @ContributesAndroidInjector abstract fun swPluginInjector(): SWPlugin
@ContributesAndroidInjector abstract fun swRadioButtonInjector(): SWRadioButton @ContributesAndroidInjector abstract fun swRadioButtonInjector(): SWRadioButton

View file

@ -0,0 +1,35 @@
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddAckWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientUpdateRemoveAckWorker
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.source.*
@Module
@Suppress("unused")
abstract class WorkersModule {
@ContributesAndroidInjector abstract fun contributesXdripWorker(): XdripPlugin.XdripWorker
@ContributesAndroidInjector abstract fun contributesDexcomWorker(): DexcomPlugin.DexcomWorker
@ContributesAndroidInjector abstract fun contributesMM640gWorker(): MM640gPlugin.MM640gWorker
@ContributesAndroidInjector abstract fun contributesGlimpWorker(): GlimpPlugin.GlimpWorker
@ContributesAndroidInjector abstract fun contributesPoctechWorker(): PoctechPlugin.PoctechWorker
@ContributesAndroidInjector abstract fun contributesTomatoWorker(): TomatoPlugin.TomatoWorker
@ContributesAndroidInjector abstract fun contributesEversenseWorker(): EversensePlugin.EversenseWorker
@ContributesAndroidInjector abstract fun contributesNSClientSourceWorker(): NSClientSourcePlugin.NSClientSourceWorker
@ContributesAndroidInjector abstract fun contributesNSProfileWorker(): LocalProfilePlugin.NSProfileWorker
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorWorker(): SmsCommunicatorPlugin.SmsCommunicatorWorker
@ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientAddUpdateWorker
@ContributesAndroidInjector abstract fun contributesNSClientAddAckWorker(): NSClientAddAckWorker
@ContributesAndroidInjector abstract fun contributesNSClientUpdateRemoveAckWorker(): NSClientUpdateRemoveAckWorker
@ContributesAndroidInjector abstract fun contributesNSClientMbgWorker(): NSClientMbgWorker
@ContributesAndroidInjector abstract fun contributesFoodWorker(): FoodPlugin.FoodWorker
@ContributesAndroidInjector abstract fun contributesCsvExportWorker(): ImportExportPrefsImpl.CsvExportWorker
}

View file

@ -6,14 +6,18 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.google.common.base.Joiner import com.google.common.base.Joiner
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.databinding.DialogCalibrationBinding import info.nightscout.androidaps.databinding.DialogCalibrationBinding
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.XdripCalibrations import info.nightscout.androidaps.utils.XDripBroadcast
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 java.text.DecimalFormat import java.text.DecimalFormat
@ -23,9 +27,11 @@ import javax.inject.Inject
class CalibrationDialog : DialogFragmentWithDate() { class CalibrationDialog : DialogFragmentWithDate() {
@Inject lateinit var injector: HasAndroidInjector @Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var xdripCalibrations: XdripCalibrations @Inject lateinit var xDripBroadcast: XDripBroadcast
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
private var _binding: DialogCalibrationBinding? = null private var _binding: DialogCalibrationBinding? = null
@ -49,15 +55,15 @@ class CalibrationDialog : DialogFragmentWithDate() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val bg = Profile.fromMgdlToUnits(GlucoseStatus(injector).glucoseStatusData?.glucose val bg = Profile.fromMgdlToUnits(glucoseStatusProvider.glucoseStatusData?.glucose
?: 0.0, units) ?: 0.0, units)
if (units == Constants.MMOL) if (units == GlucoseUnit.MMOL)
binding.bg.setParams(savedInstanceState?.getDouble("bg") binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok) ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok)
else else
binding.bg.setParams(savedInstanceState?.getDouble("bg") binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok) ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
binding.units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) binding.units.text = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -68,20 +74,20 @@ class CalibrationDialog : DialogFragmentWithDate() {
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) val unitLabel = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
val actions: LinkedList<String?> = LinkedList() val actions: LinkedList<String?> = LinkedList()
val bg = binding.bg.value ?: return false val bg = binding.bg.value
actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel) actions.add(rh.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel)
if (bg > 0) { if (bg > 0) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: CALIBRATION $bg") uel.log(Action.CALIBRATION, Sources.CalibrationDialog, ValueWithUnit.fromGlucoseUnit(bg, units.asText))
xdripCalibrations.sendIntent(bg) xDripBroadcast.sendCalibration(bg)
}) })
} }
} else } else
activity?.let { activity -> activity?.let { activity ->
OKDialog.show(activity, resourceHelper.gs(R.string.overview_calibration), resourceHelper.gs(R.string.no_action_selected)) OKDialog.show(activity, rh.gs(R.string.overview_calibration), rh.gs(R.string.no_action_selected))
} }
return true return true
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -7,45 +8,48 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.google.common.base.Joiner import com.google.common.base.Joiner
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
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.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogCarbsBinding import info.nightscout.androidaps.databinding.DialogCarbsBinding
import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.db.TempTarget import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.plugins.treatments.CarbsGenerator import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
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
import kotlin.math.max import kotlin.math.max
class CarbsDialog : DialogFragmentWithDate() { class CarbsDialog : DialogFragmentWithDate() {
@Inject lateinit var mainApp: MainApp @Inject lateinit var ctx: Context
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: 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 iobCobCalculatorPlugin: IobCobCalculatorPlugin @Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
@Inject lateinit var carbsGenerator: CarbsGenerator @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var carbTimer: CarbTimer
@Inject lateinit var bolusTimer: BolusTimer
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var repository: AppRepository
companion object { companion object {
@ -54,6 +58,8 @@ class CarbsDialog : DialogFragmentWithDate() {
private const val FAV3_DEFAULT = 20 private const val FAV3_DEFAULT = 20
} }
private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher { private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
validateInputs() validateInputs()
@ -68,15 +74,15 @@ class CarbsDialog : DialogFragmentWithDate() {
val time = binding.time.value.toInt() val time = binding.time.value.toInt()
if (time > 12 * 60 || time < -12 * 60) { if (time > 12 * 60 || time < -12 * 60) {
binding.time.value = 0.0 binding.time.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied)) ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.constraintapllied))
} }
if (binding.duration.value > 10) { if (binding.duration.value > 10) {
binding.duration.value = 0.0 binding.duration.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied)) ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.constraintapllied))
} }
if (binding.carbs.value.toInt() > maxCarbs) { if (binding.carbs.value.toInt() > maxCarbs) {
binding.carbs.value = 0.0 binding.carbs.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied)) ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.carbsconstraintapplied))
} }
} }
@ -93,48 +99,80 @@ class CarbsDialog : DialogFragmentWithDate() {
savedInstanceState.putDouble("carbs", binding.carbs.value) savedInstanceState.putDouble("carbs", binding.carbs.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(
savedInstanceState: Bundle?): View { inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
onCreateViewGeneral() onCreateViewGeneral()
_binding = DialogCarbsBinding.inflate(inflater, container, false) _binding = DialogCarbsBinding.inflate(inflater, container, false)
binding.time.setOnValueChangedListener { timeOffset: Double ->
run {
val newTime = eventTimeOriginal + timeOffset.toLong() * 1000 * 60
updateDateTime(newTime)
}
}
return binding.root return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (sp.getBoolean(R.string.key_usebolusreminder, false)) {
glucoseStatusProvider.glucoseStatusData?.let { glucoseStatus ->
if (glucoseStatus.glucose + 3 * glucoseStatus.delta < 70.0)
binding.bolusReminder.visibility = View.VISIBLE
}
}
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble() val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble()
binding.time.setParams(savedInstanceState?.getDouble("time") binding.time.setParams(
?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) savedInstanceState?.getDouble("time")
?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher
)
binding.duration.setParams(savedInstanceState?.getDouble("duration") binding.duration.setParams(
?: 0.0, 0.0, 10.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, 10.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher
)
binding.carbs.setParams(savedInstanceState?.getDouble("carbs") binding.carbs.setParams(
?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) savedInstanceState?.getDouble("carbs")
?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher
)
binding.plus1.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) binding.plus1.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT))
binding.plus1.setOnClickListener { binding.plus1.setOnClickListener {
binding.carbs.value = max(0.0, binding.carbs.value binding.carbs.value = max(
+ sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) 0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)
)
validateInputs() validateInputs()
} }
binding.plus2.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) binding.plus2.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT))
binding.plus2.setOnClickListener { binding.plus2.setOnClickListener {
binding.carbs.value = max(0.0, binding.carbs.value binding.carbs.value = max(
+ sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) 0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)
)
validateInputs() validateInputs()
} }
binding.plus3.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) binding.plus3.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT))
binding.plus3.setOnClickListener { binding.plus3.setOnClickListener {
binding.carbs.value = max(0.0, binding.carbs.value binding.carbs.value = max(
+ sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) 0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)
)
validateInputs() validateInputs()
} }
iobCobCalculatorPlugin.actualBg()?.let { bgReading -> setOnValueChangedListener { eventTime: Long ->
run {
val timeOffset = ((eventTime - eventTimeOriginal) / (1000 * 60)).toDouble()
binding.time.value = timeOffset
}
}
iobCobCalculator.ads.actualBg()?.let { bgReading ->
if (bgReading.value < 72) if (bgReading.value < 72)
binding.hypoTt.isChecked = true binding.hypoTt.isChecked = true
} }
@ -154,6 +192,7 @@ class CarbsDialog : DialogFragmentWithDate() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
disposable.clear()
_binding = null _binding = null
} }
@ -163,7 +202,7 @@ class CarbsDialog : DialogFragmentWithDate() {
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val carbs = binding.carbs.value?.toInt() ?: return false val carbs = binding.carbs.value.toInt()
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val activityTTDuration = defaultValueHelper.determineActivityTTDuration() val activityTTDuration = defaultValueHelper.determineActivityTTDuration()
@ -173,93 +212,158 @@ class CarbsDialog : DialogFragmentWithDate() {
val hypoTTDuration = defaultValueHelper.determineHypoTTDuration() val hypoTTDuration = defaultValueHelper.determineHypoTTDuration()
val hypoTT = defaultValueHelper.determineHypoTT() val hypoTT = defaultValueHelper.determineHypoTT()
val actions: LinkedList<String?> = LinkedList() val actions: LinkedList<String?> = LinkedList()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) val unitLabel = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
val useAlarm = binding.alarmCheckBox.isChecked
val remindBolus = binding.bolusReminderCheckBox.isChecked
val activitySelected = binding.activityTt.isChecked val activitySelected = binding.activityTt.isChecked
if (activitySelected) if (activitySelected)
actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, activityTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation)) actions.add(
rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, activityTTDuration) + ")").formatColor(
rh,
R.color.tempTargetConfirmation
)
)
val eatingSoonSelected = binding.eatingSoonTt.isChecked val eatingSoonSelected = binding.eatingSoonTt.isChecked
if (eatingSoonSelected) if (eatingSoonSelected)
actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, eatingSoonTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation)) actions.add(
rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + rh.gs(
R.string.format_mins,
eatingSoonTTDuration
) + ")").formatColor(rh, R.color.tempTargetConfirmation)
)
val hypoSelected = binding.hypoTt.isChecked val hypoSelected = binding.hypoTt.isChecked
if (hypoSelected) if (hypoSelected)
actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(hypoTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, hypoTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation)) actions.add(
rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(hypoTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, hypoTTDuration) + ")").formatColor(
rh,
R.color.tempTargetConfirmation
)
)
val timeOffset = binding.time.value.toInt() val timeOffset = binding.time.value.toInt()
eventTime -= eventTime % 1000 if (useAlarm && carbs > 0 && timeOffset > 0)
val time = eventTime + timeOffset * 1000 * 60 actions.add(rh.gs(R.string.alarminxmin, timeOffset).formatColor(rh, R.color.info))
if (timeOffset != 0)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time))
val duration = binding.duration.value.toInt() val duration = binding.duration.value.toInt()
if (duration > 0) if (duration > 0)
actions.add(resourceHelper.gs(R.string.duration) + ": " + duration + resourceHelper.gs(R.string.shorthour)) actions.add(rh.gs(R.string.duration) + ": " + duration + rh.gs(R.string.shorthour))
if (carbsAfterConstraints > 0) { if (carbsAfterConstraints > 0) {
actions.add(resourceHelper.gs(R.string.carbs) + ": " + "<font color='" + resourceHelper.gc(R.color.carbs) + "'>" + resourceHelper.gs(R.string.format_carbs, carbsAfterConstraints) + "</font>") actions.add(rh.gs(R.string.carbs) + ": " + "<font color='" + rh.gc(R.color.carbs) + "'>" + rh.gs(R.string.format_carbs, carbsAfterConstraints) + "</font>")
if (carbsAfterConstraints != carbs) if (carbsAfterConstraints != carbs)
actions.add("<font color='" + resourceHelper.gc(R.color.warning) + "'>" + resourceHelper.gs(R.string.carbsconstraintapplied) + "</font>") actions.add("<font color='" + rh.gc(R.color.warning) + "'>" + rh.gs(R.string.carbsconstraintapplied) + "</font>")
} }
val notes = binding.notesLayout.notes.text.toString() val notes = binding.notesLayout.notes.text.toString()
if (notes.isNotEmpty()) if (notes.isNotEmpty())
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(rh.gs(R.string.notes_label) + ": " + notes)
if (eventTimeChanged) if (eventTimeChanged)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) actions.add(rh.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
if (carbsAfterConstraints > 0 || activitySelected || eatingSoonSelected || hypoSelected) { if (carbsAfterConstraints > 0 || activitySelected || eatingSoonSelected || hypoSelected) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
when { when {
activitySelected -> { activitySelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET ACTIVITY $activityTT duration: $activityTTDuration") uel.log(
val tempTarget = TempTarget() Action.TT, Sources.CarbDialog,
.date(System.currentTimeMillis()) ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY),
.duration(activityTTDuration) ValueWithUnit.fromGlucoseUnit(activityTT, units.asText),
.reason(resourceHelper.gs(R.string.activity)) ValueWithUnit.Minute(activityTTDuration)
.source(Source.USER) )
.low(Profile.toMgdl(activityTT, profileFunction.getUnits())) disposable += repository.runTransactionForResult(
.high(Profile.toMgdl(activityTT, profileFunction.getUnits())) InsertAndCancelCurrentTemporaryTargetTransaction(
treatmentsPlugin.addToHistoryTempTarget(tempTarget) timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(activityTTDuration.toLong()),
reason = TemporaryTarget.Reason.ACTIVITY,
lowTarget = Profile.toMgdl(activityTT, profileFunction.getUnits()),
highTarget = Profile.toMgdl(activityTT, 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)
})
} }
eatingSoonSelected -> { eatingSoonSelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration") uel.log(
val tempTarget = TempTarget() Action.TT, Sources.CarbDialog,
.date(System.currentTimeMillis()) ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
.duration(eatingSoonTTDuration) ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
.reason(resourceHelper.gs(R.string.eatingsoon)) ValueWithUnit.Minute(eatingSoonTTDuration)
.source(Source.USER) )
.low(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits())) disposable += repository.runTransactionForResult(
.high(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits())) InsertAndCancelCurrentTemporaryTargetTransaction(
treatmentsPlugin.addToHistoryTempTarget(tempTarget) timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON,
lowTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()),
highTarget = Profile.toMgdl(eatingSoonTT, 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)
})
} }
hypoSelected -> { hypoSelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET HYPO $hypoTT duration: $hypoTTDuration") uel.log(
val tempTarget = TempTarget() Action.TT, Sources.CarbDialog,
.date(System.currentTimeMillis()) ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA),
.duration(hypoTTDuration) ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText),
.reason(resourceHelper.gs(R.string.hypo)) ValueWithUnit.Minute(hypoTTDuration)
.source(Source.USER) )
.low(Profile.toMgdl(hypoTT, profileFunction.getUnits())) disposable += repository.runTransactionForResult(
.high(Profile.toMgdl(hypoTT, profileFunction.getUnits())) InsertAndCancelCurrentTemporaryTargetTransaction(
treatmentsPlugin.addToHistoryTempTarget(tempTarget) timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(hypoTTDuration.toLong()),
reason = TemporaryTarget.Reason.HYPOGLYCEMIA,
lowTarget = Profile.toMgdl(hypoTT, profileFunction.getUnits()),
highTarget = Profile.toMgdl(hypoTT, 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)
})
} }
} }
if (carbsAfterConstraints > 0) { if (carbsAfterConstraints > 0) {
if (duration == 0) { val detailedBolusInfo = DetailedBolusInfo()
aapsLogger.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time") detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
carbsGenerator.createCarb(carbsAfterConstraints, time, CareportalEvent.CARBCORRECTION, notes) detailedBolusInfo.carbs = carbsAfterConstraints.toDouble()
} else { detailedBolusInfo.context = context
aapsLogger.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time duration: $duration") detailedBolusInfo.notes = notes
carbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes) detailedBolusInfo.carbsDuration = T.hours(duration.toLong()).msecs()
nsUpload.uploadEvent(CareportalEvent.NOTE, DateUtil.now() - 2000, resourceHelper.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset)) detailedBolusInfo.carbsTimestamp = eventTime
uel.log(if (duration == 0) Action.CARBS else Action.EXTENDED_CARBS, Sources.CarbDialog,
notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.Gram(carbsAfterConstraints),
ValueWithUnit.Minute(timeOffset).takeIf { timeOffset != 0 },
ValueWithUnit.Hour(duration).takeIf { duration != 0 })
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
carbTimer.removeEatReminder()
if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
} else if (sp.getBoolean(R.string.key_usebolusreminder, false) && remindBolus)
bolusTimer.scheduleBolusReminder()
} }
})
}
if (useAlarm && carbs > 0 && timeOffset > 0) {
carbTimer.scheduleReminder(T.mins(timeOffset.toLong()).secs().toInt())
} }
}, null) }, null)
} }
} else } else
activity?.let { activity -> activity?.let { activity ->
OKDialog.show(activity, resourceHelper.gs(R.string.carbs), resourceHelper.gs(R.string.no_action_selected)) OKDialog.show(activity, rh.gs(R.string.carbs), rh.gs(R.string.no_action_selected))
} }
return true return true
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -10,21 +11,28 @@ import androidx.annotation.StringRes
import com.google.common.base.Joiner import com.google.common.base.Joiner
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction
import info.nightscout.androidaps.databinding.DialogCareBinding import info.nightscout.androidaps.databinding.DialogCareBinding
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.extensions.fromConstant
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONObject 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 javax.inject.Inject import javax.inject.Inject
@ -32,11 +40,15 @@ import javax.inject.Inject
class CareDialog : DialogFragmentWithDate() { class CareDialog : DialogFragmentWithDate() {
@Inject lateinit var injector: HasAndroidInjector @Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var mainApp: MainApp @Inject lateinit var ctx: Context
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var translator: Translator @Inject lateinit var translator: Translator
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
private val disposable = CompositeDisposable()
enum class EventType { enum class EventType {
BGCHECK, BGCHECK,
@ -49,6 +61,8 @@ class CareDialog : DialogFragmentWithDate() {
} }
private var options: EventType = EventType.BGCHECK private var options: EventType = EventType.BGCHECK
//private var valuesWithUnit = mutableListOf<XXXValueWithUnit?>()
private var valuesWithUnit = mutableListOf<ValueWithUnit?>()
@StringRes @StringRes
private var event: Int = R.string.none private var event: Int = R.string.none
@ -97,7 +111,7 @@ class CareDialog : DialogFragmentWithDate() {
EventType.QUESTION -> R.drawable.ic_cp_question EventType.QUESTION -> R.drawable.ic_cp_question
EventType.ANNOUNCEMENT -> R.drawable.ic_cp_announcement EventType.ANNOUNCEMENT -> R.drawable.ic_cp_announcement
}) })
binding.title.text = resourceHelper.gs(when (options) { binding.title.text = rh.gs(when (options) {
EventType.BGCHECK -> R.string.careportal_bgcheck EventType.BGCHECK -> R.string.careportal_bgcheck
EventType.SENSOR_INSERT -> R.string.careportal_cgmsensorinsert EventType.SENSOR_INSERT -> R.string.careportal_cgmsensorinsert
EventType.BATTERY_CHANGE -> R.string.careportal_pumpbatterychange EventType.BATTERY_CHANGE -> R.string.careportal_pumpbatterychange
@ -128,7 +142,7 @@ class CareDialog : DialogFragmentWithDate() {
} }
} }
val bg = Profile.fromMgdlToUnits(GlucoseStatus(injector).glucoseStatusData?.glucose val bg = Profile.fromMgdlToUnits(glucoseStatusProvider.glucoseStatusData?.glucose
?: 0.0, profileFunction.getUnits()) ?: 0.0, profileFunction.getUnits())
val bgTextWatcher: TextWatcher = object : TextWatcher { val bgTextWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {} override fun afterTextChanged(s: Editable) {}
@ -138,18 +152,18 @@ class CareDialog : DialogFragmentWithDate() {
} }
} }
if (profileFunction.getUnits() == Constants.MMOL) { if (profileFunction.getUnits() == GlucoseUnit.MMOL) {
binding.bgunits.text = resourceHelper.gs(R.string.mmol) binding.bgUnits.text = rh.gs(R.string.mmol)
binding.bg.setParams(savedInstanceState?.getDouble("bg") binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, bgTextWatcher) ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, bgTextWatcher)
} else { } else {
binding.bgunits.text = resourceHelper.gs(R.string.mgdl) binding.bgUnits.text = rh.gs(R.string.mgdl)
binding.bg.setParams(savedInstanceState?.getDouble("bg") binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, bgTextWatcher) ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, bgTextWatcher)
} }
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)
if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT) if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT || options == EventType.EXERCISE)
binding.notesLayout.root.visibility = View.VISIBLE // independent to preferences binding.notesLayout.root.visibility = View.VISIBLE // independent to preferences
} }
@ -159,70 +173,75 @@ class CareDialog : DialogFragmentWithDate() {
} }
override fun submit(): Boolean { override fun submit(): Boolean {
val enteredBy = sp.getString("careportal_enteredby", "") val enteredBy = sp.getString("careportal_enteredby", "AndroidAPS")
val unitResId = if (profileFunction.getUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol val unitResId = if (profileFunction.getUnits() == GlucoseUnit.MGDL) R.string.mgdl else R.string.mmol
eventTime -= eventTime % 1000
val therapyEvent = TherapyEvent(
timestamp = eventTime,
type = when (options) {
EventType.BGCHECK -> TherapyEvent.Type.FINGER_STICK_BG_VALUE
EventType.SENSOR_INSERT -> TherapyEvent.Type.SENSOR_CHANGE
EventType.BATTERY_CHANGE -> TherapyEvent.Type.PUMP_BATTERY_CHANGE
EventType.NOTE -> TherapyEvent.Type.NOTE
EventType.EXERCISE -> TherapyEvent.Type.EXERCISE
EventType.QUESTION -> TherapyEvent.Type.QUESTION
EventType.ANNOUNCEMENT -> TherapyEvent.Type.ANNOUNCEMENT
},
glucoseUnit = TherapyEvent.GlucoseUnit.fromConstant(profileFunction.getUnits())
)
val json = JSONObject()
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
if (options == EventType.BGCHECK || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT) { if (options == EventType.BGCHECK || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT) {
val type = val meterType =
when { when {
binding.meter.isChecked -> CareportalEvent.FINGER binding.meter.isChecked -> TherapyEvent.MeterType.FINGER
binding.sensor.isChecked -> CareportalEvent.SENSOR binding.sensor.isChecked -> TherapyEvent.MeterType.SENSOR
else -> CareportalEvent.MANUAL else -> TherapyEvent.MeterType.MANUAL
} }
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_glucosetype) + ": " + translator.translate(type)) actions.add(rh.gs(R.string.careportal_newnstreatment_glucosetype) + ": " + translator.translate(meterType))
actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, binding.bg.value) + " " + resourceHelper.gs(unitResId)) actions.add(rh.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, binding.bg.value) + " " + rh.gs(unitResId))
json.put("glucose", binding.bg.value) therapyEvent.glucoseType = meterType
json.put("glucoseType", type) therapyEvent.glucose = binding.bg.value
valuesWithUnit.add(ValueWithUnit.fromGlucoseUnit(binding.bg.value, profileFunction.getUnits().asText))
valuesWithUnit.add(ValueWithUnit.TherapyEventMeterType(meterType))
} }
if (options == EventType.NOTE || options == EventType.EXERCISE) { if (options == EventType.NOTE || options == EventType.EXERCISE) {
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_duration_label) + ": " + resourceHelper.gs(R.string.format_mins, binding.duration.value.toInt())) actions.add(rh.gs(R.string.careportal_newnstreatment_duration_label) + ": " + rh.gs(R.string.format_mins, binding.duration.value.toInt()))
json.put("duration", binding.duration.value.toInt()) therapyEvent.duration = T.mins(binding.duration.value.toLong()).msecs()
valuesWithUnit.add(ValueWithUnit.Minute(binding.duration.value.toInt()).takeIf { !binding.duration.value.equals(0.0) } )
} }
val notes = binding.notesLayout.notes.text.toString() val notes = binding.notesLayout.notes.text.toString()
if (notes.isNotEmpty()) { if (notes.isNotEmpty()) {
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(rh.gs(R.string.notes_label) + ": " + notes)
json.put("notes", notes) therapyEvent.note = notes
} }
eventTime -= eventTime % 1000
if (eventTimeChanged) if (eventTimeChanged) actions.add(rh.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
json.put("created_at", DateUtil.toISOString(eventTime)) therapyEvent.enteredBy = enteredBy
json.put("mills", eventTime)
json.put("eventType", when (options) { val source = when (options) {
EventType.BGCHECK -> CareportalEvent.BGCHECK EventType.BGCHECK -> Sources.BgCheck
EventType.SENSOR_INSERT -> CareportalEvent.SENSORCHANGE EventType.SENSOR_INSERT -> Sources.SensorInsert
EventType.BATTERY_CHANGE -> CareportalEvent.PUMPBATTERYCHANGE EventType.BATTERY_CHANGE -> Sources.BatteryChange
EventType.NOTE -> CareportalEvent.NOTE EventType.NOTE -> Sources.Note
EventType.EXERCISE -> CareportalEvent.EXERCISE EventType.EXERCISE -> Sources.Exercise
EventType.QUESTION -> CareportalEvent.QUESTION EventType.QUESTION -> Sources.Question
EventType.ANNOUNCEMENT -> CareportalEvent.ANNOUNCEMENT EventType.ANNOUNCEMENT -> Sources.Announcement
}) }
json.put("units", profileFunction.getUnits())
if (enteredBy.isNotEmpty())
json.put("enteredBy", enteredBy)
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(event), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(event), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
val careportalEvent = CareportalEvent(injector) disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(therapyEvent))
careportalEvent.date = eventTime .subscribe(
careportalEvent.source = Source.USER { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } },
careportalEvent.eventType = when (options) { { aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) }
EventType.BGCHECK -> CareportalEvent.BGCHECK )
EventType.SENSOR_INSERT -> CareportalEvent.SENSORCHANGE valuesWithUnit.add(0, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged })
EventType.BATTERY_CHANGE -> CareportalEvent.PUMPBATTERYCHANGE valuesWithUnit.add(1, ValueWithUnit.TherapyEventType(therapyEvent.type))
EventType.NOTE -> CareportalEvent.NOTE uel.log(Action.CAREPORTAL, source, notes, valuesWithUnit)
EventType.EXERCISE -> CareportalEvent.EXERCISE
EventType.QUESTION -> CareportalEvent.QUESTION
EventType.ANNOUNCEMENT -> CareportalEvent.ANNOUNCEMENT
}
careportalEvent.json = json.toString()
aapsLogger.debug("USER ENTRY: CAREPORTAL ${careportalEvent.eventType} json: ${careportalEvent.json}")
MainApp.getDbHelper().createOrUpdate(careportalEvent)
nsUpload.uploadCareportalEntryToNS(json)
}, null) }, null)
} }
return true return true

View file

@ -1,145 +0,0 @@
package info.nightscout.androidaps.dialogs
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.os.Bundle
import android.text.format.DateFormat
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import android.widget.Button
import android.widget.TextView
import androidx.fragment.app.FragmentManager
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.*
import javax.inject.Inject
abstract class DialogFragmentWithDate : DaggerDialogFragment() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var dateUtil: DateUtil
var eventTime = DateUtil.now()
var eventTimeChanged = false
//one shot guards
private var okClicked: Boolean = false
companion object {
private var seconds: Int = (Math.random() * 59.0).toInt()
}
override fun onStart() {
super.onStart()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putLong("eventTime", eventTime)
savedInstanceState.putBoolean("eventTimeChanged", eventTimeChanged)
}
fun onCreateViewGeneral() {
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true
dialog?.setCanceledOnTouchOutside(false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val eventDateView = view.findViewById(R.id.eventdate) as TextView?
val eventTimeView = view.findViewById(R.id.eventtime) as TextView?
eventTime = savedInstanceState?.getLong("eventTime") ?: DateUtil.now()
eventTimeChanged = savedInstanceState?.getBoolean("eventTimeChanged") ?: false
eventDateView?.text = DateUtil.dateString(eventTime)
eventTimeView?.text = dateUtil.timeString(eventTime)
// create an OnDateSetListener
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
val cal = Calendar.getInstance()
cal.timeInMillis = eventTime
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, monthOfYear)
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
eventTime = cal.timeInMillis
eventTimeChanged = true
eventDateView?.text = DateUtil.dateString(eventTime)
}
eventDateView?.setOnClickListener {
context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = eventTime
DatePickerDialog(it, dateSetListener,
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH),
cal.get(Calendar.DAY_OF_MONTH)
).show()
}
}
// create an OnTimeSetListener
val timeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute ->
val cal = Calendar.getInstance()
cal.timeInMillis = eventTime
cal.set(Calendar.HOUR_OF_DAY, hour)
cal.set(Calendar.MINUTE, minute)
cal.set(Calendar.SECOND, seconds++) // randomize seconds to prevent creating record of the same time, if user choose time manually
eventTime = cal.timeInMillis
eventTimeChanged = true
eventTimeView?.text = dateUtil.timeString(eventTime)
}
eventTimeView?.setOnClickListener {
context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = eventTime
TimePickerDialog(it, timeSetListener,
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(context)
).show()
}
}
(view.findViewById(R.id.notes_layout) as View?)?.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility()
(view.findViewById(R.id.ok) as Button?)?.setOnClickListener {
synchronized(okClicked) {
if (okClicked) {
aapsLogger.warn(LTag.UI, "guarding: ok already clicked")
} else {
okClicked = true
if (submit()) dismiss()
else okClicked = false
}
}
}
(view.findViewById(R.id.cancel) as Button?)?.setOnClickListener { dismiss() }
}
override fun show(manager: FragmentManager, tag: String?) {
try {
manager.beginTransaction().let {
it.add(this, tag)
it.commitAllowingStateLoss()
}
} catch (e: IllegalStateException) {
aapsLogger.debug(e.localizedMessage)
}
}
abstract fun submit(): Boolean
}

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -9,16 +8,20 @@ import android.view.ViewGroup
import com.google.common.base.Joiner import com.google.common.base.Joiner
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.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.DialogExtendedbolusBinding import info.nightscout.androidaps.databinding.DialogExtendedbolusBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueue
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.SafeParse import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
@ -28,10 +31,11 @@ import kotlin.math.abs
class ExtendedBolusDialog : DialogFragmentWithDate() { class ExtendedBolusDialog : DialogFragmentWithDate() {
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var uel: UserEntryLogger
private var _binding: DialogExtendedbolusBinding? = null private var _binding: DialogExtendedbolusBinding? = null
@ -75,27 +79,24 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false) val insulin = SafeParse.stringToDouble(binding.insulin.text)
val durationInMinutes = binding.duration.value.toInt() val durationInMinutes = binding.duration.value.toInt()
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
val insulinAfterConstraint = constraintChecker.applyExtendedBolusConstraints(Constraint(insulin)).value() val insulinAfterConstraint = constraintChecker.applyExtendedBolusConstraints(Constraint(insulin)).value()
actions.add(resourceHelper.gs(R.string.formatinsulinunits, insulinAfterConstraint)) actions.add(rh.gs(R.string.formatinsulinunits, insulinAfterConstraint))
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, durationInMinutes)) actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
if (abs(insulinAfterConstraint - insulin) > 0.01) if (abs(insulinAfterConstraint - insulin) > 0.01)
actions.add(resourceHelper.gs(R.string.constraintapllied).formatColor(resourceHelper, R.color.warning)) actions.add(rh.gs(R.string.constraintapllied).formatColor(rh, R.color.warning))
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: EXTENDED BOLUS $insulinAfterConstraint duration: $durationInMinutes") uel.log(Action.EXTENDED_BOLUS, Sources.ExtendedBolusDialog,
ValueWithUnit.Insulin(insulinAfterConstraint),
ValueWithUnit.Minute(durationInMinutes))
commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() { commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
} }
} }
}) })

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -10,21 +9,28 @@ import com.google.common.base.Joiner
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.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction
import info.nightscout.androidaps.databinding.DialogFillBinding import info.nightscout.androidaps.databinding.DialogFillBinding
import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.interfaces.CommandQueue
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.shared.logging.LTag
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.general.nsclient.NSUpload
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.SafeParse import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
@ -32,11 +38,14 @@ import kotlin.math.abs
class FillDialog : DialogFragmentWithDate() { class FillDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private val disposable = CompositeDisposable()
private var _binding: DialogFillBinding? = null private var _binding: DialogFillBinding? = null
@ -97,52 +106,76 @@ class FillDialog : DialogFragmentWithDate() {
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val insulin = SafeParse.stringToDouble(binding.fillInsulinamount.text ?: return false) val insulin = SafeParse.stringToDouble(binding.fillInsulinamount.text)
val actions: LinkedList<String?> = LinkedList() val actions: LinkedList<String?> = LinkedList()
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
actions.add(resourceHelper.gs(R.string.fillwarning)) actions.add(rh.gs(R.string.fillwarning))
actions.add("") actions.add("")
actions.add(resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, resourceHelper).formatColor(resourceHelper, R.color.colorInsulinButton)) actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(rh, R.color.colorInsulinButton))
if (abs(insulinAfterConstraints - insulin) > 0.01) if (abs(insulinAfterConstraints - insulin) > 0.01)
actions.add(resourceHelper.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(resourceHelper, R.color.warning)) actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(rh, R.color.warning))
} }
val siteChange = binding.fillCatheterChange.isChecked val siteChange = binding.fillCatheterChange.isChecked
if (siteChange) if (siteChange)
actions.add(resourceHelper.gs(R.string.record_pump_site_change).formatColor(resourceHelper, R.color.actionsConfirm)) actions.add(rh.gs(R.string.record_pump_site_change).formatColor(rh, R.color.actionsConfirm))
val insulinChange = binding.fillCartridgeChange.isChecked val insulinChange = binding.fillCartridgeChange.isChecked
if (insulinChange) if (insulinChange)
actions.add(resourceHelper.gs(R.string.record_insulin_cartridge_change).formatColor(resourceHelper, R.color.actionsConfirm)) actions.add(rh.gs(R.string.record_insulin_cartridge_change).formatColor(rh, R.color.actionsConfirm))
val notes = binding.notesLayout.notes.text.toString() val notes: String = binding.notesLayout.notes.text.toString()
if (notes.isNotEmpty()) if (notes.isNotEmpty())
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(rh.gs(R.string.notes_label) + ": " + notes)
eventTime -= eventTime % 1000 eventTime -= eventTime % 1000
if (eventTimeChanged) if (eventTimeChanged)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) actions.add(rh.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
if (insulinAfterConstraints > 0 || binding.fillCatheterChange.isChecked || binding.fillCartridgeChange.isChecked) { if (insulinAfterConstraints > 0 || binding.fillCatheterChange.isChecked || binding.fillCartridgeChange.isChecked) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
aapsLogger.debug("USER ENTRY: PRIME BOLUS $insulinAfterConstraints") uel.log(Action.PRIME_BOLUS, Sources.FillDialog,
notes,
ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 })
requestPrimeBolus(insulinAfterConstraints, notes) requestPrimeBolus(insulinAfterConstraints, notes)
} }
if (siteChange) { if (siteChange) {
aapsLogger.debug("USER ENTRY: SITE CHANGE") uel.log(Action.SITE_CHANGE, Sources.FillDialog,
nsUpload.generateCareportalEvent(CareportalEvent.SITECHANGE, eventTime, notes) notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.TherapyEventType(TherapyEvent.Type.CANNULA_CHANGE))
disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(
timestamp = eventTime,
type = TherapyEvent.Type.CANNULA_CHANGE,
note = notes,
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) }
)
} }
if (insulinChange) { if (insulinChange) {
// add a second for case of both checked // add a second for case of both checked
aapsLogger.debug("USER ENTRY: INSULIN CHANGE") uel.log(Action.RESERVOIR_CHANGE, Sources.FillDialog,
nsUpload.generateCareportalEvent(CareportalEvent.INSULINCHANGE, eventTime + 1000, notes) notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.TherapyEventType(TherapyEvent.Type.INSULIN_CHANGE))
disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(
timestamp = eventTime + 1000,
type = TherapyEvent.Type.INSULIN_CHANGE,
note = notes,
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) }
)
} }
}, null) }, null)
} }
} else { } else {
activity?.let { activity -> activity?.let { activity ->
OKDialog.show(activity, resourceHelper.gs(R.string.primefill), resourceHelper.gs(R.string.no_action_selected)) OKDialog.show(activity, rh.gs(R.string.primefill), rh.gs(R.string.no_action_selected))
} }
} }
dismiss() dismiss()
@ -153,18 +186,12 @@ class FillDialog : DialogFragmentWithDate() {
val detailedBolusInfo = DetailedBolusInfo() val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.insulin = insulin detailedBolusInfo.insulin = insulin
detailedBolusInfo.context = context detailedBolusInfo.context = context
detailedBolusInfo.source = Source.USER detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING
detailedBolusInfo.isValid = false // do not count it in IOB (for pump history)
detailedBolusInfo.notes = notes detailedBolusInfo.notes = notes
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
} }
} }
}) })

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -9,30 +8,33 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.google.common.base.Joiner import com.google.common.base.Joiner
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.Constants
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.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile 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.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogInsulinBinding import info.nightscout.androidaps.databinding.DialogInsulinBinding
import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.db.TempTarget import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.extensions.toSignedString import info.nightscout.androidaps.utils.extensions.toSignedString
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.SafeParse
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
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
@ -40,13 +42,16 @@ import kotlin.math.max
class InsulinDialog : DialogFragmentWithDate() { class InsulinDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var repository: AppRepository
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var bolusTimer: BolusTimer
@Inject lateinit var uel: UserEntryLogger
companion object { companion object {
@ -55,6 +60,8 @@ class InsulinDialog : DialogFragmentWithDate() {
private const val PLUS3_DEFAULT = 2.0 private const val PLUS3_DEFAULT = 2.0
} }
private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher { private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
validateInputs() validateInputs()
@ -74,11 +81,11 @@ class InsulinDialog : DialogFragmentWithDate() {
val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
if (abs(binding.time.value.toInt()) > 12 * 60) { if (abs(binding.time.value.toInt()) > 12 * 60) {
binding.time.value = 0.0 binding.time.value = 0.0
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.constraintapllied)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.constraintapllied))
} }
if (binding.amount.value > maxInsulin) { if (binding.amount.value > maxInsulin) {
binding.amount.value = 0.0 binding.amount.value = 0.0
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.bolusconstraintapplied)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.bolusconstraintapplied))
} }
} }
@ -109,22 +116,22 @@ class InsulinDialog : DialogFragmentWithDate() {
binding.amount.setParams(savedInstanceState?.getDouble("amount") binding.amount.setParams(savedInstanceState?.getDouble("amount")
?: 0.0, 0.0, maxInsulin, activePlugin.activePump.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher) ?: 0.0, 0.0, maxInsulin, activePlugin.activePump.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher)
binding.plus05.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString(activePlugin.activePump) binding.plus05.text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString(activePlugin.activePump)
binding.plus05.setOnClickListener { binding.plus05.setOnClickListener {
binding.amount.value = max(0.0, binding.amount.value binding.amount.value = max(0.0, binding.amount.value
+ sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT)) + sp.getDouble(rh.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT))
validateInputs() validateInputs()
} }
binding.plus10.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump) binding.plus10.text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump)
binding.plus10.setOnClickListener { binding.plus10.setOnClickListener {
binding.amount.value = max(0.0, binding.amount.value binding.amount.value = max(0.0, binding.amount.value
+ sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT)) + sp.getDouble(rh.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT))
validateInputs() validateInputs()
} }
binding.plus20.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump) binding.plus20.text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump)
binding.plus20.setOnClickListener { binding.plus20.setOnClickListener {
binding.amount.value = max(0.0, binding.amount.value binding.amount.value = max(0.0, binding.amount.value
+ sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT)) + sp.getDouble(rh.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT))
validateInputs() validateInputs()
} }
@ -136,78 +143,94 @@ class InsulinDialog : DialogFragmentWithDate() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
disposable.clear()
_binding = null _binding = null
} }
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val pumpDescription = activePlugin.activePump.pumpDescription val pumpDescription = activePlugin.activePump.pumpDescription
val insulin = SafeParse.stringToDouble(binding.amount.text ?: return false) val insulin = SafeParse.stringToDouble(binding.amount.text)
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
val actions: LinkedList<String?> = LinkedList() val actions: LinkedList<String?> = LinkedList()
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) val unitLabel = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
val recordOnlyChecked = binding.recordOnly.isChecked val recordOnlyChecked = binding.recordOnly.isChecked
val eatingSoonChecked = binding.startEatingSoonTt.isChecked val eatingSoonChecked = binding.startEatingSoonTt.isChecked
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
actions.add(resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, resourceHelper).formatColor(resourceHelper, R.color.bolus)) actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(rh, R.color.bolus))
if (recordOnlyChecked) if (recordOnlyChecked)
actions.add(resourceHelper.gs(R.string.bolusrecordedonly).formatColor(resourceHelper, R.color.warning)) actions.add(rh.gs(R.string.bolusrecordedonly).formatColor(rh, R.color.warning))
if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
actions.add(resourceHelper.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(resourceHelper, R.color.warning)) actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(rh, R.color.warning))
} }
val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration() val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration()
val eatingSoonTT = defaultValueHelper.determineEatingSoonTT() val eatingSoonTT = defaultValueHelper.determineEatingSoonTT()
if (eatingSoonChecked) if (eatingSoonChecked)
actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, eatingSoonTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation)) actions.add(rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, eatingSoonTTDuration) + ")").formatColor(rh, R.color.tempTargetConfirmation))
val timeOffset = binding.time.value.toInt() val timeOffset = binding.time.value.toInt()
val time = DateUtil.now() + T.mins(timeOffset.toLong()).msecs() val time = dateUtil.now() + T.mins(timeOffset.toLong()).msecs()
if (timeOffset != 0) if (timeOffset != 0)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time)) actions.add(rh.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time))
val notes = binding.notesLayout.notes.text.toString() val notes = binding.notesLayout.notes.text.toString()
if (notes.isNotEmpty()) if (notes.isNotEmpty())
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(rh.gs(R.string.notes_label) + ": " + notes)
if (insulinAfterConstraints > 0 || eatingSoonChecked) { if (insulinAfterConstraints > 0 || eatingSoonChecked) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
if (eatingSoonChecked) { if (eatingSoonChecked) {
aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration") uel.log(Action.TT, Sources.InsulinDialog,
val tempTarget = TempTarget() notes,
.date(System.currentTimeMillis()) ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
.duration(eatingSoonTTDuration) ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
.reason(resourceHelper.gs(R.string.eatingsoon)) ValueWithUnit.Minute(eatingSoonTTDuration))
.source(Source.USER) disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
.low(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits())) timestamp = System.currentTimeMillis(),
.high(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits())) duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
activePlugin.activeTreatments.addToHistoryTempTarget(tempTarget) reason = TemporaryTarget.Reason.EATING_SOON,
lowTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()),
highTarget = Profile.toMgdl(eatingSoonTT, 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)
})
} }
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
val detailedBolusInfo = DetailedBolusInfo() val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
detailedBolusInfo.insulin = insulinAfterConstraints detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.context = context detailedBolusInfo.context = context
detailedBolusInfo.source = Source.USER
detailedBolusInfo.notes = notes detailedBolusInfo.notes = notes
detailedBolusInfo.timestamp = time
if (recordOnlyChecked) { if (recordOnlyChecked) {
aapsLogger.debug("USER ENTRY: BOLUS RECORD ONLY $insulinAfterConstraints") uel.log(Action.BOLUS, Sources.InsulinDialog,
detailedBolusInfo.date = time rh.gs(R.string.record) + if (notes.isNotEmpty()) ": " + notes else "",
activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false) ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.record)),
ValueWithUnit.Insulin(insulinAfterConstraints),
ValueWithUnit.Minute(timeOffset).takeIf { timeOffset!= 0 })
disposable += repository.runTransactionForResult(detailedBolusInfo.insertBolusTransaction())
.subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) }
)
if (timeOffset == 0)
bolusTimer.removeBolusReminder()
} else { } else {
aapsLogger.debug("USER ENTRY: BOLUS $insulinAfterConstraints") uel.log(Action.BOLUS, Sources.InsulinDialog,
detailedBolusInfo.date = DateUtil.now() notes,
ValueWithUnit.Insulin(insulinAfterConstraints))
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
i.putExtra("soundid", R.raw.boluserror) } else {
i.putExtra("status", result.comment) bolusTimer.removeBolusReminder()
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
} }
} }
}) })
@ -217,7 +240,7 @@ class InsulinDialog : DialogFragmentWithDate() {
} }
} else } else
activity?.let { activity -> activity?.let { activity ->
OKDialog.show(activity, resourceHelper.gs(R.string.bolus), resourceHelper.gs(R.string.no_action_selected)) OKDialog.show(activity, rh.gs(R.string.bolus), rh.gs(R.string.no_action_selected))
} }
return true return true
} }

View file

@ -1,9 +1,9 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.HandlerThread
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -13,22 +13,35 @@ 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.UserEntry.Action
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.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.databinding.DialogLoopBinding import info.nightscout.androidaps.databinding.DialogLoopBinding
import info.nightscout.androidaps.events.* import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.runOnUiThread
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
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.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.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.shared.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() {
@ -36,28 +49,37 @@ class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBus
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var loopPlugin: LoopPlugin @Inject lateinit var loop: Loop
@Inject lateinit var objectivesPlugin: ObjectivesPlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin @Inject lateinit var configBuilder: ConfigBuilder
@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 handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
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) {
@ -65,8 +87,10 @@ class LoopDialog : DaggerDialogFragment() {
savedInstanceState.putInt("showOkCancel", if (showOkCancel) 1 else 0) savedInstanceState.putInt("showOkCancel", if (showOkCancel) 1 else 0)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(
savedInstanceState: Bundle?): View { inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// load data from bundle // load data from bundle
(savedInstanceState ?: arguments)?.let { bundle -> (savedInstanceState ?: arguments)?.let { bundle ->
showOkCancel = bundle.getInt("showOkCancel", 1) == 1 showOkCancel = bundle.getInt("showOkCancel", 1) == 1
@ -104,55 +128,97 @@ class LoopDialog : DaggerDialogFragment() {
binding.cancel.setOnClickListener { dismiss() } binding.cancel.setOnClickListener { dismiss() }
refreshDialog = Runnable { refreshDialog = Runnable {
scheduleUpdateGUI("refreshDialog") runOnUiThread { updateGUI("refreshDialog") }
loopHandler.postDelayed(refreshDialog, 15 * 1000L) handler.postDelayed(refreshDialog, 15 * 1000L)
} }
loopHandler.postDelayed(refreshDialog, 15 * 1000L) handler.postDelayed(refreshDialog, 15 * 1000L)
} }
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
loopHandler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
disposable.clear()
} }
var task: Runnable? = null var task: Runnable? = null
private fun scheduleUpdateGUI(from: String) {
class UpdateRunnable : Runnable {
override fun run() {
updateGUI(from)
task = null
}
}
view?.removeCallbacks(task)
task = UpdateRunnable()
view?.postDelayed(task, 500)
}
@Synchronized @Synchronized
fun updateGUI(from: String) { fun updateGUI(from: String) {
if (_binding == null) return if (_binding == null) return
aapsLogger.debug("UpdateGUI from $from") aapsLogger.debug("UpdateGUI from $from")
val pumpDescription: PumpDescription = activePlugin.activePump.pumpDescription val pumpDescription: PumpDescription = activePlugin.activePump.pumpDescription
val closedLoopAllowed = objectivesPlugin.isClosedLoopAllowed(Constraint(true)) val closedLoopAllowed = constraintChecker.isClosedLoopAllowed(Constraint(true))
val lgsEnabled = objectivesPlugin.isLgsAllowed(Constraint(true)) val closedLoopAllowed2 = objectivePlugin.objectives[ObjectivesPlugin.MAXIOB_OBJECTIVE].isCompleted
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
}
loop.isDisconnected -> {
binding.overviewLoop.visibility = View.GONE
binding.overviewSuspend.visibility = View.GONE
binding.overviewPump.visibility = View.VISIBLE
binding.overviewPumpHeader.text = rh.gs(R.string.reconnect)
binding.overviewDisconnectButtons.visibility = View.VISIBLE
binding.overviewReconnect.visibility = View.VISIBLE
}
!(loop as PluginBase).isEnabled() -> {
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
}
loop.isSuspended -> {
binding.overviewLoop.visibility = View.GONE
binding.overviewSuspend.visibility = View.VISIBLE
binding.overviewSuspendHeader.text = rh.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 -> {
@ -161,72 +227,42 @@ 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 binding.overviewSuspendHeader.text = rh.gs(R.string.suspendloop)
if (!loopPlugin.isSuspended) {
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.overviewPumpHeader.text = rh.gs(R.string.disconnectpump)
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.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
}
}
val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileInterface.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 {
var description = "" var description = ""
when (v.id) { when (v.id) {
R.id.overview_closeloop -> description = resourceHelper.gs(R.string.closedloop) R.id.overview_closeloop -> description = rh.gs(R.string.closedloop)
R.id.overview_lgsloop -> description = resourceHelper.gs(R.string.lowglucosesuspend) R.id.overview_lgsloop -> description = rh.gs(R.string.lowglucosesuspend)
R.id.overview_openloop -> description = resourceHelper.gs(R.string.openloop) R.id.overview_openloop -> description = rh.gs(R.string.openloop)
R.id.overview_disable -> description = resourceHelper.gs(R.string.disableloop) R.id.overview_disable -> description = rh.gs(R.string.disableloop)
R.id.overview_enable -> description = resourceHelper.gs(R.string.enableloop) R.id.overview_enable -> description = rh.gs(R.string.enableloop)
R.id.overview_resume -> description = resourceHelper.gs(R.string.resume) R.id.overview_resume -> description = rh.gs(R.string.resume)
R.id.overview_reconnect -> description = resourceHelper.gs(R.string.reconnect) R.id.overview_reconnect -> description = rh.gs(R.string.reconnect)
R.id.overview_suspend_1h -> description = resourceHelper.gs(R.string.suspendloopfor1h) R.id.overview_suspend_1h -> description = rh.gs(R.string.suspendloopfor1h)
R.id.overview_suspend_2h -> description = resourceHelper.gs(R.string.suspendloopfor2h) R.id.overview_suspend_2h -> description = rh.gs(R.string.suspendloopfor2h)
R.id.overview_suspend_3h -> description = resourceHelper.gs(R.string.suspendloopfor3h) R.id.overview_suspend_3h -> description = rh.gs(R.string.suspendloopfor3h)
R.id.overview_suspend_10h -> description = resourceHelper.gs(R.string.suspendloopfor10h) R.id.overview_suspend_10h -> description = rh.gs(R.string.suspendloopfor10h)
R.id.overview_disconnect_15m -> description = resourceHelper.gs(R.string.disconnectpumpfor15m) R.id.overview_disconnect_15m -> description = rh.gs(R.string.disconnectpumpfor15m)
R.id.overview_disconnect_30m -> description = resourceHelper.gs(R.string.disconnectpumpfor30m) R.id.overview_disconnect_30m -> description = rh.gs(R.string.disconnectpumpfor30m)
R.id.overview_disconnect_1h -> description = resourceHelper.gs(R.string.disconnectpumpfor1h) R.id.overview_disconnect_1h -> description = rh.gs(R.string.disconnectpumpfor1h)
R.id.overview_disconnect_2h -> description = resourceHelper.gs(R.string.disconnectpumpfor2h) R.id.overview_disconnect_2h -> description = rh.gs(R.string.disconnectpumpfor2h)
R.id.overview_disconnect_3h -> description = resourceHelper.gs(R.string.disconnectpumpfor3h) R.id.overview_disconnect_3h -> description = rh.gs(R.string.disconnectpumpfor3h)
} }
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.confirm), description, Runnable { OKDialog.showConfirmation(activity, rh.gs(R.string.confirm), description, Runnable {
onClick(v) onClick(v)
}) })
} }
@ -234,138 +270,157 @@ 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 -> {
aapsLogger.debug("USER ENTRY: CLOSED LOOP MODE") uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog)
sp.putString(R.string.key_aps_mode, "closed") sp.putString(R.string.key_aps_mode, "closed")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.closedloop))) rxBus.send(EventPreferenceChange(rh.gs(R.string.closedloop)))
return true return true
} }
R.id.overview_lgsloop -> { R.id.overview_lgsloop -> {
aapsLogger.debug("USER ENTRY: LGS LOOP MODE") uel.log(Action.LGS_LOOP_MODE, Sources.LoopDialog)
sp.putString(R.string.key_aps_mode, "lgs") sp.putString(R.string.key_aps_mode, "lgs")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) rxBus.send(EventPreferenceChange(rh.gs(R.string.lowglucosesuspend)))
return true return true
} }
R.id.overview_openloop -> { R.id.overview_openloop -> {
aapsLogger.debug("USER ENTRY: OPEN LOOP MODE") uel.log(Action.OPEN_LOOP_MODE, Sources.LoopDialog)
sp.putString(R.string.key_aps_mode, "open") sp.putString(R.string.key_aps_mode, "open")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) rxBus.send(EventPreferenceChange(rh.gs(R.string.lowglucosesuspend)))
return true return true
} }
R.id.overview_disable -> { R.id.overview_disable -> {
aapsLogger.debug("USER ENTRY: LOOP DISABLED") uel.log(Action.LOOP_DISABLED, Sources.LoopDialog)
loopPlugin.setPluginEnabled(PluginType.LOOP, false) (loop as PluginBase).setPluginEnabled(PluginType.LOOP, false)
loopPlugin.setFragmentVisible(PluginType.LOOP, false) (loop as PluginBase).setFragmentVisible(PluginType.LOOP, false)
configBuilderPlugin.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) {
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.tempbasaldeliveryerror)) ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.tempbasaldeliveryerror))
} }
} }
}) })
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
} }
R.id.overview_enable -> { R.id.overview_enable -> {
aapsLogger.debug("USER ENTRY: LOOP ENABLED") uel.log(Action.LOOP_ENABLED, Sources.LoopDialog)
loopPlugin.setPluginEnabled(PluginType.LOOP, true) (loop as PluginBase).setPluginEnabled(PluginType.LOOP, true)
loopPlugin.setFragmentVisible(PluginType.LOOP, true) (loop as PluginBase).setFragmentVisible(PluginType.LOOP, true)
configBuilderPlugin.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 -> {
aapsLogger.debug("USER ENTRY: RESUME") 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) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
} }
} }
}) })
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 -> {
aapsLogger.debug("USER ENTRY: SUSPEND 1h") uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1))
loopPlugin.suspendLoop(60) loop.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 -> {
aapsLogger.debug("USER ENTRY: SUSPEND 2h") uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2))
loopPlugin.suspendLoop(120) loop.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 -> {
aapsLogger.debug("USER ENTRY: SUSPEND 3h") uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3))
loopPlugin.suspendLoop(180) loop.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 -> {
aapsLogger.debug("USER ENTRY: SUSPEND 10h") uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10))
loopPlugin.suspendLoop(600) loop.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 -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 15m") profileFunction.getProfile()?.let { profile ->
loopPlugin.disconnectPump(15, profile) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15))
rxBus.send(EventRefreshOverview("suspendmenu")) loop.goToZeroTemp(T.mins(15).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true return true
} }
R.id.overview_disconnect_30m -> { R.id.overview_disconnect_30m -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 30m") profileFunction.getProfile()?.let { profile ->
loopPlugin.disconnectPump(30, profile) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30))
rxBus.send(EventRefreshOverview("suspendmenu")) loop.goToZeroTemp(T.mins(30).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true return true
} }
R.id.overview_disconnect_1h -> { R.id.overview_disconnect_1h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 1h") profileFunction.getProfile()?.let { profile ->
loopPlugin.disconnectPump(60, profile) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(1))
loop.goToZeroTemp(T.hours(1).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspend_menu"))
}
sp.putBoolean(R.string.key_objectiveusedisconnect, true) sp.putBoolean(R.string.key_objectiveusedisconnect, true)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_disconnect_2h -> { R.id.overview_disconnect_2h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 2h") profileFunction.getProfile()?.let { profile ->
loopPlugin.disconnectPump(120, profile) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2))
rxBus.send(EventRefreshOverview("suspendmenu")) loop.goToZeroTemp(T.hours(2).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true return true
} }
R.id.overview_disconnect_3h -> { R.id.overview_disconnect_3h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 3h") profileFunction.getProfile()?.let { profile ->
loopPlugin.disconnectPump(180, profile) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3))
rxBus.send(EventRefreshOverview("suspendmenu")) loop.goToZeroTemp(T.hours(3).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true return true
} }
} }
@ -379,7 +434,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,33 +10,69 @@ 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.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
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.interfaces.ActivePluginProvider import info.nightscout.androidaps.extensions.toVisibility
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.plugins.treatments.TreatmentsPlugin import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
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.T
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.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
import kotlin.collections.ArrayList
class ProfileSwitchDialog : DialogFragmentWithDate() { class ProfileSwitchDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var config: Config
@Inject lateinit var hardLimits: HardLimits
@Inject lateinit var rxBus: RxBus
@Inject lateinit var defaultValueHelper: DefaultValueHelper
private var profileIndex: Int? = null private var profileIndex: Int? = null
private val disposable = CompositeDisposable()
private var _binding: DialogProfileswitchBinding? = null private var _binding: DialogProfileswitchBinding? = null
// 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!!
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)
@ -42,8 +80,10 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
savedInstanceState.putDouble("timeshift", binding.timeshift.value) savedInstanceState.putDouble("timeshift", binding.timeshift.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(
savedInstanceState: Bundle?): View { inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
onCreateViewGeneral() onCreateViewGeneral()
arguments?.let { bundle -> arguments?.let { bundle ->
profileIndex = bundle.getInt("profileIndex", 0) profileIndex = bundle.getInt("profileIndex", 0)
@ -55,18 +95,36 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.duration.setParams(savedInstanceState?.getDouble("duration") binding.duration.setParams(
?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok) savedInstanceState?.getDouble("duration")
binding.percentage.setParams(savedInstanceState?.getDouble("percentage") ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.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") )
?: 0.0, Constants.CPP_MIN_TIMESHIFT.toDouble(), Constants.CPP_MAX_TIMESHIFT.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok) 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, textWatcher
)
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
)
// profile // profile
context?.let { context -> context?.let { context ->
val profileStore = activePlugin.activeProfileInterface.profile val profileStore = activePlugin.activeProfileSource.profile
?: return ?: return
val profileList = profileStore.getProfileList() val profileListToCheck = profileStore.getProfileList()
val profileList = ArrayList<CharSequence>()
for (profileName in profileListToCheck) {
val profileToCheck = activePlugin.activeProfileSource.profile?.getSpecificProfile(profileName.toString())
if (profileToCheck != null && ProfileSealed.Pure(profileToCheck).isValid("ProfileSwitch", activePlugin.activePump, config, rh, rxBus, hardLimits, false).isValid)
profileList.add(profileName)
}
if (profileList.isEmpty()) {
dismiss()
return
}
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
binding.profile.adapter = adapter binding.profile.adapter = adapter
// set selected to actual profile // set selected to actual profile
@ -74,57 +132,113 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
binding.profile.setSelection(profileIndex as Int) binding.profile.setSelection(profileIndex as Int)
else else
for (p in profileList.indices) for (p in profileList.indices)
if (profileList[p] == profileFunction.getProfileName(false)) if (profileList[p] == profileFunction.getOriginalProfileName())
binding.profile.setSelection(p) binding.profile.setSelection(p)
} ?: return }
treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now())?.let { ps -> profileFunction.getProfile()?.let { profile ->
if (ps.isCPP) { if (profile is ProfileSealed.EPS)
if (profile.value.originalPercentage != 100 || profile.value.originalTimeshift != 0L) {
binding.reuselayout.visibility = View.VISIBLE binding.reuselayout.visibility = View.VISIBLE
binding.reusebutton.text = resourceHelper.gs(R.string.reuse_profile_pct_hours, ps.percentage, ps.timeshift) binding.reusebutton.text = rh.gs(R.string.reuse_profile_pct_hours, profile.value.originalPercentage, T.msecs(profile.value.originalTimeshift).hours().toInt())
binding.reusebutton.setOnClickListener { binding.reusebutton.setOnClickListener {
binding.percentage.value = ps.percentage.toDouble() binding.percentage.value = profile.value.originalPercentage.toDouble()
binding.timeshift.value = ps.timeshift.toDouble() binding.timeshift.value = profile.value.originalTimeshift.toDouble()
}
} else {
binding.reuselayout.visibility = View.GONE
} }
} }
} }
binding.ttLayout.visibility = View.GONE
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
disposable.clear()
_binding = null _binding = null
} }
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val profileStore = activePlugin.activeProfileInterface.profile val profileStore = activePlugin.activeProfileSource.profile
?: return false ?: return false
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
val duration = binding.duration.value?.toInt() ?: return false val duration = binding.duration.value.toInt()
if (duration > 0) if (duration > 0L)
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, duration)) actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, duration))
val profile = binding.profile.selectedItem.toString() val profileName = binding.profile.selectedItem.toString()
actions.add(resourceHelper.gs(R.string.profile) + ": " + profile) actions.add(rh.gs(R.string.profile) + ": " + profileName)
val percent = binding.percentage.value.toInt() val percent = binding.percentage.value.toInt()
if (percent != 100) if (percent != 100)
actions.add(resourceHelper.gs(R.string.percent) + ": " + percent + "%") actions.add(rh.gs(R.string.percent) + ": " + percent + "%")
val timeShift = binding.timeshift.value.toInt() val timeShift = binding.timeshift.value.toInt()
if (timeShift != 0) if (timeShift != 0)
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_timeshift_label) + ": " + resourceHelper.gs(R.string.format_hours, timeShift.toDouble())) actions.add(rh.gs(R.string.careportal_newnstreatment_timeshift_label) + ": " + rh.gs(R.string.format_hours, timeShift.toDouble()))
val notes = binding.notesLayout.notes.text.toString() val notes = binding.notesLayout.notes.text.toString()
if (notes.isNotEmpty()) if (notes.isNotEmpty())
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(rh.gs(R.string.notes_label) + ": " + notes)
if (eventTimeChanged) if (eventTimeChanged)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) actions.add(rh.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(rh.gs(R.string.careportal_temporarytarget) + ": " + rh.gs(R.string.activity))
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { val ps = profileFunction.buildProfileSwitch(profileStore, profileName, duration, percent, timeShift, eventTime) ?: return@let
aapsLogger.debug("USER ENTRY: PROFILE SWITCH $profile percent: $percent timeshift: $timeShift duration: $duration") val validity = ProfileSealed.PS(ps).isValid(rh.gs(R.string.careportal_profileswitch), activePlugin.activePump, config, rh, rxBus, hardLimits, false)
treatmentsPlugin.doProfileSwitch(profileStore, profile, duration, percent, timeShift, eventTime) if (validity.isValid)
OKDialog.showConfirmation(activity, rh.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
if (profileFunction.createProfileSwitch(
profileStore,
profileName = profileName,
durationInMinutes = duration,
percentage = percent,
timeShiftInHours = timeShift,
timestamp = eventTime
)
) {
uel.log(Action.PROFILE_SWITCH,
Sources.ProfileSwitchDialog,
notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.SimpleString(profileName),
ValueWithUnit.Percent(percent),
ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 },
ValueWithUnit.Minute(duration).takeIf { duration != 0 })
if (percent == 90 && duration == 10) sp.putBoolean(R.string.key_objectiveuseprofileswitch, true)
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,
rh.gs(R.string.careportal_profileswitch),
HtmlHelper.fromHtml(Joiner.on("<br/>").join(validity.reasons))
)
return false
}
} }
return true return true
} }

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -9,18 +8,18 @@ import android.view.ViewGroup
import com.google.common.base.Joiner import com.google.common.base.Joiner
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.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.DialogTempbasalBinding import info.nightscout.androidaps.databinding.DialogTempbasalBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.PumpDescription
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.SafeParse import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
@ -30,11 +29,12 @@ import kotlin.math.abs
class TempBasalDialog : DialogFragmentWithDate() { class TempBasalDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var uel: UserEntryLogger
private var isPercentPump = true private var isPercentPump = true
@ -47,8 +47,8 @@ class TempBasalDialog : DialogFragmentWithDate() {
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)
savedInstanceState.putDouble("basalpercentinput", binding.basalpercentinput.value) savedInstanceState.putDouble("basalPercentInput", binding.basalPercentInput.value)
savedInstanceState.putDouble("basalabsoluteinput", binding.basalabsoluteinput.value) savedInstanceState.putDouble("basalAbsoluteInput", binding.basalAbsoluteInput.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@ -67,11 +67,11 @@ class TempBasalDialog : DialogFragmentWithDate() {
val maxTempPercent = pumpDescription.maxTempPercent.toDouble() val maxTempPercent = pumpDescription.maxTempPercent.toDouble()
val tempPercentStep = pumpDescription.tempPercentStep.toDouble() val tempPercentStep = pumpDescription.tempPercentStep.toDouble()
binding.basalpercentinput.setParams(savedInstanceState?.getDouble("basalpercentinput") binding.basalPercentInput.setParams(savedInstanceState?.getDouble("basalPercentInput")
?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, binding.okcancel.ok) ?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, binding.okcancel.ok)
binding.basalabsoluteinput.setParams(savedInstanceState?.getDouble("basalabsoluteinput") binding.basalAbsoluteInput.setParams(savedInstanceState?.getDouble("basalAbsoluteInput")
?: profile.basal, 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok) ?: profile.getBasal(), 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok)
val tempDurationStep = pumpDescription.tempDurationStep.toDouble() val tempDurationStep = pumpDescription.tempDurationStep.toDouble()
val tempMaxDuration = pumpDescription.tempMaxDuration.toDouble() val tempMaxDuration = pumpDescription.tempMaxDuration.toDouble()
@ -97,43 +97,42 @@ class TempBasalDialog : DialogFragmentWithDate() {
if (_binding == null) return false if (_binding == null) return false
var percent = 0 var percent = 0
var absolute = 0.0 var absolute = 0.0
val durationInMinutes = binding.duration.value?.toInt() ?: return false val durationInMinutes = binding.duration.value.toInt()
val profile = profileFunction.getProfile() ?: return false val profile = profileFunction.getProfile() ?: return false
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
if (isPercentPump) { if (isPercentPump) {
val basalPercentInput = SafeParse.stringToInt(binding.basalpercentinput.text) val basalPercentInput = SafeParse.stringToInt(binding.basalPercentInput.text)
percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value() percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value()
actions.add(resourceHelper.gs(R.string.tempbasal_label) + ": $percent%") actions.add(rh.gs(R.string.tempbasal_label) + ": $percent%")
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, durationInMinutes)) actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
if (percent != basalPercentInput) actions.add(resourceHelper.gs(R.string.constraintapllied)) if (percent != basalPercentInput) actions.add(rh.gs(R.string.constraintapllied))
} else { } else {
val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalabsoluteinput.text) val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalAbsoluteInput.text)
absolute = constraintChecker.applyBasalConstraints(Constraint(basalAbsoluteInput), profile).value() absolute = constraintChecker.applyBasalConstraints(Constraint(basalAbsoluteInput), profile).value()
actions.add(resourceHelper.gs(R.string.tempbasal_label) + ": " + resourceHelper.gs(R.string.pump_basebasalrate, absolute)) actions.add(rh.gs(R.string.tempbasal_label) + ": " + rh.gs(R.string.pump_basebasalrate, absolute))
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, durationInMinutes)) actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
if (abs(absolute - basalAbsoluteInput) > 0.01) if (abs(absolute - basalAbsoluteInput) > 0.01)
actions.add(resourceHelper.gs(R.string.constraintapllied).formatColor(resourceHelper, R.color.warning)) actions.add(rh.gs(R.string.constraintapllied).formatColor(rh, R.color.warning))
} }
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.tempbasal_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.tempbasal_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
val callback: Callback = object : Callback() { val callback: Callback = object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
} }
} }
} }
if (isPercentPump) { if (isPercentPump) {
aapsLogger.debug("USER ENTRY: TEMP BASAL $percent% duration: $durationInMinutes") uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog,
commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, callback) ValueWithUnit.Percent(percent),
ValueWithUnit.Minute(durationInMinutes))
commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
} else { } else {
aapsLogger.debug("USER ENTRY: TEMP BASAL $absolute duration: $durationInMinutes") uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog,
commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, callback) ValueWithUnit.Insulin(absolute),
ValueWithUnit.Minute(durationInMinutes))
commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
} }
}) })
} }

View file

@ -9,32 +9,44 @@ import com.google.common.base.Joiner
import com.google.common.collect.Lists import com.google.common.collect.Lists
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.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogTemptargetBinding import info.nightscout.androidaps.databinding.DialogTemptargetBinding
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.LTag
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.utils.DefaultValueHelper import info.nightscout.androidaps.utils.DefaultValueHelper
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.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 TempTargetDialog : DialogFragmentWithDate() { class TempTargetDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var repository: AppRepository
lateinit var reasonList: List<String> private lateinit var reasonList: List<String>
private val disposable = CompositeDisposable()
private var _binding: DialogTemptargetBinding? = null private var _binding: DialogTemptargetBinding? = null
@ -45,7 +57,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
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)
savedInstanceState.putDouble("temptarget", binding.temptarget.value) savedInstanceState.putDouble("tempTarget", binding.temptarget.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@ -61,32 +73,32 @@ class TempTargetDialog : DialogFragmentWithDate() {
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)
if (profileFunction.getUnits() == Constants.MMOL) if (profileFunction.getUnits() == GlucoseUnit.MMOL)
binding.temptarget.setParams( binding.temptarget.setParams(
savedInstanceState?.getDouble("temptarget") savedInstanceState?.getDouble("tempTarget")
?: 8.0, ?: 8.0,
Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok) Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok)
else else
binding.temptarget.setParams( binding.temptarget.setParams(
savedInstanceState?.getDouble("temptarget") savedInstanceState?.getDouble("tempTarget")
?: 144.0, ?: 144.0,
Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, binding.okcancel.ok) Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
binding.units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) binding.units.text = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
// temp target // temp target
context?.let { context -> context?.let { context ->
if (activePlugin.activeTreatments.tempTargetFromHistory != null) if (repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing)
binding.targetCancel.visibility = View.VISIBLE binding.targetCancel.visibility = View.VISIBLE
else else
binding.targetCancel.visibility = View.GONE binding.targetCancel.visibility = View.GONE
reasonList = Lists.newArrayList( reasonList = Lists.newArrayList(
resourceHelper.gs(R.string.manual), rh.gs(R.string.manual),
resourceHelper.gs(R.string.eatingsoon), rh.gs(R.string.eatingsoon),
resourceHelper.gs(R.string.activity), rh.gs(R.string.activity),
resourceHelper.gs(R.string.hypo) rh.gs(R.string.hypo)
) )
val adapterReason = ArrayAdapter(context, R.layout.spinner_centered, reasonList) val adapterReason = ArrayAdapter(context, R.layout.spinner_centered, reasonList)
binding.reason.adapter = adapterReason binding.reason.adapter = adapterReason
@ -121,65 +133,88 @@ class TempTargetDialog : DialogFragmentWithDate() {
R.id.eating_soon -> { R.id.eating_soon -> {
binding.temptarget.value = defaultValueHelper.determineEatingSoonTT() binding.temptarget.value = defaultValueHelper.determineEatingSoonTT()
binding.duration.value = defaultValueHelper.determineEatingSoonTTDuration().toDouble() binding.duration.value = defaultValueHelper.determineEatingSoonTTDuration().toDouble()
binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.eatingsoon))) binding.reason.setSelection(reasonList.indexOf(rh.gs(R.string.eatingsoon)))
} }
R.id.activity -> { R.id.activity -> {
binding.temptarget.value = defaultValueHelper.determineActivityTT() binding.temptarget.value = defaultValueHelper.determineActivityTT()
binding.duration.value = defaultValueHelper.determineActivityTTDuration().toDouble() binding.duration.value = defaultValueHelper.determineActivityTTDuration().toDouble()
binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.activity))) binding.reason.setSelection(reasonList.indexOf(rh.gs(R.string.activity)))
} }
R.id.hypo -> { R.id.hypo -> {
binding.temptarget.value = defaultValueHelper.determineHypoTT() binding.temptarget.value = defaultValueHelper.determineHypoTT()
binding.duration.value = defaultValueHelper.determineHypoTTDuration().toDouble() binding.duration.value = defaultValueHelper.determineHypoTTDuration().toDouble()
binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.hypo))) binding.reason.setSelection(reasonList.indexOf(rh.gs(R.string.hypo)))
}
R.id.cancel -> {
binding.duration.value = 0.0
} }
} }
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
disposable.clear()
_binding = null _binding = null
} }
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
val reason = binding.reason.selectedItem?.toString() ?: return false var reason = binding.reason.selectedItem?.toString() ?: return false
val unitResId = if (profileFunction.getUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol val unitResId = if (profileFunction.getUnits() == GlucoseUnit.MGDL) R.string.mgdl else R.string.mmol
val target = binding.temptarget.value val target = binding.temptarget.value
val duration = binding.duration.value.toInt() val duration = binding.duration.value.toInt()
if (target != 0.0 && duration != 0) { if (target != 0.0 && duration != 0) {
actions.add(resourceHelper.gs(R.string.reason) + ": " + reason) actions.add(rh.gs(R.string.reason) + ": " + reason)
actions.add(resourceHelper.gs(R.string.target_label) + ": " + Profile.toCurrentUnitsString(profileFunction, target) + " " + resourceHelper.gs(unitResId)) actions.add(rh.gs(R.string.target_label) + ": " + Profile.toCurrentUnitsString(profileFunction, target) + " " + rh.gs(unitResId))
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, duration)) actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, duration))
} else { } else {
actions.add(resourceHelper.gs(R.string.stoptemptarget)) actions.add(rh.gs(R.string.stoptemptarget))
reason = rh.gs(R.string.stoptemptarget)
} }
if (eventTimeChanged) if (eventTimeChanged)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) actions.add(rh.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: TEMP TARGET $target duration: $duration") val units = profileFunction.getUnits()
if (target == 0.0 || duration == 0) { when(reason) {
val tempTarget = TempTarget() rh.gs(R.string.eatingsoon) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
.date(eventTime) rh.gs(R.string.activity) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
.duration(0) rh.gs(R.string.hypo) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
.low(0.0).high(0.0) rh.gs(R.string.manual) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.CUSTOM), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
.source(Source.USER) rh.gs(R.string.stoptemptarget) -> uel.log(Action.CANCEL_TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged })
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
} else {
val tempTarget = TempTarget()
.date(eventTime)
.duration(duration)
.reason(reason)
.source(Source.USER)
.low(Profile.toMgdl(target, profileFunction.getUnits()))
.high(Profile.toMgdl(target, profileFunction.getUnits()))
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
} }
if (target == 0.0 || duration == 0) {
disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(eventTime))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
})
} else {
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = eventTime,
duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
reason = when (reason) {
rh.gs(R.string.eatingsoon) -> TemporaryTarget.Reason.EATING_SOON
rh.gs(R.string.activity) -> TemporaryTarget.Reason.ACTIVITY
rh.gs(R.string.hypo) -> TemporaryTarget.Reason.HYPOGLYCEMIA
else -> TemporaryTarget.Reason.CUSTOM
},
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)
})
}
if (duration == 10) sp.putBoolean(R.string.key_objectiveusetemptarget, true) if (duration == 10) sp.putBoolean(R.string.key_objectiveusetemptarget, true)
}) })
} }

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -9,25 +8,31 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.google.common.base.Joiner import com.google.common.base.Joiner
import info.nightscout.androidaps.Config import info.nightscout.androidaps.interfaces.Config
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.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.DialogTreatmentBinding import info.nightscout.androidaps.databinding.DialogTreatmentBinding
import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.interfaces.CommandQueue
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.SafeParse import info.nightscout.shared.SafeParse
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.utils.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
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 javax.inject.Inject import javax.inject.Inject
@ -36,11 +41,15 @@ import kotlin.math.abs
class TreatmentDialog : DialogFragmentWithDate() { class TreatmentDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher { private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {} override fun afterTextChanged(s: Editable) {}
@ -55,11 +64,11 @@ class TreatmentDialog : DialogFragmentWithDate() {
val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
if (SafeParse.stringToInt(binding.carbs.text) > maxCarbs) { if (SafeParse.stringToInt(binding.carbs.text) > maxCarbs) {
binding.carbs.value = 0.0 binding.carbs.value = 0.0
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.carbsconstraintapplied)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.carbsconstraintapplied))
} }
if (SafeParse.stringToDouble(binding.insulin.text) > maxInsulin) { if (SafeParse.stringToDouble(binding.insulin.text) > maxInsulin) {
binding.insulin.value = 0.0 binding.insulin.value = 0.0
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.bolusconstraintapplied)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.bolusconstraintapplied))
} }
} }
@ -96,6 +105,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) ?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher)
binding.insulin.setParams(savedInstanceState?.getDouble("insulin") binding.insulin.setParams(savedInstanceState?.getDouble("insulin")
?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher) ?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher)
binding.recordOnlyLayout.visibility = View.GONE
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -106,7 +116,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val pumpDescription = activePlugin.activePump.pumpDescription val pumpDescription = activePlugin.activePump.pumpDescription
val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false) val insulin = SafeParse.stringToDouble(binding.insulin.text)
val carbs = SafeParse.stringToInt(binding.carbs.text) val carbs = SafeParse.stringToInt(binding.carbs.text)
val recordOnlyChecked = binding.recordOnly.isChecked val recordOnlyChecked = binding.recordOnly.isChecked
val actions: LinkedList<String?> = LinkedList() val actions: LinkedList<String?> = LinkedList()
@ -114,48 +124,70 @@ class TreatmentDialog : DialogFragmentWithDate() {
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
actions.add(resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, resourceHelper).formatColor(resourceHelper, R.color.bolus)) actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(rh, R.color.bolus))
if (recordOnlyChecked) if (recordOnlyChecked)
actions.add(resourceHelper.gs(R.string.bolusrecordedonly).formatColor(resourceHelper, R.color.warning)) actions.add(rh.gs(R.string.bolusrecordedonly).formatColor(rh, R.color.warning))
if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
actions.add(resourceHelper.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(resourceHelper, R.color.warning)) actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(rh, R.color.warning))
} }
if (carbsAfterConstraints > 0) { if (carbsAfterConstraints > 0) {
actions.add(resourceHelper.gs(R.string.carbs) + ": " + resourceHelper.gs(R.string.format_carbs, carbsAfterConstraints).formatColor(resourceHelper, R.color.carbs)) actions.add(rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carbsAfterConstraints).formatColor(rh, R.color.carbs))
if (carbsAfterConstraints != carbs) if (carbsAfterConstraints != carbs)
actions.add(resourceHelper.gs(R.string.carbsconstraintapplied).formatColor(resourceHelper, R.color.warning)) actions.add(rh.gs(R.string.carbsconstraintapplied).formatColor(rh, R.color.warning))
} }
if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) { if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: BOLUS insulin $insulin carbs: $carbs") val action = when {
insulinAfterConstraints.equals(0.0) -> Action.CARBS
carbsAfterConstraints == 0 -> Action.BOLUS
else -> Action.TREATMENT
}
val detailedBolusInfo = DetailedBolusInfo() val detailedBolusInfo = DetailedBolusInfo()
if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CARBS_CORRECTION
if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
detailedBolusInfo.insulin = insulinAfterConstraints detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.carbs = carbsAfterConstraints.toDouble() detailedBolusInfo.carbs = carbsAfterConstraints.toDouble()
detailedBolusInfo.context = context detailedBolusInfo.context = context
detailedBolusInfo.source = Source.USER if (recordOnlyChecked) {
if (!(recordOnlyChecked && (detailedBolusInfo.insulin > 0 || pumpDescription.storesCarbInfo))) { uel.log(action, Sources.TreatmentDialog, if (insulinAfterConstraints != 0.0) rh.gs(R.string.record) else "",
ValueWithUnit.Timestamp(detailedBolusInfo.timestamp).takeIf { eventTimeChanged },
ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.record)).takeIf { insulinAfterConstraints != 0.0 },
ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 },
ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbsAfterConstraints != 0 })
if (detailedBolusInfo.insulin > 0)
disposable += repository.runTransactionForResult(detailedBolusInfo.insertBolusTransaction())
.subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) }
)
if (detailedBolusInfo.carbs > 0)
disposable += repository.runTransactionForResult(detailedBolusInfo.insertCarbsTransaction())
.subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) }
)
} else {
if (detailedBolusInfo.insulin > 0) {
uel.log(action, Sources.TreatmentDialog,
ValueWithUnit.Insulin(insulinAfterConstraints),
ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbsAfterConstraints != 0 })
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
} }
} }
}) })
} else } else
activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false) uel.log(action, Sources.TreatmentDialog,
ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbs != 0 })
}
}) })
} }
} else } else
activity?.let { activity -> activity?.let { activity ->
OKDialog.show(activity, resourceHelper.gs(R.string.overview_treatment_label), resourceHelper.gs(R.string.no_action_selected)) OKDialog.show(activity, rh.gs(R.string.overview_treatment_label), rh.gs(R.string.no_action_selected))
} }
return true return true
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -13,32 +14,29 @@ import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.CompoundButton import android.widget.CompoundButton
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerDialogFragment import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.databinding.DialogWizardBinding import info.nightscout.androidaps.databinding.DialogWizardBinding
import info.nightscout.androidaps.db.BgReading import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.logging.LTag
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.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.extensions.toVisibility
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.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.BolusWizard
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
@ -47,19 +45,27 @@ import kotlin.math.abs
class WizardDialog : DaggerDialogFragment() { class WizardDialog : DaggerDialogFragment() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var mainApp: MainApp @Inject lateinit var ctx: Context
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBus
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin @Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil
private var wizard: BolusWizard? = null private var wizard: BolusWizard? = null
private var calculatedPercentage = 100.0
private var calculatedCorrection = 0.0
private var correctionPercent = false
private var carbsPassedIntoWizard = 0.0
private var notesPassedIntoWizard = ""
//one shot guards //one shot guards
private var okClicked: Boolean = false private var okClicked: Boolean = false
@ -82,6 +88,7 @@ class WizardDialog : DaggerDialogFragment() {
} }
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
private var bolusStep = 0.0
private var _binding: DialogWizardBinding? = null private var _binding: DialogWizardBinding? = null
@ -92,6 +99,7 @@ class WizardDialog : DaggerDialogFragment() {
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)
aapsLogger.debug(LTag.APS, "Dialog opened: ${this.javaClass.name}")
} }
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -104,6 +112,11 @@ class WizardDialog : DaggerDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View { savedInstanceState: Bundle?): View {
this.arguments?.let { bundle ->
carbsPassedIntoWizard = bundle.getInt("carbs_input").toDouble()
notesPassedIntoWizard = bundle.getString("notes_input").toString()
}
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true isCancelable = true
@ -116,13 +129,14 @@ class WizardDialog : DaggerDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
loadCheckedStates() loadCheckedStates()
processCobCheckBox() processCobCheckBox()
binding.sbcheckbox.visibility = sp.getBoolean(R.string.key_usesuperbolus, false).toVisibility() binding.sbCheckbox.visibility = sp.getBoolean(R.string.key_usesuperbolus, false).toVisibility()
binding.notesLayout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility() binding.notesLayout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility()
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value() val maxCarbs = constraintChecker.getMaxCarbsAllowed().value()
val maxCorrection = constraintChecker.getMaxBolusAllowed().value() val maxCorrection = constraintChecker.getMaxBolusAllowed().value()
bolusStep = activePlugin.activePump.pumpDescription.bolusStep
if (profileFunction.getUnits() == Constants.MGDL) if (profileFunction.getUnits() == GlucoseUnit.MGDL)
binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input") binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input")
?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher) ?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher)
else else
@ -130,14 +144,23 @@ class WizardDialog : DaggerDialogFragment() {
?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.ok, textWatcher) ?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.ok, textWatcher)
binding.carbsInput.setParams(savedInstanceState?.getDouble("carbs_input") binding.carbsInput.setParams(savedInstanceState?.getDouble("carbs_input")
?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.ok, textWatcher) ?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.ok, textWatcher)
val bolusStep = activePlugin.activePump.pumpDescription.bolusStep
binding.correctionInput.setParams(savedInstanceState?.getDouble("correction_input") if (correctionPercent) {
calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble()
binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 1.0, DecimalFormat("0"), false, binding.ok, textWatcher)
binding.correctionInput.value = calculatedPercentage
binding.correctionUnit.text = "%"
} else {
binding.correctionInput.setParams(
savedInstanceState?.getDouble("correction_input")
?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.ok, textWatcher) ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.ok, textWatcher)
binding.correctionUnit.text = rh.gs(R.string.insulin_unit_shortname)
}
binding.carbTimeInput.setParams(savedInstanceState?.getDouble("carb_time_input") binding.carbTimeInput.setParams(savedInstanceState?.getDouble("carb_time_input")
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher) ?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher)
initDialog() initDialog()
calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble()
binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100)) binding.percentUsed.text = rh.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100))
// ok button // ok button
binding.ok.setOnClickListener { binding.ok.setOnClickListener {
if (okClicked) { if (okClicked) {
@ -148,35 +171,59 @@ class WizardDialog : DaggerDialogFragment() {
context?.let { context -> context?.let { context ->
wizard?.confirmAndExecute(context) wizard?.confirmAndExecute(context)
} }
aapsLogger.debug(LTag.APS, "Dialog ok pressed: ${this.javaClass.name}")
} }
dismiss() dismiss()
} }
binding.bgEnabledIcon.setOnClickListener { binding.bgCheckbox.isChecked = !binding.bgCheckbox.isChecked }
binding.trendEnabledIcon.setOnClickListener { binding.bgTrendCheckbox.isChecked = !binding.bgTrendCheckbox.isChecked }
binding.cobEnabledIcon.setOnClickListener { binding.cobCheckbox.isChecked = !binding.cobCheckbox.isChecked; processCobCheckBox(); }
binding.iobEnabledIcon.setOnClickListener { if (!binding.cobCheckbox.isChecked) binding.iobCheckbox.isChecked = !binding.iobCheckbox.isChecked }
// cancel button // cancel button
binding.cancel.setOnClickListener { dismiss() } binding.cancel.setOnClickListener {
aapsLogger.debug(LTag.APS, "Dialog canceled: ${this.javaClass.name}")
dismiss()
}
// checkboxes // checkboxes
binding.bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.bgCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.ttCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.cobCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.iobCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.bgTrendCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.sbCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
val showCalc = sp.getBoolean(R.string.key_wizard_calculation_visible, false) val showCalc = sp.getBoolean(R.string.key_wizard_calculation_visible, false)
binding.delimiter.visibility = showCalc.toVisibility() binding.delimiter.visibility = showCalc.toVisibility()
binding.resulttable.visibility = showCalc.toVisibility() binding.result.visibility = showCalc.toVisibility()
binding.calculationcheckbox.isChecked = showCalc binding.calculationCheckbox.isChecked = showCalc
binding.calculationcheckbox.setOnCheckedChangeListener { _, isChecked -> binding.calculationCheckbox.setOnCheckedChangeListener { _, isChecked ->
run { run {
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_calculation_visible), isChecked) sp.putBoolean(rh.gs(R.string.key_wizard_calculation_visible), isChecked)
binding.delimiter.visibility = isChecked.toVisibility() binding.delimiter.visibility = isChecked.toVisibility()
binding.resulttable.visibility = isChecked.toVisibility() binding.result.visibility = isChecked.toVisibility()
processEnabledIcons()
}
}
processEnabledIcons()
binding.correctionPercent.setOnCheckedChangeListener {_, isChecked ->
run {
sp.putBoolean(rh.gs(R.string.key_wizard_correction_percent), isChecked)
binding.correctionUnit.text = if (isChecked) "%" else rh.gs(R.string.insulin_unit_shortname)
correctionPercent = binding.correctionPercent.isChecked
if (correctionPercent)
binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 1.0, DecimalFormat("0"), false, binding.ok, textWatcher)
else
binding.correctionInput.setParams(savedInstanceState?.getDouble("correction_input")
?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.ok, textWatcher)
binding.correctionInput.value = if (correctionPercent) calculatedPercentage else Round.roundTo(calculatedCorrection, bolusStep)
} }
} }
// profile spinner // profile spinner
binding.profile.onItemSelectedListener = object : OnItemSelectedListener { binding.profile.onItemSelectedListener = object : OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) { override fun onNothingSelected(parent: AdapterView<*>?) {
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofileselected)) ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.noprofileset))
binding.ok.visibility = View.GONE binding.ok.visibility = View.GONE
} }
@ -188,10 +235,10 @@ class WizardDialog : DaggerDialogFragment() {
// bus // bus
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
activity?.runOnUiThread { calculateInsulin() } activity?.runOnUiThread { calculateInsulin() }
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
) )
} }
@ -204,176 +251,205 @@ class WizardDialog : DaggerDialogFragment() {
private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) { private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) {
saveCheckedStates() saveCheckedStates()
binding.ttcheckbox.isEnabled = binding.bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null binding.ttCheckbox.isEnabled = binding.bgCheckbox.isChecked && repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
if (buttonView.id == binding.cobcheckbox.id) if (buttonView.id == binding.cobCheckbox.id)
processCobCheckBox() processCobCheckBox()
processEnabledIcons()
calculateInsulin() calculateInsulin()
} }
private fun processCobCheckBox() { private fun processCobCheckBox() {
if (binding.cobcheckbox.isChecked) { if (binding.cobCheckbox.isChecked) {
binding.bolusiobcheckbox.isEnabled = false binding.iobCheckbox.isEnabled = false
binding.basaliobcheckbox.isEnabled = false binding.iobCheckbox.isChecked = true
binding.bolusiobcheckbox.isChecked = true
binding.basaliobcheckbox.isChecked = true
} else { } else {
binding.bolusiobcheckbox.isEnabled = true binding.iobCheckbox.isEnabled = true
binding.basaliobcheckbox.isEnabled = true
} }
} }
private fun processEnabledIcons() {
binding.bgEnabledIcon.alpha = if (binding.bgCheckbox.isChecked) 1.0f else 0.2f
binding.trendEnabledIcon.alpha = if (binding.bgTrendCheckbox.isChecked) 1.0f else 0.2f
binding.iobEnabledIcon.alpha = if (binding.iobCheckbox.isChecked) 1.0f else 0.2f
binding.cobEnabledIcon.alpha = if (binding.cobCheckbox.isChecked) 1.0f else 0.2f
binding.bgEnabledIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility()
binding.trendEnabledIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility()
binding.iobEnabledIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility()
binding.cobEnabledIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility()
}
private fun saveCheckedStates() { private fun saveCheckedStates() {
sp.putBoolean(R.string.key_wizard_include_cob, binding.cobcheckbox.isChecked) sp.putBoolean(R.string.key_wizard_include_cob, binding.cobCheckbox.isChecked)
sp.putBoolean(R.string.key_wizard_include_trend_bg, binding.bgtrendcheckbox.isChecked) sp.putBoolean(R.string.key_wizard_include_trend_bg, binding.bgTrendCheckbox.isChecked)
sp.putBoolean(R.string.key_wizard_correction_percent, binding.correctionPercent.isChecked)
} }
private fun loadCheckedStates() { private fun loadCheckedStates() {
binding.bgtrendcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false) binding.bgTrendCheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false)
binding.cobcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_cob, false) binding.cobCheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_cob, false)
correctionPercent = sp.getBoolean(R.string.key_wizard_correction_percent,false)
binding.correctionPercent.isChecked = correctionPercent
} }
private fun valueToUnitsToString(value: Double, units: String): String =
if (units == Constants.MGDL) DecimalFormatter.to0Decimal(value)
else DecimalFormatter.to1Decimal(value * Constants.MGDL_TO_MMOLL)
private fun initDialog() { private fun initDialog() {
if(carbsPassedIntoWizard != 0.0) {
binding.carbsInput.value = carbsPassedIntoWizard
}
if(notesPassedIntoWizard.isNotBlank()) {
binding.notes.setText(notesPassedIntoWizard)
}
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileInterface.profile val profileStore = activePlugin.activeProfileSource.profile
if (profile == null || profileStore == null) { if (profile == null || profileStore == null) {
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofile)) ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.noprofile))
dismiss() dismiss()
return return
} }
val profileList: ArrayList<CharSequence> val profileList: ArrayList<CharSequence> = profileStore.getProfileList()
profileList = profileStore.getProfileList() profileList.add(0, rh.gs(R.string.active))
profileList.add(0, resourceHelper.gs(R.string.active))
context?.let { context -> context?.let { context ->
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
binding.profile.adapter = adapter binding.profile.adapter = adapter
} ?: return } ?: return
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
binding.bgunits.text = units binding.bgUnits.text = units.asText
if (units == Constants.MGDL) binding.bgInput.step = if (units == GlucoseUnit.MGDL) 1.0 else 0.1
binding.bgInput.setStep(1.0)
else
binding.bgInput.setStep(0.1)
// Set BG if not old // Set BG if not old
val lastBg = iobCobCalculatorPlugin.actualBg() binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units) ?: 0.0
binding.ttCheckbox.isEnabled = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
if (lastBg != null) {
binding.bgInput.value = lastBg.valueToUnits(units)
} else {
binding.bgInput.value = 0.0
}
binding.ttcheckbox.isEnabled = treatmentsPlugin.tempTargetFromHistory != null
// IOB calculation // IOB calculation
treatmentsPlugin.updateTotalIOBTreatments() val bolusIob = iobCobCalculator.calculateIobFromBolus().round()
val bolusIob = treatmentsPlugin.lastCalculationTreatments.round() val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
treatmentsPlugin.updateTotalIOBTempBasals()
val basalIob = treatmentsPlugin.lastCalculationTempBasals.round()
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob) binding.iobInsulin.text = rh.gs(R.string.formatinsulinunits, -bolusIob.iob - basalIob.basaliob)
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob)
calculateInsulin() calculateInsulin()
binding.percentUsed.visibility = (sp.getInt(R.string.key_boluswizard_percentage, 100) != 100).toVisibility() binding.percentUsed.visibility = (sp.getInt(R.string.key_boluswizard_percentage, 100) != 100 || correctionPercent).toVisibility()
} }
private fun calculateInsulin() { private fun calculateInsulin() {
val profileStore = activePlugin.activeProfileInterface.profile val profileStore = activePlugin.activeProfileSource.profile
if (binding.profile.selectedItem == null || profileStore == null) if (binding.profile.selectedItem == null || profileStore == null)
return // not initialized yet return // not initialized yet
var profileName = binding.profile.selectedItem.toString() var profileName = binding.profile.selectedItem.toString()
val specificProfile: Profile? val specificProfile: Profile?
if (profileName == resourceHelper.gs(R.string.active)) { if (profileName == rh.gs(R.string.active)) {
specificProfile = profileFunction.getProfile() specificProfile = profileFunction.getProfile()
profileName = profileFunction.getProfileName() profileName = profileFunction.getProfileName()
} else } else
specificProfile = profileStore.getSpecificProfile(profileName) specificProfile = profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) }
if (specificProfile == null) return if (specificProfile == null) return
// Entered values // Entered values
val usePercentage = binding.correctionPercent.isChecked
var bg = SafeParse.stringToDouble(binding.bgInput.text) var bg = SafeParse.stringToDouble(binding.bgInput.text)
val carbs = SafeParse.stringToInt(binding.carbsInput.text) val carbs = SafeParse.stringToInt(binding.carbsInput.text)
val correction = SafeParse.stringToDouble(binding.correctionInput.text) val correction = if (!usePercentage) {
if (Round.roundTo(calculatedCorrection, bolusStep) == SafeParse.stringToDouble(binding.correctionInput.text))
calculatedCorrection
else
SafeParse.stringToDouble(binding.correctionInput.text)
} else
0.0
val percentageCorrection = if (usePercentage) {
if (Round.roundTo(calculatedPercentage,1.0) == SafeParse.stringToDouble(binding.correctionInput.text))
calculatedPercentage
else
SafeParse.stringToDouble(binding.correctionInput.text)
} else
sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble()
val carbsAfterConstraint = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() val carbsAfterConstraint = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
if (abs(carbs - carbsAfterConstraint) > 0.01) { if (abs(carbs - carbsAfterConstraint) > 0.01) {
binding.carbsInput.value = 0.0 binding.carbsInput.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied)) ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.carbsconstraintapplied))
return return
} }
bg = if (binding.bgcheckbox.isChecked) bg else 0.0 bg = if (binding.bgCheckbox.isChecked) bg else 0.0
val tempTarget = if (binding.ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
val tempTarget = if (binding.ttCheckbox.isChecked && dbRecord is ValueWrapper.Existing) dbRecord.value else null
// COB // COB
var cob = 0.0 var cob = 0.0
if (binding.cobcheckbox.isChecked) { if (binding.cobCheckbox.isChecked) {
val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard COB") val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard COB")
cobInfo.displayCob?.let { cob = it } cobInfo.displayCob?.let { cob = it }
} }
val carbTime = SafeParse.stringToInt(binding.carbTimeInput.text) val carbTime = SafeParse.stringToInt(binding.carbTimeInput.text)
wizard = BolusWizard(mainApp).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction, wizard = BolusWizard(injector).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction, sp.getInt(R.string.key_boluswizard_percentage, 100),
sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(), binding.bgCheckbox.isChecked,
binding.bgcheckbox.isChecked, binding.cobCheckbox.isChecked,
binding.cobcheckbox.isChecked, binding.iobCheckbox.isChecked,
binding.bolusiobcheckbox.isChecked, binding.iobCheckbox.isChecked,
binding.basaliobcheckbox.isChecked, binding.sbCheckbox.isChecked,
binding.sbcheckbox.isChecked, binding.ttCheckbox.isChecked,
binding.ttcheckbox.isChecked, binding.bgTrendCheckbox.isChecked,
binding.bgtrendcheckbox.isChecked,
binding.alarm.isChecked, binding.alarm.isChecked,
binding.notes.text.toString(), carbTime) binding.notes.text.toString(),
carbTime,
usePercentage = usePercentage,
totalPercentage = percentageCorrection
)
wizard?.let { wizard -> wizard?.let { wizard ->
binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, profileFunction.getUnits())).valueToUnitsToString(profileFunction.getUnits()), wizard.sens) binding.bg.text = String.format(rh.gs(R.string.format_bg_isf), valueToUnitsToString(Profile.toMgdl(bg, profileFunction.getUnits()), profileFunction.getUnits().asText), wizard.sens)
binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG) binding.bgInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromBG)
binding.carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic) binding.carbs.text = String.format(rh.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic)
binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs) binding.carbsInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs)
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB) binding.iobInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB + wizard.insulinFromBasalIOB)
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBasalIOB)
binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCorrection) binding.correctionInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCorrection)
// Superbolus // Superbolus
binding.sb.text = if (binding.sbcheckbox.isChecked) resourceHelper.gs(R.string.twohours) else "" binding.sb.text = if (binding.sbCheckbox.isChecked) rh.gs(R.string.twohours) else ""
binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus) binding.sbInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus)
// Trend // Trend
if (binding.bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) { if (binding.bgTrendCheckbox.isChecked && wizard.glucoseStatus != null) {
binding.bgtrend.text = ((if (wizard.trend > 0) "+" else "") binding.bgTrend.text = ((if (wizard.trend > 0) "+" else "")
+ Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, profileFunction.getUnits()) + Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, profileFunction.getUnits())
+ " " + profileFunction.getUnits()) + " " + profileFunction.getUnits())
} else { } else {
binding.bgtrend.text = "" binding.bgTrend.text = ""
} }
binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromTrend) binding.bgTrendInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromTrend)
// COB // COB
if (binding.cobcheckbox.isChecked) { if (binding.cobCheckbox.isChecked) {
binding.cob.text = String.format(resourceHelper.gs(R.string.format_cob_ic), cob, wizard.ic) binding.cob.text = String.format(rh.gs(R.string.format_cob_ic), cob, wizard.ic)
binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCOB) binding.cobInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCOB)
} else { } else {
binding.cob.text = "" binding.cob.text = ""
binding.cobinsulin.text = "" binding.cobInsulin.text = ""
} }
if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) { if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) {
val insulinText = if (wizard.calculatedTotalInsulin > 0.0) resourceHelper.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin) else "" val insulinText = if (wizard.calculatedTotalInsulin > 0.0) rh.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin).formatColor(rh, R.color.bolus) else ""
val carbsText = if (carbsAfterConstraint > 0.0) resourceHelper.gs(R.string.format_carbs, carbsAfterConstraint) else "" val carbsText = if (carbsAfterConstraint > 0.0) rh.gs(R.string.format_carbs, carbsAfterConstraint).formatColor(rh, R.color.carbs) else ""
binding.total.text = resourceHelper.gs(R.string.result_insulin_carbs, insulinText, carbsText) binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.result_insulin_carbs, insulinText, carbsText))
binding.ok.visibility = View.VISIBLE binding.ok.visibility = View.VISIBLE
} else { } else {
binding.total.text = resourceHelper.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt()) binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt()).formatColor(rh, R.color.carbs))
binding.ok.visibility = View.INVISIBLE binding.ok.visibility = View.INVISIBLE
} }
binding.percentUsed.text = rh.gs(R.string.format_percent, wizard.percentageCorrection)
calculatedPercentage = wizard.calculatedPercentage
calculatedCorrection = wizard.calculatedCorrection
} }
} }
@ -385,7 +461,7 @@ class WizardDialog : DaggerDialogFragment() {
it.commitAllowingStateLoss() it.commitAllowingStateLoss()
} }
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
aapsLogger.debug(e.localizedMessage) aapsLogger.debug(e.localizedMessage ?: "")
} }
} }
} }

View file

@ -9,23 +9,28 @@ import android.view.WindowManager
import dagger.android.support.DaggerDialogFragment import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.BolusCalculatorResult
import info.nightscout.androidaps.databinding.DialogWizardinfoBinding import info.nightscout.androidaps.databinding.DialogWizardinfoBinding
import info.nightscout.androidaps.extensions.bolusCalculatorResultFromJson
import info.nightscout.androidaps.extensions.toJson
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONObject import org.json.JSONObject
import javax.inject.Inject import javax.inject.Inject
class WizardInfoDialog : DaggerDialogFragment() { class WizardInfoDialog : DaggerDialogFragment() {
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var dateUtil: DateUtil
private var json: JSONObject? = null private lateinit var data: BolusCalculatorResult
fun setData(json: JSONObject) { fun setData(bolusCalculatorResult: BolusCalculatorResult) {
this.json = json this.data = bolusCalculatorResult
} }
private var _binding: DialogWizardinfoBinding? = null private var _binding: DialogWizardinfoBinding? = null
@ -34,8 +39,7 @@ class WizardInfoDialog : DaggerDialogFragment() {
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
savedInstanceState: Bundle?): View {
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true isCancelable = true
@ -44,49 +48,62 @@ class WizardInfoDialog : DaggerDialogFragment() {
return binding.root return binding.root
} }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedInstanceState?.getString("data")?.let { str ->
val json = JSONObject(str).apply {
put("mills", dateUtil.now()) // fake NS response
}
data = bolusCalculatorResultFromJson(json) ?: return
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("data", data.toJson(true, dateUtil).toString())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.close.setOnClickListener { dismiss() } binding.close.setOnClickListener { dismiss() }
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val bgString = val bgString = Profile.toUnitsString(data.glucoseValue, data.glucoseValue * Constants.MGDL_TO_MMOLL, units)
if (units == Constants.MGDL) DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "bg"))
else DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg"))
// BG // BG
binding.bg.text = resourceHelper.gs(R.string.format_bg_isf, bgString, JsonHelper.safeGetDouble(json, "isf")) binding.bg.text = rh.gs(R.string.format_bg_isf, bgString, data.isf)
binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinbg")) binding.bgInsulin.text = rh.gs(R.string.formatinsulinunits, data.glucoseInsulin)
binding.bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused") binding.bgCheckbox.isChecked = data.wasGlucoseUsed
binding.ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused") binding.ttCheckbox.isChecked = data.wasTempTargetUsed
// Trend // Trend
binding.bgtrend.text = JsonHelper.safeGetString(json, "trend") binding.bgTrend.text = DecimalFormatter.to1Decimal(data.glucoseTrend)
binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulintrend")) binding.bgTrendInsulin.text = rh.gs(R.string.formatinsulinunits, data.trendInsulin)
binding.bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused") binding.bgTrendCheckbox.isChecked = data.wasTrendUsed
// COB // COB
binding.cob.text = resourceHelper.gs(R.string.format_cob_ic, JsonHelper.safeGetDouble(json, "cob"), JsonHelper.safeGetDouble(json, "ic")) binding.cob.text = rh.gs(R.string.format_cob_ic, data.cob, data.ic)
binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincob")) binding.cobInsulin.text = rh.gs(R.string.formatinsulinunits, data.cobInsulin)
binding.cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused") binding.cobCheckbox.isChecked = data.wasCOBUsed
// Bolus IOB // Bolus IOB
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "bolusiob")) binding.bolusIobInsulin.text = rh.gs(R.string.formatinsulinunits, data.bolusIOB)
binding.bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused") binding.bolusIobCheckbox.isChecked = data.wasBolusIOBUsed
// Basal IOB // Basal IOB
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "basaliob")) binding.basalIobInsulin.text = rh.gs(R.string.formatinsulinunits, data.basalIOB)
binding.basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused") binding.basalIobCheckbox.isChecked = data.wasBasalIOBUsed
// Superbolus // Superbolus
binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinsuperbolus")) binding.sbinsulin.text = rh.gs(R.string.formatinsulinunits, data.superbolusInsulin)
binding.sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused") binding.sbCheckbox.isChecked = data.wasSuperbolusUsed
// Carbs // Carbs
binding.carbs.text = resourceHelper.gs(R.string.format_carbs_ic, JsonHelper.safeGetDouble(json, "carbs"), JsonHelper.safeGetDouble(json, "ic")) binding.carbs.text = rh.gs(R.string.format_carbs_ic, data.carbs, data.ic)
binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincarbs")) binding.carbsinsulin.text = rh.gs(R.string.formatinsulinunits, data.carbsInsulin)
// Correction // Correction
binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "othercorrection")) binding.correctioninsulin.text = rh.gs(R.string.formatinsulinunits, data.otherCorrection)
// Profile // Profile
binding.profile.text = JsonHelper.safeGetString(json, "profile") binding.profile.text = data.profileName
// Notes // Notes
binding.notes.text = JsonHelper.safeGetString(json, "notes") binding.notes.text = data.note
// Percentage // Percentage
binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, (JsonHelper.safeGetInt(json, "percentageCorrection", 100))) binding.percentUsed.text = rh.gs(R.string.format_percent, data.percentageCorrection)
// Total // Total
binding.totalinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulin")) binding.totalinsulin.text = rh.gs(R.string.formatinsulinunits, data.totalInsulin)
} }
override fun onStart() { override fun onStart() {

View file

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

View file

@ -1,5 +1,5 @@
package info.nightscout.androidaps.events package info.nightscout.androidaps.events
import info.nightscout.androidaps.db.BgReading import info.nightscout.androidaps.database.entities.GlucoseValue
class EventNewBG(val bgReading: BgReading?) : EventLoop() class EventNewBG(val glucoseValue: GlucoseValue?) : EventLoop()

View file

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

View file

@ -1,20 +0,0 @@
package info.nightscout.androidaps.events
import org.json.JSONArray
/**
* Event which is published with data fetched from NightScout specific for the
* Food-class.
*
* Payload is the from NS retrieved JSON-String which should be handled by all
* subscriber.
*/
class EventNsFood(val mode: Int, val foods: JSONArray) : Event() {
companion object {
val ADD = 0
val UPDATE = 1
val REMOVE = 2
}
}

View file

@ -1,21 +0,0 @@
package info.nightscout.androidaps.events
import org.json.JSONObject
/**
* Event which is published with data fetched from NightScout specific for the
* Treatment-class.
*
*
* Payload is the from NS retrieved JSON-String which should be handled by all
* subscriber.
*/
class EventNsTreatment(val mode: Int, val payload: JSONObject) : Event() {
companion object {
val ADD = 0
val UPDATE = 1
val REMOVE = 2
}
}

View file

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

View file

@ -1,3 +0,0 @@
package info.nightscout.androidaps.events
class EventReloadTreatmentData(var next: Event) : Event()

View file

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

View file

@ -1,5 +1,3 @@
package info.nightscout.androidaps.events package info.nightscout.androidaps.events
import info.nightscout.androidaps.db.Treatment class EventTreatmentChange : EventLoop()
class EventTreatmentChange(val treatment: Treatment?) : EventLoop()

View file

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

View file

@ -1,376 +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.events.EventCustomCalculationFinished
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.ActivePluginProvider
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.EventAutosensBgLoaded
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
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.extensions.toVisibility
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_historybrowse.*
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 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 treatmentsPluginHistory: TreatmentsPluginHistory
@Inject lateinit var activePlugin: ActivePluginProvider
@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()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_historybrowse)
historybrowse_left.setOnClickListener {
start -= T.hours(rangeToDisplay.toLong()).msecs()
runCalculation("onClickLeft")
}
historybrowse_right.setOnClickListener {
start += T.hours(rangeToDisplay.toLong()).msecs()
runCalculation("onClickRight")
}
historybrowse_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")
}
historybrowse_zoom.setOnClickListener {
rangeToDisplay += 6
rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay
updateGUI("rangeChange", false)
}
historybrowse_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
historybrowse_date?.text = dateUtil.dateAndTimeString(start)
runCalculation("onClickDate")
}
historybrowse_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
historybrowse_bggraph?.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
historybrowse_bggraph?.gridLabelRenderer?.reloadStyles()
historybrowse_bggraph?.gridLabelRenderer?.labelVerticalWidth = axisWidth
overviewMenus.setupChartMenu(overview_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")
}
public override fun onResume() {
super.onResume()
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(Schedulers.io())
.subscribe({
// catch only events from iobCobCalculatorPluginHistory
if (it.cause is EventCustomCalculationFinished) {
updateGUI("EventAutosensCalculationFinished", bgOnly = false)
}
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventAutosensBgLoaded::class.java)
.observeOn(Schedulers.io())
.subscribe({
// catch only events from iobCobCalculatorPluginHistory
if (it.cause is EventCustomCalculationFinished) {
updateGUI("EventAutosensCalculationFinished", bgOnly = true)
}
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventIobCalculationProgress::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ overview_iobcalculationprogess?.text = it.progress }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventRefreshOverview::class.java)
.observeOn(AndroidSchedulers.mainThread())
.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()
history_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)
history_iobgraph.addView(relativeLayout)
secondaryGraphs.add(graph)
}
}
}
}
private fun runCalculation(from: String) {
lifecycleScope.launch(Dispatchers.Default) {
treatmentsPluginHistory.initializeData(start - T.hours(8).msecs())
val end = start + T.hours(rangeToDisplay.toLong()).msecs()
iobCobCalculatorPluginHistory.stopCalculation(from)
iobCobCalculatorPluginHistory.clearCache()
iobCobCalculatorPluginHistory.runCalculation(from, end, true, false, eventCustomCalculationFinished)
}
}
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) {
historybrowse_noprofile?.visibility = (profile == null).toVisibility()
profile ?: return@launch
historybrowse_bggraph ?: return@launch
historybrowse_date?.text = dateUtil.dateAndTimeString(start)
historybrowse_zoom?.text = rangeToDisplay.toString()
val graphData = GraphData(injector, historybrowse_bggraph, iobCobCalculatorPluginHistory, treatmentsPluginHistory)
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, lowLine, highLine, null)
// set manual x bounds to have nice steps
graphData.formatAxis(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], iobCobCalculatorPluginHistory, treatmentsPluginHistory)
var useIobForScale = false
var useCobForScale = false
var useDevForScale = false
var useRatioForScale = false
var useDSForScale = false
var useIAForScale = 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.ACT.ordinal] -> useIAForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
}
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, toTime, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal])
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)
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal]) secondGraphData.addActivity(fromTime, toTime, useIAForScale, 0.8)
if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, toTime, useABSForScale, 1.0)
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)
}
}
}
}
// 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,39 +0,0 @@
package info.nightscout.androidaps.historyBrowser
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.ActivePluginProvider
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.sharedPreferences.SP
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class IobCobCalculatorPluginHistory @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
rxBus: RxBusWrapper,
sp: SP,
resourceHelper: ResourceHelper,
profileFunction: ProfileFunction,
activePlugin: ActivePluginProvider,
treatmentsPluginHistory: TreatmentsPluginHistory,
sensitivityOref1Plugin: SensitivityOref1Plugin,
sensitivityAAPSPlugin: SensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
fabricPrivacy: FabricPrivacy,
dateUtil: DateUtil
) : IobCobCalculatorPlugin(injector, aapsLogger, rxBus, sp, resourceHelper, profileFunction,
activePlugin, treatmentsPluginHistory, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil) {
override fun onStart() { // do not attach to rxbus
}
}

View file

@ -1,44 +0,0 @@
package info.nightscout.androidaps.historyBrowser
import android.content.Context
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue
import info.nightscout.androidaps.plugins.treatments.TreatmentService
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class TreatmentsPluginHistory @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
rxBus: RxBusWrapper,
resourceHelper: ResourceHelper,
context: Context,
sp: SP,
profileFunction: ProfileFunction,
activePlugin: ActivePluginProvider,
nsUpload: NSUpload,
fabricPrivacy: FabricPrivacy,
dateUtil: DateUtil,
uploadQueue: UploadQueue
) : TreatmentsPlugin(injector, aapsLogger, rxBus, resourceHelper, context, sp, profileFunction, activePlugin, nsUpload, fabricPrivacy, dateUtil, uploadQueue) {
init {
onStart()
}
override fun onStart() {
service = TreatmentService(injector)
initializeData(range())
}
}

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.aps.events
import info.nightscout.androidaps.events.Event
class EventLoopInvoked : Event()

View file

@ -1,63 +0,0 @@
package info.nightscout.androidaps.plugins.aps.logger;
import org.mozilla.javascript.ScriptableObject;
import javax.inject.Inject;
import info.nightscout.androidaps.db.StaticInjector;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
/**
* Created by adrian on 15/10/17.
*/
public class LoggerCallback extends ScriptableObject {
@Inject
AAPSLogger aapsLogger;
private static StringBuffer errorBuffer = new StringBuffer();
private static StringBuffer logBuffer = new StringBuffer();
public LoggerCallback() {
//empty constructor needed for Rhino
errorBuffer = new StringBuffer();
logBuffer = new StringBuffer();
StaticInjector.Companion.getInstance().androidInjector().inject(this);
}
@Override
public String getClassName() {
return "LoggerCallback";
}
public void jsConstructor() {
//empty constructor on JS site; could work as setter
}
public void jsFunction_log(Object obj1) {
aapsLogger.debug(LTag.APS, obj1.toString().trim());
logBuffer.append(obj1.toString());
}
public void jsFunction_error(Object obj1) {
aapsLogger.error(LTag.APS, obj1.toString().trim());
errorBuffer.append(obj1.toString());
}
public static String getScriptDebug() {
String ret = "";
if (errorBuffer.length() > 0) {
ret += "e:\n" + errorBuffer.toString();
}
if (ret.length() > 0 && logBuffer.length() > 0) ret += '\n';
if (logBuffer.length() > 0) {
ret += "d:\n" + logBuffer.toString();
}
return ret;
}
}

View file

@ -0,0 +1,61 @@
package info.nightscout.androidaps.plugins.aps.logger
import info.nightscout.androidaps.di.StaticInjector
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import org.mozilla.javascript.ScriptableObject
import javax.inject.Inject
@Suppress("unused", "FunctionName")
class LoggerCallback : ScriptableObject() {
@Inject lateinit var aapsLogger: AAPSLogger
override fun getClassName(): String = "LoggerCallback"
fun jsConstructor() {
//empty constructor on JS site; could work as setter
}
fun jsFunction_log(obj1: Any) {
aapsLogger.debug(LTag.APS, obj1.toString().trim { it <= ' ' })
logBuffer.append(obj1.toString())
}
fun jsFunction_error(obj1: Any) {
aapsLogger.error(LTag.APS, obj1.toString().trim { it <= ' ' })
errorBuffer.append(obj1.toString())
}
companion object {
private var errorBuffer = StringBuffer()
private var logBuffer = StringBuffer()
val scriptDebug: String
get() {
var ret = ""
if (errorBuffer.isNotEmpty()) {
ret += """
e:
$errorBuffer
""".trimIndent()
}
if (ret.isNotEmpty() && logBuffer.isNotEmpty()) ret += '\n'
if (logBuffer.isNotEmpty()) {
ret += """
d:
$logBuffer
""".trimIndent()
}
return ret
}
}
init {
//empty constructor needed for Rhino
errorBuffer = StringBuffer()
logBuffer = StringBuffer()
@Suppress("DEPRECATION")
StaticInjector.Companion.getInstance().androidInjector().inject(this)
}
}

View file

@ -3,13 +3,15 @@ package info.nightscout.androidaps.plugins.aps.loop
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import dagger.android.DaggerBroadcastReceiver import dagger.android.DaggerBroadcastReceiver
import info.nightscout.androidaps.interfaces.Loop
import javax.inject.Inject import javax.inject.Inject
class CarbSuggestionReceiver : DaggerBroadcastReceiver() { class CarbSuggestionReceiver : DaggerBroadcastReceiver() {
@Inject lateinit var loopPlugin: LoopPlugin @Inject lateinit var loop: Loop
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent) super.onReceive(context, intent)
val duration = intent.getIntExtra("ignoreDuration", 5) val duration = intent.getIntExtra("ignoreDuration", 5)
loopPlugin.disableCarbSuggestions(duration) loop.disableCarbSuggestions(duration)
} }
} }

View file

@ -6,44 +6,54 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.LoopFragmentBinding
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBus
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.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.extensions.plusAssign
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.rx.AapsSchedulers
import io.reactivex.android.schedulers.AndroidSchedulers import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.loop_fragment.* import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
class LoopFragment : DaggerFragment() { class LoopFragment : DaggerFragment() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBus
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var loopPlugin: LoopPlugin @Inject lateinit var loop: Loop
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
private var _binding: LoopFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.loop_fragment, container, false) _binding = LoopFragmentBinding.inflate(inflater, container, false)
return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
loop_run.setOnClickListener { binding.run.setOnClickListener {
loop_lastrun.text = resourceHelper.gs(R.string.executing) binding.lastrun.text = rh.gs(R.string.executing)
Thread { loopPlugin.invoke("Loop button", true) }.start() Thread { loop.invoke("Loop button", true) }.start()
} }
} }
@ -52,18 +62,18 @@ class LoopFragment : DaggerFragment() {
super.onResume() super.onResume()
disposable += rxBus disposable += rxBus
.toObservable(EventLoopUpdateGui::class.java) .toObservable(EventLoopUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
updateGUI() updateGUI()
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventLoopSetLastRunGui::class.java) .toObservable(EventLoopSetLastRunGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
clearGUI() clearGUI()
loop_lastrun?.text = it.text binding.lastrun.text = it.text
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
updateGUI() updateGUI()
sp.putBoolean(R.string.key_objectiveuseloop, true) sp.putBoolean(R.string.key_objectiveuseloop, true)
@ -76,22 +86,27 @@ class LoopFragment : DaggerFragment() {
} }
@Synchronized @Synchronized
fun updateGUI() { override fun onDestroyView() {
if (loop_request == null) return super.onDestroyView()
loopPlugin.lastRun?.let { _binding = null
loop_request?.text = it.request?.toSpanned() ?: "" }
loop_constraintsprocessed?.text = it.constraintsProcessed?.toSpanned() ?: ""
loop_source?.text = it.source ?: ""
loop_lastrun?.text = dateUtil.dateAndTimeString(it.lastAPSRun)
?: ""
loop_smbrequest_time?.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBRequest)
loop_smbexecution_time?.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBEnact)
loop_tbrrequest_time?.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBRRequest)
loop_tbrexecution_time?.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBREnact)
loop_tbrsetbypump?.text = it.tbrSetByPump?.let { tbrSetByPump -> HtmlHelper.fromHtml(tbrSetByPump.toHtml()) } @Synchronized
fun updateGUI() {
if (_binding == null) return
loop.lastRun?.let {
binding.request.text = it.request?.toSpanned() ?: ""
binding.constraintsprocessed.text = it.constraintsProcessed?.toSpanned() ?: ""
binding.source.text = it.source ?: ""
binding.lastrun.text = dateUtil.dateAndTimeString(it.lastAPSRun)
binding.smbrequestTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBRequest)
binding.smbexecutionTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBEnact)
binding.tbrrequestTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBRRequest)
binding.tbrexecutionTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBREnact)
binding.tbrsetbypump.text = it.tbrSetByPump?.let { tbrSetByPump -> HtmlHelper.fromHtml(tbrSetByPump.toHtml()) }
?: "" ?: ""
loop_smbsetbypump?.text = it.smbSetByPump?.let { smbSetByPump -> HtmlHelper.fromHtml(smbSetByPump.toHtml()) } binding.smbsetbypump.text = it.smbSetByPump?.let { smbSetByPump -> HtmlHelper.fromHtml(smbSetByPump.toHtml()) }
?: "" ?: ""
val constraints = val constraints =
@ -101,22 +116,22 @@ class LoopFragment : DaggerFragment() {
constraintsProcessed.smbConstraint?.let { smbConstraint -> allConstraints.copyReasons(smbConstraint) } constraintsProcessed.smbConstraint?.let { smbConstraint -> allConstraints.copyReasons(smbConstraint) }
allConstraints.getMostLimitedReasons(aapsLogger) allConstraints.getMostLimitedReasons(aapsLogger)
} ?: "" } ?: ""
loop_constraints?.text = constraints binding.constraints.text = constraints
} }
} }
@Synchronized @Synchronized
private fun clearGUI() { private fun clearGUI() {
loop_request?.text = "" binding.request.text = ""
loop_constraints?.text = "" binding.constraints.text = ""
loop_constraintsprocessed?.text = "" binding.constraintsprocessed.text = ""
loop_source?.text = "" binding.source.text = ""
loop_lastrun?.text = "" binding.lastrun.text = ""
loop_smbrequest_time?.text = "" binding.smbrequestTime.text = ""
loop_smbexecution_time?.text = "" binding.smbexecutionTime.text = ""
loop_tbrrequest_time?.text = "" binding.tbrrequestTime.text = ""
loop_tbrexecution_time?.text = "" binding.tbrexecutionTime.text = ""
loop_tbrsetbypump?.text = "" binding.tbrsetbypump.text = ""
loop_smbsetbypump?.text = "" binding.smbsetbypump.text = ""
} }
} }

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