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
/local.properties
.DS_Store
*/jacoco.exec
/build
/captures
*.apk
build/
!.idea/dictionaries/project-dictionary.xml
.idea/*
!.idea/codeStyles/
full/
debug/
release/
app/com.crashlytics.settings.json
app/session_analytics.tap
.project

View file

@ -1,27 +1,13 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="AUTODETECT_INDENTS" value="false" />
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<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="NAME_COUNT_TO_USE_STAR_IMPORT" 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="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="METHOD_ANNOTATION_WRAP" value="0" />
@ -140,14 +126,22 @@
</arrangement>
</codeStyleSettings>
<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_ADD_SPACE" value="true" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" 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="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>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</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
jdk: oraclejdk8
jdk: openjdk11
dist: trusty
env:
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: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-allopen'
apply plugin: 'com.google.gms.google-services'
//apply plugin: 'jacoco-android'
apply plugin: 'com.hiya.jacoco-android'
apply plugin: 'com.google.firebase.crashlytics'
jacoco {
toolVersion = "0.8.3"
}
apply from: "${project.rootDir}/gradle/android_dependencies.gradle"
apply from: "${project.rootDir}/gradle/jacoco_global.gradle"
repositories {
jcenter { url "https://jcenter.bintray.com/" }
mavenCentral()
google()
}
allOpen {
// allows mocking for classes w/o directly opening them for release builds
annotation 'info.nightscout.androidaps.annotations.OpenForTesting'
}
def generateGitBuild = { ->
StringBuilder stringBuilder = new StringBuilder()
try {
@ -108,57 +99,27 @@ def allCommitted = { ->
tasks.matching { it instanceof Test }.all {
testLogging.events = ["failed", "skipped", "started"]
// testLogging.events = ["failed", "skipped", "started", "standard_out"] use to display stdout in travis
testLogging.exceptionFormat = "full"
}
android {
compileSdkVersion 28
ndkVersion "21.1.6352462"
defaultConfig {
minSdkVersion 26
targetSdkVersion 28
multiDexEnabled true
versionCode 1500
version "2.8.2.1"
version "3.0.0"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"'
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
}
}
flavorDimensions "standard"
productFlavors {
flavorDimensions "standard"
full {
applicationId "info.nightscout.androidaps"
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"
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
}
}
allprojects {
@ -235,51 +176,40 @@ allprojects {
dependencies {
wearApp project(':wear')
implementation project(':shared')
implementation project(':core')
implementation project(':automation')
implementation project(':combo')
implementation project(':database')
implementation project(':dana')
implementation project(':danars')
implementation project(':danar')
implementation project(':insight')
implementation project(':pump-common')
implementation project(':rileylink')
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')
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
* support for Activity and fragment injection so we need to include
* the following dependencies */
* support for Activity and fragment injection so we need to include
* the following dependencies */
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
/* Dagger2 - default dependency */
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
task clearMainAppData(type: Exec) {

Binary file not shown.

Binary file not shown.

View file

@ -1,40 +1,13 @@
package info.nightscout.androidaps
import android.os.SystemClock
import androidx.test.ext.junit.runners.AndroidJUnit4
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 javax.inject.Inject
@LargeTest
@RunWith(AndroidJUnit4::class)
class RealPumpTest {
/*
companion object {
const val R_PASSWORD = 1234
const val R_SERIAL = "PBB00013LR_P"
@ -89,7 +62,7 @@ class RealPumpTest {
localProfilePlugin.numOfProfiles = 0
val singleProfile = LocalProfilePlugin.SingleProfile().copyFrom(localProfilePlugin.rawProfile, profile, "TestProfile")
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)
// Insulin
configBuilderPlugin.performPluginSwitch(insulinOrefUltraRapidActingPlugin, true, PluginType.INSULIN)
@ -125,4 +98,5 @@ class RealPumpTest {
SystemClock.sleep(1000)
}
}
*/
}

View file

@ -1,48 +1,13 @@
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.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
@LargeTest
@RunWith(AndroidJUnit4::class)
class SetupWizardActivityTest {
/*
@Rule
@JvmField
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"
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.BLUETOOTH" />
<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.WRITE_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="com.dexcom.cgm.EXTERNAL_PERMISSION" />
<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" />
@ -52,7 +54,8 @@
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<activity android:name=".MainActivity">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
@ -60,7 +63,8 @@
</intent-filter>
</activity>
<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>
<action android:name="info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity" />
@ -68,15 +72,20 @@
</intent-filter>
</activity>
<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.ProfileHelperActivity"
android:theme="@style/ProfileHelperAppTheme" />
<activity android:name=".activities.StatsActivity" />
<activity
android:name="com.google.firebase.auth.internal.FederatedSignInActivity"
tools:replace="android:launchMode"
android:launchMode="standard" />
android:excludeFromRecents="true"
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 -->
<receiver
@ -110,11 +119,11 @@
</intent-filter>
</receiver>
<!-- Receiver keepalive, scheduled every 30 min -->
<!-- Receiver keep alive, scheduled every 30 min -->
<receiver android:name=".receivers.KeepAliveReceiver" />
<!-- 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 -->
<receiver
@ -136,13 +145,6 @@
android:resource="@xml/filepaths" />
</provider>
<!-- Service processing incomming data -->
<service
android:name=".services.DataService"
android:exported="false" />
<service
android:name=".services.LocationService"
android:exported="false" />
<service
android:name=".plugins.general.wear.wearintegration.WatchUpdaterService"
android:exported="true">
@ -219,8 +221,6 @@
android:exported="false" />
<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
android:name="io.fabric.ApiKey"
@ -236,20 +236,6 @@
android:name=".activities.SingleFragmentActivity"
android:theme="@style/AppTheme" />
<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=".plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity">
<intent-filter>
@ -259,27 +245,6 @@
</intent-filter>
</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"/>
</application>

View file

@ -1,7 +1,7 @@
<configuration>
<!-- Create a file appender for a log in the application's data directory -->
<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">
<file>${EXT_FILES_DIR}/AndroidAPS.log</file>
<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
# à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
# 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
# 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.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.joanzapata.iconify.Iconify
import com.joanzapata.iconify.fonts.FontAwesomeModule
import dev.doubledot.doki.ui.DokiActivity
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.activities.PreferencesActivity
import info.nightscout.androidaps.activities.ProfileHelperActivity
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.activities.StatsActivity
import info.nightscout.androidaps.activities.*
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.ActivityMainBinding
import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.PluginType
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.interfaces.*
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin
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.locale.LocaleHelper
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.IconsProvider
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.tabs.TabPageAdapter
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.rxkotlin.plusAssign
import java.util.*
import javax.inject.Inject
import kotlin.system.exitProcess
@ -70,26 +66,29 @@ class MainActivity : NoSplashAppCompatActivity() {
private val disposable = CompositeDisposable()
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBus
@Inject lateinit var androidPermission: AndroidPermission
@Inject lateinit var sp: SP
@Inject lateinit var versionCheckerUtils: VersionCheckerUtils
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var loop: Loop
@Inject lateinit var nsSettingsStatus: NSSettingsStatus
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var iconsProvider: IconsProvider
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var profileFunction: ProfileFunction
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
private var pluginPreferencesMenuItem: MenuItem? = null
private var menu: Menu? = null
private var menuOpen = false
private lateinit var binding: ActivityMainBinding
@ -109,7 +108,7 @@ class MainActivity : NoSplashAppCompatActivity() {
}
// 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() {
override fun onPageScrollStateChanged(state: 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
if (!loopPlugin.isEnabled(PluginType.LOOP)) versionCheckerUtils.triggerCheckVersion()
if (!(loop as PluginBase).isEnabled()) versionCheckerUtils.triggerCheckVersion()
setUserStats()
setupViews()
disposable.add(rxBus
disposable += rxBus
.toObservable(EventRebuildTabs::class.java)
.observeOn(AndroidSchedulers.mainThread())
.observeOn(aapsSchedulers.main)
.subscribe({
if (it.recreate) recreate()
else setupViews()
setWakeLock()
}, fabricPrivacy::logException)
)
disposable.add(rxBus
if (it.recreate) recreate()
else setupViews()
setWakeLock()
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(AndroidSchedulers.mainThread())
.observeOn(aapsSchedulers.main)
.subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException)
)
if (!sp.getBoolean(R.string.key_setupwizard_processed, false) && !isRunningRealPumpTest()) {
if (startWizard() && !isRunningRealPumpTest()) {
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
startActivity(Intent(this, SetupWizardActivity::class.java))
})
}
androidPermission.notifyForStoragePermission(this)
androidPermission.notifyForBatteryOptimizationPermission(this)
androidPermission.notifyForLocationPermissions(this)
if (!config.NSCLIENT) androidPermission.notifyForLocationPermissions(this)
if (config.PUMPDRIVERS) {
androidPermission.notifyForSMSPermissions(this, smsCommunicatorPlugin)
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
}
private fun startWizard(): Boolean =
!sp.getBoolean(R.string.key_setupwizard_processed, false)
override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onPostCreate(savedInstanceState, persistentState)
actionBarDrawerToggle.syncState()
}
public override fun onDestroy() {
override fun onDestroy() {
super.onDestroy()
disposable.clear()
}
@ -168,8 +169,8 @@ class MainActivity : NoSplashAppCompatActivity() {
override fun onResume() {
super.onResume()
protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, null,
UIRunnable { OKDialog.show(this, "", resourceHelper.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() } },
UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { finish() } }
)
}
@ -179,8 +180,8 @@ class MainActivity : NoSplashAppCompatActivity() {
}
private fun processPreferenceChange(ev: EventPreferenceChange) {
if (ev.isChanged(resourceHelper, R.string.key_keep_screen_on)) setWakeLock()
if (ev.isChanged(resourceHelper, R.string.key_skin)) recreate()
if (ev.isChanged(rh, R.string.key_keep_screen_on)) setWakeLock()
if (ev.isChanged(rh, R.string.key_skin)) recreate()
}
private fun setupViews() {
@ -224,8 +225,10 @@ class MainActivity : NoSplashAppCompatActivity() {
binding.tabsCompact.visibility = View.GONE
val typedValue = TypedValue()
if (theme.resolveAttribute(R.attr.actionBarSize, typedValue, true)) {
binding.toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT,
TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics))
binding.toolbar.layoutParams = LinearLayout.LayoutParams(
Toolbar.LayoutParams.MATCH_PARENT,
TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)
)
}
TabLayoutMediator(binding.tabsNormal, binding.mainPager) { tab, position ->
tab.text = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(position).name
@ -252,10 +255,25 @@ class MainActivity : NoSplashAppCompatActivity() {
private fun setPluginPreferenceMenuName() {
if (binding.mainPager.currentItem >= 0) {
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 {
this.menu = menu
menuInflater.inflate(R.menu.menu_main, menu)
@ -267,7 +285,7 @@ class MainActivity : NoSplashAppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.nav_preferences -> {
R.id.nav_preferences -> {
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
val i = Intent(this, PreferencesActivity::class.java)
i.putExtra("id", -1)
@ -276,33 +294,38 @@ class MainActivity : NoSplashAppCompatActivity() {
return true
}
R.id.nav_historybrowser -> {
R.id.nav_historybrowser -> {
startActivity(Intent(this, HistoryBrowseActivity::class.java))
return true
}
R.id.nav_setupwizard -> {
R.id.nav_treatments -> {
startActivity(Intent(this, TreatmentsActivity::class.java))
return true
}
R.id.nav_setupwizard -> {
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
startActivity(Intent(this, SetupWizardActivity::class.java))
})
return true
}
R.id.nav_about -> {
R.id.nav_about -> {
var message = "Build: ${BuildConfig.BUILDVERSION}\n"
message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n"
message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.nightscoutVersionName}"
if (buildHelper.isEngineeringMode()) message += "\n${resourceHelper.gs(R.string.engineering_mode_enabled)}"
if (!fabricPrivacy.fabricEnabled()) message += "\n${resourceHelper.gs(R.string.fabric_upload_disabled)}"
message += resourceHelper.gs(R.string.about_link_urls)
message += "${rh.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.getVersion()}"
if (buildHelper.isEngineeringMode()) message += "\n${rh.gs(R.string.engineering_mode_enabled)}"
if (!fabricPrivacy.fabricEnabled()) message += "\n${rh.gs(R.string.fabric_upload_disabled)}"
message += rh.gs(R.string.about_link_urls)
val messageSpanned = SpannableString(message)
Linkify.addLinks(messageSpanned, Linkify.WEB_URLS)
AlertDialog.Builder(this)
.setTitle(resourceHelper.gs(R.string.app_name) + " " + BuildConfig.VERSION)
.setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION)
.setIcon(iconsProvider.getIcon())
.setMessage(messageSpanned)
.setPositiveButton(resourceHelper.gs(R.string.ok), null)
.setNeutralButton(resourceHelper.gs(R.string.cta_dont_kill_my_app_info)) { _, _ -> DokiActivity.start(context = this@MainActivity) }
.setPositiveButton(rh.gs(R.string.ok), null)
.setNeutralButton(rh.gs(R.string.cta_dont_kill_my_app_info)) { _, _ -> DokiActivity.start(context = this@MainActivity) }
.create().apply {
show()
findViewById<TextView>(android.R.id.message)?.movementMethod = LinkMovementMethod.getInstance()
@ -310,8 +333,9 @@ class MainActivity : NoSplashAppCompatActivity() {
return true
}
R.id.nav_exit -> {
R.id.nav_exit -> {
aapsLogger.debug(LTag.CORE, "Exiting")
uel.log(Action.EXIT_AAPS, Sources.Aaps)
rxBus.send(EventAppExit())
finish()
System.runFinalization()
@ -333,12 +357,12 @@ class MainActivity : NoSplashAppCompatActivity() {
return true
}
*/
R.id.nav_defaultprofile -> {
R.id.nav_defaultprofile -> {
startActivity(Intent(this, ProfileHelperActivity::class.java))
return true
}
R.id.nav_stats -> {
R.id.nav_stats -> {
startActivity(Intent(this, StatsActivity::class.java))
return true
}
@ -346,6 +370,22 @@ class MainActivity : NoSplashAppCompatActivity() {
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
// 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
val closedLoopEnabled = if (constraintChecker.isClosedLoopAllowed().value()) "CLOSED_LOOP_ENABLED" else "CLOSED_LOOP_DISABLED"
// Size is limited to 36 chars
val remote = BuildConfig.REMOTE.toLowerCase(Locale.getDefault())
val remote = BuildConfig.REMOTE.lowercase(Locale.getDefault())
.replace("https://", "")
.replace("http://", "")
.replace(".git", "")
@ -371,7 +411,7 @@ class MainActivity : NoSplashAppCompatActivity() {
if (!config.NSCLIENT && !config.PUMPCONTROL)
activePlugin.activeAPS.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Aps", 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.activeInsulin.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Insulin", it::class.java.simpleName) }
// Add to crash log too
@ -380,6 +420,7 @@ class MainActivity : NoSplashAppCompatActivity() {
FirebaseCrashlytics.getInstance().setCustomKey("Remote", remote)
FirebaseCrashlytics.getInstance().setCustomKey("Committed", BuildConfig.COMMITTED)
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.preference.*
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.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin
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.EventRebuildTabs
import info.nightscout.androidaps.interfaces.PluginBase
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.openAPSAMA.OpenAPSAMAPlugin
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.constraints.safety.SafetyPlugin
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
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.tidepool.TidepoolPlugin
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.PoctechPlugin
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.protection.PasswordCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.shared.sharedPreferences.SP
import javax.inject.Inject
class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListener {
@ -60,8 +62,8 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
private var pluginId = -1
private var filter = ""
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rxBus: RxBus
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var pluginStore: PluginStore
@ -89,6 +91,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
@Inject lateinit var glimpPlugin: GlimpPlugin
@Inject lateinit var poctechPlugin: PoctechPlugin
@Inject lateinit var tomatoPlugin: TomatoPlugin
@Inject lateinit var glunovoPlugin: GlunovoPlugin
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var statusLinePlugin: StatusLinePlugin
@Inject lateinit var tidepoolPlugin: TidepoolPlugin
@ -99,6 +102,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
@Inject lateinit var passwordCheck: PasswordCheck
@Inject lateinit var nsSettingStatus: NSSettingsStatus
@Inject lateinit var openHumansUploader: OpenHumansUploader
@Inject lateinit var diaconnG8Plugin: DiaconnG8Plugin
override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
@ -158,6 +162,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
addPreferencesFromResourceIfEnabled(eversensePlugin, rootKey)
addPreferencesFromResourceIfEnabled(dexcomPlugin, rootKey)
addPreferencesFromResourceIfEnabled(tomatoPlugin, rootKey)
addPreferencesFromResourceIfEnabled(glunovoPlugin, rootKey)
addPreferencesFromResourceIfEnabled(poctechPlugin, rootKey)
addPreferencesFromResourceIfEnabled(glimpPlugin, rootKey)
addPreferencesFromResourceIfEnabled(loopPlugin, rootKey, config.APS)
@ -173,6 +178,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
addPreferencesFromResourceIfEnabled(localInsightPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(comboPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(medtronicPumpPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(diaconnG8Plugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResource(R.xml.pref_pump, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(virtualPumpPlugin, rootKey)
addPreferencesFromResourceIfEnabled(insulinOrefFreePeakPlugin, rootKey)
@ -182,7 +188,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
addPreferencesFromResourceIfEnabled(automationPlugin, rootKey)
addPreferencesFromResourceIfEnabled(wearPlugin, 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)
addPreferencesFromResourceIfEnabled(maintenancePlugin, rootKey)
addPreferencesFromResourceIfEnabled(openHumansUploader, rootKey)
@ -194,21 +200,21 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
rxBus.send(EventPreferenceChange(key))
if (key == resourceHelper.gs(R.string.key_language)) {
if (key == rh.gs(R.string.key_language)) {
rxBus.send(EventRebuildTabs(true))
//recreate() does not update language so better close settings
activity?.finish()
}
if (key == resourceHelper.gs(R.string.key_short_tabtitles)) {
if (key == rh.gs(R.string.key_short_tabtitles)) {
rxBus.send(EventRebuildTabs())
}
if (key == resourceHelper.gs(R.string.key_units)) {
if (key == rh.gs(R.string.key_units)) {
activity?.recreate()
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 {
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)
@ -225,15 +231,15 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
private fun checkForBiometricFallback(key: String) {
// Biometric protection activated without set master password
if ((resourceHelper.gs(R.string.key_settings_protection) == key ||
resourceHelper.gs(R.string.key_application_protection) == key ||
resourceHelper.gs(R.string.key_bolus_protection) == key) &&
if ((rh.gs(R.string.key_settings_protection) == key ||
rh.gs(R.string.key_application_protection) == key ||
rh.gs(R.string.key_bolus_protection) == key) &&
sp.getString(R.string.key_master_password, "") == "" &&
sp.getInt(key, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal
) {
activity?.let {
val title = resourceHelper.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 title = rh.gs(R.string.unsecure_fallback_biometric)
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)
}
}
@ -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 ||
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
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 {
val title = resourceHelper.gs(R.string.unsecure_fallback_biometric)
val message = resourceHelper.gs(R.string.unsecure_fallback_descriotion_biometric)
val title = rh.gs(R.string.unsecure_fallback_biometric)
val message = rh.gs(R.string.unsecure_fallback_descriotion_biometric)
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
val unitDependent = arrayOf(
resourceHelper.gs(R.string.key_hypo_target),
resourceHelper.gs(R.string.key_activity_target),
resourceHelper.gs(R.string.key_eatingsoon_target),
resourceHelper.gs(R.string.key_high_mark),
resourceHelper.gs(R.string.key_low_mark)
rh.gs(R.string.key_hypo_target),
rh.gs(R.string.key_activity_target),
rh.gs(R.string.key_eatingsoon_target),
rh.gs(R.string.key_high_mark),
rh.gs(R.string.key_low_mark)
)
if (unitDependent.toList().contains(pref.key) && pref is EditTextPreference) {
val converted = Profile.toCurrentUnits(profileFunction, SafeParse.stringToDouble(pref.text))
@ -319,20 +325,20 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
pref.setSummary(pref.entry)
// Preferences
// Preferences
if (pref.getKey() == resourceHelper.gs(R.string.key_settings_protection)) {
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_settings_password))
if (pref.getKey() == rh.gs(R.string.key_settings_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_settings_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
}
// Application
// Application
if (pref.getKey() == resourceHelper.gs(R.string.key_application_protection)) {
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_application_password))
if (pref.getKey() == rh.gs(R.string.key_application_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_application_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
}
// Bolus
// Bolus
if (pref.getKey() == resourceHelper.gs(R.string.key_bolus_protection)) {
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_bolus_password))
if (pref.getKey() == rh.gs(R.string.key_bolus_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_bolus_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
}
}
@ -350,10 +356,10 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
}
val hmacPasswords = arrayOf(
resourceHelper.gs(R.string.key_bolus_password),
resourceHelper.gs(R.string.key_master_password),
resourceHelper.gs(R.string.key_application_password),
resourceHelper.gs(R.string.key_settings_password)
rh.gs(R.string.key_bolus_password),
rh.gs(R.string.key_master_password),
rh.gs(R.string.key_application_password),
rh.gs(R.string.key_settings_password)
)
if (pref is Preference) {
@ -361,7 +367,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (sp.getString(pref.key, "").startsWith("hmac:")) {
pref.summary = "******"
} 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 {
context?.let { context ->
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.setPassword(context, R.string.master_password, R.string.key_master_password)
})
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)
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)
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)
return true
}
// 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)
return true
}

View file

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

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.os.Bundle
import android.text.Editable
@ -7,40 +8,39 @@ import android.text.TextWatcher
import android.view.Menu
import android.widget.PopupMenu
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.data.defaultProfile.DefaultProfile
import info.nightscout.androidaps.data.defaultProfile.DefaultProfileDPV
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch
import info.nightscout.androidaps.databinding.ActivityProfilehelperBinding
import info.nightscout.androidaps.db.ProfileSwitch
import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
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 java.text.DecimalFormat
import javax.inject.Inject
class ProfileHelperActivity : NoSplashAppCompatActivity() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultProfile: DefaultProfile
@Inject lateinit var defaultProfileDPV: DefaultProfileDPV
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var rxBus: RxBus
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var repository: AppRepository
enum class ProfileType {
MOTOL_DEFAULT,
@ -61,7 +61,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
private lateinit var profileList: ArrayList<CharSequence>
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 lateinit var binding: ActivityProfilehelperBinding
@ -85,10 +85,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
setOnMenuItemClickListener { item ->
binding.profiletype.setText(item.title)
when (item.itemId) {
R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT)
R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT)
R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT)
R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE)
R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT)
R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT)
R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT)
R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE)
R.id.menu_profileswitch -> switchTab(tabSelected, ProfileType.PROFILE_SWITCH)
}
true
@ -98,7 +98,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
}
// Active profile
profileList = activePlugin.activeProfileInterface.profile?.getProfileList() ?: ArrayList()
profileList = activePlugin.activeProfileSource.profile?.getProfileList() ?: ArrayList()
binding.availableProfileList.setOnClickListener {
PopupMenu(this, binding.availableProfileList).apply {
@ -114,12 +114,12 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
}
// 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 {
PopupMenu(this, binding.profileswitchList).apply {
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 ->
binding.profileswitchList.setText(item.title)
profileSwitchUsed[tabSelected] = item.itemId
@ -131,6 +131,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
// Default profile
binding.copytolocalprofile.setOnClickListener {
storeValues()
val age = ageUsed[tabSelected]
val weight = weightUsed[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())
else defaultProfileDPV.profile(age, tdd, pct / 100.0, profileFunction.getUnits())
profile?.let {
OKDialog.showConfirmation(this, resourceHelper.gs(R.string.careportal_profileswitch), resourceHelper.gs(R.string.copytolocalprofile), Runnable {
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(it, "DefaultProfile" + dateUtil.dateAndTimeAndSecondsString(dateUtil._now())))
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())
.replace(".", "/")
)
)
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.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
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 ->
ProfileViewerDialog().also { pvd ->
pvd.arguments = Bundle().also {
it.putLong("time", DateUtil.now())
it.putLong("time", dateUtil.now())
it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal)
it.putString("customProfile", profile0.data.toString())
it.putString("customProfile2", profile1.data.toString())
it.putString("customProfileUnits", profileFunction.getUnits())
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))
it.putString("customProfile", profile0.jsonObject.toString())
it.putString("customProfile2", profile1.jsonObject.toString())
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
)
)
}
}.show(supportFragmentManager, "ProfileViewDialog")
return@setOnClickListener
@ -223,14 +243,14 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
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
when (typeSelected[tab]) {
ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())
ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits())
ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile()
ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileInterface.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString())
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].profileObject?.convertToNonCustomizedProfile()
ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())
ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits())
ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile(dateUtil)
ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileSource.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString())
ProfileType.PROFILE_SWITCH -> ProfileSealed.EPS(profileSwitch[profileSwitchUsed[tab]]).convertToNonCustomizedProfile(dateUtil)
}
} catch (e: Exception) {
null
@ -238,11 +258,11 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
private fun getProfileName(age: Double, tdd: Double, weight: Double, basalSumPct: Double, tab: Int): String =
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.DPV_DEFAULT -> resourceHelper.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt())
ProfileType.CURRENT -> profileFunction.getProfileName()
ProfileType.MOTOL_DEFAULT -> if (tdd > 0) rh.gs(R.string.formatwithtdd, age, tdd) else rh.gs(R.string.formatwithweight, age, weight)
ProfileType.DPV_DEFAULT -> rh.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt())
ProfileType.CURRENT -> profileFunction.getProfileName()
ProfileType.AVAILABLE_PROFILE -> profileList[profileUsed[tab]].toString()
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].customizedName
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].originalCustomizedName
}
private fun storeValues() {
@ -259,17 +279,18 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
tabSelected = tab
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
binding.profiletype.setText(
when (typeSelected[tabSelected]) {
ProfileType.MOTOL_DEFAULT -> resourceHelper.gs(R.string.motoldefaultprofile)
ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.dpvdefaultprofile)
ProfileType.CURRENT -> resourceHelper.gs(R.string.currentprofile)
ProfileType.AVAILABLE_PROFILE -> resourceHelper.gs(R.string.availableprofile)
ProfileType.PROFILE_SWITCH -> resourceHelper.gs(R.string.careportal_profileswitch)
})
ProfileType.MOTOL_DEFAULT -> rh.gs(R.string.motoldefaultprofile)
ProfileType.DPV_DEFAULT -> rh.gs(R.string.dpvdefaultprofile)
ProfileType.CURRENT -> rh.gs(R.string.currentprofile)
ProfileType.AVAILABLE_PROFILE -> rh.gs(R.string.availableprofile)
ProfileType.PROFILE_SWITCH -> rh.gs(R.string.careportal_profileswitch)
}
)
binding.defaultProfile.visibility = (newContent == ProfileType.MOTOL_DEFAULT || newContent == ProfileType.DPV_DEFAULT).toVisibility()
binding.currentProfile.visibility = (newContent == ProfileType.CURRENT).toVisibility()
binding.availableProfile.visibility = (newContent == ProfileType.AVAILABLE_PROFILE).toVisibility()
@ -285,11 +306,11 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
if (profileList.isNotEmpty())
binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString())
if (profileSwitch.isNotEmpty())
binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].customizedName)
binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].originalCustomizedName)
}
private fun setBackgroundColorOnSelected(tab: Int) {
binding.menu1.setBackgroundColor(resourceHelper.gc(if (tab == 1) R.color.defaultbackground else R.color.tabBgColorSelected))
binding.menu2.setBackgroundColor(resourceHelper.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile))
binding.menu1.setBackgroundColor(rh.gc(if (tab == 1) R.color.defaultbackground else R.color.tempbasal))
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() {
@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?) {
super.onCreate(savedInstanceState)

View file

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

View file

@ -1,10 +1,15 @@
package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.os.Bundle
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.logging.UserEntryLogger
import info.nightscout.androidaps.utils.ActivityMonitor
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.TirCalculator
import javax.inject.Inject
@ -14,21 +19,37 @@ class StatsActivity : NoSplashAppCompatActivity() {
@Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var tirCalculator: TirCalculator
@Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var uel: UserEntryLogger
private lateinit var binding: ActivityStatsBinding
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityStatsBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.tdds.text = tddCalculator.stats()
binding.tir.text = tirCalculator.stats()
binding.activity.text = activityMonitor.stats()
binding.tdds.text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress)
binding.tir.text = getString(R.string.tir) + ": " + rh.gs(R.string.calculation_in_progress)
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.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()
recreate()
}

View file

@ -5,31 +5,30 @@ import android.widget.ArrayAdapter
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.FirebaseDatabase
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.defaultProfile.DefaultProfile
import info.nightscout.androidaps.databinding.ActivitySurveyBinding
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.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.InstanceId
import info.nightscout.androidaps.utils.SafeParse
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.TirCalculator
import info.nightscout.shared.SafeParse
import info.nightscout.shared.logging.LTag
import javax.inject.Inject
class SurveyActivity : NoSplashAppCompatActivity() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var tirCalculator: TirCalculator
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var defaultProfile: DefaultProfile
@Inject lateinit var dateUtil: DateUtil
private lateinit var binding: ActivitySurveyBinding
@ -40,7 +39,7 @@ class SurveyActivity : NoSplashAppCompatActivity() {
binding.id.text = InstanceId.instanceId()
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
val profileList = profileStore?.getProfileList() ?: return
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 ->
ProfileViewerDialog().also { pvd ->
pvd.arguments = Bundle().also {
it.putLong("time", DateUtil.now())
it.putLong("time", dateUtil.now())
it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal)
it.putString("customProfile", runningProfile.data.toString())
it.putString("customProfile2", profile.data.toString())
it.putString("customProfileUnits", profile.units)
it.putString("customProfile", runningProfile.toPureNsJson(dateUtil).toString())
it.putString("customProfile2", profile.jsonObject.toString())
it.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight")
}
}.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.android.ContributesAndroidInjector
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.DetermineBasalResultAMA
import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS
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.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.android.ContributesAndroidInjector
import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.queue.CommandQueueImplementation
import info.nightscout.androidaps.queue.commands.*
@Module
@Suppress("unused")
abstract class CommandQueueModule {
@ContributesAndroidInjector abstract fun commandQueueInjector(): CommandQueue
@ContributesAndroidInjector abstract fun commandQueueInjector(): CommandQueueImplementation
@ContributesAndroidInjector abstract fun commandBolusInjector(): CommandBolus
@ContributesAndroidInjector abstract fun commandCancelExtendedBolusInjector(): CommandCancelExtendedBolus
@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.android.ContributesAndroidInjector
@ -17,7 +17,6 @@ abstract class ObjectivesModule {
@ContributesAndroidInjector abstract fun objective5Injector(): Objective5
@ContributesAndroidInjector abstract fun objective6Injector(): Objective6
@ContributesAndroidInjector abstract fun objective7Injector(): Objective7
@ContributesAndroidInjector abstract fun objective8Injector(): Objective8
@ContributesAndroidInjector abstract fun objective9Injector(): Objective9
@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.android.ContributesAndroidInjector

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection
package info.nightscout.androidaps.di
import dagger.Binds
import dagger.Module
@ -8,11 +8,14 @@ import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
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.openAPSAMA.OpenAPSAMAPlugin
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
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.objectives.ObjectivesPlugin
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.maintenance.MaintenancePlugin
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.persistentNotification.PersistentNotificationPlugin
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.iob.iobCobCalculator.IobCobCalculatorPlugin
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.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin
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.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.plugins.source.*
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import javax.inject.Qualifier
@Suppress("unused")
@Module
abstract class PluginsModule {
@ -58,6 +60,12 @@ abstract class PluginsModule {
@AllConfigs
@IntoMap
@IntKey(0)
abstract fun bindPersistentNotificationPlugin(plugin: PersistentNotificationPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(5)
abstract fun bindOverviewPlugin(plugin: OverviewPlugin): PluginBase
@Binds
@ -150,6 +158,18 @@ abstract class PluginsModule {
@IntKey(140)
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
@PumpDriver
@IntoMap
@ -160,7 +180,7 @@ abstract class PluginsModule {
@PumpDriver
@IntoMap
@IntKey(155)
abstract fun bindOmnipodPumpPlugin(plugin: OmnipodPumpPlugin): PluginBase
abstract fun bindDiaconnG8Plugin(plugin: DiaconnG8Plugin): PluginBase
@Binds
@NotNSClient
@ -195,12 +215,6 @@ abstract class PluginsModule {
@Binds
@AllConfigs
@IntoMap
@IntKey(230)
abstract fun bindNSProfilePlugin(plugin: NSProfilePlugin): PluginBase
@Binds
@NotNSClient
@IntoMap
@IntKey(240)
abstract fun bindLocalProfilePlugin(plugin: LocalProfilePlugin): PluginBase
@ -210,12 +224,6 @@ abstract class PluginsModule {
@IntKey(250)
abstract fun bindAutomationPlugin(plugin: AutomationPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(260)
abstract fun bindTreatmentsPlugin(plugin: TreatmentsPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@ -270,12 +278,6 @@ abstract class PluginsModule {
@IntKey(340)
abstract fun bindStatusLinePlugin(plugin: StatusLinePlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(350)
abstract fun bindPersistentNotificationPlugin(plugin: PersistentNotificationPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@ -294,6 +296,12 @@ abstract class PluginsModule {
@IntKey(380)
abstract fun bindDstHelperPlugin(plugin: DstHelperPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(381)
abstract fun bindBgQualityCheckPlugin(plugin: BgQualityCheckPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@ -346,13 +354,25 @@ abstract class PluginsModule {
@AllConfigs
@IntoMap
@IntKey(470)
abstract fun bindGlunovoPlugin(plugin: GlunovoPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(475)
abstract fun bindRandomBgPlugin(plugin: RandomBgPlugin): PluginBase
// @Binds
// @NotNSClient
// @IntoMap
// @IntKey(480)
// abstract fun bindOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase
@Binds
@NotNSClient
@IntoMap
@IntKey(480)
abstract fun bindOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase
abstract fun bindsOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase
@Binds
@AllConfigs
@ -372,4 +392,4 @@ abstract class PluginsModule {
@Qualifier
annotation class APS
}
}

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.android.ContributesAndroidInjector
@ -17,6 +17,7 @@ abstract class ReceiversModule {
@ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver
@ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveWorker(): KeepAliveReceiver.KeepAliveWorker
@ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver
@ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver
@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.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.Provides
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.android.ContributesAndroidInjector

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
@ -13,12 +13,15 @@ abstract class WizardModule {
@ContributesAndroidInjector abstract fun swBreakInjector(): SWBreak
@ContributesAndroidInjector abstract fun swButtonInjector(): SWButton
@ContributesAndroidInjector abstract fun swEditNumberWithUnitsInjector(): SWEditNumberWithUnits
@ContributesAndroidInjector abstract fun swEditNumberInjector(): SWEditNumber
@ContributesAndroidInjector abstract fun swEditIntNumberInjector(): SWEditIntNumber
@ContributesAndroidInjector abstract fun swEditStringInjector(): SWEditString
@ContributesAndroidInjector abstract fun swEditEncryptedPasswordInjector(): SWEditEncryptedPassword
@ContributesAndroidInjector abstract fun swEditUrlInjector(): SWEditUrl
@ContributesAndroidInjector abstract fun swFragmentInjector(): SWFragment
@ContributesAndroidInjector abstract fun swPreferenceInjector(): SWPreference
@ContributesAndroidInjector abstract fun swHtmlLinkInjector(): SWHtmlLink
@ContributesAndroidInjector abstract fun swInfotextInjector(): SWInfotext
@ContributesAndroidInjector abstract fun swInfotextInjector(): SWInfoText
@ContributesAndroidInjector abstract fun swItemInjector(): SWItem
@ContributesAndroidInjector abstract fun swPluginInjector(): SWPlugin
@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 com.google.common.base.Joiner
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
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.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Profile
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.XdripCalibrations
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.text.DecimalFormat
@ -23,9 +27,11 @@ import javax.inject.Inject
class CalibrationDialog : DialogFragmentWithDate() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@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
@ -49,15 +55,15 @@ class CalibrationDialog : DialogFragmentWithDate() {
super.onViewCreated(view, savedInstanceState)
val units = profileFunction.getUnits()
val bg = Profile.fromMgdlToUnits(GlucoseStatus(injector).glucoseStatusData?.glucose
val bg = Profile.fromMgdlToUnits(glucoseStatusProvider.glucoseStatusData?.glucose
?: 0.0, units)
if (units == Constants.MMOL)
if (units == GlucoseUnit.MMOL)
binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok)
else
binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: 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() {
@ -68,20 +74,20 @@ class CalibrationDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
if (_binding == null) return false
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 bg = binding.bg.value ?: return false
actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel)
val bg = binding.bg.value
actions.add(rh.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel)
if (bg > 0) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: CALIBRATION $bg")
xdripCalibrations.sendIntent(bg)
OKDialog.showConfirmation(activity, rh.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
uel.log(Action.CALIBRATION, Sources.CalibrationDialog, ValueWithUnit.fromGlucoseUnit(bg, units.asText))
xDripBroadcast.sendCalibration(bg)
})
}
} else
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
}

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -7,45 +8,48 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.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.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.CarbsGenerator
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.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.math.max
class CarbsDialog : DialogFragmentWithDate() {
@Inject lateinit var mainApp: MainApp
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var ctx: Context
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var carbsGenerator: CarbsGenerator
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
@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 {
@ -54,6 +58,8 @@ class CarbsDialog : DialogFragmentWithDate() {
private const val FAV3_DEFAULT = 20
}
private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
validateInputs()
@ -68,15 +74,15 @@ class CarbsDialog : DialogFragmentWithDate() {
val time = binding.time.value.toInt()
if (time > 12 * 60 || time < -12 * 60) {
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) {
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) {
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)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
onCreateViewGeneral()
_binding = DialogCarbsBinding.inflate(inflater, container, false)
binding.time.setOnValueChangedListener { timeOffset: Double ->
run {
val newTime = eventTimeOriginal + timeOffset.toLong() * 1000 * 60
updateDateTime(newTime)
}
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
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()
binding.time.setParams(savedInstanceState?.getDouble("time")
?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher)
binding.time.setParams(
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")
?: 0.0, 0.0, 10.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher)
binding.duration.setParams(
savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, 10.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher
)
binding.carbs.setParams(savedInstanceState?.getDouble("carbs")
?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher)
binding.carbs.setParams(
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.setOnClickListener {
binding.carbs.value = max(0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT))
binding.carbs.value = max(
0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)
)
validateInputs()
}
binding.plus2.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT))
binding.plus2.setOnClickListener {
binding.carbs.value = max(0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT))
binding.carbs.value = max(
0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)
)
validateInputs()
}
binding.plus3.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT))
binding.plus3.setOnClickListener {
binding.carbs.value = max(0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT))
binding.carbs.value = max(
0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)
)
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)
binding.hypoTt.isChecked = true
}
@ -154,6 +192,7 @@ class CarbsDialog : DialogFragmentWithDate() {
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
_binding = null
}
@ -163,7 +202,7 @@ class CarbsDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
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 units = profileFunction.getUnits()
val activityTTDuration = defaultValueHelper.determineActivityTTDuration()
@ -173,93 +212,158 @@ class CarbsDialog : DialogFragmentWithDate() {
val hypoTTDuration = defaultValueHelper.determineHypoTTDuration()
val hypoTT = defaultValueHelper.determineHypoTT()
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
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
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
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()
eventTime -= eventTime % 1000
val time = eventTime + timeOffset * 1000 * 60
if (timeOffset != 0)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time))
if (useAlarm && carbs > 0 && timeOffset > 0)
actions.add(rh.gs(R.string.alarminxmin, timeOffset).formatColor(rh, R.color.info))
val duration = binding.duration.value.toInt()
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) {
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)
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()
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)
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) {
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 {
activitySelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET ACTIVITY $activityTT duration: $activityTTDuration")
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(activityTTDuration)
.reason(resourceHelper.gs(R.string.activity))
.source(Source.USER)
.low(Profile.toMgdl(activityTT, profileFunction.getUnits()))
.high(Profile.toMgdl(activityTT, profileFunction.getUnits()))
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
activitySelected -> {
uel.log(
Action.TT, Sources.CarbDialog,
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY),
ValueWithUnit.fromGlucoseUnit(activityTT, units.asText),
ValueWithUnit.Minute(activityTTDuration)
)
disposable += repository.runTransactionForResult(
InsertAndCancelCurrentTemporaryTargetTransaction(
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 -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration")
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(eatingSoonTTDuration)
.reason(resourceHelper.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()))
.high(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()))
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
uel.log(
Action.TT, Sources.CarbDialog,
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
ValueWithUnit.Minute(eatingSoonTTDuration)
)
disposable += repository.runTransactionForResult(
InsertAndCancelCurrentTemporaryTargetTransaction(
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 -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET HYPO $hypoTT duration: $hypoTTDuration")
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(hypoTTDuration)
.reason(resourceHelper.gs(R.string.hypo))
.source(Source.USER)
.low(Profile.toMgdl(hypoTT, profileFunction.getUnits()))
.high(Profile.toMgdl(hypoTT, profileFunction.getUnits()))
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
hypoSelected -> {
uel.log(
Action.TT, Sources.CarbDialog,
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA),
ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText),
ValueWithUnit.Minute(hypoTTDuration)
)
disposable += repository.runTransactionForResult(
InsertAndCancelCurrentTemporaryTargetTransaction(
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 (duration == 0) {
aapsLogger.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time")
carbsGenerator.createCarb(carbsAfterConstraints, time, CareportalEvent.CARBCORRECTION, notes)
} else {
aapsLogger.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time duration: $duration")
carbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes)
nsUpload.uploadEvent(CareportalEvent.NOTE, DateUtil.now() - 2000, resourceHelper.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset))
}
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
detailedBolusInfo.carbs = carbsAfterConstraints.toDouble()
detailedBolusInfo.context = context
detailedBolusInfo.notes = notes
detailedBolusInfo.carbsDuration = T.hours(duration.toLong()).msecs()
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)
}
} else
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
}

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -10,21 +11,28 @@ import androidx.annotation.StringRes
import com.google.common.base.Joiner
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
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.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.shared.logging.LTag
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.T
import info.nightscout.androidaps.utils.Translator
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 org.json.JSONObject
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject
@ -32,11 +40,15 @@ import javax.inject.Inject
class CareDialog : DialogFragmentWithDate() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var mainApp: MainApp
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var ctx: Context
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var nsUpload: NSUpload
@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 {
BGCHECK,
@ -49,6 +61,8 @@ class CareDialog : DialogFragmentWithDate() {
}
private var options: EventType = EventType.BGCHECK
//private var valuesWithUnit = mutableListOf<XXXValueWithUnit?>()
private var valuesWithUnit = mutableListOf<ValueWithUnit?>()
@StringRes
private var event: Int = R.string.none
@ -97,7 +111,7 @@ class CareDialog : DialogFragmentWithDate() {
EventType.QUESTION -> R.drawable.ic_cp_question
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.SENSOR_INSERT -> R.string.careportal_cgmsensorinsert
EventType.BATTERY_CHANGE -> R.string.careportal_pumpbatterychange
@ -110,7 +124,7 @@ class CareDialog : DialogFragmentWithDate() {
when (options) {
EventType.QUESTION,
EventType.ANNOUNCEMENT,
EventType.BGCHECK -> {
EventType.BGCHECK -> {
binding.durationLayout.visibility = View.GONE
}
@ -122,13 +136,13 @@ class CareDialog : DialogFragmentWithDate() {
}
EventType.NOTE,
EventType.EXERCISE -> {
EventType.EXERCISE -> {
binding.bgLayout.visibility = View.GONE
binding.bgsource.visibility = View.GONE
}
}
val bg = Profile.fromMgdlToUnits(GlucoseStatus(injector).glucoseStatusData?.glucose
val bg = Profile.fromMgdlToUnits(glucoseStatusProvider.glucoseStatusData?.glucose
?: 0.0, profileFunction.getUnits())
val bgTextWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
@ -138,18 +152,18 @@ class CareDialog : DialogFragmentWithDate() {
}
}
if (profileFunction.getUnits() == Constants.MMOL) {
binding.bgunits.text = resourceHelper.gs(R.string.mmol)
if (profileFunction.getUnits() == GlucoseUnit.MMOL) {
binding.bgUnits.text = rh.gs(R.string.mmol)
binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, bgTextWatcher)
} else {
binding.bgunits.text = resourceHelper.gs(R.string.mgdl)
binding.bgUnits.text = rh.gs(R.string.mgdl)
binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, bgTextWatcher)
}
binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: 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
}
@ -159,70 +173,75 @@ class CareDialog : DialogFragmentWithDate() {
}
override fun submit(): Boolean {
val enteredBy = sp.getString("careportal_enteredby", "")
val unitResId = if (profileFunction.getUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol
val enteredBy = sp.getString("careportal_enteredby", "AndroidAPS")
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()
if (options == EventType.BGCHECK || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT) {
val type =
val meterType =
when {
binding.meter.isChecked -> CareportalEvent.FINGER
binding.sensor.isChecked -> CareportalEvent.SENSOR
else -> CareportalEvent.MANUAL
binding.meter.isChecked -> TherapyEvent.MeterType.FINGER
binding.sensor.isChecked -> TherapyEvent.MeterType.SENSOR
else -> TherapyEvent.MeterType.MANUAL
}
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_glucosetype) + ": " + translator.translate(type))
actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, binding.bg.value) + " " + resourceHelper.gs(unitResId))
json.put("glucose", binding.bg.value)
json.put("glucoseType", type)
actions.add(rh.gs(R.string.careportal_newnstreatment_glucosetype) + ": " + translator.translate(meterType))
actions.add(rh.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, binding.bg.value) + " " + rh.gs(unitResId))
therapyEvent.glucoseType = meterType
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) {
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_duration_label) + ": " + resourceHelper.gs(R.string.format_mins, binding.duration.value.toInt()))
json.put("duration", binding.duration.value.toInt())
actions.add(rh.gs(R.string.careportal_newnstreatment_duration_label) + ": " + rh.gs(R.string.format_mins, 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()
if (notes.isNotEmpty()) {
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes)
json.put("notes", notes)
actions.add(rh.gs(R.string.notes_label) + ": " + notes)
therapyEvent.note = notes
}
eventTime -= eventTime % 1000
if (eventTimeChanged)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
if (eventTimeChanged) actions.add(rh.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
json.put("created_at", DateUtil.toISOString(eventTime))
json.put("mills", eventTime)
json.put("eventType", when (options) {
EventType.BGCHECK -> CareportalEvent.BGCHECK
EventType.SENSOR_INSERT -> CareportalEvent.SENSORCHANGE
EventType.BATTERY_CHANGE -> CareportalEvent.PUMPBATTERYCHANGE
EventType.NOTE -> CareportalEvent.NOTE
EventType.EXERCISE -> CareportalEvent.EXERCISE
EventType.QUESTION -> CareportalEvent.QUESTION
EventType.ANNOUNCEMENT -> CareportalEvent.ANNOUNCEMENT
})
json.put("units", profileFunction.getUnits())
if (enteredBy.isNotEmpty())
json.put("enteredBy", enteredBy)
therapyEvent.enteredBy = enteredBy
val source = when (options) {
EventType.BGCHECK -> Sources.BgCheck
EventType.SENSOR_INSERT -> Sources.SensorInsert
EventType.BATTERY_CHANGE -> Sources.BatteryChange
EventType.NOTE -> Sources.Note
EventType.EXERCISE -> Sources.Exercise
EventType.QUESTION -> Sources.Question
EventType.ANNOUNCEMENT -> Sources.Announcement
}
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(event), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
val careportalEvent = CareportalEvent(injector)
careportalEvent.date = eventTime
careportalEvent.source = Source.USER
careportalEvent.eventType = when (options) {
EventType.BGCHECK -> CareportalEvent.BGCHECK
EventType.SENSOR_INSERT -> CareportalEvent.SENSORCHANGE
EventType.BATTERY_CHANGE -> CareportalEvent.PUMPBATTERYCHANGE
EventType.NOTE -> CareportalEvent.NOTE
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)
OKDialog.showConfirmation(activity, rh.gs(event), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(therapyEvent))
.subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) }
)
valuesWithUnit.add(0, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged })
valuesWithUnit.add(1, ValueWithUnit.TherapyEventType(therapyEvent.type))
uel.log(Action.CAREPORTAL, source, notes, valuesWithUnit)
}, null)
}
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
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -9,16 +8,20 @@ import android.view.ViewGroup
import com.google.common.base.Joiner
import info.nightscout.androidaps.R
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.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueue
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
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.extensions.formatColor
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.text.DecimalFormat
import java.util.*
@ -28,10 +31,11 @@ import kotlin.math.abs
class ExtendedBolusDialog : DialogFragmentWithDate() {
@Inject lateinit var ctx: Context
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var uel: UserEntryLogger
private var _binding: DialogExtendedbolusBinding? = null
@ -75,27 +79,24 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
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 actions: LinkedList<String> = LinkedList()
val insulinAfterConstraint = constraintChecker.applyExtendedBolusConstraints(Constraint(insulin)).value()
actions.add(resourceHelper.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.formatinsulinunits, insulinAfterConstraint))
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
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 ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: EXTENDED BOLUS $insulinAfterConstraint duration: $durationInMinutes")
OKDialog.showConfirmation(activity, rh.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
uel.log(Action.EXTENDED_BOLUS, Sources.ExtendedBolusDialog,
ValueWithUnit.Insulin(insulinAfterConstraint),
ValueWithUnit.Minute(durationInMinutes))
commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -10,21 +9,28 @@ import com.google.common.base.Joiner
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
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.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueue
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.general.nsclient.NSUpload
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DecimalFormatter
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.extensions.formatColor
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.*
import javax.inject.Inject
import kotlin.math.abs
@ -32,11 +38,14 @@ import kotlin.math.abs
class FillDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var ctx: Context
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private val disposable = CompositeDisposable()
private var _binding: DialogFillBinding? = null
@ -97,52 +106,76 @@ class FillDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
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 insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
if (insulinAfterConstraints > 0) {
actions.add(resourceHelper.gs(R.string.fillwarning))
actions.add(rh.gs(R.string.fillwarning))
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)
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
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
if (insulinChange)
actions.add(resourceHelper.gs(R.string.record_insulin_cartridge_change).formatColor(resourceHelper, R.color.actionsConfirm))
val notes = binding.notesLayout.notes.text.toString()
actions.add(rh.gs(R.string.record_insulin_cartridge_change).formatColor(rh, R.color.actionsConfirm))
val notes: String = binding.notesLayout.notes.text.toString()
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
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) {
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) {
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)
}
if (siteChange) {
aapsLogger.debug("USER ENTRY: SITE CHANGE")
nsUpload.generateCareportalEvent(CareportalEvent.SITECHANGE, eventTime, notes)
uel.log(Action.SITE_CHANGE, Sources.FillDialog,
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) {
// add a second for case of both checked
aapsLogger.debug("USER ENTRY: INSULIN CHANGE")
nsUpload.generateCareportalEvent(CareportalEvent.INSULINCHANGE, eventTime + 1000, notes)
uel.log(Action.RESERVOIR_CHANGE, Sources.FillDialog,
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)
}
} else {
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()
@ -153,18 +186,12 @@ class FillDialog : DialogFragmentWithDate() {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.insulin = insulin
detailedBolusInfo.context = context
detailedBolusInfo.source = Source.USER
detailedBolusInfo.isValid = false // do not count it in IOB (for pump history)
detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING
detailedBolusInfo.notes = notes
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -9,30 +8,33 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.activities.ErrorHelperActivity
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.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
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.toVisibility
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.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.max
@ -40,13 +42,16 @@ import kotlin.math.max
class InsulinDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var ctx: Context
@Inject lateinit var repository: AppRepository
@Inject lateinit var config: Config
@Inject lateinit var bolusTimer: BolusTimer
@Inject lateinit var uel: UserEntryLogger
companion object {
@ -55,6 +60,8 @@ class InsulinDialog : DialogFragmentWithDate() {
private const val PLUS3_DEFAULT = 2.0
}
private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
validateInputs()
@ -74,11 +81,11 @@ class InsulinDialog : DialogFragmentWithDate() {
val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
if (abs(binding.time.value.toInt()) > 12 * 60) {
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) {
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")
?: 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.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()
}
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.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()
}
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.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()
}
@ -136,78 +143,94 @@ class InsulinDialog : DialogFragmentWithDate() {
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
_binding = null
}
override fun submit(): Boolean {
if (_binding == null) return false
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 actions: LinkedList<String?> = LinkedList()
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 eatingSoonChecked = binding.startEatingSoonTt.isChecked
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)
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))
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 eatingSoonTT = defaultValueHelper.determineEatingSoonTT()
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 time = DateUtil.now() + T.mins(timeOffset.toLong()).msecs()
val time = dateUtil.now() + T.mins(timeOffset.toLong()).msecs()
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()
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) {
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) {
aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration")
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(eatingSoonTTDuration)
.reason(resourceHelper.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()))
.high(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()))
activePlugin.activeTreatments.addToHistoryTempTarget(tempTarget)
uel.log(Action.TT, Sources.InsulinDialog,
notes,
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
ValueWithUnit.Minute(eatingSoonTTDuration))
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
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)
})
}
if (insulinAfterConstraints > 0) {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS
detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.context = context
detailedBolusInfo.source = Source.USER
detailedBolusInfo.notes = notes
detailedBolusInfo.timestamp = time
if (recordOnlyChecked) {
aapsLogger.debug("USER ENTRY: BOLUS RECORD ONLY $insulinAfterConstraints")
detailedBolusInfo.date = time
activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false)
uel.log(Action.BOLUS, Sources.InsulinDialog,
rh.gs(R.string.record) + if (notes.isNotEmpty()) ": " + notes else "",
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 {
aapsLogger.debug("USER ENTRY: BOLUS $insulinAfterConstraints")
detailedBolusInfo.date = DateUtil.now()
uel.log(Action.BOLUS, Sources.InsulinDialog,
notes,
ValueWithUnit.Insulin(insulinAfterConstraints))
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
} else {
bolusTimer.removeBolusReminder()
}
}
})
@ -217,7 +240,7 @@ class InsulinDialog : DialogFragmentWithDate() {
}
} else
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
}

View file

@ -1,9 +1,9 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -13,22 +13,35 @@ import androidx.fragment.app.FragmentManager
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R
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.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.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.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.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
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.sharedPreferences.SP
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject
class LoopDialog : DaggerDialogFragment() {
@ -36,28 +49,37 @@ class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var ctx: Context
@Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var rxBus: RxBus
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var objectivesPlugin: ObjectivesPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var loop: Loop
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@Inject lateinit var commandQueue: CommandQueue
@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 _binding: DialogLoopBinding? = null
private var loopHandler = Handler()
private var refreshDialog: Runnable? = null
private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
private lateinit var refreshDialog: Runnable
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
val disposable = CompositeDisposable()
override fun 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) {
@ -65,8 +87,10 @@ class LoopDialog : DaggerDialogFragment() {
savedInstanceState.putInt("showOkCancel", if (showOkCancel) 1 else 0)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// load data from bundle
(savedInstanceState ?: arguments)?.let { bundle ->
showOkCancel = bundle.getInt("showOkCancel", 1) == 1
@ -104,129 +128,141 @@ class LoopDialog : DaggerDialogFragment() {
binding.cancel.setOnClickListener { dismiss() }
refreshDialog = Runnable {
scheduleUpdateGUI("refreshDialog")
loopHandler.postDelayed(refreshDialog, 15 * 1000L)
runOnUiThread { updateGUI("refreshDialog") }
handler.postDelayed(refreshDialog, 15 * 1000L)
}
loopHandler.postDelayed(refreshDialog, 15 * 1000L)
handler.postDelayed(refreshDialog, 15 * 1000L)
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
_binding = null
loopHandler.removeCallbacksAndMessages(null)
handler.removeCallbacksAndMessages(null)
disposable.clear()
}
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
fun updateGUI(from: String) {
if (_binding == null) return
aapsLogger.debug("UpdateGUI from $from")
val pumpDescription: PumpDescription = activePlugin.activePump.pumpDescription
val closedLoopAllowed = objectivesPlugin.isClosedLoopAllowed(Constraint(true))
val lgsEnabled = objectivesPlugin.isLgsAllowed(Constraint(true))
val closedLoopAllowed = constraintChecker.isClosedLoopAllowed(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")
if (profileFunction.isProfileValid("LoopDialogUpdateGUI")) {
if (loopPlugin.isEnabled(PluginType.LOOP)) {
val pump = activePlugin.activePump
binding.overviewDisconnect15m.visibility = pumpDescription.tempDurationStep15mAllowed.toVisibility()
binding.overviewDisconnect30m.visibility = pumpDescription.tempDurationStep30mAllowed.toVisibility()
when {
pump.isSuspended() -> {
binding.overviewLoop.visibility = View.GONE
binding.overviewSuspend.visibility = View.GONE
binding.overviewPump.visibility = View.GONE
}
!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 {
closedLoopAllowed.value() -> {
binding.overviewCloseloop.visibility = (apsMode != "closed").toVisibility()
binding.overviewLgsloop.visibility = (apsMode != "lgs").toVisibility()
binding.overviewOpenloop.visibility = (apsMode != "open").toVisibility()
}
lgsEnabled.value() -> {
apsMode == "closed" -> {
binding.overviewCloseloop.visibility = View.GONE
binding.overviewLgsloop.visibility = (apsMode != "lgs").toVisibility()
binding.overviewOpenloop.visibility = (apsMode != "open").toVisibility()
binding.overviewLgsloop.visibility = View.VISIBLE
binding.overviewOpenloop.visibility = View.VISIBLE
}
else -> {
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 -> {
binding.overviewCloseloop.visibility = View.GONE
binding.overviewLgsloop.visibility = View.GONE
binding.overviewOpenloop.visibility = View.GONE
}
}
binding.overviewEnable.visibility = View.GONE
binding.overviewDisable.visibility = View.VISIBLE
if (!loopPlugin.isSuspended) {
binding.overviewSuspendHeader.text = resourceHelper.gs(R.string.suspendloop)
binding.overviewResume.visibility = View.GONE
binding.overviewSuspendButtons.visibility = View.VISIBLE
binding.overviewSuspend.visibility = View.VISIBLE
} else {
if (!loopPlugin.isDisconnected) {
binding.overviewSuspendHeader.text = resourceHelper.gs(R.string.resumeloop)
binding.overviewResume.visibility = View.VISIBLE
binding.overviewSuspendButtons.visibility = View.GONE
binding.overviewSuspend.visibility = View.VISIBLE
} else
binding.overviewSuspend.visibility = View.GONE
}
} else {
binding.overviewEnable.visibility = View.VISIBLE
binding.overviewDisable.visibility = View.GONE
binding.overviewSuspend.visibility = View.GONE
}
if (!loopPlugin.isDisconnected) {
binding.overviewPumpHeader.text = resourceHelper.gs(R.string.disconnectpump)
binding.overviewDisconnect15m.visibility = pumpDescription.tempDurationStep15mAllowed.toVisibility()
binding.overviewDisconnect30m.visibility = pumpDescription.tempDurationStep30mAllowed.toVisibility()
binding.overviewSuspend.visibility = View.VISIBLE
binding.overviewSuspendHeader.text = rh.gs(R.string.suspendloop)
binding.overviewSuspendButtons.visibility = View.VISIBLE
binding.overviewResume.visibility = View.GONE
binding.overviewPump.visibility = View.VISIBLE
binding.overviewPumpHeader.text = rh.gs(R.string.disconnectpump)
binding.overviewDisconnectButtons.visibility = View.VISIBLE
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 {
var description = ""
when (v.id) {
R.id.overview_closeloop -> description = resourceHelper.gs(R.string.closedloop)
R.id.overview_lgsloop -> description = resourceHelper.gs(R.string.lowglucosesuspend)
R.id.overview_openloop -> description = resourceHelper.gs(R.string.openloop)
R.id.overview_disable -> description = resourceHelper.gs(R.string.disableloop)
R.id.overview_enable -> description = resourceHelper.gs(R.string.enableloop)
R.id.overview_resume -> description = resourceHelper.gs(R.string.resume)
R.id.overview_reconnect -> description = resourceHelper.gs(R.string.reconnect)
R.id.overview_suspend_1h -> description = resourceHelper.gs(R.string.suspendloopfor1h)
R.id.overview_suspend_2h -> description = resourceHelper.gs(R.string.suspendloopfor2h)
R.id.overview_suspend_3h -> description = resourceHelper.gs(R.string.suspendloopfor3h)
R.id.overview_suspend_10h -> description = resourceHelper.gs(R.string.suspendloopfor10h)
R.id.overview_disconnect_15m -> description = resourceHelper.gs(R.string.disconnectpumpfor15m)
R.id.overview_disconnect_30m -> description = resourceHelper.gs(R.string.disconnectpumpfor30m)
R.id.overview_disconnect_1h -> description = resourceHelper.gs(R.string.disconnectpumpfor1h)
R.id.overview_disconnect_2h -> description = resourceHelper.gs(R.string.disconnectpumpfor2h)
R.id.overview_disconnect_3h -> description = resourceHelper.gs(R.string.disconnectpumpfor3h)
R.id.overview_closeloop -> description = rh.gs(R.string.closedloop)
R.id.overview_lgsloop -> description = rh.gs(R.string.lowglucosesuspend)
R.id.overview_openloop -> description = rh.gs(R.string.openloop)
R.id.overview_disable -> description = rh.gs(R.string.disableloop)
R.id.overview_enable -> description = rh.gs(R.string.enableloop)
R.id.overview_resume -> description = rh.gs(R.string.resume)
R.id.overview_reconnect -> description = rh.gs(R.string.reconnect)
R.id.overview_suspend_1h -> description = rh.gs(R.string.suspendloopfor1h)
R.id.overview_suspend_2h -> description = rh.gs(R.string.suspendloopfor2h)
R.id.overview_suspend_3h -> description = rh.gs(R.string.suspendloopfor3h)
R.id.overview_suspend_10h -> description = rh.gs(R.string.suspendloopfor10h)
R.id.overview_disconnect_15m -> description = rh.gs(R.string.disconnectpumpfor15m)
R.id.overview_disconnect_30m -> description = rh.gs(R.string.disconnectpumpfor30m)
R.id.overview_disconnect_1h -> description = rh.gs(R.string.disconnectpumpfor1h)
R.id.overview_disconnect_2h -> description = rh.gs(R.string.disconnectpumpfor2h)
R.id.overview_disconnect_3h -> description = rh.gs(R.string.disconnectpumpfor3h)
}
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)
})
}
@ -234,138 +270,157 @@ class LoopDialog : DaggerDialogFragment() {
}
fun onClick(v: View): Boolean {
val profile = profileFunction.getProfile() ?: return true
when (v.id) {
R.id.overview_closeloop -> {
aapsLogger.debug("USER ENTRY: CLOSED LOOP MODE")
R.id.overview_closeloop -> {
uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog)
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
}
R.id.overview_lgsloop -> {
aapsLogger.debug("USER ENTRY: LGS LOOP MODE")
R.id.overview_lgsloop -> {
uel.log(Action.LGS_LOOP_MODE, Sources.LoopDialog)
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
}
R.id.overview_openloop -> {
aapsLogger.debug("USER ENTRY: OPEN LOOP MODE")
R.id.overview_openloop -> {
uel.log(Action.OPEN_LOOP_MODE, Sources.LoopDialog)
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
}
R.id.overview_disable -> {
aapsLogger.debug("USER ENTRY: LOOP DISABLED")
loopPlugin.setPluginEnabled(PluginType.LOOP, false)
loopPlugin.setFragmentVisible(PluginType.LOOP, false)
configBuilderPlugin.storeSettings("DisablingLoop")
rxBus.send(EventRefreshOverview("suspendmenu"))
R.id.overview_disable -> {
uel.log(Action.LOOP_DISABLED, Sources.LoopDialog)
(loop as PluginBase).setPluginEnabled(PluginType.LOOP, false)
(loop as PluginBase).setFragmentVisible(PluginType.LOOP, false)
configBuilder.storeSettings("DisablingLoop")
rxBus.send(EventRefreshOverview("suspend_menu"))
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
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
}
R.id.overview_enable -> {
aapsLogger.debug("USER ENTRY: LOOP ENABLED")
loopPlugin.setPluginEnabled(PluginType.LOOP, true)
loopPlugin.setFragmentVisible(PluginType.LOOP, true)
configBuilderPlugin.storeSettings("EnablingLoop")
rxBus.send(EventRefreshOverview("suspendmenu"))
loopPlugin.createOfflineEvent(0)
R.id.overview_enable -> {
uel.log(Action.LOOP_ENABLED, Sources.LoopDialog)
(loop as PluginBase).setPluginEnabled(PluginType.LOOP, true)
(loop as PluginBase).setFragmentVisible(PluginType.LOOP, true)
configBuilder.storeSettings("EnablingLoop")
rxBus.send(EventRefreshOverview("suspend_menu"))
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
}
R.id.overview_resume, R.id.overview_reconnect -> {
aapsLogger.debug("USER ENTRY: RESUME")
loopPlugin.suspendTo(0L)
rxBus.send(EventRefreshOverview("suspendmenu"))
uel.log(if (v.id == R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog)
disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspend_menu"))
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
}
}
})
sp.putBoolean(R.string.key_objectiveusereconnect, true)
loopPlugin.createOfflineEvent(0)
return true
}
R.id.overview_suspend_1h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 1h")
loopPlugin.suspendLoop(60)
rxBus.send(EventRefreshOverview("suspendmenu"))
R.id.overview_suspend_1h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1))
loop.suspendLoop(T.hours(1).mins().toInt())
rxBus.send(EventRefreshOverview("suspend_menu"))
return true
}
R.id.overview_suspend_2h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 2h")
loopPlugin.suspendLoop(120)
rxBus.send(EventRefreshOverview("suspendmenu"))
R.id.overview_suspend_2h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2))
loop.suspendLoop(T.hours(2).mins().toInt())
rxBus.send(EventRefreshOverview("suspend_menu"))
return true
}
R.id.overview_suspend_3h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 3h")
loopPlugin.suspendLoop(180)
rxBus.send(EventRefreshOverview("suspendmenu"))
R.id.overview_suspend_3h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3))
loop.suspendLoop(T.hours(3).mins().toInt())
rxBus.send(EventRefreshOverview("suspend_menu"))
return true
}
R.id.overview_suspend_10h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 10h")
loopPlugin.suspendLoop(600)
rxBus.send(EventRefreshOverview("suspendmenu"))
R.id.overview_suspend_10h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10))
loop.suspendLoop(T.hours(10).mins().toInt())
rxBus.send(EventRefreshOverview("suspend_menu"))
return true
}
R.id.overview_disconnect_15m -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 15m")
loopPlugin.disconnectPump(15, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
R.id.overview_disconnect_15m -> {
profileFunction.getProfile()?.let { profile ->
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15))
loop.goToZeroTemp(T.mins(15).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true
}
R.id.overview_disconnect_30m -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 30m")
loopPlugin.disconnectPump(30, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
R.id.overview_disconnect_30m -> {
profileFunction.getProfile()?.let { profile ->
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30))
loop.goToZeroTemp(T.mins(30).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true
}
R.id.overview_disconnect_1h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 1h")
loopPlugin.disconnectPump(60, profile)
R.id.overview_disconnect_1h -> {
profileFunction.getProfile()?.let { 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)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_2h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 2h")
loopPlugin.disconnectPump(120, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
R.id.overview_disconnect_2h -> {
profileFunction.getProfile()?.let { profile ->
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2))
loop.goToZeroTemp(T.hours(2).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true
}
R.id.overview_disconnect_3h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 3h")
loopPlugin.disconnectPump(180, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
R.id.overview_disconnect_3h -> {
profileFunction.getProfile()?.let { profile ->
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3))
loop.goToZeroTemp(T.hours(3).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("suspend_menu"))
}
return true
}
}
@ -379,7 +434,7 @@ class LoopDialog : DaggerDialogFragment() {
it.commitAllowingStateLoss()
}
} catch (e: IllegalStateException) {
aapsLogger.debug(e.localizedMessage)
aapsLogger.debug(e.localizedMessage ?: e.toString())
}
}
}

View file

@ -1,6 +1,8 @@
package info.nightscout.androidaps.dialogs
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -8,33 +10,69 @@ import android.widget.ArrayAdapter
import com.google.common.base.Joiner
import info.nightscout.androidaps.Constants
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.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.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.shared.logging.LTag
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.T
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.collections.ArrayList
class ProfileSwitchDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@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 val disposable = CompositeDisposable()
private var _binding: DialogProfileswitchBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
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) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("duration", binding.duration.value)
@ -42,8 +80,10 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
savedInstanceState.putDouble("timeshift", binding.timeshift.value)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
onCreateViewGeneral()
arguments?.let { bundle ->
profileIndex = bundle.getInt("profileIndex", 0)
@ -55,18 +95,36 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.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)
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.duration.setParams(
savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok,
textWatcher
)
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
context?.let { context ->
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
?: 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)
binding.profile.adapter = adapter
// set selected to actual profile
@ -74,57 +132,113 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
binding.profile.setSelection(profileIndex as Int)
else
for (p in profileList.indices)
if (profileList[p] == profileFunction.getProfileName(false))
if (profileList[p] == profileFunction.getOriginalProfileName())
binding.profile.setSelection(p)
} ?: return
treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now())?.let { ps ->
if (ps.isCPP) {
binding.reuselayout.visibility = View.VISIBLE
binding.reusebutton.text = resourceHelper.gs(R.string.reuse_profile_pct_hours, ps.percentage, ps.timeshift)
binding.reusebutton.setOnClickListener {
binding.percentage.value = ps.percentage.toDouble()
binding.timeshift.value = ps.timeshift.toDouble()
}
} else {
binding.reuselayout.visibility = View.GONE
}
}
profileFunction.getProfile()?.let { profile ->
if (profile is ProfileSealed.EPS)
if (profile.value.originalPercentage != 100 || profile.value.originalTimeshift != 0L) {
binding.reuselayout.visibility = View.VISIBLE
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.percentage.value = profile.value.originalPercentage.toDouble()
binding.timeshift.value = profile.value.originalTimeshift.toDouble()
}
}
}
binding.ttLayout.visibility = View.GONE
}
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
_binding = null
}
override fun submit(): Boolean {
if (_binding == null) return false
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
?: return false
val actions: LinkedList<String> = LinkedList()
val duration = binding.duration.value?.toInt() ?: return false
if (duration > 0)
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, duration))
val profile = binding.profile.selectedItem.toString()
actions.add(resourceHelper.gs(R.string.profile) + ": " + profile)
val duration = binding.duration.value.toInt()
if (duration > 0L)
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, duration))
val profileName = binding.profile.selectedItem.toString()
actions.add(rh.gs(R.string.profile) + ": " + profileName)
val percent = binding.percentage.value.toInt()
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()
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()
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)
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 ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: PROFILE SWITCH $profile percent: $percent timeshift: $timeShift duration: $duration")
treatmentsPlugin.doProfileSwitch(profileStore, profile, duration, percent, timeShift, eventTime)
})
val ps = profileFunction.buildProfileSwitch(profileStore, profileName, duration, percent, timeShift, eventTime) ?: return@let
val validity = ProfileSealed.PS(ps).isValid(rh.gs(R.string.careportal_profileswitch), activePlugin.activePump, config, rh, rxBus, hardLimits, false)
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
}

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -9,18 +8,18 @@ import android.view.ViewGroup
import com.google.common.base.Joiner
import info.nightscout.androidaps.R
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.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.PumpDescription
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
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.extensions.formatColor
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.text.DecimalFormat
import java.util.*
@ -30,11 +29,12 @@ import kotlin.math.abs
class TempBasalDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var ctx: Context
@Inject lateinit var uel: UserEntryLogger
private var isPercentPump = true
@ -47,8 +47,8 @@ class TempBasalDialog : DialogFragmentWithDate() {
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("duration", binding.duration.value)
savedInstanceState.putDouble("basalpercentinput", binding.basalpercentinput.value)
savedInstanceState.putDouble("basalabsoluteinput", binding.basalabsoluteinput.value)
savedInstanceState.putDouble("basalPercentInput", binding.basalPercentInput.value)
savedInstanceState.putDouble("basalAbsoluteInput", binding.basalAbsoluteInput.value)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@ -67,11 +67,11 @@ class TempBasalDialog : DialogFragmentWithDate() {
val maxTempPercent = pumpDescription.maxTempPercent.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)
binding.basalabsoluteinput.setParams(savedInstanceState?.getDouble("basalabsoluteinput")
?: profile.basal, 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok)
binding.basalAbsoluteInput.setParams(savedInstanceState?.getDouble("basalAbsoluteInput")
?: profile.getBasal(), 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok)
val tempDurationStep = pumpDescription.tempDurationStep.toDouble()
val tempMaxDuration = pumpDescription.tempMaxDuration.toDouble()
@ -97,43 +97,42 @@ class TempBasalDialog : DialogFragmentWithDate() {
if (_binding == null) return false
var percent = 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 actions: LinkedList<String> = LinkedList()
if (isPercentPump) {
val basalPercentInput = SafeParse.stringToInt(binding.basalpercentinput.text)
val basalPercentInput = SafeParse.stringToInt(binding.basalPercentInput.text)
percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value()
actions.add(resourceHelper.gs(R.string.tempbasal_label) + ": $percent%")
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, durationInMinutes))
if (percent != basalPercentInput) actions.add(resourceHelper.gs(R.string.constraintapllied))
actions.add(rh.gs(R.string.tempbasal_label) + ": $percent%")
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
if (percent != basalPercentInput) actions.add(rh.gs(R.string.constraintapllied))
} else {
val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalabsoluteinput.text)
val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalAbsoluteInput.text)
absolute = constraintChecker.applyBasalConstraints(Constraint(basalAbsoluteInput), profile).value()
actions.add(resourceHelper.gs(R.string.tempbasal_label) + ": " + resourceHelper.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.tempbasal_label) + ": " + rh.gs(R.string.pump_basebasalrate, absolute))
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
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 ->
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() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
}
}
}
if (isPercentPump) {
aapsLogger.debug("USER ENTRY: TEMP BASAL $percent% duration: $durationInMinutes")
commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, callback)
uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog,
ValueWithUnit.Percent(percent),
ValueWithUnit.Minute(durationInMinutes))
commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
} else {
aapsLogger.debug("USER ENTRY: TEMP BASAL $absolute duration: $durationInMinutes")
commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, callback)
uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog,
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 info.nightscout.androidaps.Constants
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.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.GlucoseUnit
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.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class TempTargetDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var uel: UserEntryLogger
@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
@ -45,7 +57,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
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?,
@ -61,32 +73,32 @@ class TempTargetDialog : DialogFragmentWithDate() {
binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: 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(
savedInstanceState?.getDouble("temptarget")
savedInstanceState?.getDouble("tempTarget")
?: 8.0,
Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok)
else
binding.temptarget.setParams(
savedInstanceState?.getDouble("temptarget")
savedInstanceState?.getDouble("tempTarget")
?: 144.0,
Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
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
context?.let { context ->
if (activePlugin.activeTreatments.tempTargetFromHistory != null)
if (repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing)
binding.targetCancel.visibility = View.VISIBLE
else
binding.targetCancel.visibility = View.GONE
reasonList = Lists.newArrayList(
resourceHelper.gs(R.string.manual),
resourceHelper.gs(R.string.eatingsoon),
resourceHelper.gs(R.string.activity),
resourceHelper.gs(R.string.hypo)
rh.gs(R.string.manual),
rh.gs(R.string.eatingsoon),
rh.gs(R.string.activity),
rh.gs(R.string.hypo)
)
val adapterReason = ArrayAdapter(context, R.layout.spinner_centered, reasonList)
binding.reason.adapter = adapterReason
@ -121,65 +133,88 @@ class TempTargetDialog : DialogFragmentWithDate() {
R.id.eating_soon -> {
binding.temptarget.value = defaultValueHelper.determineEatingSoonTT()
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.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.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() {
super.onDestroyView()
disposable.clear()
_binding = null
}
override fun submit(): Boolean {
if (_binding == null) return false
val actions: LinkedList<String> = LinkedList()
val reason = binding.reason.selectedItem?.toString() ?: return false
val unitResId = if (profileFunction.getUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol
var reason = binding.reason.selectedItem?.toString() ?: return false
val unitResId = if (profileFunction.getUnits() == GlucoseUnit.MGDL) R.string.mgdl else R.string.mmol
val target = binding.temptarget.value
val duration = binding.duration.value.toInt()
if (target != 0.0 && duration != 0) {
actions.add(resourceHelper.gs(R.string.reason) + ": " + reason)
actions.add(resourceHelper.gs(R.string.target_label) + ": " + Profile.toCurrentUnitsString(profileFunction, target) + " " + resourceHelper.gs(unitResId))
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, duration))
actions.add(rh.gs(R.string.reason) + ": " + reason)
actions.add(rh.gs(R.string.target_label) + ": " + Profile.toCurrentUnitsString(profileFunction, target) + " " + rh.gs(unitResId))
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, duration))
} else {
actions.add(resourceHelper.gs(R.string.stoptemptarget))
actions.add(rh.gs(R.string.stoptemptarget))
reason = rh.gs(R.string.stoptemptarget)
}
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 ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: TEMP TARGET $target duration: $duration")
if (target == 0.0 || duration == 0) {
val tempTarget = TempTarget()
.date(eventTime)
.duration(0)
.low(0.0).high(0.0)
.source(Source.USER)
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)
OKDialog.showConfirmation(activity, rh.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
val units = profileFunction.getUnits()
when(reason) {
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))
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))
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))
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))
rh.gs(R.string.stoptemptarget) -> uel.log(Action.CANCEL_TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged })
}
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)
})
}

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -9,25 +8,31 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.activities.ErrorHelperActivity
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.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueue
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.queue.Callback
import info.nightscout.androidaps.utils.DecimalFormatter
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.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject
@ -36,11 +41,15 @@ import kotlin.math.abs
class TreatmentDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var ctx: Context
@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 {
override fun afterTextChanged(s: Editable) {}
@ -55,11 +64,11 @@ class TreatmentDialog : DialogFragmentWithDate() {
val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
if (SafeParse.stringToInt(binding.carbs.text) > maxCarbs) {
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) {
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)
binding.insulin.setParams(savedInstanceState?.getDouble("insulin")
?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher)
binding.recordOnlyLayout.visibility = View.GONE
}
override fun onDestroyView() {
@ -106,7 +116,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
if (_binding == null) return false
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 recordOnlyChecked = binding.recordOnly.isChecked
val actions: LinkedList<String?> = LinkedList()
@ -114,48 +124,70 @@ class TreatmentDialog : DialogFragmentWithDate() {
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
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)
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))
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) {
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)
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) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: BOLUS insulin $insulin carbs: $carbs")
OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
val action = when {
insulinAfterConstraints.equals(0.0) -> Action.CARBS
carbsAfterConstraints == 0 -> Action.BOLUS
else -> Action.TREATMENT
}
val detailedBolusInfo = DetailedBolusInfo()
if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION
if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS
if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CARBS_CORRECTION
if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.carbs = carbsAfterConstraints.toDouble()
detailedBolusInfo.context = context
detailedBolusInfo.source = Source.USER
if (!(recordOnlyChecked && (detailedBolusInfo.insulin > 0 || pumpDescription.storesCarbInfo))) {
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
if (recordOnlyChecked) {
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() {
override fun run() {
if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
}
}
}
})
} else
activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false)
})
} else
uel.log(action, Sources.TreatmentDialog,
ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbs != 0 })
}
})
}
} else
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
}

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -13,32 +14,29 @@ import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter
import android.widget.CompoundButton
import androidx.fragment.app.FragmentManager
import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
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.db.BgReading
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.Constraint
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.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.utils.*
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 io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import java.text.DecimalFormat
import java.util.*
@ -47,19 +45,27 @@ import kotlin.math.abs
class WizardDialog : DaggerDialogFragment() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var mainApp: MainApp
@Inject lateinit var ctx: Context
@Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var rxBus: RxBus
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil
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
private var okClicked: Boolean = false
@ -82,6 +88,7 @@ class WizardDialog : DaggerDialogFragment() {
}
private var disposable: CompositeDisposable = CompositeDisposable()
private var bolusStep = 0.0
private var _binding: DialogWizardBinding? = null
@ -92,6 +99,7 @@ class WizardDialog : DaggerDialogFragment() {
override fun onStart() {
super.onStart()
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) {
@ -104,6 +112,11 @@ class WizardDialog : DaggerDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
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?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true
@ -116,13 +129,14 @@ class WizardDialog : DaggerDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
loadCheckedStates()
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()
val maxCarbs = constraintChecker.getMaxCarbsAllowed().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")
?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher)
else
@ -130,14 +144,23 @@ class WizardDialog : DaggerDialogFragment() {
?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.ok, textWatcher)
binding.carbsInput.setParams(savedInstanceState?.getDouble("carbs_input")
?: 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")
?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.ok, textWatcher)
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)
binding.correctionUnit.text = rh.gs(R.string.insulin_unit_shortname)
}
binding.carbTimeInput.setParams(savedInstanceState?.getDouble("carb_time_input")
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher)
initDialog()
binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100))
calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble()
binding.percentUsed.text = rh.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100))
// ok button
binding.ok.setOnClickListener {
if (okClicked) {
@ -148,35 +171,59 @@ class WizardDialog : DaggerDialogFragment() {
context?.let { context ->
wizard?.confirmAndExecute(context)
}
aapsLogger.debug(LTag.APS, "Dialog ok pressed: ${this.javaClass.name}")
}
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
binding.cancel.setOnClickListener { dismiss() }
binding.cancel.setOnClickListener {
aapsLogger.debug(LTag.APS, "Dialog canceled: ${this.javaClass.name}")
dismiss()
}
// checkboxes
binding.bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.bgCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.ttCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.cobCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.iobCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.bgTrendCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.sbCheckbox.setOnCheckedChangeListener(::onCheckedChanged)
val showCalc = sp.getBoolean(R.string.key_wizard_calculation_visible, false)
binding.delimiter.visibility = showCalc.toVisibility()
binding.resulttable.visibility = showCalc.toVisibility()
binding.calculationcheckbox.isChecked = showCalc
binding.calculationcheckbox.setOnCheckedChangeListener { _, isChecked ->
binding.result.visibility = showCalc.toVisibility()
binding.calculationCheckbox.isChecked = showCalc
binding.calculationCheckbox.setOnCheckedChangeListener { _, isChecked ->
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.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
binding.profile.onItemSelectedListener = object : OnItemSelectedListener {
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
}
@ -188,10 +235,10 @@ class WizardDialog : DaggerDialogFragment() {
// bus
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(AndroidSchedulers.mainThread())
.observeOn(aapsSchedulers.main)
.subscribe({
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) {
saveCheckedStates()
binding.ttcheckbox.isEnabled = binding.bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null
if (buttonView.id == binding.cobcheckbox.id)
binding.ttCheckbox.isEnabled = binding.bgCheckbox.isChecked && repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
if (buttonView.id == binding.cobCheckbox.id)
processCobCheckBox()
processEnabledIcons()
calculateInsulin()
}
private fun processCobCheckBox() {
if (binding.cobcheckbox.isChecked) {
binding.bolusiobcheckbox.isEnabled = false
binding.basaliobcheckbox.isEnabled = false
binding.bolusiobcheckbox.isChecked = true
binding.basaliobcheckbox.isChecked = true
if (binding.cobCheckbox.isChecked) {
binding.iobCheckbox.isEnabled = false
binding.iobCheckbox.isChecked = true
} else {
binding.bolusiobcheckbox.isEnabled = true
binding.basaliobcheckbox.isEnabled = true
binding.iobCheckbox.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() {
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_cob, binding.cobCheckbox.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() {
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.bgTrendCheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, 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() {
if(carbsPassedIntoWizard != 0.0) {
binding.carbsInput.value = carbsPassedIntoWizard
}
if(notesPassedIntoWizard.isNotBlank()) {
binding.notes.setText(notesPassedIntoWizard)
}
val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
if (profile == null || profileStore == null) {
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofile))
ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.noprofile))
dismiss()
return
}
val profileList: ArrayList<CharSequence>
profileList = profileStore.getProfileList()
profileList.add(0, resourceHelper.gs(R.string.active))
val profileList: ArrayList<CharSequence> = profileStore.getProfileList()
profileList.add(0, rh.gs(R.string.active))
context?.let { context ->
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
binding.profile.adapter = adapter
} ?: return
val units = profileFunction.getUnits()
binding.bgunits.text = units
if (units == Constants.MGDL)
binding.bgInput.setStep(1.0)
else
binding.bgInput.setStep(0.1)
binding.bgUnits.text = units.asText
binding.bgInput.step = if (units == GlucoseUnit.MGDL) 1.0 else 0.1
// Set BG if not old
val lastBg = iobCobCalculatorPlugin.actualBg()
if (lastBg != null) {
binding.bgInput.value = lastBg.valueToUnits(units)
} else {
binding.bgInput.value = 0.0
}
binding.ttcheckbox.isEnabled = treatmentsPlugin.tempTargetFromHistory != null
binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units) ?: 0.0
binding.ttCheckbox.isEnabled = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
// IOB calculation
treatmentsPlugin.updateTotalIOBTreatments()
val bolusIob = treatmentsPlugin.lastCalculationTreatments.round()
treatmentsPlugin.updateTotalIOBTempBasals()
val basalIob = treatmentsPlugin.lastCalculationTempBasals.round()
val bolusIob = iobCobCalculator.calculateIobFromBolus().round()
val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob)
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob)
binding.iobInsulin.text = rh.gs(R.string.formatinsulinunits, -bolusIob.iob - basalIob.basaliob)
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() {
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
if (binding.profile.selectedItem == null || profileStore == null)
return // not initialized yet
var profileName = binding.profile.selectedItem.toString()
val specificProfile: Profile?
if (profileName == resourceHelper.gs(R.string.active)) {
if (profileName == rh.gs(R.string.active)) {
specificProfile = profileFunction.getProfile()
profileName = profileFunction.getProfileName()
} else
specificProfile = profileStore.getSpecificProfile(profileName)
specificProfile = profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) }
if (specificProfile == null) return
// Entered values
val usePercentage = binding.correctionPercent.isChecked
var bg = SafeParse.stringToDouble(binding.bgInput.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()
if (abs(carbs - carbsAfterConstraint) > 0.01) {
binding.carbsInput.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied))
ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.carbsconstraintapplied))
return
}
bg = if (binding.bgcheckbox.isChecked) bg else 0.0
val tempTarget = if (binding.ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null
bg = if (binding.bgCheckbox.isChecked) bg else 0.0
val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
val tempTarget = if (binding.ttCheckbox.isChecked && dbRecord is ValueWrapper.Existing) dbRecord.value else null
// COB
var cob = 0.0
if (binding.cobcheckbox.isChecked) {
val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard COB")
if (binding.cobCheckbox.isChecked) {
val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard COB")
cobInfo.displayCob?.let { cob = it }
}
val carbTime = SafeParse.stringToInt(binding.carbTimeInput.text)
wizard = BolusWizard(mainApp).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction,
sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(),
binding.bgcheckbox.isChecked,
binding.cobcheckbox.isChecked,
binding.bolusiobcheckbox.isChecked,
binding.basaliobcheckbox.isChecked,
binding.sbcheckbox.isChecked,
binding.ttcheckbox.isChecked,
binding.bgtrendcheckbox.isChecked,
wizard = BolusWizard(injector).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction, sp.getInt(R.string.key_boluswizard_percentage, 100),
binding.bgCheckbox.isChecked,
binding.cobCheckbox.isChecked,
binding.iobCheckbox.isChecked,
binding.iobCheckbox.isChecked,
binding.sbCheckbox.isChecked,
binding.ttCheckbox.isChecked,
binding.bgTrendCheckbox.isChecked,
binding.alarm.isChecked,
binding.notes.text.toString(), carbTime)
binding.notes.text.toString(),
carbTime,
usePercentage = usePercentage,
totalPercentage = percentageCorrection
)
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.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG)
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 = rh.gs(R.string.formatinsulinunits, wizard.insulinFromBG)
binding.carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic)
binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs)
binding.carbs.text = String.format(rh.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic)
binding.carbsInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs)
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB)
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBasalIOB)
binding.iobInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB + wizard.insulinFromBasalIOB)
binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCorrection)
binding.correctionInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCorrection)
// Superbolus
binding.sb.text = if (binding.sbcheckbox.isChecked) resourceHelper.gs(R.string.twohours) else ""
binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus)
binding.sb.text = if (binding.sbCheckbox.isChecked) rh.gs(R.string.twohours) else ""
binding.sbInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus)
// Trend
if (binding.bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) {
binding.bgtrend.text = ((if (wizard.trend > 0) "+" else "")
if (binding.bgTrendCheckbox.isChecked && wizard.glucoseStatus != null) {
binding.bgTrend.text = ((if (wizard.trend > 0) "+" else "")
+ Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, profileFunction.getUnits())
+ " " + profileFunction.getUnits())
} 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
if (binding.cobcheckbox.isChecked) {
binding.cob.text = String.format(resourceHelper.gs(R.string.format_cob_ic), cob, wizard.ic)
binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCOB)
if (binding.cobCheckbox.isChecked) {
binding.cob.text = String.format(rh.gs(R.string.format_cob_ic), cob, wizard.ic)
binding.cobInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCOB)
} else {
binding.cob.text = ""
binding.cobinsulin.text = ""
binding.cobInsulin.text = ""
}
if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) {
val insulinText = if (wizard.calculatedTotalInsulin > 0.0) resourceHelper.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin) else ""
val carbsText = if (carbsAfterConstraint > 0.0) resourceHelper.gs(R.string.format_carbs, carbsAfterConstraint) else ""
binding.total.text = resourceHelper.gs(R.string.result_insulin_carbs, insulinText, carbsText)
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) rh.gs(R.string.format_carbs, carbsAfterConstraint).formatColor(rh, R.color.carbs) else ""
binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.result_insulin_carbs, insulinText, carbsText))
binding.ok.visibility = View.VISIBLE
} 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.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()
}
} 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 info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.BolusCalculatorResult
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.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONObject
import javax.inject.Inject
class WizardInfoDialog : DaggerDialogFragment() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var dateUtil: DateUtil
private var json: JSONObject? = null
private lateinit var data: BolusCalculatorResult
fun setData(json: JSONObject) {
this.json = json
fun setData(bolusCalculatorResult: BolusCalculatorResult) {
this.data = bolusCalculatorResult
}
private var _binding: DialogWizardinfoBinding? = null
@ -34,8 +39,7 @@ class WizardInfoDialog : DaggerDialogFragment() {
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true
@ -44,49 +48,62 @@ class WizardInfoDialog : DaggerDialogFragment() {
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?) {
super.onViewCreated(view, savedInstanceState)
binding.close.setOnClickListener { dismiss() }
val units = profileFunction.getUnits()
val bgString =
if (units == Constants.MGDL) DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "bg"))
else DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg"))
val bgString = Profile.toUnitsString(data.glucoseValue, data.glucoseValue * Constants.MGDL_TO_MMOLL, units)
// BG
binding.bg.text = resourceHelper.gs(R.string.format_bg_isf, bgString, JsonHelper.safeGetDouble(json, "isf"))
binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinbg"))
binding.bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused")
binding.ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused")
binding.bg.text = rh.gs(R.string.format_bg_isf, bgString, data.isf)
binding.bgInsulin.text = rh.gs(R.string.formatinsulinunits, data.glucoseInsulin)
binding.bgCheckbox.isChecked = data.wasGlucoseUsed
binding.ttCheckbox.isChecked = data.wasTempTargetUsed
// Trend
binding.bgtrend.text = JsonHelper.safeGetString(json, "trend")
binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulintrend"))
binding.bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused")
binding.bgTrend.text = DecimalFormatter.to1Decimal(data.glucoseTrend)
binding.bgTrendInsulin.text = rh.gs(R.string.formatinsulinunits, data.trendInsulin)
binding.bgTrendCheckbox.isChecked = data.wasTrendUsed
// COB
binding.cob.text = resourceHelper.gs(R.string.format_cob_ic, JsonHelper.safeGetDouble(json, "cob"), JsonHelper.safeGetDouble(json, "ic"))
binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincob"))
binding.cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused")
binding.cob.text = rh.gs(R.string.format_cob_ic, data.cob, data.ic)
binding.cobInsulin.text = rh.gs(R.string.formatinsulinunits, data.cobInsulin)
binding.cobCheckbox.isChecked = data.wasCOBUsed
// Bolus IOB
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "bolusiob"))
binding.bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused")
binding.bolusIobInsulin.text = rh.gs(R.string.formatinsulinunits, data.bolusIOB)
binding.bolusIobCheckbox.isChecked = data.wasBolusIOBUsed
// Basal IOB
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "basaliob"))
binding.basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused")
binding.basalIobInsulin.text = rh.gs(R.string.formatinsulinunits, data.basalIOB)
binding.basalIobCheckbox.isChecked = data.wasBasalIOBUsed
// Superbolus
binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinsuperbolus"))
binding.sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused")
binding.sbinsulin.text = rh.gs(R.string.formatinsulinunits, data.superbolusInsulin)
binding.sbCheckbox.isChecked = data.wasSuperbolusUsed
// Carbs
binding.carbs.text = resourceHelper.gs(R.string.format_carbs_ic, JsonHelper.safeGetDouble(json, "carbs"), JsonHelper.safeGetDouble(json, "ic"))
binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincarbs"))
binding.carbs.text = rh.gs(R.string.format_carbs_ic, data.carbs, data.ic)
binding.carbsinsulin.text = rh.gs(R.string.formatinsulinunits, data.carbsInsulin)
// Correction
binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "othercorrection"))
binding.correctioninsulin.text = rh.gs(R.string.formatinsulinunits, data.otherCorrection)
// Profile
binding.profile.text = JsonHelper.safeGetString(json, "profile")
binding.profile.text = data.profileName
// Notes
binding.notes.text = JsonHelper.safeGetString(json, "notes")
binding.notes.text = data.note
// 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
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() {

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
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
import info.nightscout.androidaps.db.Treatment
class EventTreatmentChange(val treatment: Treatment?) : EventLoop()
class EventTreatmentChange : 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.Intent
import dagger.android.DaggerBroadcastReceiver
import info.nightscout.androidaps.interfaces.Loop
import javax.inject.Inject
class CarbSuggestionReceiver : DaggerBroadcastReceiver() {
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var loop: Loop
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
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 dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.LoopFragmentBinding
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.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.FabricPrivacy
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.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.loop_fragment.*
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject
class LoopFragment : DaggerFragment() {
@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 resourceHelper: ResourceHelper
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var loop: Loop
@Inject lateinit var dateUtil: DateUtil
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?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.loop_fragment, container, false)
savedInstanceState: Bundle?): View {
_binding = LoopFragmentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
loop_run.setOnClickListener {
loop_lastrun.text = resourceHelper.gs(R.string.executing)
Thread { loopPlugin.invoke("Loop button", true) }.start()
binding.run.setOnClickListener {
binding.lastrun.text = rh.gs(R.string.executing)
Thread { loop.invoke("Loop button", true) }.start()
}
}
@ -52,18 +62,18 @@ class LoopFragment : DaggerFragment() {
super.onResume()
disposable += rxBus
.toObservable(EventLoopUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread())
.observeOn(aapsSchedulers.main)
.subscribe({
updateGUI()
}, { fabricPrivacy.logException(it) })
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventLoopSetLastRunGui::class.java)
.observeOn(AndroidSchedulers.mainThread())
.observeOn(aapsSchedulers.main)
.subscribe({
clearGUI()
loop_lastrun?.text = it.text
}, { fabricPrivacy.logException(it) })
binding.lastrun.text = it.text
}, fabricPrivacy::logException)
updateGUI()
sp.putBoolean(R.string.key_objectiveuseloop, true)
@ -76,22 +86,27 @@ class LoopFragment : DaggerFragment() {
}
@Synchronized
fun updateGUI() {
if (loop_request == null) return
loopPlugin.lastRun?.let {
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)
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
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 =
@ -101,22 +116,22 @@ class LoopFragment : DaggerFragment() {
constraintsProcessed.smbConstraint?.let { smbConstraint -> allConstraints.copyReasons(smbConstraint) }
allConstraints.getMostLimitedReasons(aapsLogger)
} ?: ""
loop_constraints?.text = constraints
binding.constraints.text = constraints
}
}
@Synchronized
private fun clearGUI() {
loop_request?.text = ""
loop_constraints?.text = ""
loop_constraintsprocessed?.text = ""
loop_source?.text = ""
loop_lastrun?.text = ""
loop_smbrequest_time?.text = ""
loop_smbexecution_time?.text = ""
loop_tbrrequest_time?.text = ""
loop_tbrexecution_time?.text = ""
loop_tbrsetbypump?.text = ""
loop_smbsetbypump?.text = ""
binding.request.text = ""
binding.constraints.text = ""
binding.constraintsprocessed.text = ""
binding.source.text = ""
binding.lastrun.text = ""
binding.smbrequestTime.text = ""
binding.smbexecutionTime.text = ""
binding.tbrrequestTime.text = ""
binding.tbrexecutionTime.text = ""
binding.tbrsetbypump.text = ""
binding.smbsetbypump.text = ""
}
}

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