Merge branch 'nightscout:dev' into dev
This commit is contained in:
commit
b57ca6f94c
1619 changed files with 60155 additions and 30621 deletions
|
@ -68,6 +68,6 @@ workflows:
|
|||
# Inside the workflow, you provide the jobs you want to run, e.g this workflow runs the build-and-test job above.
|
||||
# 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
|
||||
sample:
|
||||
dotests:
|
||||
jobs:
|
||||
- build-and-test
|
5
.editorconfig
Normal file
5
.editorconfig
Normal file
|
@ -0,0 +1,5 @@
|
|||
root = true
|
||||
|
||||
|
||||
[*{kt,kts}]
|
||||
disabled_rules=no-wildcard-imports
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -11,8 +11,6 @@ build/
|
|||
.idea/*
|
||||
!.idea/codeStyles/
|
||||
full/
|
||||
debug/
|
||||
release/
|
||||
app/com.crashlytics.settings.json
|
||||
app/session_analytics.tap
|
||||
.project
|
||||
|
|
|
@ -1,11 +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="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" />
|
||||
|
@ -124,14 +126,22 @@
|
|||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="RIGHT_MARGIN" value="120" />
|
||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="LINE_COMMENT_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>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<dictionary name="project-dictionary">
|
||||
<words>
|
||||
<w>aaps</w>
|
||||
<w>abcdef</w>
|
||||
<w>acked</w>
|
||||
<w>actionstring</w>
|
||||
<w>allowednumbers</w>
|
||||
|
@ -14,8 +15,10 @@
|
|||
<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>
|
||||
|
@ -46,6 +49,7 @@
|
|||
<w>medtronic</w>
|
||||
<w>mgdl</w>
|
||||
<w>mmol</w>
|
||||
<w>motol</w>
|
||||
<w>multiwave</w>
|
||||
<w>netinsulin</w>
|
||||
<w>netratio</w>
|
||||
|
@ -55,9 +59,14 @@
|
|||
<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>
|
||||
|
@ -66,14 +75,18 @@
|
|||
<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>
|
||||
|
@ -82,9 +95,11 @@
|
|||
<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>
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-allopen'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.hiya.jacoco-android'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
|
||||
apply from: "${project.rootDir}/gradle/android_dependencies.gradle"
|
||||
|
||||
jacoco {
|
||||
toolVersion = "0.8.6"
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
jacoco.includeNoLocationClasses = true
|
||||
jacoco.excludes = ['jdk.internal.*']
|
||||
}
|
||||
apply from: "${project.rootDir}/gradle/jacoco_global.gradle"
|
||||
|
||||
repositories {
|
||||
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 {
|
||||
|
@ -111,7 +109,7 @@ android {
|
|||
defaultConfig {
|
||||
multiDexEnabled true
|
||||
versionCode 1500
|
||||
version "2.8.2.1-dev-e4"
|
||||
version "2.8.2.4-dev"
|
||||
buildConfigField "String", "VERSION", '"' + version + '"'
|
||||
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
|
||||
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
|
||||
|
@ -186,19 +184,20 @@ dependencies {
|
|||
implementation project(':danars')
|
||||
implementation project(':danar')
|
||||
implementation project(':insight')
|
||||
implementation project(':pump-common')
|
||||
implementation project(':rileylink')
|
||||
implementation project(':medtronic')
|
||||
implementation project(':omnipod-common')
|
||||
implementation project(':omnipod-eros')
|
||||
implementation project(':omnipod-dash')
|
||||
implementation project(':diaconn')
|
||||
implementation project(':openhumans')
|
||||
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
|
||||
/* Dagger2 - We are going to use dagger.android which includes
|
||||
* 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"
|
||||
|
|
|
@ -1,42 +1,7 @@
|
|||
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
|
||||
|
|
|
@ -52,7 +52,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 +61,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,7 +70,7 @@
|
|||
</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"
|
||||
|
@ -115,11 +117,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
|
||||
|
@ -241,20 +243,6 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".plugins.general.openhumans.OpenHumansLoginActivity"
|
||||
android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="setup-openhumans"
|
||||
android:scheme="androidaps" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
|
||||
|
||||
</application>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -34,7 +34,7 @@ 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.activities.HistoryBrowseActivity
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.interfaces.IconsProvider
|
||||
|
@ -95,7 +95,6 @@ class MainActivity : NoSplashAppCompatActivity() {
|
|||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Iconify.with(FontAwesomeModule())
|
||||
|
@ -166,7 +165,7 @@ class MainActivity : NoSplashAppCompatActivity() {
|
|||
actionBarDrawerToggle.syncState()
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
disposable.clear()
|
||||
}
|
||||
|
@ -361,7 +360,6 @@ class MainActivity : NoSplashAppCompatActivity() {
|
|||
// Correct place for calling setUserStats() would be probably MainApp
|
||||
// but we need to have it called at least once a day. Thus this location
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun setUserStats() {
|
||||
if (!fabricPrivacy.fabricEnabled()) return
|
||||
val closedLoopEnabled = if (constraintChecker.isClosedLoopAllowed().value()) "CLOSED_LOOP_ENABLED" else "CLOSED_LOOP_DISABLED"
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.content.IntentFilter
|
|||
import android.net.ConnectivityManager
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.Build
|
||||
import com.j256.ormlite.android.apptools.OpenHelperManager
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.DaggerApplication
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
|
@ -15,11 +14,10 @@ import info.nightscout.androidaps.database.entities.UserEntry
|
|||
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction
|
||||
import info.nightscout.androidaps.database.transactions.VersionChangeTransaction
|
||||
import info.nightscout.androidaps.db.CompatDBHelper
|
||||
import info.nightscout.androidaps.db.DatabaseHelper
|
||||
import info.nightscout.androidaps.db.StaticInjector
|
||||
import info.nightscout.androidaps.di.StaticInjector
|
||||
import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent
|
||||
import info.nightscout.androidaps.interfaces.ConfigBuilder
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.interfaces.ConfigBuilder
|
||||
import info.nightscout.androidaps.interfaces.PluginBase
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
|
@ -34,6 +32,7 @@ import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver
|
|||
import info.nightscout.androidaps.utils.ActivityMonitor
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.locale.LocaleHelper.update
|
||||
import info.nightscout.androidaps.utils.protection.PasswordCheck
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
|
@ -58,12 +57,12 @@ class MainApp : DaggerApplication() {
|
|||
@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
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
aapsLogger.debug("onCreate")
|
||||
update(this)
|
||||
dbHelper = OpenHelperManager.getHelper(this, DatabaseHelper::class.java)
|
||||
|
||||
var gitRemote: String? = BuildConfig.REMOTE
|
||||
var commitHash: String? = BuildConfig.HEAD
|
||||
|
@ -90,6 +89,7 @@ class MainApp : DaggerApplication() {
|
|||
keepAliveManager.setAlarm(this)
|
||||
doMigrations()
|
||||
uel.log(UserEntry.Action.START_AAPS, UserEntry.Sources.Aaps)
|
||||
passwordCheck.passwordResetCheck(this)
|
||||
}
|
||||
|
||||
private fun doMigrations() {
|
||||
|
@ -134,9 +134,4 @@ class MainApp : DaggerApplication() {
|
|||
keepAliveManager.cancelAlarm(this)
|
||||
super.onTerminate()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
lateinit var dbHelper: DatabaseHelper
|
||||
}
|
||||
}
|
|
@ -0,0 +1,406 @@
|
|||
package info.nightscout.androidaps.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.DatePickerDialog
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import com.jjoe64.graphview.GraphView
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding
|
||||
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
|
||||
import info.nightscout.androidaps.events.EventCustomCalculationFinished
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview
|
||||
import info.nightscout.androidaps.extensions.toVisibility
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
|
||||
import info.nightscout.androidaps.plugins.general.overview.OverviewData
|
||||
import info.nightscout.androidaps.plugins.general.overview.OverviewMenus
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
|
||||
import info.nightscout.androidaps.utils.*
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.min
|
||||
|
||||
class HistoryBrowseActivity : NoSplashAppCompatActivity() {
|
||||
|
||||
@Inject lateinit var injector: HasAndroidInjector
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
||||
@Inject lateinit var activePlugin: ActivePlugin
|
||||
@Inject lateinit var buildHelper: BuildHelper
|
||||
@Inject lateinit var sensitivityOref1Plugin: SensitivityOref1Plugin
|
||||
@Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
|
||||
@Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
|
||||
@Inject lateinit var repository: AppRepository
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Inject lateinit var overviewMenus: OverviewMenus
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
@Inject lateinit var config: Config
|
||||
@Inject lateinit var loopPlugin: LoopPlugin
|
||||
@Inject lateinit var nsDeviceStatus: NSDeviceStatus
|
||||
@Inject lateinit var translator: Translator
|
||||
|
||||
private val disposable = CompositeDisposable()
|
||||
|
||||
private val secondaryGraphs = ArrayList<GraphView>()
|
||||
private val secondaryGraphsLabel = ArrayList<TextView>()
|
||||
|
||||
private var axisWidth: Int = 0
|
||||
private var rangeToDisplay = 24 // for graph
|
||||
// private var start: Long = 0
|
||||
|
||||
private lateinit var iobCobCalculator: IobCobCalculatorPlugin
|
||||
private lateinit var overviewData: OverviewData
|
||||
|
||||
private lateinit var binding: ActivityHistorybrowseBinding
|
||||
private var destroyed = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityHistorybrowseBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
// We don't want to use injected singletons but own instance working on top of different data
|
||||
iobCobCalculator = IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction, activePlugin, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository)
|
||||
overviewData = OverviewData(injector, aapsLogger, resourceHelper, dateUtil, sp, activePlugin, defaultValueHelper, profileFunction, config, loopPlugin, nsDeviceStatus, repository, overviewMenus, iobCobCalculator, translator)
|
||||
|
||||
binding.left.setOnClickListener {
|
||||
adjustTimeRange(overviewData.fromTime - T.hours(rangeToDisplay.toLong()).msecs())
|
||||
loadAll("onClickLeft")
|
||||
}
|
||||
binding.right.setOnClickListener {
|
||||
adjustTimeRange(overviewData.fromTime + T.hours(rangeToDisplay.toLong()).msecs())
|
||||
loadAll("onClickRight")
|
||||
}
|
||||
binding.end.setOnClickListener {
|
||||
setTime(dateUtil.now())
|
||||
loadAll("onClickEnd")
|
||||
}
|
||||
binding.zoom.setOnClickListener {
|
||||
rangeToDisplay += 6
|
||||
rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay
|
||||
setTime(overviewData.fromTime)
|
||||
loadAll("rangeChange")
|
||||
}
|
||||
binding.zoom.setOnLongClickListener {
|
||||
Calendar.getInstance().also { calendar ->
|
||||
calendar.timeInMillis = overviewData.fromTime
|
||||
calendar[Calendar.MILLISECOND] = 0
|
||||
calendar[Calendar.SECOND] = 0
|
||||
calendar[Calendar.MINUTE] = 0
|
||||
calendar[Calendar.HOUR_OF_DAY] = 0
|
||||
setTime(calendar.timeInMillis)
|
||||
}
|
||||
loadAll("onLongClickZoom")
|
||||
true
|
||||
}
|
||||
|
||||
// create an OnDateSetListener
|
||||
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
|
||||
Calendar.getInstance().also { calendar ->
|
||||
calendar.timeInMillis = overviewData.fromTime
|
||||
calendar[Calendar.YEAR] = year
|
||||
calendar[Calendar.MONTH] = monthOfYear
|
||||
calendar[Calendar.DAY_OF_MONTH] = dayOfMonth
|
||||
calendar[Calendar.MILLISECOND] = 0
|
||||
calendar[Calendar.SECOND] = 0
|
||||
calendar[Calendar.MINUTE] = 0
|
||||
calendar[Calendar.HOUR_OF_DAY] = 0
|
||||
setTime(calendar.timeInMillis)
|
||||
binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime)
|
||||
}
|
||||
loadAll("onClickDate")
|
||||
}
|
||||
|
||||
binding.date.setOnClickListener {
|
||||
val cal = Calendar.getInstance()
|
||||
cal.timeInMillis = overviewData.fromTime
|
||||
DatePickerDialog(this, dateSetListener,
|
||||
cal.get(Calendar.YEAR),
|
||||
cal.get(Calendar.MONTH),
|
||||
cal.get(Calendar.DAY_OF_MONTH)
|
||||
).show()
|
||||
}
|
||||
|
||||
val dm = DisplayMetrics()
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
|
||||
display?.getRealMetrics(dm)
|
||||
else
|
||||
@Suppress("DEPRECATION") windowManager.defaultDisplay.getMetrics(dm)
|
||||
|
||||
|
||||
axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80
|
||||
binding.bgGraph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
|
||||
binding.bgGraph.gridLabelRenderer?.reloadStyles()
|
||||
binding.bgGraph.gridLabelRenderer?.labelVerticalWidth = axisWidth
|
||||
|
||||
overviewMenus.setupChartMenu(binding.chartMenuButton)
|
||||
prepareGraphsIfNeeded(overviewMenus.setting.size)
|
||||
savedInstanceState?.let { bundle ->
|
||||
rangeToDisplay = bundle.getInt("rangeToDisplay", 0)
|
||||
overviewData.fromTime = bundle.getLong("start", 0)
|
||||
overviewData.toTime = bundle.getLong("end", 0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
disposable.clear()
|
||||
iobCobCalculator.stopCalculation("onPause")
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onDestroy() {
|
||||
destroyed = true
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventAutosensCalculationFinished::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
// catch only events from iobCobCalculator
|
||||
if (it.cause is EventCustomCalculationFinished)
|
||||
refreshLoop("EventAutosensCalculationFinished")
|
||||
}, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventIobCalculationProgress::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({
|
||||
if (it.cause is EventCustomCalculationFinished)
|
||||
binding.overviewIobcalculationprogess.text = it.progress
|
||||
}, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventRefreshOverview::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({ updateGUI("EventRefreshOverview") }, fabricPrivacy::logException)
|
||||
)
|
||||
disposable += rxBus
|
||||
.toObservable(EventBucketedDataCreated::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
overviewData.prepareBucketedData("EventBucketedDataCreated")
|
||||
overviewData.prepareBgData("EventBucketedDataCreated")
|
||||
rxBus.send(EventRefreshOverview("EventBucketedDataCreated"))
|
||||
}, fabricPrivacy::logException)
|
||||
|
||||
if (overviewData.fromTime == 0L) {
|
||||
// set start of current day
|
||||
setTime(dateUtil.now())
|
||||
loadAll("onResume")
|
||||
} else {
|
||||
updateGUI("onResume")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt("rangeToDisplay", rangeToDisplay)
|
||||
outState.putLong("start", overviewData.fromTime)
|
||||
outState.putLong("end", overviewData.toTime)
|
||||
}
|
||||
|
||||
private fun prepareGraphsIfNeeded(numOfGraphs: Int) {
|
||||
if (numOfGraphs != secondaryGraphs.size - 1) {
|
||||
//aapsLogger.debug("New secondary graph count ${numOfGraphs-1}")
|
||||
// rebuild needed
|
||||
secondaryGraphs.clear()
|
||||
secondaryGraphsLabel.clear()
|
||||
binding.iobGraph.removeAllViews()
|
||||
for (i in 1 until numOfGraphs) {
|
||||
val relativeLayout = RelativeLayout(this)
|
||||
relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
val graph = GraphView(this)
|
||||
graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(100)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) }
|
||||
graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
|
||||
graph.gridLabelRenderer?.reloadStyles()
|
||||
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
|
||||
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
|
||||
graph.gridLabelRenderer?.numVerticalLabels = 3
|
||||
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray
|
||||
relativeLayout.addView(graph)
|
||||
|
||||
val label = TextView(this)
|
||||
val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) }
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP)
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
|
||||
label.layoutParams = layoutParams
|
||||
relativeLayout.addView(label)
|
||||
secondaryGraphsLabel.add(label)
|
||||
|
||||
binding.iobGraph.addView(relativeLayout)
|
||||
secondaryGraphs.add(graph)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
private fun loadAll(from: String) {
|
||||
updateDate()
|
||||
Thread {
|
||||
overviewData.prepareBgData("$from")
|
||||
overviewData.prepareTreatmentsData(from)
|
||||
rxBus.send(EventRefreshOverview("loadAll_$from"))
|
||||
overviewData.prepareTemporaryTargetData(from)
|
||||
rxBus.send(EventRefreshOverview("loadAll_$from"))
|
||||
overviewData.prepareBasalData(from)
|
||||
rxBus.send(EventRefreshOverview(from))
|
||||
aapsLogger.debug(LTag.UI, "loadAll $from finished")
|
||||
runCalculation(from)
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun setTime(start: Long) {
|
||||
GregorianCalendar().also { calendar ->
|
||||
calendar.timeInMillis = start
|
||||
calendar[Calendar.MILLISECOND] = 0
|
||||
calendar[Calendar.SECOND] = 0
|
||||
calendar[Calendar.MINUTE] = 0
|
||||
calendar[Calendar.HOUR_OF_DAY] -= (calendar[Calendar.HOUR_OF_DAY] % rangeToDisplay)
|
||||
adjustTimeRange(calendar.timeInMillis)
|
||||
}
|
||||
}
|
||||
|
||||
private fun adjustTimeRange(start: Long) {
|
||||
overviewData.fromTime = start
|
||||
overviewData.toTime = overviewData.fromTime + T.hours(rangeToDisplay.toLong()).msecs()
|
||||
overviewData.endTime = overviewData.toTime
|
||||
}
|
||||
|
||||
private fun runCalculation(from: String) {
|
||||
Thread {
|
||||
iobCobCalculator.stopCalculation(from)
|
||||
iobCobCalculator.stopCalculationTrigger = false
|
||||
iobCobCalculator.runCalculation(from, overviewData.toTime, bgDataReload = true, limitDataToOldestAvailable = false, cause = EventCustomCalculationFinished())
|
||||
}.start()
|
||||
}
|
||||
|
||||
@Volatile
|
||||
var runningRefresh = false
|
||||
private fun refreshLoop(from: String) {
|
||||
if (runningRefresh) return
|
||||
runningRefresh = true
|
||||
overviewData.prepareIobAutosensData(from)
|
||||
rxBus.send(EventRefreshOverview(from))
|
||||
aapsLogger.debug(LTag.UI, "refreshLoop finished")
|
||||
runningRefresh = false
|
||||
}
|
||||
|
||||
fun updateDate() {
|
||||
binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime)
|
||||
binding.zoom.text = rangeToDisplay.toString()
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun updateGUI(from: String) {
|
||||
aapsLogger.debug(LTag.UI, "updateGui $from")
|
||||
|
||||
updateDate()
|
||||
|
||||
val pump = activePlugin.activePump
|
||||
val graphData = GraphData(injector, binding.bgGraph, overviewData)
|
||||
val menuChartSettings = overviewMenus.setting
|
||||
graphData.addInRangeArea(overviewData.fromTime, overviewData.endTime, defaultValueHelper.determineLowLine(), defaultValueHelper.determineHighLine())
|
||||
graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
|
||||
if (buildHelper.isDev()) graphData.addBucketedData()
|
||||
graphData.addTreatments()
|
||||
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
|
||||
graphData.addActivity(0.8)
|
||||
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
|
||||
graphData.addBasals()
|
||||
graphData.addTargetLine()
|
||||
graphData.addNowLine(dateUtil.now())
|
||||
|
||||
// set manual x bounds to have nice steps
|
||||
graphData.setNumVerticalLabels()
|
||||
graphData.formatAxis(overviewData.fromTime, overviewData.endTime)
|
||||
|
||||
graphData.performUpdate()
|
||||
|
||||
// 2nd graphs
|
||||
prepareGraphsIfNeeded(menuChartSettings.size)
|
||||
val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
|
||||
val secondGraphData = GraphData(injector, secondaryGraphs[g], overviewData)
|
||||
var useABSForScale = false
|
||||
var useIobForScale = false
|
||||
var useCobForScale = false
|
||||
var useDevForScale = false
|
||||
var useRatioForScale = false
|
||||
var useDSForScale = false
|
||||
var useBGIForScale = false
|
||||
when {
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
|
||||
}
|
||||
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
|
||||
|
||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(useABSForScale, 1.0)
|
||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(useIobForScale, 1.0)
|
||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(useCobForScale, if (useCobForScale) 1.0 else 0.5)
|
||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(useDevForScale, 1.0)
|
||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8)
|
||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(useRatioForScale, if (useRatioForScale) 1.0 else 0.8)
|
||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(useDSForScale, 1.0)
|
||||
|
||||
// set manual x bounds to have nice steps
|
||||
secondGraphData.formatAxis(overviewData.fromTime, overviewData.endTime)
|
||||
secondGraphData.addNowLine(now)
|
||||
secondaryGraphsData.add(secondGraphData)
|
||||
}
|
||||
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
|
||||
secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1)
|
||||
secondaryGraphs[g].visibility = (
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal]
|
||||
).toVisibility()
|
||||
secondaryGraphsData[g].performUpdate()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,11 +14,13 @@ 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.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
|
||||
|
@ -29,7 +31,6 @@ import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
|||
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
|
||||
import info.nightscout.androidaps.plugins.general.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
|
||||
|
@ -99,6 +100,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)
|
||||
|
@ -173,6 +175,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 +185,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)
|
||||
|
|
|
@ -7,10 +7,8 @@ import javax.inject.Inject
|
|||
class RequestDexcomPermissionActivity : DialogAppCompatActivity() {
|
||||
@Inject lateinit var dexcomPlugin: DexcomPlugin
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private val requestCode = "AndroidAPS <3".map { it.code }.sum()
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
requestPermissions(arrayOf(DexcomPlugin.PERMISSION), requestCode)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
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
|
||||
|
@ -21,14 +22,28 @@ class StatsActivity : NoSplashAppCompatActivity() {
|
|||
|
||||
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) + ": " + getString(R.string.calculation_in_progress)
|
||||
binding.tir.text = getString(R.string.tir) + ": " + getString(R.string.calculation_in_progress)
|
||||
binding.activity.text = getString(R.string.activitymonitor) + ": " + getString(R.string.calculation_in_progress)
|
||||
|
||||
Thread {
|
||||
val tdds = tddCalculator.stats()
|
||||
runOnUiThread { binding.tdds.text = tdds }
|
||||
}.start()
|
||||
Thread {
|
||||
val tir = tirCalculator.stats()
|
||||
runOnUiThread { binding.tir.text = tir }
|
||||
}.start()
|
||||
Thread {
|
||||
val activity = activityMonitor.stats()
|
||||
runOnUiThread { binding.activity.text = activity }
|
||||
}.start()
|
||||
|
||||
binding.ok.setOnClickListener { finish() }
|
||||
binding.reset.setOnClickListener {
|
||||
|
|
|
@ -31,7 +31,7 @@ import info.nightscout.androidaps.logging.LTag
|
|||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
|
||||
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.T
|
||||
|
|
|
@ -24,7 +24,7 @@ import info.nightscout.androidaps.logging.LTag
|
|||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
|
||||
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.activities.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
|
|
|
@ -29,7 +29,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientR
|
|||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
|
||||
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
|
||||
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
|
||||
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.activities.fragments.TreatmentsProfileSwitchFragment.RecyclerProfileViewAdapter.ProfileSwitchViewHolder
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
|
|
|
@ -28,7 +28,7 @@ import info.nightscout.androidaps.logging.LTag
|
|||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
|
||||
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
|
|
|
@ -190,7 +190,6 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
|
|||
|
||||
override fun getItemCount(): Int = tempBasalList.size
|
||||
|
||||
@Deprecated("remove remove functionality after finish")
|
||||
inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
val binding = TreatmentsTempbasalsItemBinding.bind(itemView)
|
||||
|
|
|
@ -19,7 +19,7 @@ import info.nightscout.androidaps.interfaces.ImportExportPrefs
|
|||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.T
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.json.JSONObject
|
|||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.math.abs
|
||||
|
||||
@Singleton
|
||||
class DefaultProfile @Inject constructor(val dateUtil: DateUtil) {
|
||||
|
@ -120,12 +121,12 @@ class DefaultProfile @Inject constructor(val dateUtil: DateUtil) {
|
|||
val high = map.ceilingEntry(key)
|
||||
var res: Array<Double>? = null
|
||||
if (low != null && high != null) {
|
||||
res = if (Math.abs(key - low.key) < Math.abs(key - high.key))
|
||||
res = if (abs(key - low.key) < abs(key - high.key))
|
||||
low.value
|
||||
else
|
||||
high.value
|
||||
} else if (low != null || high != null) {
|
||||
res = if (low != null) low.value else high.value
|
||||
res = if (low != null) low.value else high!!.value
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -82,5 +82,9 @@ class CompatDBHelper @Inject constructor(
|
|||
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged")
|
||||
rxBus.send(EventProfileSwitchChanged())
|
||||
}
|
||||
it.filterIsInstance<OfflineEvent>().firstOrNull()?.let {
|
||||
aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange")
|
||||
rxBus.send(EventOfflineChange())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,296 +0,0 @@
|
|||
package info.nightscout.androidaps.db;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
|
||||
import com.j256.ormlite.dao.CloseableIterator;
|
||||
import com.j256.ormlite.dao.Dao;
|
||||
import com.j256.ormlite.stmt.DeleteBuilder;
|
||||
import com.j256.ormlite.stmt.PreparedQuery;
|
||||
import com.j256.ormlite.stmt.QueryBuilder;
|
||||
import com.j256.ormlite.stmt.Where;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin;
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
|
||||
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader;
|
||||
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
|
||||
/**
|
||||
* This Helper contains all resource to provide a central DB management functionality. Only methods handling
|
||||
* data-structure (and not the DB content) should be contained in here (meaning DDL and not SQL).
|
||||
* <p>
|
||||
* This class can safely be called from Services, but should not call Services to avoid circular dependencies.
|
||||
* One major issue with this (right now) are the scheduled events, which are put into the service. Therefor all
|
||||
* direct calls to the corresponding methods (eg. resetDatabases) should be done by a central service.
|
||||
*/
|
||||
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject RxBusWrapper rxBus;
|
||||
@Inject VirtualPumpPlugin virtualPumpPlugin;
|
||||
@Inject OpenHumansUploader openHumansUploader;
|
||||
@Inject ActivePlugin activePlugin;
|
||||
@Inject DateUtil dateUtil;
|
||||
|
||||
public static final String DATABASE_NAME = "AndroidAPSDb";
|
||||
|
||||
private static final int DATABASE_VERSION = 13;
|
||||
|
||||
public static Long earliestDataChange = null;
|
||||
|
||||
private int oldVersion = 0;
|
||||
private int newVersion = 0;
|
||||
|
||||
public DatabaseHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
StaticInjector.Companion.getInstance().androidInjector().inject(this);
|
||||
onCreate(getWritableDatabase(), getConnectionSource());
|
||||
//onUpgrade(getWritableDatabase(), getConnectionSource(), 1,1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {
|
||||
try {
|
||||
aapsLogger.info(LTag.DATABASE, "onCreate");
|
||||
TableUtils.createTableIfNotExists(connectionSource, OmnipodHistoryRecord.class);
|
||||
TableUtils.createTableIfNotExists(connectionSource, OHQueueItem.class);
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Can't create database", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) {
|
||||
try {
|
||||
this.oldVersion = oldVersion;
|
||||
this.newVersion = newVersion;
|
||||
|
||||
if (oldVersion < 7) {
|
||||
aapsLogger.info(LTag.DATABASE, "onUpgrade");
|
||||
onCreate(database, connectionSource);
|
||||
}
|
||||
TableUtils.createTableIfNotExists(connectionSource, OHQueueItem.class);
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Can't drop databases", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
aapsLogger.info(LTag.DATABASE, "Do nothing for downgrading...");
|
||||
aapsLogger.info(LTag.DATABASE, "oldVersion: {}, newVersion: {}", oldVersion, newVersion);
|
||||
}
|
||||
|
||||
public int getOldVersion() {
|
||||
return oldVersion;
|
||||
}
|
||||
|
||||
public int getNewVersion() {
|
||||
return newVersion;
|
||||
}
|
||||
|
||||
public long size(String database) {
|
||||
return DatabaseUtils.queryNumEntries(getReadableDatabase(), database);
|
||||
}
|
||||
|
||||
// --------------------- DB resets ---------------------
|
||||
|
||||
public void resetDatabases() {
|
||||
try {
|
||||
TableUtils.dropTable(connectionSource, OmnipodHistoryRecord.class, true);
|
||||
TableUtils.createTableIfNotExists(connectionSource, OmnipodHistoryRecord.class);
|
||||
updateEarliestDataChange(0);
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
virtualPumpPlugin.setFakingStatus(true);
|
||||
new java.util.Timer().schedule(
|
||||
new java.util.TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
rxBus.send(new EventRefreshOverview("resetDatabases", false));
|
||||
}
|
||||
},
|
||||
3000
|
||||
);
|
||||
}
|
||||
|
||||
// ------------------ getDao -------------------------------------------
|
||||
|
||||
private Dao<OmnipodHistoryRecord, Long> getDaoPodHistory() throws SQLException {
|
||||
return getDao(OmnipodHistoryRecord.class);
|
||||
}
|
||||
|
||||
private Dao<OHQueueItem, Long> getDaoOpenHumansQueue() throws SQLException {
|
||||
return getDao(OHQueueItem.class);
|
||||
}
|
||||
|
||||
public static void updateEarliestDataChange(long newDate) {
|
||||
if (earliestDataChange == null) {
|
||||
earliestDataChange = newDate;
|
||||
return;
|
||||
}
|
||||
if (newDate < earliestDataChange) {
|
||||
earliestDataChange = newDate;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- Food handling ---------------
|
||||
|
||||
// ---------------- PodHistory handling ---------------
|
||||
|
||||
public void createOrUpdate(OmnipodHistoryRecord omnipodHistoryRecord) {
|
||||
try {
|
||||
getDaoPodHistory().createOrUpdate(omnipodHistoryRecord);
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<OmnipodHistoryRecord> getAllOmnipodHistoryRecordsFromTimeStamp(long from, boolean ascending) {
|
||||
try {
|
||||
Dao<OmnipodHistoryRecord, Long> daoPodHistory = getDaoPodHistory();
|
||||
List<OmnipodHistoryRecord> podHistories;
|
||||
QueryBuilder<OmnipodHistoryRecord, Long> queryBuilder = daoPodHistory.queryBuilder();
|
||||
queryBuilder.orderBy("date", ascending);
|
||||
//queryBuilder.limit(100L);
|
||||
Where where = queryBuilder.where();
|
||||
where.ge("date", from);
|
||||
PreparedQuery<OmnipodHistoryRecord> preparedQuery = queryBuilder.prepare();
|
||||
podHistories = daoPodHistory.query(preparedQuery);
|
||||
return podHistories;
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public OmnipodHistoryRecord findOmnipodHistoryRecordByPumpId(long pumpId) {
|
||||
try {
|
||||
Dao<OmnipodHistoryRecord, Long> daoPodHistory = getDaoPodHistory();
|
||||
QueryBuilder<OmnipodHistoryRecord, Long> queryBuilder = daoPodHistory.queryBuilder();
|
||||
queryBuilder.orderBy("date", false);
|
||||
Where<OmnipodHistoryRecord, Long> where = queryBuilder.where();
|
||||
where.eq("pumpId", pumpId);
|
||||
PreparedQuery<OmnipodHistoryRecord> preparedQuery = queryBuilder.prepare();
|
||||
return daoPodHistory.queryForFirst(preparedQuery);
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO implement again for database branch // Copied from xDrip+
|
||||
String calculateDirection(BgReading bgReading) {
|
||||
// Rework to get bgreaings from internal DB and calculate on that base
|
||||
|
||||
List<BgReading> bgReadingsList = MainApp.getDbHelper().getAllBgreadingsDataFromTime(bgReading.date - T.mins(10).msecs(), false);
|
||||
if (bgReadingsList == null || bgReadingsList.size() < 2)
|
||||
return "NONE";
|
||||
BgReading current = bgReadingsList.get(1);
|
||||
BgReading previous = bgReadingsList.get(0);
|
||||
|
||||
if (bgReadingsList.get(1).date < bgReadingsList.get(0).date) {
|
||||
current = bgReadingsList.get(0);
|
||||
previous = bgReadingsList.get(1);
|
||||
}
|
||||
|
||||
double slope;
|
||||
|
||||
// Avoid division by 0
|
||||
if (current.date == previous.date)
|
||||
slope = 0;
|
||||
else
|
||||
slope = (previous.value - current.value) / (previous.date - current.date);
|
||||
|
||||
// aapsLogger.error(LTag.GLUCOSE, "Slope is :" + slope + " delta " + (previous.value - current.value) + " date difference " + (current.date - previous.date));
|
||||
|
||||
double slope_by_minute = slope * 60000;
|
||||
String arrow = "NONE";
|
||||
|
||||
if (slope_by_minute <= (-3.5)) {
|
||||
arrow = "DoubleDown";
|
||||
} else if (slope_by_minute <= (-2)) {
|
||||
arrow = "SingleDown";
|
||||
} else if (slope_by_minute <= (-1)) {
|
||||
arrow = "FortyFiveDown";
|
||||
} else if (slope_by_minute <= (1)) {
|
||||
arrow = "Flat";
|
||||
} else if (slope_by_minute <= (2)) {
|
||||
arrow = "FortyFiveUp";
|
||||
} else if (slope_by_minute <= (3.5)) {
|
||||
arrow = "SingleUp";
|
||||
} else if (slope_by_minute <= (40)) {
|
||||
arrow = "DoubleUp";
|
||||
}
|
||||
// aapsLogger.error(LTag.GLUCOSE, "Direction set to: " + arrow);
|
||||
return arrow;
|
||||
}
|
||||
*/
|
||||
// ---------------- Open Humans Queue handling ---------------
|
||||
|
||||
public void clearOpenHumansQueue() {
|
||||
try {
|
||||
TableUtils.clearTable(connectionSource, OHQueueItem.class);
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void createOrUpdate(OHQueueItem item) {
|
||||
try {
|
||||
getDaoOpenHumansQueue().createOrUpdate(item);
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllOHQueueItemsWithIdSmallerThan(long id) {
|
||||
try {
|
||||
DeleteBuilder<OHQueueItem, Long> deleteBuilder = getDaoOpenHumansQueue().deleteBuilder();
|
||||
deleteBuilder.where().le("id", id);
|
||||
deleteBuilder.delete();
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<OHQueueItem> getAllOHQueueItems(Long maxEntries) {
|
||||
try {
|
||||
return getDaoOpenHumansQueue()
|
||||
.queryBuilder()
|
||||
.orderBy("id", true)
|
||||
.limit(maxEntries)
|
||||
.query();
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public long getOHQueueSize() {
|
||||
try {
|
||||
return getDaoOpenHumansQueue().countOf();
|
||||
} catch (SQLException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package info.nightscout.androidaps.db;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.j256.ormlite.dao.CloseableIterator;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
|
||||
|
||||
@Deprecated
|
||||
@Singleton
|
||||
public class DatabaseHelperProvider implements DatabaseHelperInterface {
|
||||
|
||||
@Inject DatabaseHelperProvider() {
|
||||
}
|
||||
|
||||
@Override public void createOrUpdate(@NonNull OmnipodHistoryRecord record) {
|
||||
MainApp.Companion.getDbHelper().createOrUpdate(record);
|
||||
}
|
||||
|
||||
@Override public boolean createOrUpdate(@NonNull TemporaryBasal tempBasal) {
|
||||
// return MainApp.Companion.getDbHelper().createOrUpdate(tempBasal);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable @Override public TemporaryBasal findTempBasalByPumpId(long id) {
|
||||
// return MainApp.Companion.getDbHelper().findTempBasalByPumpId(id);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@NonNull @Override public List<TemporaryBasal> getTemporaryBasalsDataFromTime(long mills, boolean ascending) {
|
||||
// return MainApp.Companion.getDbHelper().getTemporaryBasalsDataFromTime(mills, ascending);
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull @Override public List<OmnipodHistoryRecord> getAllOmnipodHistoryRecordsFromTimestamp(long timestamp, boolean ascending) {
|
||||
return MainApp.Companion.getDbHelper().getAllOmnipodHistoryRecordsFromTimeStamp(timestamp, ascending);
|
||||
}
|
||||
|
||||
@Nullable @Override public OmnipodHistoryRecord findOmnipodHistoryRecordByPumpId(long pumpId) {
|
||||
return MainApp.Companion.getDbHelper().findOmnipodHistoryRecordByPumpId(pumpId);
|
||||
}
|
||||
|
||||
@Override public void delete(@NonNull ExtendedBolus extendedBolus) {
|
||||
// MainApp.Companion.getDbHelper().delete(extendedBolus);
|
||||
}
|
||||
|
||||
@Nullable @Override public ExtendedBolus getExtendedBolusByPumpId(long pumpId) {
|
||||
// return MainApp.Companion.getDbHelper().getExtendedBolusByPumpId(pumpId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public void resetDatabases() {
|
||||
MainApp.Companion.getDbHelper().resetDatabases();
|
||||
}
|
||||
|
||||
@Override public void createOrUpdate(@NonNull OHQueueItem record) {
|
||||
MainApp.Companion.getDbHelper().createOrUpdate(record);
|
||||
}
|
||||
|
||||
@NonNull @Override public List<OHQueueItem> getAllOHQueueItems(long maxEntries) {
|
||||
return MainApp.Companion.getDbHelper().getAllOHQueueItems(maxEntries);
|
||||
}
|
||||
|
||||
@Override public long getOHQueueSize() {
|
||||
return MainApp.Companion.getDbHelper().getOHQueueSize();
|
||||
}
|
||||
|
||||
@Override public void clearOpenHumansQueue() {
|
||||
MainApp.Companion.getDbHelper().clearOpenHumansQueue();
|
||||
}
|
||||
|
||||
@Override public void removeAllOHQueueItemsWithIdSmallerThan(long id) {
|
||||
MainApp.Companion.getDbHelper().removeAllOHQueueItemsWithIdSmallerThan(id);
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
package info.nightscout.androidaps.db
|
||||
|
|
@ -4,9 +4,8 @@ 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.activities.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.setupwizard.SetupWizardActivity
|
||||
|
@ -28,6 +27,5 @@ abstract class ActivitiesModule {
|
|||
@ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity
|
||||
@ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity
|
||||
@ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity
|
||||
@ContributesAndroidInjector abstract fun contributesOpenHumansLoginActivity(): OpenHumansLoginActivity
|
||||
|
||||
}
|
|
@ -8,15 +8,19 @@ 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.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.database.DatabaseModule
|
||||
import info.nightscout.androidaps.di.CoreModule
|
||||
import info.nightscout.androidaps.diaconn.di.DiaconnG8Module
|
||||
import info.nightscout.androidaps.insight.di.InsightDatabaseModule
|
||||
import info.nightscout.androidaps.insight.di.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 javax.inject.Singleton
|
||||
|
||||
|
@ -36,8 +40,10 @@ import javax.inject.Singleton
|
|||
CommandQueueModule::class,
|
||||
ObjectivesModule::class,
|
||||
WizardModule::class,
|
||||
PumpCommonModule::class,
|
||||
RileyLinkModule::class,
|
||||
MedtronicModule::class,
|
||||
OmnipodDashModule::class,
|
||||
OmnipodErosModule::class,
|
||||
APSModule::class,
|
||||
PreferencesModule::class,
|
||||
|
@ -54,7 +60,8 @@ import javax.inject.Singleton
|
|||
InsightModule::class,
|
||||
InsightDatabaseModule::class,
|
||||
WorkersModule::class,
|
||||
OHUploaderModule::class
|
||||
DiaconnG8Module::class,
|
||||
OpenHumansModule::class
|
||||
]
|
||||
)
|
||||
interface AppComponent : AndroidInjector<MainApp> {
|
||||
|
|
|
@ -8,22 +8,25 @@ import dagger.Provides
|
|||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.MainApp
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.db.DatabaseHelperProvider
|
||||
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.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.smsCommunicator.SmsCommunicatorPlugin
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.PumpSyncImplementation
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
|
||||
import info.nightscout.androidaps.queue.CommandQueue
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
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
|
||||
|
@ -56,9 +59,11 @@ open class AppModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStorage(): Storage {
|
||||
return FileStorage()
|
||||
}
|
||||
fun provideStorage(): Storage = FileStorage()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBuildHelper(config: Config, fileListProvider: PrefFileListProvider): BuildHelper = BuildHelperImpl(config, fileListProvider)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
@ -66,29 +71,35 @@ open class AppModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideProfileFunction(aapsLogger: AAPSLogger, sp: SP, resourceHelper: ResourceHelper, activePlugin: ActivePlugin, repository: AppRepository, dateUtil: DateUtil): ProfileFunction {
|
||||
return ProfileFunctionImplementation(aapsLogger, sp, resourceHelper, activePlugin, repository, dateUtil)
|
||||
}
|
||||
fun provideProfileFunction(
|
||||
aapsLogger: AAPSLogger, sp: SP, rxBus: RxBusWrapper, resourceHelper:
|
||||
ResourceHelper, activePlugin:
|
||||
ActivePlugin, repository: AppRepository, dateUtil: DateUtil, config: Config, hardLimits: HardLimits
|
||||
): ProfileFunction =
|
||||
ProfileFunctionImplementation(
|
||||
aapsLogger, sp, rxBus, resourceHelper, activePlugin, repository, dateUtil,
|
||||
config, hardLimits
|
||||
)
|
||||
|
||||
@Module
|
||||
interface AppBindings {
|
||||
|
||||
@Binds fun bindContext(mainApp: MainApp): Context
|
||||
@Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector
|
||||
@Binds fun bindActivePluginProvider(pluginStore: PluginStore): ActivePlugin
|
||||
@Binds fun bindCommandQueueProvider(commandQueue: CommandQueue): CommandQueueProvider
|
||||
@Binds fun bindConfigInterface(config: ConfigImpl): Config
|
||||
|
||||
@Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilder
|
||||
@Binds fun bindTreatmentsInterface(treatmentsPlugin: TreatmentsPlugin): TreatmentsInterface
|
||||
@Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface
|
||||
@Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder
|
||||
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs
|
||||
@Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider
|
||||
@Binds fun bindLoopInterface(loopPlugin: LoopPlugin): LoopInterface
|
||||
@Binds fun bindLoopInterface(loopPlugin: LoopPlugin): Loop
|
||||
@Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
|
||||
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
|
||||
@Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
|
||||
@Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync
|
||||
|
||||
@Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,7 @@ package info.nightscout.androidaps.dependencyInjection
|
|||
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import info.nightscout.androidaps.db.DatabaseHelper
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentService
|
||||
import info.nightscout.androidaps.utils.wizard.BolusWizard
|
||||
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
|
||||
|
||||
|
@ -14,9 +12,6 @@ abstract class DataClassesModule {
|
|||
|
||||
@ContributesAndroidInjector abstract fun glucoseStatusInjector(): GlucoseStatus
|
||||
|
||||
@ContributesAndroidInjector abstract fun databaseHelperInjector(): DatabaseHelper
|
||||
@ContributesAndroidInjector abstract fun treatmentServiceInjector(): TreatmentService
|
||||
|
||||
@ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard
|
||||
@ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry
|
||||
}
|
|
@ -20,8 +20,6 @@ import info.nightscout.androidaps.plugins.general.automation.dialogs.EditTrigger
|
|||
import info.nightscout.androidaps.plugins.general.food.FoodFragment
|
||||
import info.nightscout.androidaps.plugins.general.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
|
||||
|
@ -70,8 +68,6 @@ abstract class FragmentsModule {
|
|||
|
||||
@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
|
||||
|
@ -95,7 +91,5 @@ abstract class FragmentsModule {
|
|||
@ContributesAndroidInjector abstract fun contributesWizardDialog(): WizardDialog
|
||||
@ContributesAndroidInjector abstract fun contributesWizardInfoDialog(): WizardInfoDialog
|
||||
|
||||
@ContributesAndroidInjector abstract fun contributesExchangeAuthTokenDialog(): OpenHumansLoginActivity.ExchangeAuthTokenDialog
|
||||
|
||||
@ContributesAndroidInjector abstract fun contributesPasswordCheck(): PasswordCheck
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@ 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
|
||||
|
@ -25,7 +27,6 @@ import info.nightscout.androidaps.plugins.general.dataBroadcaster.DataBroadcastP
|
|||
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
|
||||
import info.nightscout.androidaps.plugins.general.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
|
||||
|
@ -41,13 +42,11 @@ import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
|
|||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
|
||||
import info.nightscout.androidaps.plugins.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
|
||||
|
||||
@Module
|
||||
|
@ -149,11 +148,11 @@ abstract class PluginsModule {
|
|||
@IntKey(140)
|
||||
abstract fun bindComboPlugin(plugin: ComboPlugin): PluginBase
|
||||
|
||||
// @Binds
|
||||
// @PumpDriver
|
||||
// @IntoMap
|
||||
// @IntKey(150)
|
||||
// abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase
|
||||
@Binds
|
||||
@PumpDriver
|
||||
@IntoMap
|
||||
@IntKey(150)
|
||||
abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase
|
||||
|
||||
// @Binds
|
||||
// @PumpDriver
|
||||
|
@ -161,6 +160,12 @@ abstract class PluginsModule {
|
|||
// @IntKey(155)
|
||||
// abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase
|
||||
|
||||
@Binds
|
||||
@PumpDriver
|
||||
@IntoMap
|
||||
@IntKey(155)
|
||||
abstract fun bindDiaconnG8Plugin(plugin: DiaconnG8Plugin): PluginBase
|
||||
|
||||
@Binds
|
||||
@NotNSClient
|
||||
@IntoMap
|
||||
|
@ -203,12 +208,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
|
||||
|
@ -347,6 +346,12 @@ abstract class PluginsModule {
|
|||
// @IntKey(480)
|
||||
// abstract fun bindOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase
|
||||
|
||||
@Binds
|
||||
@NotNSClient
|
||||
@IntoMap
|
||||
@IntKey(480)
|
||||
abstract fun bindsOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase
|
||||
|
||||
@Binds
|
||||
@AllConfigs
|
||||
@IntoMap
|
||||
|
|
|
@ -17,7 +17,7 @@ import info.nightscout.androidaps.database.entities.TemporaryTarget
|
|||
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
||||
import info.nightscout.androidaps.database.entities.ValueWithUnit
|
||||
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
|
||||
import info.nightscout.androidaps.databinding.DialogCarbsBinding
|
||||
import info.nightscout.androidaps.extensions.formatColor
|
||||
import info.nightscout.androidaps.interfaces.Constraint
|
||||
|
@ -27,7 +27,6 @@ import info.nightscout.androidaps.interfaces.ProfileFunction
|
|||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
|
||||
import info.nightscout.androidaps.queue.Callback
|
||||
import info.nightscout.androidaps.queue.CommandQueue
|
||||
import info.nightscout.androidaps.utils.*
|
||||
|
@ -47,7 +46,6 @@ class CarbsDialog : DialogFragmentWithDate() {
|
|||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var constraintChecker: ConstraintChecker
|
||||
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
||||
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var iobCobCalculator: IobCobCalculator
|
||||
@Inject lateinit var uel: UserEntryLogger
|
||||
|
@ -228,7 +226,7 @@ class CarbsDialog : DialogFragmentWithDate() {
|
|||
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY),
|
||||
ValueWithUnit.fromGlucoseUnit(activityTT, units.asText),
|
||||
ValueWithUnit.Minute(activityTTDuration))
|
||||
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
duration = TimeUnit.MINUTES.toMillis(activityTTDuration.toLong()),
|
||||
reason = TemporaryTarget.Reason.ACTIVITY,
|
||||
|
@ -247,7 +245,7 @@ class CarbsDialog : DialogFragmentWithDate() {
|
|||
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
|
||||
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
|
||||
ValueWithUnit.Minute(eatingSoonTTDuration))
|
||||
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
|
||||
reason = TemporaryTarget.Reason.EATING_SOON,
|
||||
|
@ -266,7 +264,7 @@ class CarbsDialog : DialogFragmentWithDate() {
|
|||
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA),
|
||||
ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText),
|
||||
ValueWithUnit.Minute(hypoTTDuration))
|
||||
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
duration = TimeUnit.MINUTES.toMillis(hypoTTDuration.toLong()),
|
||||
reason = TemporaryTarget.Reason.HYPOGLYCEMIA,
|
||||
|
|
|
@ -191,7 +191,7 @@ class FillDialog : DialogFragmentWithDate() {
|
|||
commandQueue.bolus(detailedBolusInfo, object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -16,7 +16,7 @@ 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.InsertTemporaryTargetAndCancelCurrentTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
|
||||
import info.nightscout.androidaps.databinding.DialogInsulinBinding
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
|
@ -186,7 +186,7 @@ class InsulinDialog : DialogFragmentWithDate() {
|
|||
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
|
||||
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
|
||||
ValueWithUnit.Minute(eatingSoonTTDuration))
|
||||
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
|
||||
reason = TemporaryTarget.Reason.EATING_SOON,
|
||||
|
@ -224,7 +224,7 @@ class InsulinDialog : DialogFragmentWithDate() {
|
|||
commandQueue.bolus(detailedBolusInfo, object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -3,6 +3,7 @@ package info.nightscout.androidaps.dialogs
|
|||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -12,9 +13,13 @@ 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.ValueWithUnit
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
||||
import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
|
||||
import info.nightscout.androidaps.databinding.DialogLoopBinding
|
||||
import info.nightscout.androidaps.events.EventPreferenceChange
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview
|
||||
|
@ -29,8 +34,14 @@ import info.nightscout.androidaps.utils.FabricPrivacy
|
|||
import info.nightscout.androidaps.utils.ToastUtils
|
||||
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||
import info.nightscout.androidaps.extensions.toVisibility
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoopDialog : DaggerDialogFragment() {
|
||||
|
@ -48,19 +59,27 @@ class LoopDialog : DaggerDialogFragment() {
|
|||
@Inject lateinit var commandQueue: CommandQueueProvider
|
||||
@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 loopHandler = Handler(Looper.getMainLooper())
|
||||
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) {
|
||||
|
@ -118,6 +137,7 @@ class LoopDialog : DaggerDialogFragment() {
|
|||
super.onDestroyView()
|
||||
_binding = null
|
||||
loopHandler.removeCallbacksAndMessages(null)
|
||||
disposable.clear()
|
||||
}
|
||||
|
||||
var task: Runnable? = null
|
||||
|
@ -141,21 +161,74 @@ class LoopDialog : DaggerDialogFragment() {
|
|||
aapsLogger.debug("UpdateGUI from $from")
|
||||
val pumpDescription: PumpDescription = activePlugin.activePump.pumpDescription
|
||||
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 {
|
||||
closedLoopAllowed.value() -> {
|
||||
binding.overviewCloseloop.visibility = (apsMode != "closed").toVisibility()
|
||||
binding.overviewLgsloop.visibility = (apsMode != "lgs").toVisibility()
|
||||
binding.overviewOpenloop.visibility = (apsMode != "open").toVisibility()
|
||||
pump.isSuspended() -> {
|
||||
binding.overviewLoop.visibility = View.GONE
|
||||
binding.overviewSuspend.visibility = View.GONE
|
||||
binding.overviewPump.visibility = View.GONE
|
||||
}
|
||||
|
||||
lgsEnabled.value() -> {
|
||||
!profileFunction.isProfileValid("LoopDialogUpdateGUI") -> {
|
||||
binding.overviewLoop.visibility = View.GONE
|
||||
binding.overviewSuspend.visibility = View.GONE
|
||||
binding.overviewPump.visibility = View.GONE
|
||||
}
|
||||
|
||||
loopPlugin.isDisconnected -> {
|
||||
binding.overviewLoop.visibility = View.GONE
|
||||
binding.overviewSuspend.visibility = View.GONE
|
||||
binding.overviewPump.visibility = View.VISIBLE
|
||||
binding.overviewPumpHeader.text = resourceHelper.gs(R.string.reconnect)
|
||||
binding.overviewDisconnectButtons.visibility = View.VISIBLE
|
||||
binding.overviewReconnect.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
!loopPlugin.isEnabled(PluginType.LOOP) -> {
|
||||
binding.overviewLoop.visibility = View.VISIBLE
|
||||
binding.overviewEnable.visibility = View.VISIBLE
|
||||
binding.overviewDisable.visibility = View.GONE
|
||||
binding.overviewSuspend.visibility = View.GONE
|
||||
binding.overviewPump.visibility = View.VISIBLE
|
||||
binding.overviewReconnect.visibility = View.GONE
|
||||
}
|
||||
|
||||
loopPlugin.isSuspended -> {
|
||||
binding.overviewLoop.visibility = View.GONE
|
||||
binding.overviewSuspend.visibility = View.VISIBLE
|
||||
binding.overviewSuspendHeader.text = resourceHelper.gs(R.string.resumeloop)
|
||||
binding.overviewSuspendButtons.visibility = View.VISIBLE
|
||||
binding.overviewResume.visibility = View.VISIBLE
|
||||
binding.overviewPump.visibility = View.GONE
|
||||
binding.overviewReconnect.visibility = View.GONE
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding.overviewLoop.visibility = View.VISIBLE
|
||||
binding.overviewEnable.visibility = View.GONE
|
||||
when {
|
||||
apsMode == "closed" -> {
|
||||
binding.overviewCloseloop.visibility = View.GONE
|
||||
binding.overviewLgsloop.visibility = (apsMode != "lgs").toVisibility()
|
||||
binding.overviewOpenloop.visibility = (apsMode != "open").toVisibility()
|
||||
binding.overviewLgsloop.visibility = View.VISIBLE
|
||||
binding.overviewOpenloop.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
apsMode == "lgs" -> {
|
||||
binding.overviewCloseloop.visibility = closedLoopAllowed.value().toVisibility() //show Close loop button only if Close loop allowed
|
||||
binding.overviewLgsloop.visibility = View.GONE
|
||||
binding.overviewOpenloop.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
apsMode == "open" -> {
|
||||
binding.overviewCloseloop.visibility = closedLoopAllowed2.toVisibility() //show CloseLoop button only if Objective 6 is completed (closedLoopAllowed always false in open loop mode)
|
||||
binding.overviewLgsloop.visibility = lgsEnabled.value().toVisibility()
|
||||
binding.overviewOpenloop.visibility = View.GONE
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
@ -164,49 +237,18 @@ class LoopDialog : DaggerDialogFragment() {
|
|||
binding.overviewOpenloop.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
binding.overviewEnable.visibility = View.GONE
|
||||
binding.overviewDisable.visibility = View.VISIBLE
|
||||
if (!loopPlugin.isSuspended) {
|
||||
binding.overviewSuspend.visibility = View.VISIBLE
|
||||
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.overviewResume.visibility = View.GONE
|
||||
|
||||
binding.overviewPump.visibility = View.VISIBLE
|
||||
binding.overviewPumpHeader.text = resourceHelper.gs(R.string.disconnectpump)
|
||||
binding.overviewDisconnect15m.visibility = pumpDescription.tempDurationStep15mAllowed.toVisibility()
|
||||
binding.overviewDisconnect30m.visibility = pumpDescription.tempDurationStep30mAllowed.toVisibility()
|
||||
binding.overviewDisconnectButtons.visibility = View.VISIBLE
|
||||
binding.overviewReconnect.visibility = View.GONE
|
||||
} else {
|
||||
binding.overviewPumpHeader.text = resourceHelper.gs(R.string.reconnect)
|
||||
binding.overviewDisconnectButtons.visibility = View.GONE
|
||||
binding.overviewReconnect.visibility = View.VISIBLE
|
||||
}
|
||||
binding.overviewLoop.visibility = (!loopPlugin.isSuspended && !loopPlugin.isDisconnected).toVisibility()
|
||||
}
|
||||
val profile = profileFunction.getProfile()
|
||||
val profileStore = activePlugin.activeProfileSource.profile
|
||||
|
||||
if (profile == null || profileStore == null) {
|
||||
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofile))
|
||||
dismiss()
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun onClickOkCancelEnabled(v: View): Boolean {
|
||||
|
@ -238,7 +280,6 @@ class LoopDialog : DaggerDialogFragment() {
|
|||
}
|
||||
|
||||
fun onClick(v: View): Boolean {
|
||||
val profile = profileFunction.getProfile() ?: return true
|
||||
when (v.id) {
|
||||
R.id.overview_closeloop -> {
|
||||
uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog)
|
||||
|
@ -266,7 +307,7 @@ class LoopDialog : DaggerDialogFragment() {
|
|||
loopPlugin.setPluginEnabled(PluginType.LOOP, false)
|
||||
loopPlugin.setFragmentVisible(PluginType.LOOP, false)
|
||||
configBuilder.storeSettings("DisablingLoop")
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
commandQueue.cancelTempBasal(true, object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
|
@ -274,7 +315,13 @@ class LoopDialog : DaggerDialogFragment() {
|
|||
}
|
||||
}
|
||||
})
|
||||
loopPlugin.createOfflineEvent(24 * 60) // upload 24h, we don't know real duration
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.days(365).msecs(), OfflineEvent.Reason.DISABLE_LOOP))
|
||||
.subscribe({ result ->
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -283,88 +330,106 @@ class LoopDialog : DaggerDialogFragment() {
|
|||
loopPlugin.setPluginEnabled(PluginType.LOOP, true)
|
||||
loopPlugin.setFragmentVisible(PluginType.LOOP, true)
|
||||
configBuilder.storeSettings("EnablingLoop")
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.createOfflineEvent(0)
|
||||
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 -> {
|
||||
uel.log(if (v.id==R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog)
|
||||
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) {
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
|
||||
}
|
||||
}
|
||||
})
|
||||
sp.putBoolean(R.string.key_objectiveusereconnect, true)
|
||||
loopPlugin.createOfflineEvent(0)
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.overview_suspend_1h -> {
|
||||
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1))
|
||||
loopPlugin.suspendLoop(60)
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.suspendLoop(T.hours(1).mins().toInt())
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.overview_suspend_2h -> {
|
||||
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2))
|
||||
loopPlugin.suspendLoop(120)
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.suspendLoop(T.hours(2).mins().toInt())
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.overview_suspend_3h -> {
|
||||
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3))
|
||||
loopPlugin.suspendLoop(180)
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.suspendLoop(T.hours(3).mins().toInt())
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.overview_suspend_10h -> {
|
||||
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10))
|
||||
loopPlugin.suspendLoop(600)
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.suspendLoop(T.hours(10).mins().toInt())
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.overview_disconnect_15m -> {
|
||||
profileFunction.getProfile()?.let { profile ->
|
||||
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15))
|
||||
loopPlugin.disconnectPump(15, profile)
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.goToZeroTemp(T.mins(15).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.overview_disconnect_30m -> {
|
||||
profileFunction.getProfile()?.let { profile ->
|
||||
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30))
|
||||
loopPlugin.disconnectPump(30, profile)
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.goToZeroTemp(T.mins(30).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.overview_disconnect_1h -> {
|
||||
profileFunction.getProfile()?.let { profile ->
|
||||
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(1))
|
||||
loopPlugin.disconnectPump(60, profile)
|
||||
sp.putBoolean(R.string.key_objectiveusedisconnect, true)
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.goToZeroTemp(T.hours(1).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.overview_disconnect_2h -> {
|
||||
profileFunction.getProfile()?.let { profile ->
|
||||
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2))
|
||||
loopPlugin.disconnectPump(120, profile)
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.goToZeroTemp(T.hours(2).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.overview_disconnect_3h -> {
|
||||
profileFunction.getProfile()?.let { profile ->
|
||||
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3))
|
||||
loopPlugin.disconnectPump(180, profile)
|
||||
rxBus.send(EventRefreshOverview("suspendmenu"))
|
||||
loopPlugin.goToZeroTemp(T.hours(3).mins().toInt(), profile, OfflineEvent.Reason.DISCONNECT_PUMP)
|
||||
rxBus.send(EventRefreshOverview("suspend_menu"))
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -378,7 +443,7 @@ class LoopDialog : DaggerDialogFragment() {
|
|||
it.commitAllowingStateLoss()
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
aapsLogger.debug(e.localizedMessage)
|
||||
aapsLogger.debug(e.localizedMessage ?: e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,20 +10,32 @@ 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.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.logging.LTag
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.utils.DefaultValueHelper
|
||||
import info.nightscout.androidaps.utils.HardLimits
|
||||
import info.nightscout.androidaps.utils.HtmlHelper
|
||||
import info.nightscout.androidaps.utils.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 ProfileSwitchDialog : DialogFragmentWithDate() {
|
||||
|
@ -31,6 +45,10 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
|
|||
@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: RxBusWrapper
|
||||
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
||||
|
||||
private var profileIndex: Int? = null
|
||||
|
||||
|
@ -42,6 +60,17 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
|
|||
// 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)
|
||||
|
@ -63,9 +92,11 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
|
|||
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)
|
||||
?: 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)
|
||||
?: 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)
|
||||
|
||||
|
@ -97,6 +128,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
|
|||
binding.reuselayout.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
binding.ttLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -128,7 +160,16 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
|
|||
if (eventTimeChanged)
|
||||
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
|
||||
|
||||
val isTT = binding.duration.value > 0 && binding.percentage.value < 100 && binding.tt.isChecked
|
||||
val target = defaultValueHelper.determineActivityTT()
|
||||
val units = profileFunction.getUnits()
|
||||
if (isTT)
|
||||
actions.add(resourceHelper.gs(R.string.careportal_temporarytarget) + ": " + resourceHelper.gs(R.string.activity))
|
||||
|
||||
activity?.let { activity ->
|
||||
val ps = profileFunction.buildProfileSwitch(profileStore, profileName, duration, percent, timeShift, eventTime)
|
||||
val validity = ProfileSealed.PS(ps).isValid(resourceHelper.gs(R.string.careportal_profileswitch), activePlugin.activePump, config, resourceHelper, rxBus, hardLimits)
|
||||
if (validity.isValid)
|
||||
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
|
||||
profileFunction.createProfileSwitch(profileStore,
|
||||
profileName = profileName,
|
||||
|
@ -144,7 +185,33 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
|
|||
ValueWithUnit.Percent(percent),
|
||||
ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 },
|
||||
ValueWithUnit.Minute(duration).takeIf { duration != 0 })
|
||||
if (isTT) {
|
||||
disposable += repository.runTransactionForResult(
|
||||
InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = eventTime,
|
||||
duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
|
||||
reason = TemporaryTarget.Reason.ACTIVITY,
|
||||
lowTarget = Profile.toMgdl(target, profileFunction.getUnits()),
|
||||
highTarget = Profile.toMgdl(target, profileFunction.getUnits())
|
||||
)
|
||||
).subscribe({ result ->
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
|
||||
})
|
||||
uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(
|
||||
TemporaryTarget.Reason.ACTIVITY), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
|
||||
}
|
||||
})
|
||||
else {
|
||||
OKDialog.show(
|
||||
activity,
|
||||
resourceHelper.gs(R.string.careportal_profileswitch),
|
||||
HtmlHelper.fromHtml(Joiner.on("<br/>").join(validity.reasons))
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ class TempBasalDialog : DialogFragmentWithDate() {
|
|||
val callback: Callback = object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import info.nightscout.androidaps.database.entities.TemporaryTarget
|
|||
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
||||
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
|
||||
import info.nightscout.androidaps.databinding.DialogTemptargetBinding
|
||||
import info.nightscout.androidaps.interfaces.GlucoseUnit
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
|
@ -196,7 +196,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
|
|||
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
|
||||
})
|
||||
} else {
|
||||
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = eventTime,
|
||||
duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
|
||||
reason = when (reason) {
|
||||
|
|
|
@ -174,7 +174,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
|
|||
commandQueue.bolus(detailedBolusInfo, object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
|
||||
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -384,7 +384,7 @@ class WizardDialog : DaggerDialogFragment() {
|
|||
it.commitAllowingStateLoss()
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
aapsLogger.debug(e.localizedMessage)
|
||||
aapsLogger.debug(e.localizedMessage ?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package info.nightscout.androidaps.events
|
||||
|
||||
class EventTreatmentUpdateGui : EventUpdateGui()
|
|
@ -1,379 +0,0 @@
|
|||
package info.nightscout.androidaps.historyBrowser
|
||||
|
||||
import android.app.DatePickerDialog
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.jjoe64.graphview.GraphView
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
|
||||
import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding
|
||||
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
|
||||
import info.nightscout.androidaps.events.EventCustomCalculationFinished
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview
|
||||
import info.nightscout.androidaps.extensions.toVisibility
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.overview.OverviewMenus
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.DefaultValueHelper
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class HistoryBrowseActivity : NoSplashAppCompatActivity() {
|
||||
|
||||
@Inject lateinit var injector: HasAndroidInjector
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
||||
@Inject lateinit var iobCobCalculatorPluginHistory: IobCobCalculatorPluginHistory
|
||||
@Inject lateinit var activePlugin: ActivePlugin
|
||||
@Inject lateinit var buildHelper: BuildHelper
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Inject lateinit var overviewMenus: OverviewMenus
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
|
||||
private val disposable = CompositeDisposable()
|
||||
|
||||
private val secondaryGraphs = ArrayList<GraphView>()
|
||||
private val secondaryGraphsLabel = ArrayList<TextView>()
|
||||
|
||||
private var axisWidth: Int = 0
|
||||
private var rangeToDisplay = 24 // for graph
|
||||
private var start: Long = 0
|
||||
|
||||
private val graphLock = Object()
|
||||
|
||||
private var eventCustomCalculationFinished = EventCustomCalculationFinished()
|
||||
|
||||
private lateinit var binding: ActivityHistorybrowseBinding
|
||||
private var destroyed = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityHistorybrowseBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.left.setOnClickListener {
|
||||
start -= T.hours(rangeToDisplay.toLong()).msecs()
|
||||
runCalculation("onClickLeft")
|
||||
}
|
||||
binding.right.setOnClickListener {
|
||||
start += T.hours(rangeToDisplay.toLong()).msecs()
|
||||
runCalculation("onClickRight")
|
||||
}
|
||||
binding.end.setOnClickListener {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.timeInMillis = System.currentTimeMillis()
|
||||
calendar[Calendar.MILLISECOND] = 0
|
||||
calendar[Calendar.SECOND] = 0
|
||||
calendar[Calendar.MINUTE] = 0
|
||||
calendar[Calendar.HOUR_OF_DAY] = 0
|
||||
start = calendar.timeInMillis
|
||||
runCalculation("onClickEnd")
|
||||
}
|
||||
binding.zoom.setOnClickListener {
|
||||
rangeToDisplay += 6
|
||||
rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay
|
||||
updateGUI("rangeChange", false)
|
||||
}
|
||||
binding.zoom.setOnLongClickListener {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.timeInMillis = start
|
||||
calendar[Calendar.MILLISECOND] = 0
|
||||
calendar[Calendar.SECOND] = 0
|
||||
calendar[Calendar.MINUTE] = 0
|
||||
calendar[Calendar.HOUR_OF_DAY] = 0
|
||||
start = calendar.timeInMillis
|
||||
runCalculation("onLongClickZoom")
|
||||
true
|
||||
}
|
||||
|
||||
// create an OnDateSetListener
|
||||
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
|
||||
val cal = Calendar.getInstance()
|
||||
cal.timeInMillis = start
|
||||
cal[Calendar.YEAR] = year
|
||||
cal[Calendar.MONTH] = monthOfYear
|
||||
cal[Calendar.DAY_OF_MONTH] = dayOfMonth
|
||||
cal[Calendar.MILLISECOND] = 0
|
||||
cal[Calendar.SECOND] = 0
|
||||
cal[Calendar.MINUTE] = 0
|
||||
cal[Calendar.HOUR_OF_DAY] = 0
|
||||
start = cal.timeInMillis
|
||||
binding.date.text = dateUtil.dateAndTimeString(start)
|
||||
runCalculation("onClickDate")
|
||||
}
|
||||
|
||||
binding.date.setOnClickListener {
|
||||
val cal = Calendar.getInstance()
|
||||
cal.timeInMillis = start
|
||||
DatePickerDialog(this, dateSetListener,
|
||||
cal.get(Calendar.YEAR),
|
||||
cal.get(Calendar.MONTH),
|
||||
cal.get(Calendar.DAY_OF_MONTH)
|
||||
).show()
|
||||
}
|
||||
|
||||
val dm = DisplayMetrics()
|
||||
windowManager?.defaultDisplay?.getMetrics(dm)
|
||||
|
||||
axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80
|
||||
binding.bggraph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
|
||||
binding.bggraph.gridLabelRenderer?.reloadStyles()
|
||||
binding.bggraph.gridLabelRenderer?.labelVerticalWidth = axisWidth
|
||||
|
||||
overviewMenus.setupChartMenu(binding.chartMenuButton)
|
||||
prepareGraphsIfNeeded(overviewMenus.setting.size)
|
||||
savedInstanceState?.let { bundle ->
|
||||
rangeToDisplay = bundle.getInt("rangeToDisplay", 0)
|
||||
start = bundle.getLong("start", 0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override fun onPause() {
|
||||
super.onPause()
|
||||
disposable.clear()
|
||||
iobCobCalculatorPluginHistory.stopCalculation("onPause")
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onDestroy() {
|
||||
destroyed = true
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
public override fun onResume() {
|
||||
super.onResume()
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventAutosensCalculationFinished::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
// catch only events from iobCobCalculatorPluginHistory
|
||||
if (it.cause is EventCustomCalculationFinished) {
|
||||
updateGUI("EventAutosensCalculationFinished", bgOnly = false)
|
||||
}
|
||||
}, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventIobCalculationProgress::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({ binding.overviewIobcalculationprogess.text = it.progress }, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventRefreshOverview::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({
|
||||
if (it.now) {
|
||||
updateGUI("EventRefreshOverview", bgOnly = false)
|
||||
}
|
||||
}, fabricPrivacy::logException)
|
||||
)
|
||||
if (start == 0L) {
|
||||
// set start of current day
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.timeInMillis = System.currentTimeMillis()
|
||||
calendar[Calendar.MILLISECOND] = 0
|
||||
calendar[Calendar.SECOND] = 0
|
||||
calendar[Calendar.MINUTE] = 0
|
||||
calendar[Calendar.HOUR_OF_DAY] = 0
|
||||
start = calendar.timeInMillis
|
||||
runCalculation("onResume")
|
||||
} else {
|
||||
updateGUI("onResume", bgOnly = false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt("rangeToDisplay", rangeToDisplay)
|
||||
outState.putLong("start", start)
|
||||
|
||||
}
|
||||
|
||||
private fun prepareGraphsIfNeeded(numOfGraphs: Int) {
|
||||
synchronized(graphLock) {
|
||||
if (numOfGraphs != secondaryGraphs.size - 1) {
|
||||
//aapsLogger.debug("New secondary graph count ${numOfGraphs-1}")
|
||||
// rebuild needed
|
||||
secondaryGraphs.clear()
|
||||
secondaryGraphsLabel.clear()
|
||||
binding.iobGraph.removeAllViews()
|
||||
for (i in 1 until numOfGraphs) {
|
||||
val relativeLayout = RelativeLayout(this)
|
||||
relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
val graph = GraphView(this)
|
||||
graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(100)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) }
|
||||
graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
|
||||
graph.gridLabelRenderer?.reloadStyles()
|
||||
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
|
||||
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
|
||||
graph.gridLabelRenderer?.numVerticalLabels = 3
|
||||
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray
|
||||
relativeLayout.addView(graph)
|
||||
|
||||
val label = TextView(this)
|
||||
val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) }
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP)
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
|
||||
label.layoutParams = layoutParams
|
||||
relativeLayout.addView(label)
|
||||
secondaryGraphsLabel.add(label)
|
||||
|
||||
binding.iobGraph.addView(relativeLayout)
|
||||
secondaryGraphs.add(graph)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun runCalculation(from: String) {
|
||||
lifecycleScope.launch(Dispatchers.Default) {
|
||||
val end = start + T.hours(rangeToDisplay.toLong()).msecs()
|
||||
iobCobCalculatorPluginHistory.stopCalculation(from)
|
||||
iobCobCalculatorPluginHistory.clearCache()
|
||||
iobCobCalculatorPluginHistory.runCalculation(from, end, bgDataReload = true, limitDataToOldestAvailable = false, cause = eventCustomCalculationFinished)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun updateGUI(from: String, bgOnly: Boolean) {
|
||||
val menuChartSettings = overviewMenus.setting
|
||||
prepareGraphsIfNeeded(menuChartSettings.size)
|
||||
aapsLogger.debug(LTag.UI, "updateGUI from: $from")
|
||||
val pump = activePlugin.activePump
|
||||
val profile = profileFunction.getProfile()
|
||||
|
||||
val lowLine = defaultValueHelper.determineLowLine()
|
||||
val highLine = defaultValueHelper.determineHighLine()
|
||||
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
binding.noprofile.visibility = (profile == null).toVisibility()
|
||||
profile ?: return@launch
|
||||
|
||||
if (destroyed) return@launch
|
||||
binding.date.text = dateUtil.dateAndTimeString(start)
|
||||
binding.zoom.text = rangeToDisplay.toString()
|
||||
val graphData = GraphData(injector, binding.bggraph)
|
||||
val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
|
||||
|
||||
// do preparation in different thread
|
||||
withContext(Dispatchers.Default) {
|
||||
val fromTime: Long = start + T.secs(100).msecs()
|
||||
val toTime: Long = start + T.hours(rangeToDisplay.toLong()).msecs() + T.secs(100).msecs()
|
||||
aapsLogger.debug(LTag.UI, "Period: " + dateUtil.dateAndTimeString(fromTime) + " - " + dateUtil.dateAndTimeString(toTime))
|
||||
val pointer = System.currentTimeMillis()
|
||||
|
||||
// **** In range Area ****
|
||||
graphData.addInRangeArea(fromTime, toTime, lowLine, highLine)
|
||||
|
||||
// **** BG ****
|
||||
// graphData.addBgReadings(fromTime, toTime, highLine, null)
|
||||
// if (buildHelper.isDev()) graphData.addBucketedData(fromTime, toTime)
|
||||
|
||||
// add target line
|
||||
// graphData.addTargetLine(fromTime, toTime, profile, null)
|
||||
|
||||
// **** NOW line ****
|
||||
graphData.addNowLine(pointer)
|
||||
|
||||
if (!bgOnly) {
|
||||
// Treatments
|
||||
// graphData.addTreatments(fromTime, toTime)
|
||||
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
|
||||
// graphData.addActivity(fromTime, toTime, false, 0.8)
|
||||
|
||||
// add basal data
|
||||
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) {
|
||||
// graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2)
|
||||
}
|
||||
// ------------------ 2nd graph
|
||||
synchronized(graphLock) {
|
||||
for (g in 0 until secondaryGraphs.size) {
|
||||
val secondGraphData = GraphData(injector, secondaryGraphs[g])
|
||||
var useIobForScale = false
|
||||
var useCobForScale = false
|
||||
var useDevForScale = false
|
||||
var useRatioForScale = false
|
||||
var useDSForScale = false
|
||||
var useBGIForScale = false
|
||||
var useABSForScale = false
|
||||
when {
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
|
||||
}
|
||||
|
||||
val alignIobScale = menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]
|
||||
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
|
||||
|
||||
// if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, toTime, useABSForScale, 1.0)
|
||||
// if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, toTime, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal], alignIobScale)
|
||||
// if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, toTime, useCobForScale, if (useCobForScale) 1.0 else 0.5)
|
||||
// if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1.0, alignDevBgiScale)
|
||||
// if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1.0)
|
||||
// if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, toTime, useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale)
|
||||
// if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, toTime, useDSForScale, 1.0)
|
||||
|
||||
// set manual x bounds to have nice steps
|
||||
secondGraphData.formatAxis(fromTime, toTime)
|
||||
secondGraphData.addNowLine(pointer)
|
||||
secondaryGraphsData.add(secondGraphData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set manual x bounds to have nice steps
|
||||
graphData.setNumVerticalLabels()
|
||||
graphData.formatAxis(fromTime, toTime)
|
||||
}
|
||||
// finally enforce drawing of graphs in UI thread
|
||||
graphData.performUpdate()
|
||||
if (!bgOnly)
|
||||
synchronized(graphLock) {
|
||||
for (g in 0 until secondaryGraphs.size) {
|
||||
secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1)
|
||||
secondaryGraphs[g].visibility = (!bgOnly && (
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] ||
|
||||
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal]
|
||||
)).toVisibility()
|
||||
secondaryGraphsData[g].performUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package info.nightscout.androidaps.historyBrowser
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class IobCobCalculatorPluginHistory @Inject constructor(
|
||||
injector: HasAndroidInjector,
|
||||
aapsLogger: AAPSLogger,
|
||||
aapsSchedulers: AapsSchedulers,
|
||||
rxBus: RxBusWrapper,
|
||||
sp: SP,
|
||||
resourceHelper: ResourceHelper,
|
||||
profileFunction: ProfileFunction,
|
||||
activePlugin: ActivePlugin,
|
||||
sensitivityOref1Plugin: SensitivityOref1Plugin,
|
||||
sensitivityAAPSPlugin: SensitivityAAPSPlugin,
|
||||
sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
|
||||
fabricPrivacy: FabricPrivacy,
|
||||
dateUtil: DateUtil,
|
||||
repository: AppRepository
|
||||
) : IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction,
|
||||
activePlugin, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository) {
|
||||
|
||||
override fun onStart() { // do not attach to rxbus
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.aps.logger
|
||||
|
||||
import info.nightscout.androidaps.db.StaticInjector
|
||||
import info.nightscout.androidaps.di.StaticInjector
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import org.mozilla.javascript.ScriptableObject
|
||||
|
|
|
@ -12,22 +12,24 @@ import androidx.core.app.NotificationCompat
|
|||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.*
|
||||
import info.nightscout.androidaps.activities.ErrorHelperActivity
|
||||
import info.nightscout.androidaps.annotations.OpenForTesting
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.data.PumpEnactResult
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.entities.TherapyEvent
|
||||
import info.nightscout.androidaps.database.ValueWrapper
|
||||
import info.nightscout.androidaps.database.entities.OfflineEvent
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
||||
import info.nightscout.androidaps.database.entities.ValueWithUnit
|
||||
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction
|
||||
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
|
||||
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
|
||||
import info.nightscout.androidaps.events.EventNewBG
|
||||
import info.nightscout.androidaps.events.EventTempTargetChange
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
import info.nightscout.androidaps.interfaces.LoopInterface.LastRun
|
||||
import info.nightscout.androidaps.interfaces.Loop.LastRun
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
|
@ -44,7 +46,6 @@ import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAc
|
|||
import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
|
||||
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
|
||||
import info.nightscout.androidaps.queue.Callback
|
||||
import info.nightscout.androidaps.queue.commands.Command
|
||||
import info.nightscout.androidaps.receivers.ReceiverStatusStore
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
|
@ -64,8 +65,9 @@ import javax.inject.Inject
|
|||
import javax.inject.Singleton
|
||||
import kotlin.math.abs
|
||||
|
||||
@OpenForTesting
|
||||
@Singleton
|
||||
open class LoopPlugin @Inject constructor(
|
||||
class LoopPlugin @Inject constructor(
|
||||
injector: HasAndroidInjector,
|
||||
aapsLogger: AAPSLogger,
|
||||
private val aapsSchedulers: AapsSchedulers,
|
||||
|
@ -96,7 +98,7 @@ open class LoopPlugin @Inject constructor(
|
|||
.enableByDefault(config.APS)
|
||||
.description(R.string.description_loop),
|
||||
aapsLogger, resourceHelper, injector
|
||||
), LoopInterface {
|
||||
), Loop {
|
||||
|
||||
private val disposable = CompositeDisposable()
|
||||
private var lastBgTriggeredRun: Long = 0
|
||||
|
@ -158,48 +160,21 @@ open class LoopPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun suspendTo(endTime: Long) {
|
||||
sp.putLong("loopSuspendedTill", endTime)
|
||||
sp.putBoolean("isSuperBolus", false)
|
||||
sp.putBoolean("isDisconnected", false)
|
||||
override fun minutesToEndOfSuspend(): Int {
|
||||
val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet()
|
||||
return if (offlineEventWrapped is ValueWrapper.Existing) T.msecs(offlineEventWrapped.value.timestamp + offlineEventWrapped.value.duration - dateUtil.now()).mins().toInt()
|
||||
else 0
|
||||
}
|
||||
|
||||
fun superBolusTo(endTime: Long) {
|
||||
sp.putLong("loopSuspendedTill", endTime)
|
||||
sp.putBoolean("isSuperBolus", true)
|
||||
sp.putBoolean("isDisconnected", false)
|
||||
}
|
||||
|
||||
private fun disconnectTo(endTime: Long) {
|
||||
sp.putLong("loopSuspendedTill", endTime)
|
||||
sp.putBoolean("isSuperBolus", false)
|
||||
sp.putBoolean("isDisconnected", true)
|
||||
}
|
||||
|
||||
fun minutesToEndOfSuspend(): Int {
|
||||
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
|
||||
if (loopSuspendedTill == 0L) return 0
|
||||
val now = System.currentTimeMillis()
|
||||
val millisDiff = loopSuspendedTill - now
|
||||
if (loopSuspendedTill <= now) { // time exceeded
|
||||
suspendTo(0L)
|
||||
return 0
|
||||
}
|
||||
return (millisDiff / 60.0 / 1000.0).toInt()
|
||||
}
|
||||
|
||||
// time exceeded
|
||||
override val isSuspended: Boolean
|
||||
get() {
|
||||
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
|
||||
if (loopSuspendedTill == 0L) return false
|
||||
val now = System.currentTimeMillis()
|
||||
if (loopSuspendedTill <= now) { // time exceeded
|
||||
suspendTo(0L)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
get() = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
|
||||
|
||||
override var enabled: Boolean
|
||||
get() = isEnabled()
|
||||
set(value) {
|
||||
setPluginEnabled(PluginType.LOOP, value)
|
||||
}
|
||||
|
||||
val isLGS: Boolean
|
||||
get() {
|
||||
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
|
||||
|
@ -211,30 +186,16 @@ open class LoopPlugin @Inject constructor(
|
|||
return isLGS
|
||||
}
|
||||
|
||||
// time exceeded
|
||||
val isSuperBolus: Boolean
|
||||
get() {
|
||||
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
|
||||
if (loopSuspendedTill == 0L) return false
|
||||
val now = System.currentTimeMillis()
|
||||
if (loopSuspendedTill <= now) { // time exceeded
|
||||
suspendTo(0L)
|
||||
return false
|
||||
}
|
||||
return sp.getBoolean("isSuperBolus", false)
|
||||
val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet()
|
||||
return offlineEventWrapped is ValueWrapper.Existing && offlineEventWrapped.value.reason == OfflineEvent.Reason.SUPER_BOLUS
|
||||
}
|
||||
|
||||
// time exceeded
|
||||
val isDisconnected: Boolean
|
||||
get() {
|
||||
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
|
||||
if (loopSuspendedTill == 0L) return false
|
||||
val now = System.currentTimeMillis()
|
||||
if (loopSuspendedTill <= now) { // time exceeded
|
||||
suspendTo(0L)
|
||||
return false
|
||||
}
|
||||
return sp.getBoolean("isDisconnected", false)
|
||||
val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet()
|
||||
return offlineEventWrapped is ValueWrapper.Existing && offlineEventWrapped.value.reason == OfflineEvent.Reason.DISCONNECT_PUMP
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
|
@ -251,6 +212,17 @@ open class LoopPlugin @Inject constructor(
|
|||
invoke(initiator, allowNotification, false)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun isEmptyQueue(): Boolean {
|
||||
val maxMinutes = 2L
|
||||
val start = dateUtil.now()
|
||||
while (start + T.mins(maxMinutes).msecs() > dateUtil.now()) {
|
||||
if (commandQueue.size() == 0 && commandQueue.performing() == null) return true
|
||||
SystemClock.sleep(100)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
operator fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean) {
|
||||
try {
|
||||
|
@ -289,6 +261,12 @@ open class LoopPlugin @Inject constructor(
|
|||
return
|
||||
} else rxBus.send(EventLoopInvoked())
|
||||
|
||||
if (!isEmptyQueue()) {
|
||||
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.pumpbusy))
|
||||
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpbusy)))
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare for pumps using % basals
|
||||
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
|
||||
apsResult.usePercent = true
|
||||
|
@ -353,15 +331,15 @@ open class LoopPlugin @Inject constructor(
|
|||
if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
|
||||
val intentAction5m = Intent(context, CarbSuggestionReceiver::class.java)
|
||||
intentAction5m.putExtra("ignoreDuration", 5)
|
||||
val pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val actionIgnore5m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m, "Ignore 5m"), pendingIntent5m)
|
||||
val intentAction15m = Intent(context, CarbSuggestionReceiver::class.java)
|
||||
intentAction15m.putExtra("ignoreDuration", 15)
|
||||
val pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val actionIgnore15m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m, "Ignore 15m"), pendingIntent15m)
|
||||
val intentAction30m = Intent(context, CarbSuggestionReceiver::class.java)
|
||||
intentAction30m.putExtra("ignoreDuration", 30)
|
||||
val pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val actionIgnore30m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m, "Ignore 30m"), pendingIntent30m)
|
||||
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
builder.setSmallIcon(R.drawable.notif_icon)
|
||||
|
@ -399,8 +377,7 @@ open class LoopPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
if (resultAfterConstraints.isChangeRequested
|
||||
&& !commandQueue.bolusInQueue()
|
||||
&& !commandQueue.isRunning(Command.CommandType.BOLUS)) {
|
||||
&& !commandQueue.bolusInQueue()) {
|
||||
val waiting = PumpEnactResult(injector)
|
||||
waiting.queued = true
|
||||
if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting
|
||||
|
@ -483,7 +460,7 @@ open class LoopPlugin @Inject constructor(
|
|||
stackBuilder.addParentStack(MainActivity::class.java)
|
||||
// Adds the Intent that starts the Activity to the top of the stack
|
||||
stackBuilder.addNextIntent(resultIntent)
|
||||
val resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
builder.setContentIntent(resultPendingIntent)
|
||||
builder.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
|
||||
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
@ -642,22 +619,28 @@ open class LoopPlugin @Inject constructor(
|
|||
return virtualPumpPlugin.isEnabled(PluginType.PUMP)
|
||||
}
|
||||
|
||||
fun disconnectPump(durationInMinutes: Int, profile: Profile?) {
|
||||
override fun goToZeroTemp(durationInMinutes: Int, profile: Profile, reason: OfflineEvent.Reason) {
|
||||
val pump = activePlugin.activePump
|
||||
disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L)
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), reason))
|
||||
.subscribe({ result ->
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
|
||||
})
|
||||
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
|
||||
commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
|
||||
commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
|
||||
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
|
||||
commandQueue.tempBasalPercent(0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
|
||||
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -666,37 +649,28 @@ open class LoopPlugin @Inject constructor(
|
|||
commandQueue.cancelExtended(object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.extendedbolusdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
|
||||
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.extendedbolusdeliveryerror), R.raw.boluserror)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
createOfflineEvent(durationInMinutes)
|
||||
}
|
||||
|
||||
override fun suspendLoop(durationInMinutes: Int) {
|
||||
suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000)
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), OfflineEvent.Reason.SUSPEND))
|
||||
.subscribe({ result ->
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
|
||||
})
|
||||
commandQueue.cancelTempBasal(true, object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
|
||||
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
|
||||
}
|
||||
}
|
||||
})
|
||||
createOfflineEvent(durationInMinutes)
|
||||
}
|
||||
|
||||
override fun createOfflineEvent(durationInMinutes: Int) {
|
||||
disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(
|
||||
timestamp = dateUtil.now(),
|
||||
type = TherapyEvent.Type.APS_OFFLINE,
|
||||
duration = T.mins(durationInMinutes.toLong()).msecs(),
|
||||
enteredBy = "openaps://" + "AndroidAPS",
|
||||
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL
|
||||
)).subscribe(
|
||||
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } },
|
||||
{ aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) }
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -17,7 +17,6 @@ import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback
|
|||
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
|
||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
|
||||
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import org.json.JSONArray
|
||||
|
@ -40,7 +39,6 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader
|
|||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var iobCobCalculator: IobCobCalculator
|
||||
@Inject lateinit var openHumansUploader: OpenHumansUploader
|
||||
|
||||
private val mScriptReader: ScriptReader
|
||||
private var profile = JSONObject()
|
||||
|
@ -116,7 +114,6 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader
|
|||
aapsLogger.debug(LTag.APS, "Result: $result")
|
||||
try {
|
||||
val resultJson = JSONObject(result)
|
||||
openHumansUploader.enqueueAMAData(profile, glucoseStatus, iobData, mealData, currentTemp, autosensData, resultJson)
|
||||
determineBasalResultAMA = DetermineBasalResultAMA(injector, jsResult, resultJson)
|
||||
} catch (e: JSONException) {
|
||||
aapsLogger.error(LTag.APS, "Unhandled exception", e)
|
||||
|
|
|
@ -58,7 +58,6 @@ class OpenAPSAMAFragment : DaggerFragment() {
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
|
@ -91,7 +90,6 @@ class OpenAPSAMAFragment : DaggerFragment() {
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun updateGUI() {
|
||||
if (_binding == null) return
|
||||
openAPSAMAPlugin.lastAPSResult?.let { lastAPSResult ->
|
||||
|
|
|
@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.aps.openAPSAMA
|
|||
import android.content.Context
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.annotations.OpenForTesting
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.ValueWrapper
|
||||
|
@ -27,8 +28,9 @@ import org.json.JSONException
|
|||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@OpenForTesting
|
||||
@Singleton
|
||||
open class OpenAPSAMAPlugin @Inject constructor(
|
||||
class OpenAPSAMAPlugin @Inject constructor(
|
||||
injector: HasAndroidInjector,
|
||||
aapsLogger: AAPSLogger,
|
||||
private val rxBus: RxBusWrapper,
|
||||
|
@ -123,11 +125,11 @@ open class OpenAPSAMAPlugin @Inject constructor(
|
|||
maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
|
||||
targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
|
||||
}
|
||||
if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
|
||||
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
|
||||
if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
|
||||
if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
|
||||
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
|
||||
if (!hardLimits.checkHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
|
||||
if (!hardLimits.checkHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
|
||||
if (!hardLimits.checkHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
|
||||
if (!hardLimits.checkHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
|
||||
if (!hardLimits.checkHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
|
||||
startPart = System.currentTimeMillis()
|
||||
if (constraintChecker.isAutosensModeEnabled().value()) {
|
||||
val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin")
|
||||
|
|
|
@ -17,7 +17,6 @@ import info.nightscout.androidaps.logging.LTag
|
|||
import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback
|
||||
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
|
||||
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
|
||||
import info.nightscout.androidaps.utils.SafeParse
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
|
@ -41,7 +40,6 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
|
|||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var iobCobCalculator: IobCobCalculator
|
||||
@Inject lateinit var activePlugin: ActivePlugin
|
||||
@Inject lateinit var openHumansUploader: OpenHumansUploader
|
||||
|
||||
private var profile = JSONObject()
|
||||
private var mGlucoseStatus = JSONObject()
|
||||
|
@ -129,7 +127,6 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
|
|||
aapsLogger.debug(LTag.APS, "Result: $result")
|
||||
try {
|
||||
val resultJson = JSONObject(result)
|
||||
openHumansUploader.enqueueSMBData(profile, mGlucoseStatus, iobData, mealData, currentTemp, autosensData, microBolusAllowed, smbAlwaysAllowed, resultJson)
|
||||
determineBasalResultSMB = DetermineBasalResultSMB(injector, resultJson)
|
||||
} catch (e: JSONException) {
|
||||
aapsLogger.error(LTag.APS, "Unhandled exception", e)
|
||||
|
|
|
@ -59,7 +59,6 @@ class OpenAPSSMBFragment : DaggerFragment() {
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
disposable += rxBus
|
||||
|
@ -91,7 +90,6 @@ class OpenAPSSMBFragment : DaggerFragment() {
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
fun updateGUI() {
|
||||
if (_binding == null) return
|
||||
openAPSSMBPlugin.lastAPSResult?.let { lastAPSResult ->
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.preference.PreferenceFragmentCompat
|
|||
import androidx.preference.SwitchPreference
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.annotations.OpenForTesting
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.ValueWrapper
|
||||
import info.nightscout.androidaps.extensions.target
|
||||
|
@ -27,8 +28,9 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
|
|||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@OpenForTesting
|
||||
@Singleton
|
||||
open class OpenAPSSMBPlugin @Inject constructor(
|
||||
class OpenAPSSMBPlugin @Inject constructor(
|
||||
injector: HasAndroidInjector,
|
||||
aapsLogger: AAPSLogger,
|
||||
private val rxBus: RxBusWrapper,
|
||||
|
@ -128,11 +130,11 @@ open class OpenAPSSMBPlugin @Inject constructor(
|
|||
maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
|
||||
targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
|
||||
}
|
||||
if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
|
||||
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
|
||||
if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
|
||||
if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
|
||||
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
|
||||
if (!hardLimits.checkHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
|
||||
if (!hardLimits.checkHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
|
||||
if (!hardLimits.checkHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
|
||||
if (!hardLimits.checkHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
|
||||
if (!hardLimits.checkHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
|
||||
startPart = System.currentTimeMillis()
|
||||
if (constraintChecker.isAutosensModeEnabled().value()) {
|
||||
val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin")
|
||||
|
|
|
@ -114,7 +114,6 @@ class ConfigBuilderFragment : DaggerFragment() {
|
|||
createViewsForPlugins(R.string.configbuilder_loop, R.string.configbuilder_loop_description, PluginType.LOOP, activePlugin.getSpecificPluginsVisibleInList(PluginType.LOOP))
|
||||
createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, activePlugin.getSpecificPluginsVisibleInList(PluginType.CONSTRAINTS))
|
||||
}
|
||||
createViewsForPlugins(R.string.configbuilder_treatments, R.string.configbuilder_treatments_description, PluginType.TREATMENT, activePlugin.getSpecificPluginsVisibleInList(PluginType.TREATMENT))
|
||||
createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, activePlugin.getSpecificPluginsVisibleInList(PluginType.GENERAL))
|
||||
}
|
||||
|
||||
|
@ -139,22 +138,16 @@ class ConfigBuilderFragment : DaggerFragment() {
|
|||
|
||||
@Suppress("InflateParams")
|
||||
val baseView: LinearLayout = fragment.layoutInflater.inflate(R.layout.configbuilder_single_plugin, null) as LinearLayout
|
||||
private val enabledExclusive: RadioButton
|
||||
private val enabledInclusive: CheckBox
|
||||
private val pluginIcon: ImageView
|
||||
private val pluginName: TextView
|
||||
private val pluginDescription: TextView
|
||||
private val pluginPreferences: ImageButton
|
||||
private val pluginVisibility: CheckBox
|
||||
private val enabledExclusive: RadioButton = baseView.findViewById(R.id.plugin_enabled_exclusive)
|
||||
private val enabledInclusive: CheckBox = baseView.findViewById(R.id.plugin_enabled_inclusive)
|
||||
private val pluginIcon: ImageView = baseView.findViewById(R.id.plugin_icon)
|
||||
private val pluginIcon2: ImageView = baseView.findViewById(R.id.plugin_icon2)
|
||||
private val pluginName: TextView = baseView.findViewById(R.id.plugin_name)
|
||||
private val pluginDescription: TextView = baseView.findViewById(R.id.plugin_description)
|
||||
private val pluginPreferences: ImageButton = baseView.findViewById(R.id.plugin_preferences)
|
||||
private val pluginVisibility: CheckBox = baseView.findViewById(R.id.plugin_visibility)
|
||||
|
||||
init {
|
||||
enabledExclusive = baseView.findViewById(R.id.plugin_enabled_exclusive)
|
||||
enabledInclusive = baseView.findViewById(R.id.plugin_enabled_inclusive)
|
||||
pluginIcon = baseView.findViewById(R.id.plugin_icon)
|
||||
pluginName = baseView.findViewById(R.id.plugin_name)
|
||||
pluginDescription = baseView.findViewById(R.id.plugin_description)
|
||||
pluginPreferences = baseView.findViewById(R.id.plugin_preferences)
|
||||
pluginVisibility = baseView.findViewById(R.id.plugin_visibility)
|
||||
|
||||
pluginVisibility.setOnClickListener {
|
||||
plugin.setFragmentVisible(pluginType, pluginVisibility.isChecked)
|
||||
|
@ -192,6 +185,12 @@ class ConfigBuilderFragment : DaggerFragment() {
|
|||
if (plugin.menuIcon != -1) {
|
||||
pluginIcon.visibility = View.VISIBLE
|
||||
pluginIcon.setImageDrawable(context?.let { ContextCompat.getDrawable(it, plugin.menuIcon) })
|
||||
if (plugin.menuIcon2 != -1) {
|
||||
pluginIcon2.visibility = View.VISIBLE
|
||||
pluginIcon2.setImageDrawable(context?.let { ContextCompat.getDrawable(it, plugin.menuIcon2) })
|
||||
} else {
|
||||
pluginIcon2.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
pluginIcon.visibility = View.GONE
|
||||
}
|
||||
|
|
|
@ -120,7 +120,6 @@ class ConfigBuilderPlugin @Inject constructor(
|
|||
for (p in activePlugin.getPluginsList()) {
|
||||
aapsLogger.debug(LTag.CONFIGBUILDER, p.name + ":" +
|
||||
(if (p.isEnabled(PluginType.GENERAL)) " GENERAL" else "") +
|
||||
(if (p.isEnabled(PluginType.TREATMENT)) " TREATMENT" else "") +
|
||||
(if (p.isEnabled(PluginType.SENSITIVITY)) " SENSITIVITY" else "") +
|
||||
(if (p.isEnabled(PluginType.PROFILE)) " PROFILE" else "") +
|
||||
(if (p.isEnabled(PluginType.APS)) " APS" else "") +
|
||||
|
@ -135,7 +134,12 @@ class ConfigBuilderPlugin @Inject constructor(
|
|||
|
||||
// Ask when switching to physical pump plugin
|
||||
fun switchAllowed(changedPlugin: PluginBase, newState: Boolean, activity: FragmentActivity?, type: PluginType) {
|
||||
if (changedPlugin.getType() == PluginType.PUMP && changedPlugin.name != resourceHelper.gs(R.string.virtualpump)) confirmPumpPluginActivation(changedPlugin, newState, activity, type) else performPluginSwitch(changedPlugin, newState, type)
|
||||
if (changedPlugin.getType() == PluginType.PUMP && changedPlugin.name != resourceHelper.gs(R.string.virtualpump))
|
||||
confirmPumpPluginActivation(changedPlugin, newState, activity, type)
|
||||
else if (changedPlugin.getType() == PluginType.PUMP) {
|
||||
performPluginSwitch(changedPlugin, newState, type)
|
||||
pumpSync.connectNewPump()
|
||||
} else performPluginSwitch(changedPlugin, newState, type)
|
||||
}
|
||||
|
||||
private fun confirmPumpPluginActivation(changedPlugin: PluginBase, newState: Boolean, activity: FragmentActivity?, type: PluginType) {
|
||||
|
@ -185,7 +189,6 @@ class ConfigBuilderPlugin @Inject constructor(
|
|||
PluginType.APS -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(APS::class.java)
|
||||
PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileSource::class.java)
|
||||
PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSource::class.java)
|
||||
PluginType.TREATMENT -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(TreatmentsInterface::class.java)
|
||||
PluginType.PUMP -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Pump::class.java)
|
||||
|
||||
else -> {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package info.nightscout.androidaps.plugins.configBuilder
|
||||
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
|
@ -21,7 +20,6 @@ class PluginStore @Inject constructor(
|
|||
private var activeAPSStore: APS? = null
|
||||
private var activeInsulinStore: Insulin? = null
|
||||
private var activeSensitivityStore: Sensitivity? = null
|
||||
private var activeTreatmentsStore: TreatmentsInterface? = null
|
||||
|
||||
fun loadDefaults() {
|
||||
verifySelectionInCategories()
|
||||
|
@ -121,16 +119,6 @@ class PluginStore @Inject constructor(
|
|||
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting PumpInterface")
|
||||
}
|
||||
setFragmentVisibilities((activePumpStore as PluginBase).name, pluginsInCategory, PluginType.PUMP)
|
||||
|
||||
// PluginType.TREATMENT
|
||||
pluginsInCategory = getSpecificPluginsList(PluginType.TREATMENT)
|
||||
activeTreatmentsStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.TREATMENT) as TreatmentsInterface?
|
||||
if (activeTreatmentsStore == null) {
|
||||
activeTreatmentsStore = getDefaultPlugin(PluginType.TREATMENT) as TreatmentsInterface
|
||||
(activeTreatmentsStore as PluginBase).setPluginEnabled(PluginType.TREATMENT, true)
|
||||
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting PumpInterface")
|
||||
}
|
||||
setFragmentVisibilities((activeTreatmentsStore as PluginBase).name, pluginsInCategory, PluginType.TREATMENT)
|
||||
}
|
||||
|
||||
private fun setFragmentVisibilities(activePluginName: String, pluginsInCategory: ArrayList<PluginBase>,
|
||||
|
@ -175,13 +163,12 @@ class PluginStore @Inject constructor(
|
|||
get() = activeSensitivityStore
|
||||
?: checkNotNull(activeSensitivityStore) { "No sensitivity selected" }
|
||||
|
||||
override val activeTreatments: TreatmentsInterface
|
||||
get() = activeTreatmentsStore
|
||||
?: checkNotNull(activeTreatmentsStore) { "No treatments selected" }
|
||||
|
||||
override val activeOverview: Overview
|
||||
get() = getSpecificPluginsListByInterface(Overview::class.java).first() as Overview
|
||||
|
||||
override val activeSafety: Safety
|
||||
get() = getSpecificPluginsListByInterface(Safety::class.java).first() as Safety
|
||||
|
||||
override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins)
|
||||
|
||||
}
|
|
@ -9,14 +9,12 @@ import info.nightscout.androidaps.database.ValueWrapper
|
|||
import info.nightscout.androidaps.database.entities.ProfileSwitch
|
||||
import info.nightscout.androidaps.database.transactions.InsertOrUpdateProfileSwitch
|
||||
import info.nightscout.androidaps.extensions.fromConstant
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.GlucoseUnit
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.interfaces.ProfileStore
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.HardLimits
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
|
@ -30,10 +28,13 @@ import javax.inject.Singleton
|
|||
class ProfileFunctionImplementation @Inject constructor(
|
||||
private val aapsLogger: AAPSLogger,
|
||||
private val sp: SP,
|
||||
private val rxBus: RxBusWrapper,
|
||||
private val resourceHelper: ResourceHelper,
|
||||
private val activePlugin: ActivePlugin,
|
||||
private val repository: AppRepository,
|
||||
private val dateUtil: DateUtil
|
||||
private val dateUtil: DateUtil,
|
||||
private val config: Config,
|
||||
private val hardLimits: HardLimits
|
||||
) : ProfileFunction {
|
||||
|
||||
val cache = LongSparseArray<Profile>()
|
||||
|
@ -67,14 +68,15 @@ class ProfileFunctionImplementation @Inject constructor(
|
|||
override fun getProfile(): Profile? =
|
||||
getProfile(dateUtil.now())
|
||||
|
||||
@Synchronized
|
||||
override fun getProfile(time: Long): Profile? {
|
||||
val rounded = time - time % 1000
|
||||
val cached = cache[rounded]
|
||||
if (cached != null) {
|
||||
// aapsLogger.debug("XXXXXXXXXXXXXXX HIT getProfile for $time $rounded")
|
||||
// aapsLogger.debug("HIT getProfile for $time $rounded")
|
||||
return cached
|
||||
}
|
||||
// aapsLogger.debug("XXXXXXXXXXXXXXX getProfile called for $time")
|
||||
// aapsLogger.debug("getProfile called for $time")
|
||||
val ps = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet()
|
||||
if (ps is ValueWrapper.Existing) {
|
||||
val sealed = ProfileSealed.EPS(ps.value)
|
||||
|
@ -90,10 +92,10 @@ class ProfileFunctionImplementation @Inject constructor(
|
|||
if (sp.getString(R.string.key_units, Constants.MGDL) == Constants.MGDL) GlucoseUnit.MGDL
|
||||
else GlucoseUnit.MMOL
|
||||
|
||||
override fun createProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) {
|
||||
override fun buildProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) : ProfileSwitch {
|
||||
val pureProfile = profileStore.getSpecificProfile(profileName)
|
||||
?: throw InvalidParameterSpecException(profileName)
|
||||
val ps = ProfileSwitch(
|
||||
return ProfileSwitch(
|
||||
timestamp = timestamp,
|
||||
basalBlocks = pureProfile.basalBlocks,
|
||||
isfBlocks = pureProfile.isfBlocks,
|
||||
|
@ -104,7 +106,14 @@ class ProfileFunctionImplementation @Inject constructor(
|
|||
timeshift = T.hours(timeShiftInHours.toLong()).msecs(),
|
||||
percentage = percentage,
|
||||
duration = T.mins(durationInMinutes.toLong()).msecs(),
|
||||
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration)
|
||||
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also {
|
||||
it.insulinEndTime = (pureProfile.dia * 3600 * 1000).toLong()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun createProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) {
|
||||
val ps = buildProfileSwitch(profileStore, profileName, durationInMinutes, percentage, timeShiftInHours, timestamp)
|
||||
disposable += repository.runTransactionForResult(InsertOrUpdateProfileSwitch(ps))
|
||||
.subscribe({ result ->
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") }
|
||||
|
@ -114,10 +123,32 @@ class ProfileFunctionImplementation @Inject constructor(
|
|||
})
|
||||
}
|
||||
|
||||
override fun createProfileSwitch(durationInMinutes: Int, percentage: Int, timeShiftInHours: Int) {
|
||||
val profileStore = activePlugin.activeProfileSource.profile ?: return
|
||||
val profileName = activePlugin.activeProfileSource.profile?.getDefaultProfileName()
|
||||
?: return
|
||||
createProfileSwitch(profileStore, profileName, durationInMinutes, percentage, timeShiftInHours, dateUtil.now())
|
||||
override fun createProfileSwitch(durationInMinutes: Int, percentage: Int, timeShiftInHours: Int): Boolean {
|
||||
val profile = repository.getPermanentProfileSwitch(dateUtil.now())
|
||||
?: throw InvalidParameterSpecException("No active ProfileSwitch")
|
||||
val profileStore = activePlugin.activeProfileSource.profile ?: return false
|
||||
val ps = buildProfileSwitch(profileStore, profile.profileName, durationInMinutes, percentage, 0, dateUtil.now())
|
||||
val validity = ProfileSealed.PS(ps).isValid(
|
||||
resourceHelper.gs(info.nightscout.androidaps.automation.R.string.careportal_profileswitch),
|
||||
activePlugin.activePump,
|
||||
config,
|
||||
resourceHelper,
|
||||
rxBus,
|
||||
hardLimits
|
||||
)
|
||||
var returnValue = true
|
||||
if (validity.isValid) {
|
||||
repository.runTransactionForResult(InsertOrUpdateProfileSwitch(ps))
|
||||
.doOnError {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving ProfileSwitch", it)
|
||||
returnValue = false
|
||||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") }
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated ProfileSwitch $it") }
|
||||
}
|
||||
} else returnValue = false
|
||||
return returnValue
|
||||
}
|
||||
}
|
|
@ -45,6 +45,7 @@ class ObjectivesPlugin @Inject constructor(
|
|||
var objectives: MutableList<Objective> = ArrayList()
|
||||
|
||||
companion object {
|
||||
|
||||
const val FIRST_OBJECTIVE = 0
|
||||
@Suppress("unused") const val USAGE_OBJECTIVE = 1
|
||||
@Suppress("unused") const val EXAM_OBJECTIVE = 2
|
||||
|
@ -53,40 +54,17 @@ class ObjectivesPlugin @Inject constructor(
|
|||
const val MAXIOB_ZERO_CL_OBJECTIVE = 5
|
||||
@Suppress("unused") const val MAXIOB_OBJECTIVE = 6
|
||||
const val AUTOSENS_OBJECTIVE = 7
|
||||
const val AMA_OBJECTIVE = 8
|
||||
const val SMB_OBJECTIVE = 9
|
||||
const val AUTO_OBJECTIVE = 10
|
||||
const val SMB_OBJECTIVE = 8
|
||||
const val AUTO_OBJECTIVE = 9
|
||||
}
|
||||
|
||||
public override fun onStart() {
|
||||
super.onStart()
|
||||
convertSP()
|
||||
setupObjectives()
|
||||
}
|
||||
|
||||
override fun specialEnableCondition(): Boolean {
|
||||
return activePlugin.activePump.pumpDescription.isTempBasalCapable
|
||||
}
|
||||
|
||||
// convert 2.3 SP version
|
||||
private fun convertSP() {
|
||||
doConvertSP(0, "config")
|
||||
doConvertSP(1, "openloop")
|
||||
doConvertSP(2, "maxbasal")
|
||||
doConvertSP(3, "maxiobzero")
|
||||
doConvertSP(4, "maxiob")
|
||||
doConvertSP(5, "autosens")
|
||||
doConvertSP(6, "ama")
|
||||
doConvertSP(7, "smb")
|
||||
}
|
||||
|
||||
private fun doConvertSP(number: Int, name: String) {
|
||||
if (!sp.contains("Objectives_" + name + "_started")) {
|
||||
sp.putLong("Objectives_" + name + "_started", sp.getLong("Objectives" + number + "started", 0L))
|
||||
sp.putLong("Objectives_" + name + "_accomplished", sp.getLong("Objectives" + number + "accomplished", 0L))
|
||||
}
|
||||
// TODO: we can remove Objectives1accomplished sometimes later
|
||||
}
|
||||
override fun specialEnableCondition(): Boolean =
|
||||
activePlugin.activePump.pumpDescription.isTempBasalCapable
|
||||
|
||||
private fun setupObjectives() {
|
||||
objectives.clear()
|
||||
|
@ -98,9 +76,9 @@ class ObjectivesPlugin @Inject constructor(
|
|||
objectives.add(Objective5(injector))
|
||||
objectives.add(Objective6(injector))
|
||||
objectives.add(Objective7(injector))
|
||||
objectives.add(Objective8(injector))
|
||||
objectives.add(Objective9(injector))
|
||||
objectives.add(Objective10(injector))
|
||||
// edit companion object if you remove/add Objective
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
|
@ -120,12 +98,11 @@ class ObjectivesPlugin @Inject constructor(
|
|||
sp.putBoolean(R.string.key_objectiveusescale, false)
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
fun completeObjectives(activity: FragmentActivity, request: String) {
|
||||
val requestCode = sp.getString(R.string.key_objectives_request_code, "")
|
||||
var url = sp.getString(R.string.key_nsclientinternal_url, "").lowercase(Locale.getDefault())
|
||||
if (!url.endsWith("/")) url = "$url/"
|
||||
@Suppress("DEPRECATION") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString()
|
||||
@Suppress("DEPRECATION", "UnstableApiUsage") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString()
|
||||
if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) {
|
||||
sp.putLong("Objectives_" + "openloop" + "_started", dateUtil.now())
|
||||
sp.putLong("Objectives_" + "openloop" + "_accomplished", dateUtil.now())
|
||||
|
@ -137,8 +114,6 @@ class ObjectivesPlugin @Inject constructor(
|
|||
sp.putLong("Objectives_" + "maxiob" + "_accomplished", dateUtil.now())
|
||||
sp.putLong("Objectives_" + "autosens" + "_started", dateUtil.now())
|
||||
sp.putLong("Objectives_" + "autosens" + "_accomplished", dateUtil.now())
|
||||
sp.putLong("Objectives_" + "ama" + "_started", dateUtil.now())
|
||||
sp.putLong("Objectives_" + "ama" + "_accomplished", dateUtil.now())
|
||||
sp.putLong("Objectives_" + "smb" + "_started", dateUtil.now())
|
||||
sp.putLong("Objectives_" + "smb" + "_accomplished", dateUtil.now())
|
||||
sp.putLong("Objectives_" + "auto" + "_started", dateUtil.now())
|
||||
|
@ -164,49 +139,43 @@ class ObjectivesPlugin @Inject constructor(
|
|||
*/
|
||||
override fun isLoopInvocationAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
|
||||
if (!objectives[FIRST_OBJECTIVE].isStarted)
|
||||
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), FIRST_OBJECTIVE + 1), this)
|
||||
value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, FIRST_OBJECTIVE + 1), this)
|
||||
return value
|
||||
}
|
||||
|
||||
override fun isLgsAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
|
||||
if (!objectives[MAXBASAL_OBJECTIVE].isStarted)
|
||||
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), MAXBASAL_OBJECTIVE + 1), this)
|
||||
value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, MAXBASAL_OBJECTIVE + 1), this)
|
||||
return value
|
||||
}
|
||||
|
||||
override fun isClosedLoopAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
|
||||
if (!objectives[MAXIOB_ZERO_CL_OBJECTIVE].isStarted)
|
||||
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), MAXIOB_ZERO_CL_OBJECTIVE + 1), this)
|
||||
value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, MAXIOB_ZERO_CL_OBJECTIVE + 1), this)
|
||||
return value
|
||||
}
|
||||
|
||||
override fun isAutosensModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
|
||||
if (!objectives[AUTOSENS_OBJECTIVE].isStarted)
|
||||
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), AUTOSENS_OBJECTIVE + 1), this)
|
||||
return value
|
||||
}
|
||||
|
||||
override fun isAMAModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
|
||||
if (!objectives[AMA_OBJECTIVE].isStarted)
|
||||
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), AMA_OBJECTIVE + 1), this)
|
||||
value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, AUTOSENS_OBJECTIVE + 1), this)
|
||||
return value
|
||||
}
|
||||
|
||||
override fun isSMBModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
|
||||
if (!objectives[SMB_OBJECTIVE].isStarted)
|
||||
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), SMB_OBJECTIVE + 1), this)
|
||||
value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, SMB_OBJECTIVE + 1), this)
|
||||
return value
|
||||
}
|
||||
|
||||
override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> {
|
||||
if (objectives[MAXIOB_ZERO_CL_OBJECTIVE].isStarted && !objectives[MAXIOB_ZERO_CL_OBJECTIVE].isAccomplished)
|
||||
maxIob.set(aapsLogger, 0.0, String.format(resourceHelper.gs(R.string.objectivenotfinished), MAXIOB_ZERO_CL_OBJECTIVE + 1), this)
|
||||
maxIob.set(aapsLogger, 0.0, resourceHelper.gs(R.string.objectivenotfinished, MAXIOB_ZERO_CL_OBJECTIVE + 1), this)
|
||||
return maxIob
|
||||
}
|
||||
|
||||
override fun isAutomationEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
|
||||
if (!objectives[AUTO_OBJECTIVE].isStarted)
|
||||
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), AUTO_OBJECTIVE + 1), this)
|
||||
value.set(aapsLogger, false, resourceHelper.gs(R.string.objectivenotstarted, AUTO_OBJECTIVE + 1), this)
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(i
|
|||
override fun specialActionEnabled(): Boolean =
|
||||
nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun specialAction(activity: FragmentActivity, input: String) {
|
||||
objectivesPlugin.completeObjectives(activity, input)
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.utils.T
|
||||
|
||||
class Objective8(injector: HasAndroidInjector) : Objective(injector, "ama", R.string.objectives_ama_objective, 0) {
|
||||
|
||||
init {
|
||||
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.constraints.safety
|
|||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.extensions.*
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
|
@ -20,6 +21,7 @@ import info.nightscout.androidaps.utils.Round
|
|||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import org.json.JSONObject
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.math.floor
|
||||
|
@ -49,7 +51,7 @@ class SafetyPlugin @Inject constructor(
|
|||
.pluginName(R.string.safety)
|
||||
.preferencesId(R.xml.pref_safety),
|
||||
aapsLogger, resourceHelper, injector
|
||||
), Constraints {
|
||||
), Constraints, Safety {
|
||||
|
||||
/**
|
||||
* Constraints interface
|
||||
|
@ -186,11 +188,23 @@ class SafetyPlugin @Inject constructor(
|
|||
|
||||
override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> {
|
||||
val apsMode = sp.getString(R.string.key_aps_mode, "open")
|
||||
val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled(PluginType.APS)) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string.key_openapsma_max_iob, 1.5)
|
||||
val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled()) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string.key_openapsma_max_iob, 1.5)
|
||||
maxIob.setIfSmaller(aapsLogger, maxIobPref, String.format(resourceHelper.gs(R.string.limitingiob), maxIobPref, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
|
||||
if (openAPSAMAPlugin.isEnabled(PluginType.APS)) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobAMA(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobAMA(), resourceHelper.gs(R.string.hardlimit)), this)
|
||||
if (openAPSSMBPlugin.isEnabled(PluginType.APS)) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobSMB(), resourceHelper.gs(R.string.hardlimit)), this)
|
||||
if (openAPSAMAPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobAMA(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobAMA(), resourceHelper.gs(R.string.hardlimit)), this)
|
||||
if (openAPSSMBPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobSMB(), resourceHelper.gs(R.string.hardlimit)), this)
|
||||
if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, HardLimits.MAX_IOB_LGS, String.format(resourceHelper.gs(R.string.limitingiob), HardLimits.MAX_IOB_LGS, resourceHelper.gs(R.string.lowglucosesuspend)), this)
|
||||
return maxIob
|
||||
}
|
||||
|
||||
override fun configuration(): JSONObject =
|
||||
JSONObject()
|
||||
.putString(R.string.key_age, sp, resourceHelper)
|
||||
.putDouble(R.string.key_treatmentssafety_maxbolus, sp, resourceHelper)
|
||||
.putDouble(R.string.key_treatmentssafety_maxcarbs, sp, resourceHelper)
|
||||
|
||||
override fun applyConfiguration(configuration: JSONObject) {
|
||||
configuration.storeString(R.string.key_age, sp, resourceHelper)
|
||||
configuration.storeDouble(R.string.key_treatmentssafety_maxbolus, sp, resourceHelper)
|
||||
configuration.storeDouble(R.string.key_treatmentssafety_maxcarbs, sp, resourceHelper)
|
||||
}
|
||||
}
|
|
@ -152,7 +152,6 @@ class SignatureVerifierPlugin @Inject constructor(
|
|||
return sb.toString()
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
fun singleCharUnMap(shortHash: String): String {
|
||||
val array = ByteArray(shortHash.length)
|
||||
val sb = StringBuilder()
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.os.StatFs
|
|||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.annotations.OpenForTesting
|
||||
import info.nightscout.androidaps.interfaces.Constraint
|
||||
import info.nightscout.androidaps.interfaces.Constraints
|
||||
import info.nightscout.androidaps.interfaces.PluginBase
|
||||
|
@ -20,8 +21,9 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
|
|||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@OpenForTesting
|
||||
@Singleton
|
||||
open class StorageConstraintPlugin @Inject constructor(
|
||||
class StorageConstraintPlugin @Inject constructor(
|
||||
injector: HasAndroidInjector,
|
||||
aapsLogger: AAPSLogger,
|
||||
resourceHelper: ResourceHelper,
|
||||
|
@ -48,7 +50,7 @@ open class StorageConstraintPlugin @Inject constructor(
|
|||
return value
|
||||
}
|
||||
|
||||
open fun availableInternalMemorySize(): Long {
|
||||
fun availableInternalMemorySize(): Long {
|
||||
val path = Environment.getDataDirectory()
|
||||
val stat = StatFs(path.path)
|
||||
val blockSize = stat.blockSizeLong
|
||||
|
|
|
@ -28,7 +28,8 @@ import info.nightscout.androidaps.events.EventTherapyEventChange
|
|||
import info.nightscout.androidaps.extensions.toStringMedium
|
||||
import info.nightscout.androidaps.extensions.toStringShort
|
||||
import info.nightscout.androidaps.extensions.toVisibility
|
||||
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity
|
||||
import info.nightscout.androidaps.activities.HistoryBrowseActivity
|
||||
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.CommandQueueProvider
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
|
@ -114,7 +115,10 @@ class ActionsFragment : DaggerFragment() {
|
|||
savedInstanceState: Bundle?): View? {
|
||||
//check screen width
|
||||
dm = DisplayMetrics()
|
||||
activity?.windowManager?.defaultDisplay?.getMetrics(dm)
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
|
||||
activity?.display?.getRealMetrics(dm)
|
||||
else
|
||||
@Suppress("DEPRECATION") activity?.windowManager?.defaultDisplay?.getMetrics(dm)
|
||||
|
||||
val screenWidth = dm.widthPixels
|
||||
val screenHeight = dm.heightPixels
|
||||
|
@ -152,7 +156,7 @@ class ActionsFragment : DaggerFragment() {
|
|||
pbLevelLabel = view.findViewById(R.id.pb_level_label)
|
||||
|
||||
profileSwitch?.setOnClickListener {
|
||||
ProfileSwitchDialog().show(childFragmentManager, "Actions")
|
||||
ProfileSwitchDialog().show(childFragmentManager, "ProfileSwitchDialog")
|
||||
}
|
||||
tempTarget?.setOnClickListener {
|
||||
TempTargetDialog().show(childFragmentManager, "Actions")
|
||||
|
@ -304,7 +308,11 @@ class ActionsFragment : DaggerFragment() {
|
|||
val activeBgSource = activePlugin.activeBgSource
|
||||
historyBrowser?.visibility = (profile != null).toVisibility()
|
||||
fill?.visibility = (pump.pumpDescription.isRefillingCapable && pump.isInitialized() && !pump.isSuspended()).toVisibility()
|
||||
if(pump is DiaconnG8Plugin) {
|
||||
pumpBatteryChange?.visibility = (pump.pumpDescription.isBatteryReplaceable && !pump.isBatteryChangeLoggingEnabled()).toVisibility()
|
||||
} else {
|
||||
pumpBatteryChange?.visibility = (pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)).toVisibility()
|
||||
}
|
||||
tempTarget?.visibility = (profile != null && config.APS).toVisibility()
|
||||
tddStats?.visibility = pump.pumpDescription.supportsTDDs.toVisibility()
|
||||
|
||||
|
|
|
@ -67,7 +67,6 @@ class FoodFragment : DaggerFragment() {
|
|||
return binding.root
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
@ -129,7 +128,6 @@ class FoodFragment : DaggerFragment() {
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
disposable.add(rxBus
|
||||
|
@ -141,7 +139,6 @@ class FoodFragment : DaggerFragment() {
|
|||
swapAdapter()
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun swapAdapter() {
|
||||
disposable += repository
|
||||
.getFoodData()
|
||||
|
@ -202,7 +199,6 @@ class FoodFragment : DaggerFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun filterData() {
|
||||
val textFilter = binding.filter.text.toString()
|
||||
val categoryFilter = binding.category.selectedItem?.toString()
|
||||
|
|
|
@ -363,7 +363,7 @@ class ImportExportPrefsImpl @Inject constructor(
|
|||
override fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single<List<UserEntry>>) {
|
||||
val entries = singleEntries.blockingGet()
|
||||
prefFileList.ensureExportDirExists()
|
||||
val newFile = prefFileList.newExportXmlFile()
|
||||
val newFile = prefFileList.newExportCsvFile()
|
||||
|
||||
try {
|
||||
classicPrefsFormat.saveCsv(newFile, entries)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.plugins.general.maintenance
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext
|
||||
import info.nightscout.androidaps.annotations.OpenForTesting
|
||||
import org.slf4j.LoggerFactory
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -8,6 +9,7 @@ import javax.inject.Singleton
|
|||
/**
|
||||
* This class provides several methods for log-handling (eg. sending logs as emails).
|
||||
*/
|
||||
@OpenForTesting
|
||||
@Singleton
|
||||
class LoggerUtils @Inject constructor() {
|
||||
|
||||
|
|
|
@ -16,11 +16,13 @@ import info.nightscout.androidaps.events.EventNewBG
|
|||
import info.nightscout.androidaps.insight.database.InsightDatabase
|
||||
import info.nightscout.androidaps.interfaces.DataSyncSelector
|
||||
import info.nightscout.androidaps.interfaces.ImportExportPrefs
|
||||
import info.nightscout.androidaps.interfaces.IobCobCalculator
|
||||
import info.nightscout.androidaps.interfaces.PumpSync
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
|
||||
import info.nightscout.androidaps.plugins.general.overview.OverviewData
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
|
||||
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
|
@ -44,6 +46,8 @@ class MaintenanceFragment : DaggerFragment() {
|
|||
@Inject lateinit var uel: UserEntryLogger
|
||||
@Inject lateinit var dataSyncSelector: DataSyncSelector
|
||||
@Inject lateinit var pumpSync: PumpSync
|
||||
@Inject lateinit var iobCobCalculator: IobCobCalculator
|
||||
@Inject lateinit var overviewData: OverviewData
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
|
@ -63,7 +67,9 @@ class MaintenanceFragment : DaggerFragment() {
|
|||
binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() }
|
||||
binding.logDelete.setOnClickListener {
|
||||
uel.log(Action.DELETE_LOGS, Sources.Maintenance)
|
||||
maintenancePlugin.deleteLogs()
|
||||
Thread {
|
||||
maintenancePlugin.deleteLogs(5)
|
||||
}.start()
|
||||
}
|
||||
binding.navResetdb.setOnClickListener {
|
||||
activity?.let { activity ->
|
||||
|
@ -75,6 +81,9 @@ class MaintenanceFragment : DaggerFragment() {
|
|||
insightDatabase.clearAllTables()
|
||||
dataSyncSelector.resetToNextFullSync()
|
||||
pumpSync.connectNewPump()
|
||||
overviewData.reset()
|
||||
iobCobCalculator.ads.reset()
|
||||
iobCobCalculator.clearCache()
|
||||
}
|
||||
.subscribeOn(aapsSchedulers.io)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
|
|
|
@ -8,8 +8,8 @@ import androidx.preference.PreferenceFragmentCompat
|
|||
import androidx.preference.SwitchPreference
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.BuildConfig
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.interfaces.PluginBase
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
|
@ -35,8 +35,10 @@ class MaintenancePlugin @Inject constructor(
|
|||
aapsLogger: AAPSLogger,
|
||||
private val buildHelper: BuildHelper,
|
||||
private val config: Config,
|
||||
private val fileListProvider: PrefFileListProvider,
|
||||
private val loggerUtils: LoggerUtils
|
||||
) : PluginBase(PluginDescription()
|
||||
) : PluginBase(
|
||||
PluginDescription()
|
||||
.mainType(PluginType.GENERAL)
|
||||
.fragmentClass(MaintenanceFragment::class.java.name)
|
||||
.alwaysVisible(false)
|
||||
|
@ -53,11 +55,12 @@ class MaintenancePlugin @Inject constructor(
|
|||
val recipient = sp.getString(R.string.key_maintenance_logs_email, "logs@androidaps.org")
|
||||
val amount = sp.getInt(R.string.key_maintenance_logs_amount, 2)
|
||||
val logs = getLogFiles(amount)
|
||||
val zipDir = context.getExternalFilesDir("exports")
|
||||
val zipDir = fileListProvider.ensureTempDirExists()
|
||||
val zipFile = File(zipDir, constructName())
|
||||
aapsLogger.debug("zipFile: ${zipFile.absolutePath}")
|
||||
val zip = zipLogs(zipFile, logs)
|
||||
val attachmentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", zip)
|
||||
val attachmentUri =
|
||||
FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", zip)
|
||||
val emailIntent: Intent = this.sendMail(attachmentUri, recipient, "Log Export")
|
||||
aapsLogger.debug("sending emailIntent")
|
||||
context.startActivity(emailIntent)
|
||||
|
@ -65,14 +68,15 @@ class MaintenancePlugin @Inject constructor(
|
|||
|
||||
//todo replace this with a call on startup of the application, specifically to remove
|
||||
// unnecessary garbage from the log exports
|
||||
fun deleteLogs() {
|
||||
fun deleteLogs(keep: Int) {
|
||||
val logDir = File(loggerUtils.logDirectory)
|
||||
val files = logDir.listFiles { _: File?, name: String ->
|
||||
(name.startsWith("AndroidAPS") && name.endsWith(".zip"))
|
||||
}
|
||||
Arrays.sort(files) { f1: File, f2: File -> f1.name.compareTo(f2.name) }
|
||||
if (files == null || files.isEmpty()) return
|
||||
Arrays.sort(files) { f1: File, f2: File -> f2.name.compareTo(f1.name) }
|
||||
var delFiles = listOf(*files)
|
||||
val amount = sp.getInt(R.string.key_logshipper_amount, 2)
|
||||
val amount = sp.getInt(R.string.key_logshipper_amount, keep)
|
||||
val keepIndex = amount - 1
|
||||
if (keepIndex < delFiles.size) {
|
||||
delFiles = delFiles.subList(keepIndex, delFiles.size)
|
||||
|
@ -80,11 +84,10 @@ class MaintenancePlugin @Inject constructor(
|
|||
file.delete()
|
||||
}
|
||||
}
|
||||
val exportDir = File(loggerUtils.logDirectory, "exports")
|
||||
val exportDir = fileListProvider.ensureTempDirExists()
|
||||
if (exportDir.exists()) {
|
||||
val expFiles = exportDir.listFiles()
|
||||
for (file in expFiles) {
|
||||
file.delete()
|
||||
exportDir.listFiles()?.let { expFiles ->
|
||||
for (file in expFiles) file.delete()
|
||||
}
|
||||
exportDir.delete()
|
||||
}
|
||||
|
@ -106,7 +109,7 @@ class MaintenancePlugin @Inject constructor(
|
|||
(name.startsWith("AndroidAPS")
|
||||
&& (name.endsWith(".log")
|
||||
|| name.endsWith(".zip") && !name.endsWith(loggerUtils.suffix)))
|
||||
}
|
||||
} ?: emptyArray()
|
||||
Arrays.sort(files) { f1: File, f2: File -> f2.name.compareTo(f1.name) }
|
||||
val result = listOf(*files)
|
||||
var toIndex = amount
|
||||
|
@ -192,7 +195,12 @@ class MaintenancePlugin @Inject constructor(
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
private fun sendMail(attachmentUri: Uri, recipient: String, subject: String, body: String): Intent {
|
||||
private fun sendMail(
|
||||
attachmentUri: Uri,
|
||||
recipient: String,
|
||||
subject: String,
|
||||
body: String
|
||||
): Intent {
|
||||
aapsLogger.debug("sending email to $recipient with subject $subject")
|
||||
val emailIntent = Intent(Intent.ACTION_SEND)
|
||||
emailIntent.type = "text/plain"
|
||||
|
@ -207,7 +215,8 @@ class MaintenancePlugin @Inject constructor(
|
|||
|
||||
override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) {
|
||||
super.preprocessPreferences(preferenceFragment)
|
||||
val encryptSwitch = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_maintenance_encrypt_exported_prefs)) as SwitchPreference?
|
||||
val encryptSwitch =
|
||||
preferenceFragment.findPreference(resourceHelper.gs(R.string.key_maintenance_encrypt_exported_prefs)) as SwitchPreference?
|
||||
?: return
|
||||
encryptSwitch.isVisible = buildHelper.isEngineeringMode()
|
||||
encryptSwitch.isEnabled = buildHelper.isEngineeringMode()
|
||||
|
|
|
@ -34,11 +34,13 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
processChangedTemporaryBasalsCompat()
|
||||
processChangedExtendedBolusesCompat()
|
||||
processChangedProfileSwitchesCompat()
|
||||
processChangedEffectiveProfileSwitchesCompat()
|
||||
processChangedGlucoseValuesCompat()
|
||||
processChangedTempTargetsCompat()
|
||||
processChangedFoodsCompat()
|
||||
processChangedTherapyEventsCompat()
|
||||
processChangedDeviceStatusesCompat()
|
||||
processChangedOfflineEventsCompat()
|
||||
processChangedProfileStore()
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +57,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
sp.remove(R.string.key_ns_extended_bolus_last_synced_id)
|
||||
sp.remove(R.string.key_ns_therapy_event_last_synced_id)
|
||||
sp.remove(R.string.key_ns_profile_switch_last_synced_id)
|
||||
sp.remove(R.string.key_ns_effective_profile_switch_last_synced_id)
|
||||
sp.remove(R.string.key_ns_offline_event_last_synced_id)
|
||||
sp.remove(R.string.key_ns_profile_store_last_synced_timestamp)
|
||||
}
|
||||
|
||||
|
@ -76,8 +80,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastBolusId = -1L
|
||||
private var lastBolusTime = -1L
|
||||
@Volatile private var lastBolusId = -1L
|
||||
@Volatile private var lastBolusTime = -1L
|
||||
override fun processChangedBolusesCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastBolusIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
|
@ -119,8 +123,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastCarbsId = -1L
|
||||
private var lastCarbsTime = -1L
|
||||
@Volatile private var lastCarbsId = -1L
|
||||
@Volatile private var lastCarbsTime = -1L
|
||||
override fun processChangedCarbsCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastCarbsIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
|
@ -162,8 +166,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastBcrId = -1L
|
||||
private var lastBcrTime = -1L
|
||||
@Volatile private var lastBcrId = -1L
|
||||
@Volatile private var lastBcrTime = -1L
|
||||
override fun processChangedBolusCalculatorResultsCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastBolusCalculatorResultIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
|
@ -205,8 +209,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastTtId = -1L
|
||||
private var lastTtTime = -1L
|
||||
@Volatile private var lastTtId = -1L
|
||||
@Volatile private var lastTtTime = -1L
|
||||
override fun processChangedTempTargetsCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastTempTargetIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
|
@ -248,8 +252,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastFoodId = -1L
|
||||
private var lastFoodTime = -1L
|
||||
@Volatile private var lastFoodId = -1L
|
||||
@Volatile private var lastFoodTime = -1L
|
||||
override fun processChangedFoodsCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastFoodIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
|
@ -291,8 +295,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastGvId = -1L
|
||||
private var lastGvTime = -1L
|
||||
@Volatile private var lastGvId = -1L
|
||||
@Volatile private var lastGvTime = -1L
|
||||
override fun processChangedGlucoseValuesCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastGlucoseValueIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
|
@ -340,8 +344,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastTeId = -1L
|
||||
private var lastTeTime = -1L
|
||||
@Volatile private var lastTeId = -1L
|
||||
@Volatile private var lastTeTime = -1L
|
||||
override fun processChangedTherapyEventsCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastTherapyEventIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
|
@ -358,10 +362,10 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
when {
|
||||
// without nsId = create new
|
||||
te.first.interfaceIDs.nightscoutId == null ->
|
||||
nsClientPlugin.nsClientService?.dbAdd("treatments", te.first.toJson(true), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId")
|
||||
nsClientPlugin.nsClientService?.dbAdd("treatments", te.first.toJson(true, dateUtil), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId")
|
||||
// nsId = update
|
||||
te.first.interfaceIDs.nightscoutId != null ->
|
||||
nsClientPlugin.nsClientService?.dbUpdate("treatments", te.first.interfaceIDs.nightscoutId, te.first.toJson(false), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId")
|
||||
nsClientPlugin.nsClientService?.dbUpdate("treatments", te.first.interfaceIDs.nightscoutId, te.first.toJson(false, dateUtil), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -382,8 +386,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastDsId = -1L
|
||||
private var lastDsTime = -1L
|
||||
@Volatile private var lastDsId = -1L
|
||||
@Volatile private var lastDsTime = -1L
|
||||
override fun processChangedDeviceStatusesCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
|
@ -424,8 +428,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastTbrId = -1L
|
||||
private var lastTbrTime = -1L
|
||||
@Volatile private var lastTbrId = -1L
|
||||
@Volatile private var lastTbrTime = -1L
|
||||
override fun processChangedTemporaryBasalsCompat(): Boolean {
|
||||
val useAbsolute = sp.getBoolean(R.string.key_ns_sync_use_absolute, false)
|
||||
val lastDbIdWrapped = appRepository.getLastTemporaryBasalIdWrapped().blockingGet()
|
||||
|
@ -475,8 +479,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastEbId = -1L
|
||||
private var lastEbTime = -1L
|
||||
@Volatile private var lastEbId = -1L
|
||||
@Volatile private var lastEbTime = -1L
|
||||
override fun processChangedExtendedBolusesCompat(): Boolean {
|
||||
val useAbsolute = sp.getBoolean(R.string.key_ns_sync_use_absolute, false)
|
||||
val lastDbIdWrapped = appRepository.getLastExtendedBolusIdWrapped().blockingGet()
|
||||
|
@ -525,8 +529,8 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastPsId = -1L
|
||||
private var lastPsTime = -1L
|
||||
@Volatile private var lastPsId = -1L
|
||||
@Volatile private var lastPsTime = -1L
|
||||
override fun processChangedProfileSwitchesCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastProfileSwitchIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
|
@ -553,6 +557,91 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
return false
|
||||
}
|
||||
|
||||
override fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) {
|
||||
if (lastSynced > sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)) {
|
||||
aapsLogger.debug(LTag.NSCLIENT, "Setting EffectiveProfileSwitch data sync from $lastSynced")
|
||||
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, lastSynced)
|
||||
}
|
||||
}
|
||||
|
||||
override fun changedEffectiveProfileSwitch(): List<EffectiveProfileSwitch> {
|
||||
val startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
|
||||
return appRepository.getModifiedEffectiveProfileSwitchDataFromId(startId).blockingGet().also {
|
||||
aapsLogger.debug(LTag.NSCLIENT, "Loading EffectiveProfileSwitch data for sync from $startId. Records ${it.size}")
|
||||
}
|
||||
}
|
||||
|
||||
@Volatile private var lastEpsId = -1L
|
||||
@Volatile private var lastEpsTime = -1L
|
||||
override fun processChangedEffectiveProfileSwitchesCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastEffectiveProfileSwitchIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
var startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
|
||||
if (startId > lastDbId) {
|
||||
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
|
||||
startId = 0
|
||||
}
|
||||
if (startId == lastEpsId && dateUtil.now() - lastEpsTime < 5000) return false
|
||||
lastEpsId = startId
|
||||
lastEpsTime = dateUtil.now()
|
||||
appRepository.getNextSyncElementEffectiveProfileSwitch(startId).blockingGet()?.let { ps ->
|
||||
aapsLogger.info(LTag.DATABASE, "Loading EffectiveProfileSwitch data Start: $startId ID: ${ps.first.id} HistoryID: ${ps.second} ")
|
||||
when {
|
||||
// without nsId = create new
|
||||
ps.first.interfaceIDs.nightscoutId == null ->
|
||||
nsClientPlugin.nsClientService?.dbAdd("treatments", ps.first.toJson(true, dateUtil), DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second), "$startId/$lastDbId")
|
||||
// with nsId = update
|
||||
ps.first.interfaceIDs.nightscoutId != null ->
|
||||
nsClientPlugin.nsClientService?.dbUpdate("treatments", ps.first.interfaceIDs.nightscoutId, ps.first.toJson(false, dateUtil), DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second), "$startId/$lastDbId")
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) {
|
||||
if (lastSynced > sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)) {
|
||||
aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced")
|
||||
sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced)
|
||||
}
|
||||
}
|
||||
|
||||
// Prepared for v3 (returns all modified after)
|
||||
override fun changedOfflineEvents(): List<OfflineEvent> {
|
||||
val startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
|
||||
return appRepository.getModifiedOfflineEventsDataFromId(startId).blockingGet().also {
|
||||
aapsLogger.debug(LTag.NSCLIENT, "Loading OfflineEvent data for sync from $startId. Records ${it.size}")
|
||||
}
|
||||
}
|
||||
|
||||
@Volatile private var lastOeId = -1L
|
||||
@Volatile private var lastOeTime = -1L
|
||||
override fun processChangedOfflineEventsCompat(): Boolean {
|
||||
val lastDbIdWrapped = appRepository.getLastOfflineEventIdWrapped().blockingGet()
|
||||
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
|
||||
var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
|
||||
if (startId > lastDbId) {
|
||||
sp.putLong(R.string.key_ns_offline_event_last_synced_id, 0)
|
||||
startId = 0
|
||||
}
|
||||
if (startId == lastOeId && dateUtil.now() - lastOeTime < 5000) return false
|
||||
lastOeId = startId
|
||||
lastOeTime = dateUtil.now()
|
||||
appRepository.getNextSyncElementOfflineEvent(startId).blockingGet()?.let { oe ->
|
||||
aapsLogger.info(LTag.DATABASE, "Loading OfflineEvent data Start: $startId ID: ${oe.first.id} HistoryID: ${oe.second} ")
|
||||
when {
|
||||
// without nsId = create new
|
||||
oe.first.interfaceIDs.nightscoutId == null ->
|
||||
nsClientPlugin.nsClientService?.dbAdd("treatments", oe.first.toJson(true, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second), "$startId/$lastDbId")
|
||||
// existing with nsId = update
|
||||
oe.first.interfaceIDs.nightscoutId != null ->
|
||||
nsClientPlugin.nsClientService?.dbUpdate("treatments", oe.first.interfaceIDs.nightscoutId, oe.first.toJson(false, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second), "$startId/$lastDbId")
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun confirmLastProfileStore(lastSynced: Long) {
|
||||
sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced)
|
||||
}
|
||||
|
@ -561,9 +650,9 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0)
|
||||
val lastChange = sp.getLong(R.string.key_local_profile_last_change, 0)
|
||||
if (lastChange == 0L) return
|
||||
localProfilePlugin.createProfileStore()
|
||||
if (lastChange > lastSync) {
|
||||
val profileJson = localProfilePlugin.profile?.data ?: return
|
||||
if (lastChange > lastSync)
|
||||
nsClientPlugin.nsClientService?.dbAdd("profile", profileJson, DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -228,6 +228,25 @@ class NSClientAddAckWorker(
|
|||
dataSyncSelector.processChangedProfileSwitchesCompat()
|
||||
}
|
||||
|
||||
is PairEffectiveProfileSwitch -> {
|
||||
val pair = ack.originalObject
|
||||
pair.value.interfaceIDs.nightscoutId = ack.id
|
||||
repository.runTransactionForResult(UpdateNsIdEffectiveProfileSwitchTransaction(pair.value))
|
||||
.doOnError { error ->
|
||||
aapsLogger.error(LTag.DATABASE, "Updated ns id of EffectiveProfileSwitch failed", error)
|
||||
ret = Result.failure((workDataOf("Error" to error.toString())))
|
||||
}
|
||||
.doOnSuccess {
|
||||
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated ns id of EffectiveProfileSwitch " + pair.value)
|
||||
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.updateRecordId)
|
||||
}
|
||||
.blockingGet()
|
||||
rxBus.send(EventNSClientNewLog("DBADD", "Acked EffectiveProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
|
||||
// Send new if waiting
|
||||
dataSyncSelector.processChangedEffectiveProfileSwitchesCompat()
|
||||
}
|
||||
|
||||
is DeviceStatus -> {
|
||||
val deviceStatus = ack.originalObject
|
||||
deviceStatus.interfaceIDs.nightscoutId = ack.id
|
||||
|
@ -251,6 +270,26 @@ class NSClientAddAckWorker(
|
|||
dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync)
|
||||
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id))
|
||||
}
|
||||
|
||||
is PairOfflineEvent -> {
|
||||
val pair = ack.originalObject
|
||||
pair.value.interfaceIDs.nightscoutId = ack.id
|
||||
repository.runTransactionForResult(UpdateNsIdOfflineEventTransaction(pair.value))
|
||||
.doOnError { error ->
|
||||
aapsLogger.error(LTag.DATABASE, "Updated ns id of OfflineEvent failed", error)
|
||||
ret = Result.failure((workDataOf("Error" to error.toString())))
|
||||
}
|
||||
.doOnSuccess {
|
||||
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated ns id of OfflineEvent " + pair.value)
|
||||
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId)
|
||||
}
|
||||
.blockingGet()
|
||||
rxBus.send(EventNSClientNewLog("DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId))
|
||||
// Send new if waiting
|
||||
dataSyncSelector.processChangedOfflineEventsCompat()
|
||||
}
|
||||
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import info.nightscout.androidaps.receivers.DataWorker
|
|||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.JsonHelper
|
||||
import info.nightscout.androidaps.utils.JsonHelper.safeGetLong
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -186,6 +185,32 @@ class NSClientAddUpdateWorker(
|
|||
}
|
||||
} ?: aapsLogger.error("Error parsing TT json $json")
|
||||
}
|
||||
eventType == TherapyEvent.Type.NOTE.text && json.isEffectiveProfileSwitch() -> // replace this by new Type when available in NS
|
||||
if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) {
|
||||
effectiveProfileSwitchFromJson(json, dateUtil)?.let { effectiveProfileSwitch ->
|
||||
repository.runTransactionForResult(SyncNsEffectiveProfileSwitchTransaction(effectiveProfileSwitch, invalidateByNsOnly = false))
|
||||
.doOnError {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving EffectiveProfileSwitch", it)
|
||||
ret = Result.failure(workDataOf("Error" to it.toString()))
|
||||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
result.inserted.forEach {
|
||||
uel.log(Action.PROFILE_SWITCH, Sources.NSClient,
|
||||
ValueWithUnit.Timestamp(it.timestamp))
|
||||
aapsLogger.debug(LTag.DATABASE, "Inserted EffectiveProfileSwitch $it")
|
||||
}
|
||||
result.invalidated.forEach {
|
||||
uel.log(Action.PROFILE_SWITCH_REMOVED, Sources.NSClient,
|
||||
ValueWithUnit.Timestamp(it.timestamp))
|
||||
aapsLogger.debug(LTag.DATABASE, "Invalidated EffectiveProfileSwitch $it")
|
||||
}
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId EffectiveProfileSwitch $it")
|
||||
}
|
||||
}
|
||||
} ?: aapsLogger.error("Error parsing EffectiveProfileSwitch json $json")
|
||||
}
|
||||
eventType == TherapyEvent.Type.CANNULA_CHANGE.text ||
|
||||
eventType == TherapyEvent.Type.INSULIN_CHANGE.text ||
|
||||
eventType == TherapyEvent.Type.SENSOR_CHANGE.text ||
|
||||
|
@ -194,7 +219,7 @@ class NSClientAddUpdateWorker(
|
|||
eventType == TherapyEvent.Type.ANNOUNCEMENT.text ||
|
||||
eventType == TherapyEvent.Type.QUESTION.text ||
|
||||
eventType == TherapyEvent.Type.EXERCISE.text ||
|
||||
eventType == TherapyEvent.Type.APS_OFFLINE.text ||
|
||||
eventType == TherapyEvent.Type.NOTE.text ||
|
||||
eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text ->
|
||||
if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) {
|
||||
therapyEventFromJson(json)?.let { therapyEvent ->
|
||||
|
@ -341,6 +366,43 @@ class NSClientAddUpdateWorker(
|
|||
}
|
||||
} ?: aapsLogger.error("Error parsing ProfileSwitch json $json")
|
||||
}
|
||||
eventType == TherapyEvent.Type.APS_OFFLINE.text ->
|
||||
if (sp.getBoolean(R.string.key_ns_receive_offline_event, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) {
|
||||
offlineEventFromJson(json)?.let { offlineEvent ->
|
||||
repository.runTransactionForResult(SyncNsOfflineEventTransaction(offlineEvent, invalidateByNsOnly = false))
|
||||
.doOnError {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
|
||||
ret = Result.failure(workDataOf("Error" to it.toString()))
|
||||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
result.inserted.forEach { oe ->
|
||||
uel.log(Action.LOOP_CHANGE, Sources.NSClient,
|
||||
ValueWithUnit.OfflineEventReason(oe.reason),
|
||||
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt())
|
||||
)
|
||||
aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $oe")
|
||||
}
|
||||
result.invalidated.forEach { oe ->
|
||||
uel.log(Action.LOOP_REMOVED, Sources.NSClient,
|
||||
ValueWithUnit.OfflineEventReason(oe.reason),
|
||||
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt())
|
||||
)
|
||||
aapsLogger.debug(LTag.DATABASE, "Invalidated OfflineEvent $oe")
|
||||
}
|
||||
result.ended.forEach { oe ->
|
||||
uel.log(Action.LOOP_CHANGE, Sources.NSClient,
|
||||
ValueWithUnit.OfflineEventReason(oe.reason),
|
||||
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt())
|
||||
)
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $oe")
|
||||
}
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId OfflineEvent $it")
|
||||
}
|
||||
}
|
||||
} ?: aapsLogger.error("Error parsing OfflineEvent json $json")
|
||||
}
|
||||
}
|
||||
if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT)
|
||||
if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) {
|
||||
|
|
|
@ -34,7 +34,7 @@ class NSClientMbgWorker(
|
|||
var ret = Result.success()
|
||||
|
||||
val acceptNSData = sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT
|
||||
if (!acceptNSData) return ret
|
||||
if (!acceptNSData) return Result.success(workDataOf("Result" to "Sync not enabled"))
|
||||
|
||||
val mbgArray = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
|
||||
?: return Result.failure(workDataOf("Error" to "missing input data"))
|
||||
|
|
|
@ -125,6 +125,24 @@ class NSClientUpdateRemoveAckWorker(
|
|||
dataSyncSelector.processChangedProfileSwitchesCompat()
|
||||
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
|
||||
}
|
||||
|
||||
is PairEffectiveProfileSwitch -> {
|
||||
val pair = ack.originalObject
|
||||
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.updateRecordId)
|
||||
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked EffectiveProfileSwitch " + ack._id))
|
||||
// Send new if waiting
|
||||
dataSyncSelector.processChangedEffectiveProfileSwitchesCompat()
|
||||
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
|
||||
}
|
||||
|
||||
is PairOfflineEvent -> {
|
||||
val pair = ack.originalObject
|
||||
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId)
|
||||
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked OfflineEvent" + ack._id))
|
||||
// Send new if waiting
|
||||
dataSyncSelector.processChangedOfflineEventsCompat()
|
||||
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ class NSAddAck(
|
|||
) : Event(), Ack {
|
||||
|
||||
var id: String? = null
|
||||
@JvmField var nsClientID: String? = null
|
||||
@JvmField var json: JSONObject? = null
|
||||
var nsClientID: String? = null
|
||||
var json: JSONObject? = null
|
||||
override fun call(vararg args: Any) {
|
||||
// Regular response
|
||||
try {
|
||||
|
|
|
@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.general.nsclient.data
|
|||
import android.content.Context
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.annotations.OpenForTesting
|
||||
import info.nightscout.androidaps.database.entities.UserEntry
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
|
@ -113,6 +114,7 @@ import javax.inject.Singleton
|
|||
}
|
||||
*/
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@OpenForTesting
|
||||
@Singleton
|
||||
class NSSettingsStatus @Inject constructor(
|
||||
private val aapsLogger: AAPSLogger,
|
||||
|
|
|
@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.general.nsclient.services
|
|||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.os.*
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import com.google.common.base.Charsets
|
||||
|
@ -16,7 +17,6 @@ import info.nightscout.androidaps.events.EventConfigBuilderChange
|
|||
import info.nightscout.androidaps.events.EventPreferenceChange
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.interfaces.DataSyncSelector
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
|
@ -71,7 +71,6 @@ class NSClientService : DaggerService() {
|
|||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
@Inject lateinit var nsSettingsStatus: NSSettingsStatus
|
||||
@Inject lateinit var nsDeviceStatus: NSDeviceStatus
|
||||
@Inject lateinit var databaseHelper: DatabaseHelperInterface
|
||||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var sp: SP
|
||||
|
@ -113,7 +112,6 @@ class NSClientService : DaggerService() {
|
|||
var latestDateInReceivedData: Long = 0
|
||||
|
||||
@SuppressLint("WakelockTimeout")
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:NSClientService")
|
||||
|
@ -199,7 +197,7 @@ class NSClientService : DaggerService() {
|
|||
.build())
|
||||
}
|
||||
|
||||
fun processAuthAck(ack: NSAuthAck) {
|
||||
private fun processAuthAck(ack: NSAuthAck) {
|
||||
var connectionStatus = "Authenticated ("
|
||||
if (ack.read) connectionStatus += "R"
|
||||
if (ack.write) connectionStatus += "W"
|
||||
|
@ -237,7 +235,6 @@ class NSClientService : DaggerService() {
|
|||
return START_STICKY
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
fun initialize() {
|
||||
dataCounter = 0
|
||||
readPreferences()
|
||||
|
@ -488,7 +485,7 @@ class NSClientService : DaggerService() {
|
|||
val intent = Intent(Intents.ACTION_NEW_PROFILE)
|
||||
intent.putExtras(bundle)
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
|
||||
sendBroadcast(intent)
|
||||
broadcast(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -516,7 +513,7 @@ class NSClientService : DaggerService() {
|
|||
val intent = Intent(Intents.ACTION_REMOVED_TREATMENT)
|
||||
intent.putExtras(bundle)
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
|
||||
sendBroadcast(intent)
|
||||
broadcast(intent)
|
||||
}
|
||||
}
|
||||
if (addedOrUpdatedTreatments.length() > 0) {
|
||||
|
@ -533,7 +530,7 @@ class NSClientService : DaggerService() {
|
|||
val intent = Intent(Intents.ACTION_CHANGED_TREATMENT)
|
||||
intent.putExtras(bundle)
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
|
||||
sendBroadcast(intent)
|
||||
broadcast(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -581,7 +578,7 @@ class NSClientService : DaggerService() {
|
|||
val intent = Intent(Intents.ACTION_NEW_SGV)
|
||||
intent.putExtras(bundle)
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
|
||||
sendBroadcast(intent)
|
||||
broadcast(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -652,7 +649,6 @@ class NSClientService : DaggerService() {
|
|||
}
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
fun restart() {
|
||||
destroy()
|
||||
initialize()
|
||||
|
@ -721,6 +717,16 @@ class NSClientService : DaggerService() {
|
|||
return ret
|
||||
}
|
||||
|
||||
private fun broadcast(intent: Intent) {
|
||||
val receivers: List<ResolveInfo> = packageManager.queryBroadcastReceivers(intent, 0)
|
||||
for (resolveInfo in receivers)
|
||||
resolveInfo.activityInfo.packageName?.let {
|
||||
intent.setPackage(it)
|
||||
sendBroadcast(intent)
|
||||
aapsLogger.debug(LTag.CORE, "Sending broadcast " + intent.action + " to: " + it)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if (handler == null) {
|
||||
val handlerThread = HandlerThread(NSClientService::class.java.simpleName + "Handler")
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.openhumans
|
||||
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
import android.net.wifi.WifiManager
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.RxWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import info.nightscout.androidaps.MainApp
|
||||
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader.Companion.NOTIFICATION_CHANNEL
|
||||
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader.Companion.UPLOAD_NOTIFICATION_ID
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.Single
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class OHUploadWorker(context: Context, workerParameters: WorkerParameters)
|
||||
: RxWorker(context, workerParameters) {
|
||||
|
||||
@Inject
|
||||
lateinit var sp: SP
|
||||
|
||||
@Inject
|
||||
lateinit var openHumansUploader: OpenHumansUploader
|
||||
|
||||
@Inject
|
||||
lateinit var resourceHelper: ResourceHelper
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun createWork(): Single<Result> = Single.defer {
|
||||
|
||||
// Here we inject every time we create work
|
||||
// We could build our own WorkerFactory with dagger but this will create conflicts with other Workers
|
||||
// (see https://medium.com/wonderquill/how-to-pass-custom-parameters-to-rxworker-worker-using-dagger-2-f4cfbc9892ba)
|
||||
// This class will be replaced with new DB
|
||||
|
||||
(applicationContext as MainApp).androidInjector().inject(this)
|
||||
|
||||
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as? WifiManager
|
||||
val wifiOnly = sp.getBoolean("key_oh_wifi_only", true)
|
||||
val isConnectedToWifi = wifiManager?.isWifiEnabled ?: false && wifiManager?.connectionInfo?.networkId != -1
|
||||
if (!wifiOnly || (wifiOnly && isConnectedToWifi)) {
|
||||
setForegroundAsync(createForegroundInfo())
|
||||
openHumansUploader.uploadDataSegmentally()
|
||||
.andThen(Single.just(Result.success()))
|
||||
.onErrorResumeNext { Single.just(Result.retry()) }
|
||||
} else {
|
||||
Single.just(Result.retry())
|
||||
}
|
||||
}
|
||||
|
||||
private fun createForegroundInfo(): ForegroundInfo {
|
||||
val title = resourceHelper.gs(info.nightscout.androidaps.R.string.open_humans)
|
||||
|
||||
val notification: Notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL)
|
||||
.setContentTitle(title)
|
||||
.setTicker(title)
|
||||
.setContentText(resourceHelper.gs(info.nightscout.androidaps.R.string.your_phone_is_upload_data))
|
||||
.setSmallIcon(info.nightscout.androidaps.R.drawable.notif_icon)
|
||||
.setOngoing(true)
|
||||
.setProgress(0, 0 , true)
|
||||
.build()
|
||||
return ForegroundInfo(UPLOAD_NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.openhumans
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Base64
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.disposables.Disposables
|
||||
import okhttp3.*
|
||||
import okio.BufferedSink
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class OpenHumansAPI(
|
||||
private val baseUrl: String,
|
||||
clientId: String,
|
||||
clientSecret: String,
|
||||
private val redirectUri: String
|
||||
) {
|
||||
|
||||
private val authHeader = "Basic " + Base64.encodeToString("$clientId:$clientSecret".toByteArray(), Base64.NO_WRAP)
|
||||
private val client = OkHttpClient()
|
||||
|
||||
fun exchangeAuthToken(code: String): Single<OAuthTokens> = sendTokenRequest(FormBody.Builder()
|
||||
.add("grant_type", "authorization_code")
|
||||
.add("redirect_uri", redirectUri)
|
||||
.add("code", code)
|
||||
.build())
|
||||
|
||||
fun refreshAccessToken(refreshToken: String): Single<OAuthTokens> = sendTokenRequest(FormBody.Builder()
|
||||
.add("grant_type", "refresh_token")
|
||||
.add("redirect_uri", redirectUri)
|
||||
.add("refresh_token", refreshToken)
|
||||
.build())
|
||||
|
||||
private fun sendTokenRequest(body: FormBody) = Request.Builder()
|
||||
.url("$baseUrl/oauth2/token/")
|
||||
.addHeader("Authorization", authHeader)
|
||||
.post(body)
|
||||
.build()
|
||||
.toSingle()
|
||||
.map { response ->
|
||||
response.use { _ ->
|
||||
val responseBody = response.body
|
||||
val jsonObject = responseBody?.let { JSONObject(it.string()) }
|
||||
if (!response.isSuccessful) throw OHHttpException(response.code, response.message, jsonObject?.getString("error"))
|
||||
if (jsonObject == null) throw OHHttpException(response.code, response.message, "No body")
|
||||
if (!jsonObject.has("expires_in")) throw OHMissingFieldException("expires_in")
|
||||
OAuthTokens(
|
||||
accessToken = jsonObject.getString("access_token")
|
||||
?: throw OHMissingFieldException("access_token"),
|
||||
refreshToken = jsonObject.getString("refresh_token")
|
||||
?: throw OHMissingFieldException("refresh_token"),
|
||||
expiresAt = response.sentRequestAtMillis + jsonObject.getInt("expires_in") * 1000L
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getProjectMemberId(accessToken: String): Single<String> = Request.Builder()
|
||||
.url("$baseUrl/api/direct-sharing/project/exchange-member/?access_token=$accessToken")
|
||||
.get()
|
||||
.build()
|
||||
.toSingle()
|
||||
.map {
|
||||
it.jsonBody.getString("project_member_id")
|
||||
?: throw OHMissingFieldException("project_member_id")
|
||||
}
|
||||
|
||||
fun prepareFileUpload(accessToken: String, fileName: String, metadata: FileMetadata): Single<PreparedUpload> = Request.Builder()
|
||||
.url("$baseUrl/api/direct-sharing/project/files/upload/direct/?access_token=$accessToken")
|
||||
.post(FormBody.Builder()
|
||||
.add("filename", fileName)
|
||||
.add("metadata", metadata.toJSON().toString())
|
||||
.build())
|
||||
.build()
|
||||
.toSingle()
|
||||
.map {
|
||||
val json = it.jsonBody
|
||||
PreparedUpload(
|
||||
fileId = json.getString("id") ?: throw OHMissingFieldException("id"),
|
||||
uploadURL = json.getString("url") ?: throw OHMissingFieldException("url")
|
||||
)
|
||||
}
|
||||
|
||||
fun uploadFile(url: String, content: ByteArray): Completable = Request.Builder()
|
||||
.url(url)
|
||||
.put(object : RequestBody() {
|
||||
override fun contentType(): MediaType? = null
|
||||
|
||||
override fun contentLength() = content.size.toLong()
|
||||
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
sink.write(content)
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.toSingle()
|
||||
.doOnSuccess { response ->
|
||||
response.use { _ ->
|
||||
if (!response.isSuccessful) throw OHHttpException(response.code, response.message, null)
|
||||
}
|
||||
}
|
||||
.ignoreElement()
|
||||
|
||||
fun completeFileUpload(accessToken: String, fileId: String): Completable = Request.Builder()
|
||||
.url("$baseUrl/api/direct-sharing/project/files/upload/complete/?access_token=$accessToken")
|
||||
.post(FormBody.Builder()
|
||||
.add("file_id", fileId)
|
||||
.build())
|
||||
.build()
|
||||
.toSingle()
|
||||
.doOnSuccess { it.jsonBody }
|
||||
.ignoreElement()
|
||||
|
||||
private fun Request.toSingle() = Single.create<Response> {
|
||||
val call = client.newCall(this)
|
||||
call.enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
it.tryOnError(e)
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
it.onSuccess(response)
|
||||
}
|
||||
})
|
||||
it.setDisposable(Disposables.fromRunnable { call.cancel() })
|
||||
}
|
||||
|
||||
private val Response.jsonBody
|
||||
get() = use { _ ->
|
||||
val jsonObject = body?.let { JSONObject(it.string()) }
|
||||
?: throw OHHttpException(code, message, null)
|
||||
if (!isSuccessful) throw OHHttpException(code, message, jsonObject.getString("detail"))
|
||||
jsonObject
|
||||
}
|
||||
|
||||
data class OAuthTokens(
|
||||
val accessToken: String,
|
||||
val refreshToken: String,
|
||||
val expiresAt: Long
|
||||
)
|
||||
|
||||
data class FileMetadata(
|
||||
val tags: List<String>,
|
||||
val description: String,
|
||||
val md5: String? = null,
|
||||
val creationDate: Long? = null,
|
||||
val startDate: Long? = null,
|
||||
val endDate: Long? = null
|
||||
) {
|
||||
|
||||
fun toJSON(): JSONObject {
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject.put("tags", JSONArray().apply { tags.forEach { put(it) } })
|
||||
jsonObject.put("description", description)
|
||||
jsonObject.put("md5", md5)
|
||||
creationDate?.let { jsonObject.put("creation_date", iso8601DateFormatter.format(Date(it))) }
|
||||
startDate?.let { jsonObject.put("start_date", iso8601DateFormatter.format(Date(it))) }
|
||||
endDate?.let { jsonObject.put("end_date", iso8601DateFormatter.format(Date(it))) }
|
||||
return jsonObject
|
||||
}
|
||||
}
|
||||
|
||||
data class PreparedUpload(
|
||||
val fileId: String,
|
||||
val uploadURL: String
|
||||
)
|
||||
|
||||
data class OHHttpException(
|
||||
val code: Int,
|
||||
val meaning: String,
|
||||
val detail: String?
|
||||
) : RuntimeException() {
|
||||
|
||||
override val message: String get() = toString()
|
||||
}
|
||||
|
||||
data class OHMissingFieldException(
|
||||
val name: String
|
||||
) : RuntimeException() {
|
||||
|
||||
override val message: String get() = toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private val iso8601DateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.openhumans
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.work.WorkManager
|
||||
import dagger.android.support.DaggerFragment
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.events.Event
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import io.reactivex.BackpressureStrategy
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class OpenHumansFragment : DaggerFragment() {
|
||||
|
||||
private var viewsCreated = false
|
||||
private var login: Button? = null
|
||||
private var logout: Button? = null
|
||||
private var memberId: TextView? = null
|
||||
private var queueSize: TextView? = null
|
||||
private var workerState: TextView? = null
|
||||
private var queueSizeValue = 0L
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var openHumansUploader: OpenHumansUploader
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
@Inject lateinit var databaseHelper: DatabaseHelperInterface
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
compositeDisposable += Single.fromCallable { databaseHelper.getOHQueueSize() }
|
||||
.subscribeOn(aapsSchedulers.io)
|
||||
.repeatWhen {
|
||||
rxBus.toObservable(UpdateViewEvent::class.java)
|
||||
.cast(Any::class.java)
|
||||
.mergeWith(rxBus.toObservable(UpdateQueueEvent::class.java)
|
||||
.throttleLatest(5, TimeUnit.SECONDS))
|
||||
.toFlowable(BackpressureStrategy.LATEST)
|
||||
}
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({
|
||||
queueSizeValue = it
|
||||
updateGUI()
|
||||
}, {})
|
||||
context?.applicationContext?.let { appContext ->
|
||||
WorkManager.getInstance(appContext).getWorkInfosForUniqueWorkLiveData(OpenHumansUploader.WORK_NAME).observe(this, {
|
||||
val workInfo = it.lastOrNull()
|
||||
if (workInfo == null) {
|
||||
workerState?.visibility = View.GONE
|
||||
} else {
|
||||
workerState?.visibility = View.VISIBLE
|
||||
workerState?.text = getString(R.string.worker_state, workInfo.state.toString())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
compositeDisposable.clear()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val view = inflater.inflate(R.layout.fragment_open_humans, container, false)
|
||||
login = view.findViewById(R.id.login)
|
||||
logout = view.findViewById(R.id.logout)
|
||||
memberId = view.findViewById(R.id.member_id)
|
||||
queueSize = view.findViewById(R.id.queue_size)
|
||||
workerState = view.findViewById(R.id.worker_state)
|
||||
login!!.setOnClickListener { startActivity(Intent(context, OpenHumansLoginActivity::class.java)) }
|
||||
logout!!.setOnClickListener {
|
||||
activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.oh_logout_confirmation)) { openHumansUploader.logout() } }
|
||||
}
|
||||
viewsCreated = true
|
||||
updateGUI()
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
viewsCreated = false
|
||||
login = null
|
||||
logout = null
|
||||
memberId = null
|
||||
queueSize = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
fun updateGUI() {
|
||||
if (viewsCreated) {
|
||||
queueSize!!.text = getString(R.string.queue_size, queueSizeValue)
|
||||
val projectMemberId = openHumansUploader.projectMemberId
|
||||
memberId!!.text = getString(R.string.project_member_id, projectMemberId
|
||||
?: getString(R.string.not_logged_in))
|
||||
login!!.visibility = if (projectMemberId == null) View.VISIBLE else View.GONE
|
||||
logout!!.visibility = if (projectMemberId != null) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
object UpdateViewEvent : Event()
|
||||
|
||||
object UpdateQueueEvent : Event()
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.openhumans
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Button
|
||||
import android.widget.CheckBox
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import dagger.android.support.DaggerDialogFragment
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import javax.inject.Inject
|
||||
|
||||
class OpenHumansLoginActivity : NoSplashAppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_open_humans_login)
|
||||
val button = findViewById<Button>(R.id.button)
|
||||
val checkbox = findViewById<CheckBox>(R.id.checkbox)
|
||||
|
||||
button.setOnClickListener {
|
||||
if (checkbox.isChecked) {
|
||||
CustomTabsIntent.Builder().build().launchUrl(this, Uri.parse(OpenHumansUploader.AUTH_URL))
|
||||
} else {
|
||||
Toast.makeText(this, R.string.you_need_to_accept_the_of_use_first, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
val code = intent.data?.getQueryParameter("code")
|
||||
if (supportFragmentManager.fragments.size == 0 && code != null) {
|
||||
ExchangeAuthTokenDialog(code).show(supportFragmentManager, "ExchangeAuthTokenDialog")
|
||||
}
|
||||
}
|
||||
|
||||
class ExchangeAuthTokenDialog : DaggerDialogFragment() {
|
||||
|
||||
@Inject lateinit var openHumansUploader: OpenHumansUploader
|
||||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
init {
|
||||
isCancelable = false
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.completing_login)
|
||||
.setMessage(R.string.please_wait)
|
||||
.create()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
disposable = openHumansUploader.login(arguments?.getString("authToken")!!).subscribeOn(aapsSchedulers.io).subscribe({
|
||||
dismiss()
|
||||
SetupDoneDialog().show(parentFragmentManager, "SetupDoneDialog")
|
||||
}, {
|
||||
dismiss()
|
||||
ErrorDialog(it.message).show(parentFragmentManager, "ErrorDialog")
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
disposable?.dispose()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
operator fun invoke(authToken: String): ExchangeAuthTokenDialog {
|
||||
val dialog = ExchangeAuthTokenDialog()
|
||||
val args = Bundle()
|
||||
args.putString("authToken", authToken)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ErrorDialog : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val message = arguments?.getString("message")
|
||||
val shownMessage = if (message == null) getString(R.string.there_was_an_error)
|
||||
else "${getString(R.string.there_was_an_error)}\n\n$message"
|
||||
return AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(shownMessage)
|
||||
.setPositiveButton(R.string.close, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
operator fun invoke(message: String?): ErrorDialog {
|
||||
val dialog = ErrorDialog()
|
||||
val args = Bundle()
|
||||
args.putString("message", message)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SetupDoneDialog : DialogFragment() {
|
||||
|
||||
init {
|
||||
isCancelable = false
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.successfully_logged_in)
|
||||
.setMessage(R.string.setup_will_continue_in_background)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.close) { _, _ ->
|
||||
requireActivity().run {
|
||||
setResult(FragmentActivity.RESULT_OK)
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
.create()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,652 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.openhumans
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.WindowManager
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.work.*
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.BuildConfig
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||
import info.nightscout.androidaps.database.entities.TemporaryTarget
|
||||
import info.nightscout.androidaps.database.entities.TherapyEvent
|
||||
import info.nightscout.androidaps.db.*
|
||||
import info.nightscout.androidaps.events.EventPreferenceChange
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
|
||||
import info.nightscout.androidaps.interfaces.PluginBase
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.extensions.toConstant
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.security.MessageDigest
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class OpenHumansUploader @Inject constructor(
|
||||
injector: HasAndroidInjector,
|
||||
resourceHelper: ResourceHelper,
|
||||
aapsLogger: AAPSLogger,
|
||||
private val aapsSchedulers: AapsSchedulers,
|
||||
private val sp: SP,
|
||||
private val rxBus: RxBusWrapper,
|
||||
private val context: Context,
|
||||
private val databaseHelper: DatabaseHelperInterface,
|
||||
val repository: AppRepository
|
||||
) : PluginBase(
|
||||
PluginDescription()
|
||||
.mainType(PluginType.GENERAL)
|
||||
.pluginIcon(R.drawable.open_humans_white)
|
||||
.pluginName(R.string.open_humans)
|
||||
.shortName(R.string.open_humans_short)
|
||||
.description(R.string.donate_your_data_to_science)
|
||||
.fragmentClass(OpenHumansFragment::class.qualifiedName)
|
||||
.preferencesId(R.xml.pref_openhumans),
|
||||
aapsLogger, resourceHelper, injector) {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val OPEN_HUMANS_URL = "https://www.openhumans.org"
|
||||
private const val CLIENT_ID = "oie6DvnaEOagTxSoD6BukkLPwDhVr6cMlN74Ihz1"
|
||||
private const val CLIENT_SECRET = "jR0N8pkH1jOwtozHc7CsB1UPcJzFN95ldHcK4VGYIApecr8zGJox0v06xLwPLMASScngT12aIaIHXAVCJeKquEXAWG1XekZdbubSpccgNiQBmuVmIF8nc1xSKSNJltCf"
|
||||
private const val REDIRECT_URL = "androidaps://setup-openhumans"
|
||||
const val AUTH_URL = "https://www.openhumans.org/direct-sharing/projects/oauth2/authorize/?client_id=$CLIENT_ID&response_type=code"
|
||||
const val WORK_NAME = "Open Humans"
|
||||
const val NOTIFICATION_CHANNEL = "OpenHumans"
|
||||
private const val COPY_NOTIFICATION_ID = 3122
|
||||
private const val FAILURE_NOTIFICATION_ID = 3123
|
||||
private const val SUCCESS_NOTIFICATION_ID = 3124
|
||||
private const val SIGNED_OUT_NOTIFICATION_ID = 3125
|
||||
const val UPLOAD_NOTIFICATION_ID = 3126
|
||||
private const val UPLOAD_SEGMENT_SIZE = 10000L
|
||||
}
|
||||
|
||||
private val openHumansAPI = OpenHumansAPI(OPEN_HUMANS_URL, CLIENT_ID, CLIENT_SECRET, REDIRECT_URL)
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
private val FILE_NAME_DATE_FORMAT = SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.US).apply { timeZone = TimeZone.getTimeZone("UTC") }
|
||||
|
||||
private var isSetup
|
||||
get() = sp.getBoolean("openhumans_is_setup", false)
|
||||
set(value) = sp.putBoolean("openhumans_is_setup", value)
|
||||
private var oAuthTokens: OpenHumansAPI.OAuthTokens?
|
||||
get() {
|
||||
return if (sp.contains("openhumans_access_token") && sp.contains("openhumans_refresh_token") && sp.contains("openhumans_expires_at")) {
|
||||
OpenHumansAPI.OAuthTokens(
|
||||
accessToken = sp.getStringOrNull("openhumans_access_token", null)!!,
|
||||
refreshToken = sp.getStringOrNull("openhumans_refresh_token", null)!!,
|
||||
expiresAt = sp.getLong("openhumans_expires_at", 0)
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
set(value) {
|
||||
if (value != null) {
|
||||
sp.putString("openhumans_access_token", value.accessToken)
|
||||
sp.putString("openhumans_refresh_token", value.refreshToken)
|
||||
sp.putLong("openhumans_expires_at", value.expiresAt)
|
||||
} else {
|
||||
sp.remove("openhumans_access_token")
|
||||
sp.remove("openhumans_refresh_token")
|
||||
sp.remove("openhumans_expires_at")
|
||||
sp.remove("openhumans_expires_at")
|
||||
}
|
||||
}
|
||||
var projectMemberId: String?
|
||||
get() = sp.getStringOrNull("openhumans_project_member_id", null)
|
||||
private set(value) {
|
||||
if (value == null) sp.remove("openhumans_project_member_id")
|
||||
else sp.putString("openhumans_project_member_id", value)
|
||||
}
|
||||
private var uploadCounter: Int
|
||||
get() = sp.getInt("openhumans_counter", 1)
|
||||
set(value) = sp.putInt("openhumans_counter", value)
|
||||
private val appId: UUID
|
||||
get() {
|
||||
val id = sp.getStringOrNull("openhumans_appid", null)
|
||||
return if (id == null) {
|
||||
val generated = UUID.randomUUID()
|
||||
sp.putString("openhumans_appid", generated.toString())
|
||||
generated
|
||||
} else {
|
||||
UUID.fromString(id)
|
||||
}
|
||||
}
|
||||
|
||||
private var copyDisposable: Disposable? = null
|
||||
|
||||
private val wakeLock = (context.getSystemService(Context.POWER_SERVICE) as PowerManager)
|
||||
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS::OpenHumans")
|
||||
|
||||
private val preferenceChangeDisposable = CompositeDisposable()
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setupNotificationChannel()
|
||||
if (isSetup) scheduleWorker(false)
|
||||
preferenceChangeDisposable += rxBus.toObservable(EventPreferenceChange::class.java).subscribe {
|
||||
onSharedPreferenceChanged(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
copyDisposable?.dispose()
|
||||
cancelWorker()
|
||||
preferenceChangeDisposable.clear()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
fun enqueueBGReading(glucoseValue: GlucoseValue?) = glucoseValue?.let {
|
||||
insertQueueItem("BgReadings") {
|
||||
put("date", glucoseValue.timestamp)
|
||||
put("isValid", glucoseValue.isValid)
|
||||
put("value", glucoseValue.value)
|
||||
put("direction", glucoseValue.trendArrow)
|
||||
put("raw", glucoseValue.raw)
|
||||
put("source", glucoseValue.sourceSensor)
|
||||
put("nsId", glucoseValue.interfaceIDs.nightscoutId)
|
||||
}
|
||||
}
|
||||
|
||||
// @JvmOverloads
|
||||
// fun enqueueTreatment(treatment: Treatment?, deleted: Boolean = false) = treatment?.let {
|
||||
// insertQueueItem("Treatments") {
|
||||
// put("date", treatment.date)
|
||||
// put("isValid", treatment.isValid)
|
||||
// put("source", treatment.source)
|
||||
// put("nsId", treatment._id)
|
||||
// put("boluscalc", treatment.boluscalc)
|
||||
// put("carbs", treatment.carbs)
|
||||
// put("dia", treatment.dia)
|
||||
// put("insulin", treatment.insulin)
|
||||
// put("insulinInterfaceID", treatment.insulinInterfaceID)
|
||||
// put("isSMB", treatment.isSMB)
|
||||
// put("mealBolus", treatment.mealBolus)
|
||||
// put("bolusCalcJson", treatment.getBoluscalc())
|
||||
// put("isDeletion", deleted)
|
||||
// }
|
||||
// }
|
||||
|
||||
@JvmOverloads
|
||||
fun enqueueTherapyEvent(therapyEvent: TherapyEvent, deleted: Boolean = false) = insertQueueItem("TherapyEvents") {
|
||||
put("date", therapyEvent.timestamp)
|
||||
put("isValid", therapyEvent.isValid)
|
||||
put("nsId", therapyEvent.interfaceIDs.nightscoutId)
|
||||
put("eventType", therapyEvent.type.text)
|
||||
put("glucose", therapyEvent.glucose)
|
||||
put("units", therapyEvent.glucoseUnit.toConstant())
|
||||
put("glucoseType", therapyEvent.glucoseType?.text)
|
||||
put("duration", therapyEvent.duration)
|
||||
put("isDeletion", deleted)
|
||||
}
|
||||
|
||||
// @JvmOverloads
|
||||
// fun enqueueExtendedBolus(extendedBolus: ExtendedBolus, deleted: Boolean = false) = insertQueueItem("ExtendedBoluses") {
|
||||
// put("date", extendedBolus.date)
|
||||
// put("isValid", extendedBolus.isValid)
|
||||
// put("source", extendedBolus.source)
|
||||
// put("nsId", extendedBolus._id)
|
||||
// put("pumpId", extendedBolus.pumpId)
|
||||
// put("insulin", extendedBolus.insulin)
|
||||
// put("durationInMinutes", extendedBolus.durationInMinutes)
|
||||
// put("isDeletion", deleted)
|
||||
// }
|
||||
|
||||
// @JvmOverloads
|
||||
// fun enqueueProfileSwitch(profileSwitch: ProfileSwitch, deleted: Boolean = false) = insertQueueItem("ProfileSwitches") {
|
||||
// put("date", profileSwitch.date)
|
||||
// put("isValid", profileSwitch.isValid)
|
||||
// put("source", profileSwitch.source)
|
||||
// put("nsId", profileSwitch._id)
|
||||
// put("isCPP", profileSwitch.isCPP)
|
||||
// put("timeshift", profileSwitch.timeshift)
|
||||
// put("percentage", profileSwitch.percentage)
|
||||
// put("profile", JSONObject(profileSwitch.profileJson))
|
||||
// put("profilePlugin", profileSwitch.profilePlugin)
|
||||
// put("durationInMinutes", profileSwitch.durationInMinutes)
|
||||
// put("isDeletion", deleted)
|
||||
// }
|
||||
|
||||
// fun enqueueTotalDailyDose(tdd: TDD) = insertQueueItem("TotalDailyDoses") {
|
||||
// put("double", tdd.date)
|
||||
// put("double", tdd.bolus)
|
||||
// put("double", tdd.basal)
|
||||
// put("double", tdd.total)
|
||||
// }
|
||||
|
||||
// @JvmOverloads
|
||||
// fun enqueueTemporaryBasal(temporaryBasal: TemporaryBasal?, deleted: Boolean = false) = temporaryBasal?.let {
|
||||
// insertQueueItem("TemporaryBasals") {
|
||||
// put("date", temporaryBasal.date)
|
||||
// put("isValid", temporaryBasal.isValid)
|
||||
// put("source", temporaryBasal.source)
|
||||
// put("nsId", temporaryBasal._id)
|
||||
// put("pumpId", temporaryBasal.pumpId)
|
||||
// put("durationInMinutes", temporaryBasal.durationInMinutes)
|
||||
// put("durationInMinutes", temporaryBasal.durationInMinutes)
|
||||
// put("isAbsolute", temporaryBasal.isAbsolute)
|
||||
// put("percentRate", temporaryBasal.percentRate)
|
||||
// put("absoluteRate", temporaryBasal.absoluteRate)
|
||||
// put("isDeletion", deleted)
|
||||
// }
|
||||
// }
|
||||
|
||||
@JvmOverloads
|
||||
fun enqueueTempTarget(tempTarget: TemporaryTarget?, deleted: Boolean = false) = tempTarget?.let {
|
||||
insertQueueItem("TempTargets") {
|
||||
put("date", tempTarget.timestamp)
|
||||
put("isValid", tempTarget.isValid)
|
||||
put("nsId", tempTarget.interfaceIDs_backing?.nightscoutId)
|
||||
put("low", tempTarget.lowTarget)
|
||||
put("high", tempTarget.highTarget)
|
||||
put("reason", tempTarget.reason)
|
||||
put("durationInMinutes", tempTarget.duration)
|
||||
put("isDeletion", deleted)
|
||||
}
|
||||
}
|
||||
|
||||
fun enqueueSMBData(profile: JSONObject?, glucoseStatus: JSONObject?, iobData: JSONArray?, mealData: JSONObject?, currentTemp: JSONObject?, autosensData: JSONObject?, smbAllowed: Boolean, smbAlwaysAllowed: Boolean, result: JSONObject?) = insertQueueItem("APSData") {
|
||||
put("algorithm", "SMB")
|
||||
put("profile", profile)
|
||||
put("glucoseStatus", glucoseStatus)
|
||||
put("iobData", iobData)
|
||||
put("mealData", mealData)
|
||||
put("currentTemp", currentTemp)
|
||||
put("autosensData", autosensData)
|
||||
put("smbAllowed", smbAllowed)
|
||||
put("smbAlwaysAllowed", smbAlwaysAllowed)
|
||||
put("result", result)
|
||||
}
|
||||
|
||||
fun enqueueAMAData(profile: JSONObject?, glucoseStatus: JSONObject?, iobData: JSONArray?, mealData: JSONObject?, currentTemp: JSONObject?, autosensData: JSONObject?, result: JSONObject?) = insertQueueItem("APSData") {
|
||||
put("algorithm", "AMA")
|
||||
put("profile", profile)
|
||||
put("glucoseStatus", glucoseStatus)
|
||||
put("iobData", iobData)
|
||||
put("mealData", mealData)
|
||||
put("currentTemp", currentTemp)
|
||||
put("autosensData", autosensData)
|
||||
put("result", result)
|
||||
}
|
||||
|
||||
private fun insertQueueItem(file: String, structureVersion: Int = 1, generator: JSONObject.() -> Unit) {
|
||||
if (oAuthTokens != null && this.isEnabled(PluginType.GENERAL)) {
|
||||
try {
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject.put("structureVersion", structureVersion)
|
||||
jsonObject.put("queuedOn", System.currentTimeMillis())
|
||||
generator(jsonObject)
|
||||
val queueItem = OHQueueItem(
|
||||
file = file,
|
||||
content = jsonObject.toString()
|
||||
)
|
||||
databaseHelper.createOrUpdate(queueItem)
|
||||
rxBus.send(OpenHumansFragment.UpdateQueueEvent)
|
||||
} catch (e: JSONException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun login(authCode: String): Completable =
|
||||
openHumansAPI.exchangeAuthToken(authCode)
|
||||
.doOnSuccess {
|
||||
oAuthTokens = it
|
||||
}
|
||||
.flatMap { openHumansAPI.getProjectMemberId(it.accessToken) }
|
||||
.doOnSuccess {
|
||||
projectMemberId = it
|
||||
copyExistingDataToQueue()
|
||||
rxBus.send(OpenHumansFragment.UpdateViewEvent)
|
||||
}
|
||||
.doOnError {
|
||||
aapsLogger.error("Failed to login to Open Humans", it)
|
||||
}
|
||||
.ignoreElement()
|
||||
|
||||
fun logout() {
|
||||
cancelWorker()
|
||||
copyDisposable?.dispose()
|
||||
isSetup = false
|
||||
oAuthTokens = null
|
||||
projectMemberId = null
|
||||
databaseHelper.clearOpenHumansQueue()
|
||||
rxBus.send(OpenHumansFragment.UpdateViewEvent)
|
||||
}
|
||||
|
||||
private fun copyExistingDataToQueue() {
|
||||
copyDisposable?.dispose()
|
||||
var currentProgress = 0L
|
||||
var maxProgress = 0L
|
||||
val increaseCounter = {
|
||||
currentProgress++
|
||||
//Updating the notification for every item drastically slows down the operation
|
||||
if (currentProgress % 1000L == 0L) showOngoingNotification(maxProgress, currentProgress)
|
||||
}
|
||||
copyDisposable = Completable.fromCallable { databaseHelper.clearOpenHumansQueue() }
|
||||
// .andThen(Single.defer { Single.just(databaseHelper.getCountOfAllRows() + treatmentsPlugin.service.count()) })
|
||||
// .doOnSuccess { maxProgress = it }
|
||||
// .flatMapObservable { Observable.defer { Observable.fromIterable(treatmentsPlugin.service.getTreatmentData()) } }
|
||||
// .map { enqueueTreatment(it); increaseCounter() }
|
||||
// .ignoreElements()
|
||||
.andThen(Observable.defer { Observable.fromIterable(repository.compatGetBgReadingsDataFromTime(0, true).blockingGet()) })
|
||||
.map { enqueueBGReading(it); increaseCounter() }
|
||||
.ignoreElements()
|
||||
.andThen(Observable.defer { Observable.fromIterable(repository.compatGetTherapyEventDataFromTime(0, true).blockingGet()) })
|
||||
.map { enqueueTherapyEvent(it); increaseCounter() }
|
||||
.ignoreElements()
|
||||
// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllExtendedBoluses()) })
|
||||
// .map { enqueueExtendedBolus(it); increaseCounter() }
|
||||
// .ignoreElements()
|
||||
// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllProfileSwitches()) })
|
||||
// .map { enqueueProfileSwitch(it); increaseCounter() }
|
||||
// .ignoreElements()
|
||||
// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllTDDs()) })
|
||||
// .map { enqueueTotalDailyDose(it); increaseCounter() }
|
||||
// .ignoreElements()
|
||||
// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllTemporaryBasals()) })
|
||||
// .map { enqueueTemporaryBasal(it); increaseCounter() }
|
||||
// .ignoreElements()
|
||||
.andThen(Observable.defer { Observable.fromIterable(repository.compatGetTemporaryTargetData().blockingGet()) })
|
||||
.map { enqueueTempTarget(it); increaseCounter() }
|
||||
.ignoreElements()
|
||||
.doOnSubscribe {
|
||||
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
|
||||
showOngoingNotification()
|
||||
}
|
||||
.doOnComplete {
|
||||
isSetup = true
|
||||
scheduleWorker(false)
|
||||
showSetupFinishedNotification()
|
||||
}
|
||||
.doOnError {
|
||||
logout()
|
||||
showSetupFailedNotification()
|
||||
}
|
||||
.doFinally {
|
||||
copyDisposable = null
|
||||
NotificationManagerCompat.from(context).cancel(COPY_NOTIFICATION_ID)
|
||||
wakeLock.release()
|
||||
}
|
||||
.onErrorComplete()
|
||||
.subscribeOn(aapsSchedulers.io)
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
private fun showOngoingNotification(maxProgress: Long? = null, currentProgress: Long? = null) {
|
||||
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
|
||||
.setContentTitle(resourceHelper.gs(R.string.finishing_open_humans_setup))
|
||||
.setContentText(resourceHelper.gs(R.string.this_may_take_a_while))
|
||||
.setStyle(NotificationCompat.BigTextStyle())
|
||||
.setProgress(maxProgress?.toInt() ?: 0, currentProgress?.toInt()
|
||||
?: 0, maxProgress == null || currentProgress == null)
|
||||
.setOngoing(true)
|
||||
.setAutoCancel(false)
|
||||
.setSmallIcon(R.drawable.notif_icon)
|
||||
.build()
|
||||
NotificationManagerCompat.from(context).notify(COPY_NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
private fun showSetupFinishedNotification() {
|
||||
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
|
||||
.setContentTitle(resourceHelper.gs(R.string.setup_finished))
|
||||
.setContentText(resourceHelper.gs(R.string.your_phone_will_upload_data))
|
||||
.setStyle(NotificationCompat.BigTextStyle())
|
||||
.setSmallIcon(R.drawable.notif_icon)
|
||||
.build()
|
||||
val notificationManager = NotificationManagerCompat.from(context)
|
||||
notificationManager.notify(SUCCESS_NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
private fun showSetupFailedNotification() {
|
||||
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
|
||||
.setContentTitle(resourceHelper.gs(R.string.setup_failed))
|
||||
.setContentText(resourceHelper.gs(R.string.there_was_an_error))
|
||||
.setStyle(NotificationCompat.BigTextStyle())
|
||||
.setSmallIcon(R.drawable.notif_icon)
|
||||
.build()
|
||||
val notificationManager = NotificationManagerCompat.from(context)
|
||||
notificationManager.notify(FAILURE_NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
fun uploadDataSegmentally(): Completable =
|
||||
uploadData(UPLOAD_SEGMENT_SIZE)
|
||||
.repeatUntil { databaseHelper.getOHQueueSize() == 0L }
|
||||
.doOnSubscribe {
|
||||
aapsLogger.info(LTag.OHUPLOADER, "Starting segmental upload")
|
||||
}
|
||||
.doOnComplete {
|
||||
aapsLogger.info(LTag.OHUPLOADER, "Segmental upload successful")
|
||||
}
|
||||
.doOnError {
|
||||
aapsLogger.error(LTag.OHUPLOADER, "Segmental upload exceptional", it)
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@Suppress("SameParameterValue")
|
||||
private fun uploadData(maxEntries: Long): Completable = gatherData(maxEntries)
|
||||
.flatMap { data -> refreshAccessTokensIfNeeded().map { accessToken -> accessToken to data } }
|
||||
.flatMap { uploadFile(it.first, it.second).andThen(Single.just(it.second)) }
|
||||
.flatMapCompletable {
|
||||
if (it.highestQueueId != null) {
|
||||
removeUploadedEntriesFromQueue(it.highestQueueId)
|
||||
} else {
|
||||
Completable.complete()
|
||||
}
|
||||
}
|
||||
.doOnError {
|
||||
if (it is OpenHumansAPI.OHHttpException && it.code == 401 && it.detail == "Invalid token.") {
|
||||
handleSignOut()
|
||||
}
|
||||
aapsLogger.error("Error while uploading to Open Humans", it)
|
||||
}
|
||||
.doOnComplete {
|
||||
aapsLogger.info(LTag.OHUPLOADER, "Upload successful")
|
||||
rxBus.send(OpenHumansFragment.UpdateQueueEvent)
|
||||
}
|
||||
.doOnSubscribe {
|
||||
aapsLogger.info(LTag.OHUPLOADER, "Starting upload")
|
||||
}
|
||||
|
||||
private fun uploadFile(accessToken: String, uploadData: UploadData) = Completable.defer {
|
||||
openHumansAPI.prepareFileUpload(accessToken, uploadData.fileName, uploadData.metadata)
|
||||
.flatMap { openHumansAPI.uploadFile(it.uploadURL, uploadData.content).andThen(Single.just(it.fileId)) }
|
||||
.flatMapCompletable { openHumansAPI.completeFileUpload(accessToken, it) }
|
||||
}
|
||||
|
||||
private fun refreshAccessTokensIfNeeded() = Single.defer {
|
||||
val oAuthTokens = this.oAuthTokens!!
|
||||
if (oAuthTokens.expiresAt <= System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)) {
|
||||
openHumansAPI.refreshAccessToken(oAuthTokens.refreshToken)
|
||||
.doOnSuccess { this.oAuthTokens = it }
|
||||
.map { it.accessToken }
|
||||
} else {
|
||||
Single.just(oAuthTokens.accessToken)
|
||||
}
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun gatherData(maxEntries: Long) = Single.defer {
|
||||
val items = databaseHelper.getAllOHQueueItems(maxEntries)
|
||||
val baos = ByteArrayOutputStream()
|
||||
val zos = ZipOutputStream(baos)
|
||||
val tags = mutableListOf<String>()
|
||||
|
||||
items.groupBy { it.file }.forEach { entry ->
|
||||
tags.add(entry.key)
|
||||
val jsonArray = JSONArray()
|
||||
entry.value.map { it.content }.forEach { jsonArray.put(JSONObject(it)) }
|
||||
zos.writeFile("${entry.key}.json", jsonArray.toString().toByteArray())
|
||||
}
|
||||
|
||||
val applicationInfo = JSONObject()
|
||||
applicationInfo.put("versionName", BuildConfig.VERSION_NAME)
|
||||
applicationInfo.put("versionCode", BuildConfig.VERSION_CODE)
|
||||
val hasGitInfo = !BuildConfig.HEAD.endsWith("NoGitSystemAvailable", true)
|
||||
val customRemote = !BuildConfig.REMOTE.equals("https://github.com/nightscout/AndroidAPS.git", true)
|
||||
applicationInfo.put("hasGitInfo", hasGitInfo)
|
||||
applicationInfo.put("customRemote", customRemote)
|
||||
applicationInfo.put("applicationId", appId.toString())
|
||||
zos.writeFile("ApplicationInfo.json", applicationInfo.toString().toByteArray())
|
||||
tags.add("ApplicationInfo")
|
||||
|
||||
val preferences = JSONObject(sp.getAll().filterKeys { it.isAllowedKey() })
|
||||
zos.writeFile("Preferences.json", preferences.toString().toByteArray())
|
||||
tags.add("Preferences")
|
||||
|
||||
val deviceInfo = JSONObject()
|
||||
deviceInfo.put("brand", Build.BRAND)
|
||||
deviceInfo.put("device", Build.DEVICE)
|
||||
deviceInfo.put("manufacturer", Build.MANUFACTURER)
|
||||
deviceInfo.put("model", Build.MODEL)
|
||||
deviceInfo.put("product", Build.PRODUCT)
|
||||
zos.writeFile("DeviceInfo.json", deviceInfo.toString().toByteArray())
|
||||
tags.add("DeviceInfo")
|
||||
|
||||
val displayMetrics = DisplayMetrics()
|
||||
(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getMetrics(displayMetrics)
|
||||
val displayInfo = JSONObject()
|
||||
displayInfo.put("height", displayMetrics.heightPixels)
|
||||
displayInfo.put("width", displayMetrics.widthPixels)
|
||||
displayInfo.put("density", displayMetrics.density)
|
||||
displayInfo.put("scaledDensity", displayMetrics.scaledDensity)
|
||||
displayInfo.put("xdpi", displayMetrics.xdpi)
|
||||
displayInfo.put("ydpi", displayMetrics.ydpi)
|
||||
zos.writeFile("DisplayInfo.json", displayInfo.toString().toByteArray())
|
||||
tags.add("DisplayInfo")
|
||||
|
||||
val uploadNumber = this.uploadCounter++
|
||||
val uploadDate = Date()
|
||||
val uploadInfo = JSONObject()
|
||||
uploadInfo.put("fileVersion", 1)
|
||||
uploadInfo.put("counter", uploadNumber)
|
||||
uploadInfo.put("timestamp", uploadDate.time)
|
||||
uploadInfo.put("utcOffset", TimeZone.getDefault().getOffset(uploadDate.time))
|
||||
zos.writeFile("UploadInfo.json", uploadInfo.toString().toByteArray())
|
||||
tags.add("UploadInfo")
|
||||
|
||||
zos.close()
|
||||
val bytes = baos.toByteArray()
|
||||
|
||||
Single.just(UploadData(
|
||||
fileName = "upload-num$uploadNumber-ver1-date${FILE_NAME_DATE_FORMAT.format(uploadDate)}-appid${appId.toString().replace("-", "")}.zip",
|
||||
metadata = OpenHumansAPI.FileMetadata(
|
||||
tags = tags,
|
||||
description = "AndroidAPS Database Upload",
|
||||
md5 = MessageDigest.getInstance("MD5").digest(bytes).toHexString(),
|
||||
creationDate = uploadDate.time
|
||||
),
|
||||
content = bytes,
|
||||
highestQueueId = items.map { it.id }.maxOrNull()
|
||||
))
|
||||
}
|
||||
|
||||
private fun ZipOutputStream.writeFile(name: String, bytes: ByteArray) {
|
||||
putNextEntry(ZipEntry(name))
|
||||
write(bytes)
|
||||
closeEntry()
|
||||
}
|
||||
|
||||
private fun removeUploadedEntriesFromQueue(highestId: Long) = Completable.fromCallable {
|
||||
databaseHelper.removeAllOHQueueItemsWithIdSmallerThan(highestId)
|
||||
}
|
||||
|
||||
private fun handleSignOut() {
|
||||
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
|
||||
.setContentTitle(resourceHelper.gs(R.string.you_have_been_signed_out_of_open_humans))
|
||||
.setContentText(resourceHelper.gs(R.string.click_here_to_sign_in_again_if_this_wasnt_on_purpose))
|
||||
.setStyle(NotificationCompat.BigTextStyle())
|
||||
.setSmallIcon(R.drawable.notif_icon)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
Intent(context, OpenHumansLoginActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
},
|
||||
0
|
||||
))
|
||||
.build()
|
||||
NotificationManagerCompat.from(context).notify(SIGNED_OUT_NOTIFICATION_ID, notification)
|
||||
logout()
|
||||
}
|
||||
|
||||
private fun cancelWorker() {
|
||||
WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME)
|
||||
}
|
||||
|
||||
private fun scheduleWorker(replace: Boolean) {
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.setRequiresCharging(sp.getBoolean("key_oh_charging_only", false))
|
||||
.build()
|
||||
val workRequest = PeriodicWorkRequestBuilder<OHUploadWorker>(1, TimeUnit.DAYS)
|
||||
.setConstraints(constraints)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 20, TimeUnit.MINUTES)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_NAME, if (replace) ExistingPeriodicWorkPolicy.REPLACE else ExistingPeriodicWorkPolicy.KEEP, workRequest)
|
||||
}
|
||||
|
||||
private fun setupNotificationChannel() {
|
||||
val notificationManagerCompat = NotificationManagerCompat.from(context)
|
||||
notificationManagerCompat.createNotificationChannel(NotificationChannel(
|
||||
NOTIFICATION_CHANNEL,
|
||||
resourceHelper.gs(R.string.open_humans),
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
))
|
||||
}
|
||||
|
||||
private class UploadData(
|
||||
val fileName: String,
|
||||
val metadata: OpenHumansAPI.FileMetadata,
|
||||
val content: ByteArray,
|
||||
val highestQueueId: Long?
|
||||
)
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
private val HEX_DIGITS = "0123456789ABCDEF".toCharArray()
|
||||
|
||||
private fun ByteArray.toHexString(): String {
|
||||
val stringBuilder = StringBuilder()
|
||||
map { it.toInt() }.forEach {
|
||||
stringBuilder.append(HEX_DIGITS[(it shr 4) and 0x0F])
|
||||
stringBuilder.append(HEX_DIGITS[it and 0x0F])
|
||||
}
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
|
||||
private fun onSharedPreferenceChanged(event: EventPreferenceChange) {
|
||||
if (event.changedKey == "key_oh_charging_only" && isSetup) scheduleWorker(true)
|
||||
}
|
||||
}
|
|
@ -1,46 +1,56 @@
|
|||
package info.nightscout.androidaps.plugins.general.overview
|
||||
|
||||
import android.graphics.DashPathEffect
|
||||
import android.graphics.Paint
|
||||
import com.jjoe64.graphview.series.BarGraphSeries
|
||||
import com.jjoe64.graphview.series.DataPoint
|
||||
import com.jjoe64.graphview.series.LineGraphSeries
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.data.IobTotal
|
||||
import info.nightscout.androidaps.database.entities.ExtendedBolus
|
||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||
import info.nightscout.androidaps.database.entities.TemporaryBasal
|
||||
import info.nightscout.androidaps.database.entities.TemporaryTarget
|
||||
import info.nightscout.androidaps.extensions.convertedToPercent
|
||||
import info.nightscout.androidaps.extensions.toStringFull
|
||||
import info.nightscout.androidaps.extensions.toStringShort
|
||||
import info.nightscout.androidaps.extensions.valueToUnits
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.FixedLineGraphSeries
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.ValueWrapper
|
||||
import info.nightscout.androidaps.database.entities.*
|
||||
import info.nightscout.androidaps.extensions.*
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
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.aps.openAPSSMB.SMBDefaults
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.*
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.DefaultValueHelper
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.*
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@Singleton
|
||||
class OverviewData @Inject constructor(
|
||||
private val injector: HasAndroidInjector,
|
||||
private val aapsLogger: AAPSLogger,
|
||||
private val resourceHelper: ResourceHelper,
|
||||
private val dateUtil: DateUtil,
|
||||
private val sp: SP,
|
||||
private val activePlugin: ActivePlugin,
|
||||
private val defaultValueHelper: DefaultValueHelper,
|
||||
private val profileFunction: ProfileFunction
|
||||
private val profileFunction: ProfileFunction,
|
||||
private val config: Config,
|
||||
private val loopPlugin: LoopPlugin,
|
||||
private val nsDeviceStatus: NSDeviceStatus,
|
||||
private val repository: AppRepository,
|
||||
private val overviewMenus: OverviewMenus,
|
||||
private val iobCobCalculator: IobCobCalculator,
|
||||
private val translator: Translator
|
||||
) {
|
||||
|
||||
enum class Property {
|
||||
|
@ -53,7 +63,8 @@ class OverviewData @Inject constructor(
|
|||
BG,
|
||||
IOB_COB,
|
||||
SENSITIVITY,
|
||||
GRAPH
|
||||
GRAPH,
|
||||
PUMPSTATUS
|
||||
}
|
||||
|
||||
var rangeToDisplay = 6 // for graph
|
||||
|
@ -61,6 +72,42 @@ class OverviewData @Inject constructor(
|
|||
var fromTime: Long = 0
|
||||
var endTime: Long = 0
|
||||
|
||||
fun reset() {
|
||||
pumpStatus = ""
|
||||
calcProgress = ""
|
||||
lastBg = null
|
||||
temporaryBasal = null
|
||||
extendedBolus = null
|
||||
bolusIob = null
|
||||
basalIob = null
|
||||
cobInfo = null
|
||||
lastCarbsTime = 0L
|
||||
temporaryTarget = null
|
||||
lastAutosensData = null
|
||||
bgReadingsArray = ArrayList()
|
||||
bucketedGraphSeries = PointsWithLabelGraphSeries()
|
||||
bgReadingGraphSeries = PointsWithLabelGraphSeries()
|
||||
predictionsGraphSeries = PointsWithLabelGraphSeries()
|
||||
baseBasalGraphSeries = LineGraphSeries()
|
||||
tempBasalGraphSeries = LineGraphSeries()
|
||||
basalLineGraphSeries = LineGraphSeries()
|
||||
absoluteBasalGraphSeries = LineGraphSeries()
|
||||
activitySeries = FixedLineGraphSeries()
|
||||
activityPredictionSeries = FixedLineGraphSeries()
|
||||
iobSeries = FixedLineGraphSeries()
|
||||
absIobSeries = FixedLineGraphSeries()
|
||||
iobPredictions1Series = PointsWithLabelGraphSeries()
|
||||
iobPredictions2Series = PointsWithLabelGraphSeries()
|
||||
minusBgiSeries = FixedLineGraphSeries()
|
||||
minusBgiHistSeries = FixedLineGraphSeries()
|
||||
cobSeries = FixedLineGraphSeries()
|
||||
cobMinFailOverSeries = PointsWithLabelGraphSeries()
|
||||
deviationsSeries = BarGraphSeries()
|
||||
ratioSeries = LineGraphSeries()
|
||||
dsMaxSeries = LineGraphSeries()
|
||||
dsMinSeries = LineGraphSeries()
|
||||
}
|
||||
|
||||
fun initRange() {
|
||||
rangeToDisplay = sp.getInt(R.string.key_rangetodisplay, 6)
|
||||
|
||||
|
@ -78,25 +125,10 @@ class OverviewData @Inject constructor(
|
|||
}
|
||||
|
||||
/*
|
||||
* PROFILE
|
||||
* PUMP STATUS
|
||||
*/
|
||||
var profile: Profile? = null
|
||||
var profileName: String? = null
|
||||
var profileNameWithRemainingTime: String? = null
|
||||
|
||||
val profileBackgroudColor: Int
|
||||
get() =
|
||||
profile?.let { profile ->
|
||||
if (profile.percentage != 100 || profile.timeshift != 0) resourceHelper.gc(R.color.ribbonWarning)
|
||||
else resourceHelper.gc(R.color.ribbonDefault)
|
||||
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
|
||||
|
||||
val profileTextColor: Int
|
||||
get() =
|
||||
profile?.let { profile ->
|
||||
if (profile.percentage != 100 || profile.timeshift != 0) resourceHelper.gc(R.color.ribbonTextWarning)
|
||||
else resourceHelper.gc(R.color.ribbonTextDefault)
|
||||
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
|
||||
var pumpStatus: String = ""
|
||||
|
||||
/*
|
||||
* CALC PROGRESS
|
||||
|
@ -133,13 +165,14 @@ class OverviewData @Inject constructor(
|
|||
|
||||
val temporaryBasalText: String
|
||||
get() =
|
||||
profile?.let { profile ->
|
||||
profileFunction.getProfile()?.let { profile ->
|
||||
if (temporaryBasal?.isInProgress == false) temporaryBasal = null
|
||||
temporaryBasal?.let { "T:" + it.toStringShort() }
|
||||
?: resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal())
|
||||
} ?: resourceHelper.gs(R.string.notavailable)
|
||||
|
||||
val temporaryBasalDialogText: String
|
||||
get() = profile?.let { profile ->
|
||||
get() = profileFunction.getProfile()?.let { profile ->
|
||||
temporaryBasal?.let { temporaryBasal ->
|
||||
"${resourceHelper.gs(R.string.basebasalrate_label)}: ${resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal())}" +
|
||||
"\n" + resourceHelper.gs(R.string.tempbasal_label) + ": " + temporaryBasal.toStringFull(profile, dateUtil)
|
||||
|
@ -149,7 +182,7 @@ class OverviewData @Inject constructor(
|
|||
|
||||
val temporaryBasalIcon: Int
|
||||
get() =
|
||||
profile?.let { profile ->
|
||||
profileFunction.getProfile()?.let { profile ->
|
||||
temporaryBasal?.let { temporaryBasal ->
|
||||
val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile)
|
||||
when {
|
||||
|
@ -173,7 +206,10 @@ class OverviewData @Inject constructor(
|
|||
val extendedBolusText: String
|
||||
get() =
|
||||
extendedBolus?.let { extendedBolus ->
|
||||
if (activePlugin.activePump.isFakingTempsByExtendedBoluses) resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.rate)
|
||||
if (!extendedBolus.isInProgress(dateUtil)) {
|
||||
this@OverviewData.extendedBolus = null
|
||||
""
|
||||
} else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.rate)
|
||||
else ""
|
||||
} ?: ""
|
||||
|
||||
|
@ -211,7 +247,7 @@ class OverviewData @Inject constructor(
|
|||
* TEMP TARGET
|
||||
*/
|
||||
|
||||
var temporarytarget: TemporaryTarget? = null
|
||||
var temporaryTarget: TemporaryTarget? = null
|
||||
|
||||
/*
|
||||
* SENSITIVITY
|
||||
|
@ -278,4 +314,520 @@ class OverviewData @Inject constructor(
|
|||
var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
|
||||
var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
|
||||
|
||||
@Synchronized
|
||||
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
|
||||
fun prepareBgData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
maxBgValue = Double.MIN_VALUE
|
||||
bgReadingsArray = repository.compatGetBgReadingsDataFromTime(fromTime, toTime, false).blockingGet()
|
||||
val bgListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
|
||||
for (bg in bgReadingsArray) {
|
||||
if (bg.timestamp < fromTime || bg.timestamp > toTime) continue
|
||||
if (bg.value > maxBgValue) maxBgValue = bg.value
|
||||
bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper))
|
||||
}
|
||||
bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
|
||||
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, profileFunction.getUnits())
|
||||
if (defaultValueHelper.determineHighLine() > maxBgValue) maxBgValue = defaultValueHelper.determineHighLine()
|
||||
maxBgValue = addUpperChartMargin(maxBgValue)
|
||||
// profiler.log(LTag.UI, "prepareBgData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
fun preparePredictions(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
val apsResult = if (config.APS) loopPlugin.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
|
||||
val predictionsAvailable = if (config.APS) loopPlugin.lastRun?.request?.hasPredictions == true else config.NSCLIENT
|
||||
val menuChartSettings = overviewMenus.setting
|
||||
// align to hours
|
||||
val calendar = Calendar.getInstance().also {
|
||||
it.timeInMillis = System.currentTimeMillis()
|
||||
it[Calendar.MILLISECOND] = 0
|
||||
it[Calendar.SECOND] = 0
|
||||
it[Calendar.MINUTE] = 0
|
||||
it.add(Calendar.HOUR, 1)
|
||||
}
|
||||
if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) {
|
||||
var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt()
|
||||
predictionHours = min(2, predictionHours)
|
||||
predictionHours = max(0, predictionHours)
|
||||
val hoursToFetch = rangeToDisplay - predictionHours
|
||||
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
|
||||
fromTime = toTime - T.hours(hoursToFetch.toLong()).msecs()
|
||||
endTime = toTime + T.hours(predictionHours.toLong()).msecs()
|
||||
} else {
|
||||
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
|
||||
fromTime = toTime - T.hours(rangeToDisplay.toLong()).msecs()
|
||||
endTime = toTime
|
||||
}
|
||||
|
||||
val bgListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
|
||||
val predictions: MutableList<GlucoseValueDataPoint>? = apsResult?.predictions
|
||||
?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper) }
|
||||
?.toMutableList()
|
||||
if (predictions != null) {
|
||||
predictions.sortWith { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) }
|
||||
for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction)
|
||||
}
|
||||
predictionsGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
|
||||
// profiler.log(LTag.UI, "preparePredictions() $from", start)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
|
||||
fun prepareBucketedData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
val bucketedData = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return
|
||||
if (bucketedData.isEmpty()) {
|
||||
aapsLogger.debug("No bucketed data.")
|
||||
return
|
||||
}
|
||||
val bucketedListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
|
||||
for (inMemoryGlucoseValue in bucketedData) {
|
||||
if (inMemoryGlucoseValue.timestamp < fromTime || inMemoryGlucoseValue.timestamp > toTime) continue
|
||||
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, resourceHelper))
|
||||
}
|
||||
bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })
|
||||
// profiler.log(LTag.UI, "prepareBucketedData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
fun prepareBasalData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
maxBasalValueFound = 0.0
|
||||
val baseBasalArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
val tempBasalArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
val basalLineArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
val absoluteBasalLineArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
var lastLineBasal = 0.0
|
||||
var lastAbsoluteLineBasal = -1.0
|
||||
var lastBaseBasal = 0.0
|
||||
var lastTempBasal = 0.0
|
||||
var time = fromTime
|
||||
while (time < toTime) {
|
||||
val profile = profileFunction.getProfile(time)
|
||||
if (profile == null) {
|
||||
time += 60 * 1000L
|
||||
continue
|
||||
}
|
||||
val basalData = iobCobCalculator.getBasalData(profile, time)
|
||||
val baseBasalValue = basalData.basal
|
||||
var absoluteLineValue = baseBasalValue
|
||||
var tempBasalValue = 0.0
|
||||
var basal = 0.0
|
||||
if (basalData.isTempBasalRunning) {
|
||||
tempBasalValue = basalData.tempBasalAbsolute
|
||||
absoluteLineValue = tempBasalValue
|
||||
if (tempBasalValue != lastTempBasal) {
|
||||
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale))
|
||||
tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, basalScale))
|
||||
}
|
||||
if (lastBaseBasal != 0.0) {
|
||||
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale))
|
||||
baseBasalArray.add(ScaledDataPoint(time, 0.0, basalScale))
|
||||
lastBaseBasal = 0.0
|
||||
}
|
||||
} else {
|
||||
if (baseBasalValue != lastBaseBasal) {
|
||||
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale))
|
||||
baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, basalScale))
|
||||
lastBaseBasal = baseBasalValue
|
||||
}
|
||||
if (lastTempBasal != 0.0) {
|
||||
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale))
|
||||
tempBasalArray.add(ScaledDataPoint(time, 0.0, basalScale))
|
||||
}
|
||||
}
|
||||
if (baseBasalValue != lastLineBasal) {
|
||||
basalLineArray.add(ScaledDataPoint(time, lastLineBasal, basalScale))
|
||||
basalLineArray.add(ScaledDataPoint(time, baseBasalValue, basalScale))
|
||||
}
|
||||
if (absoluteLineValue != lastAbsoluteLineBasal) {
|
||||
absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, basalScale))
|
||||
absoluteBasalLineArray.add(ScaledDataPoint(time, basal, basalScale))
|
||||
}
|
||||
lastAbsoluteLineBasal = absoluteLineValue
|
||||
lastLineBasal = baseBasalValue
|
||||
lastTempBasal = tempBasalValue
|
||||
maxBasalValueFound = max(maxBasalValueFound, max(tempBasalValue, baseBasalValue))
|
||||
time += 60 * 1000L
|
||||
}
|
||||
|
||||
// final points
|
||||
basalLineArray.add(ScaledDataPoint(toTime, lastLineBasal, basalScale))
|
||||
baseBasalArray.add(ScaledDataPoint(toTime, lastBaseBasal, basalScale))
|
||||
tempBasalArray.add(ScaledDataPoint(toTime, lastTempBasal, basalScale))
|
||||
absoluteBasalLineArray.add(ScaledDataPoint(toTime, lastAbsoluteLineBasal, basalScale))
|
||||
|
||||
// create series
|
||||
baseBasalGraphSeries = LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = resourceHelper.gc(R.color.basebasal)
|
||||
it.thickness = 0
|
||||
}
|
||||
tempBasalGraphSeries = LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = resourceHelper.gc(R.color.tempbasal)
|
||||
it.thickness = 0
|
||||
}
|
||||
basalLineGraphSeries = LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
|
||||
it.setCustomPaint(Paint().also { paint ->
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
|
||||
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
|
||||
paint.color = resourceHelper.gc(R.color.basal)
|
||||
})
|
||||
}
|
||||
absoluteBasalGraphSeries = LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
|
||||
it.setCustomPaint(Paint().also { absolutePaint ->
|
||||
absolutePaint.style = Paint.Style.STROKE
|
||||
absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
|
||||
absolutePaint.color = resourceHelper.gc(R.color.basal)
|
||||
})
|
||||
}
|
||||
// profiler.log(LTag.UI, "prepareBasalData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
fun prepareTemporaryTargetData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
val profile = profileFunction.getProfile() ?: return
|
||||
val units = profileFunction.getUnits()
|
||||
var toTime = toTime
|
||||
val targetsSeriesArray: MutableList<DataPoint> = java.util.ArrayList()
|
||||
var lastTarget = -1.0
|
||||
loopPlugin.lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) }
|
||||
var time = fromTime
|
||||
while (time < toTime) {
|
||||
val tt = repository.getTemporaryTargetActiveAt(time).blockingGet()
|
||||
val value: Double = if (tt is ValueWrapper.Existing) {
|
||||
Profile.fromMgdlToUnits(tt.value.target(), units)
|
||||
} else {
|
||||
Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units)
|
||||
}
|
||||
if (lastTarget != value) {
|
||||
if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget))
|
||||
targetsSeriesArray.add(DataPoint(time.toDouble(), value))
|
||||
}
|
||||
lastTarget = value
|
||||
time += 5 * 60 * 1000L
|
||||
}
|
||||
// final point
|
||||
targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget))
|
||||
// create series
|
||||
temporaryTargetSeries = LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
|
||||
it.isDrawBackground = false
|
||||
it.color = resourceHelper.gc(R.color.tempTargetBackground)
|
||||
it.thickness = 2
|
||||
}
|
||||
// profiler.log(LTag.UI, "prepareTemporaryTargetData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
fun prepareTreatmentsData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
maxTreatmentsValue = 0.0
|
||||
val filteredTreatments: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
|
||||
repository.getBolusesDataFromTimeToTime(fromTime, endTime, true).blockingGet()
|
||||
.map { BolusDataPoint(it, resourceHelper, activePlugin, defaultValueHelper) }
|
||||
.filter { it.data.type == Bolus.Type.NORMAL || it.data.type == Bolus.Type.SMB }
|
||||
.forEach {
|
||||
it.y = getNearestBg(it.x.toLong())
|
||||
filteredTreatments.add(it)
|
||||
}
|
||||
repository.getCarbsDataFromTimeToTimeExpanded(fromTime, endTime, true).blockingGet()
|
||||
.map { CarbsDataPoint(it, resourceHelper) }
|
||||
.forEach {
|
||||
it.y = getNearestBg(it.x.toLong())
|
||||
filteredTreatments.add(it)
|
||||
}
|
||||
|
||||
// ProfileSwitch
|
||||
repository.getEffectiveProfileSwitchDataFromTimeToTime(fromTime, endTime, true).blockingGet()
|
||||
.map { EffectiveProfileSwitchDataPoint(it) }
|
||||
.forEach(filteredTreatments::add)
|
||||
|
||||
// OfflineEvent
|
||||
repository.getOfflineEventDataFromTimeToTime(fromTime, endTime, true).blockingGet()
|
||||
.map { TherapyEventDataPoint(TherapyEvent(timestamp = it.timestamp, duration = it.duration, type = TherapyEvent.Type.APS_OFFLINE, glucoseUnit = TherapyEvent.GlucoseUnit.MMOL), resourceHelper, profileFunction, translator) }
|
||||
.forEach(filteredTreatments::add)
|
||||
|
||||
// Extended bolus
|
||||
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
|
||||
repository.getExtendedBolusDataFromTimeToTime(fromTime, endTime, true).blockingGet()
|
||||
.map { ExtendedBolusDataPoint(it) }
|
||||
.filter { it.duration != 0L }
|
||||
.forEach {
|
||||
it.y = getNearestBg(it.x.toLong())
|
||||
filteredTreatments.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
// Careportal
|
||||
repository.compatGetTherapyEventDataFromToTime(fromTime - T.hours(6).msecs(), endTime).blockingGet()
|
||||
.map { TherapyEventDataPoint(it, resourceHelper, profileFunction, translator) }
|
||||
.filterTimeframe(fromTime, endTime)
|
||||
.forEach {
|
||||
if (it.y == 0.0) it.y = getNearestBg(it.x.toLong())
|
||||
filteredTreatments.add(it)
|
||||
}
|
||||
|
||||
// increase maxY if a treatment forces it's own height that's higher than a BG value
|
||||
filteredTreatments.map { it.y }
|
||||
.maxOrNull()
|
||||
?.let(::addUpperChartMargin)
|
||||
?.let { maxTreatmentsValue = maxOf(maxTreatmentsValue, it) }
|
||||
|
||||
treatmentsSeries = PointsWithLabelGraphSeries(filteredTreatments.toTypedArray())
|
||||
// profiler.log(LTag.UI, "prepareTreatmentsData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
fun prepareIobAutosensData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
val iobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
val absIobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
maxIobValueFound = Double.MIN_VALUE
|
||||
var lastIob = 0.0
|
||||
var absLastIob = 0.0
|
||||
var time = fromTime
|
||||
|
||||
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
|
||||
val cobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
maxCobValueFound = Double.MIN_VALUE
|
||||
var lastCob = 0
|
||||
|
||||
val actArrayHist: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
val actArrayPrediction: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
val now = dateUtil.now().toDouble()
|
||||
maxIAValue = 0.0
|
||||
|
||||
val bgiArrayHist: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
val bgiArrayPrediction: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
maxBGIValue = Double.MIN_VALUE
|
||||
|
||||
val devArray: MutableList<OverviewPlugin.DeviationDataPoint> = java.util.ArrayList()
|
||||
maxDevValueFound = Double.MIN_VALUE
|
||||
|
||||
val ratioArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
|
||||
minRatioValueFound = -5.0
|
||||
|
||||
val dsMaxArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
val dsMinArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
|
||||
maxFromMaxValueFound = Double.MIN_VALUE
|
||||
maxFromMinValueFound = Double.MIN_VALUE
|
||||
|
||||
val adsData = iobCobCalculator.ads.clone()
|
||||
|
||||
while (time <= toTime) {
|
||||
val profile = profileFunction.getProfile(time)
|
||||
if (profile == null) {
|
||||
time += 5 * 60 * 1000L
|
||||
continue
|
||||
}
|
||||
// IOB
|
||||
val iob = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
|
||||
val baseBasalIob = iobCobCalculator.calculateAbsoluteIobFromBaseBasals(time)
|
||||
val absIob = IobTotal.combine(iob, baseBasalIob)
|
||||
val autosensData = adsData.getAutosensDataAtTime(time)
|
||||
if (abs(lastIob - iob.iob) > 0.02) {
|
||||
if (abs(lastIob - iob.iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
|
||||
iobArray.add(ScaledDataPoint(time, iob.iob, iobScale))
|
||||
maxIobValueFound = maxOf(maxIobValueFound, abs(iob.iob))
|
||||
lastIob = iob.iob
|
||||
}
|
||||
if (abs(absLastIob - absIob.iob) > 0.02) {
|
||||
if (abs(absLastIob - absIob.iob) > 0.2) absIobArray.add(ScaledDataPoint(time, absLastIob, iobScale))
|
||||
absIobArray.add(ScaledDataPoint(time, absIob.iob, iobScale))
|
||||
maxIobValueFound = maxOf(maxIobValueFound, abs(absIob.iob))
|
||||
absLastIob = absIob.iob
|
||||
}
|
||||
|
||||
// COB
|
||||
if (autosensData != null) {
|
||||
val cob = autosensData.cob.toInt()
|
||||
if (cob != lastCob) {
|
||||
if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), cobScale))
|
||||
cobArray.add(ScaledDataPoint(time, cob.toDouble(), cobScale))
|
||||
maxCobValueFound = max(maxCobValueFound, cob.toDouble())
|
||||
lastCob = cob
|
||||
}
|
||||
if (autosensData.failoverToMinAbsorbtionRate) {
|
||||
autosensData.setScale(cobScale)
|
||||
autosensData.setChartTime(time)
|
||||
minFailOverActiveList.add(autosensData)
|
||||
}
|
||||
}
|
||||
|
||||
// ACTIVITY
|
||||
if (time <= now) actArrayHist.add(ScaledDataPoint(time, iob.activity, actScale))
|
||||
else actArrayPrediction.add(ScaledDataPoint(time, iob.activity, actScale))
|
||||
maxIAValue = max(maxIAValue, abs(iob.activity))
|
||||
|
||||
// BGI
|
||||
val devBgiScale = overviewMenus.isEnabledIn(OverviewMenus.CharType.DEV) == overviewMenus.isEnabledIn(OverviewMenus.CharType.BGI)
|
||||
val deviation = if (devBgiScale) autosensData?.deviation ?: 0.0 else 0.0
|
||||
val bgi: Double = iob.activity * profile.getIsfMgdl(time) * 5.0
|
||||
if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, bgiScale))
|
||||
else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, bgiScale))
|
||||
maxBGIValue = max(maxBGIValue, max(abs(bgi), deviation))
|
||||
|
||||
// DEVIATIONS
|
||||
if (autosensData != null) {
|
||||
var color = resourceHelper.gc(R.color.deviationblack) // "="
|
||||
if (autosensData.type == "" || autosensData.type == "non-meal") {
|
||||
if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey)
|
||||
if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen)
|
||||
if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred)
|
||||
} else if (autosensData.type == "uam") {
|
||||
color = resourceHelper.gc(R.color.uam)
|
||||
} else if (autosensData.type == "csf") {
|
||||
color = resourceHelper.gc(R.color.deviationgrey)
|
||||
}
|
||||
devArray.add(OverviewPlugin.DeviationDataPoint(time.toDouble(), autosensData.deviation, color, devScale))
|
||||
maxDevValueFound = maxOf(maxDevValueFound, abs(autosensData.deviation), abs(bgi))
|
||||
}
|
||||
|
||||
// RATIO
|
||||
if (autosensData != null) {
|
||||
ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1), ratioScale))
|
||||
maxRatioValueFound = max(maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
|
||||
minRatioValueFound = min(minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
|
||||
}
|
||||
|
||||
// DEV SLOPE
|
||||
if (autosensData != null) {
|
||||
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale))
|
||||
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale))
|
||||
maxFromMaxValueFound = max(maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
|
||||
maxFromMinValueFound = max(maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation))
|
||||
}
|
||||
|
||||
time += 5 * 60 * 1000L
|
||||
}
|
||||
// IOB
|
||||
iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
|
||||
it.color = resourceHelper.gc(R.color.iob)
|
||||
it.thickness = 3
|
||||
}
|
||||
absIobSeries = FixedLineGraphSeries(Array(absIobArray.size) { i -> absIobArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
|
||||
it.color = resourceHelper.gc(R.color.iob)
|
||||
it.thickness = 3
|
||||
}
|
||||
|
||||
if (overviewMenus.setting[0][OverviewMenus.CharType.PRE.ordinal]) {
|
||||
val autosensData = adsData.getLastAutosensData("GraphData", aapsLogger, dateUtil)
|
||||
val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult()
|
||||
val isTempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
|
||||
val iobPrediction: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
|
||||
val iobPredictionArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
|
||||
for (i in iobPredictionArray) {
|
||||
iobPrediction.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)))
|
||||
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
|
||||
}
|
||||
iobPredictions1Series = PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] })
|
||||
val iobPrediction2: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
|
||||
val iobPredictionArray2 = iobCobCalculator.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
|
||||
for (i in iobPredictionArray2) {
|
||||
iobPrediction2.add(i.setColor(resourceHelper.gc(R.color.iobPred)))
|
||||
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
|
||||
}
|
||||
iobPredictions2Series = PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] })
|
||||
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray))
|
||||
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray2))
|
||||
} else {
|
||||
iobPredictions1Series = PointsWithLabelGraphSeries()
|
||||
iobPredictions2Series = PointsWithLabelGraphSeries()
|
||||
}
|
||||
|
||||
// COB
|
||||
cobSeries = FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50%
|
||||
it.color = resourceHelper.gc(R.color.cob)
|
||||
it.thickness = 3
|
||||
}
|
||||
cobMinFailOverSeries = PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })
|
||||
|
||||
// ACTIVITY
|
||||
activitySeries = FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
|
||||
it.isDrawBackground = false
|
||||
it.color = resourceHelper.gc(R.color.activity)
|
||||
it.thickness = 3
|
||||
}
|
||||
activityPredictionSeries = FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[i] }).also {
|
||||
it.setCustomPaint(Paint().also { paint ->
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.strokeWidth = 3f
|
||||
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
|
||||
paint.color = resourceHelper.gc(R.color.activity)
|
||||
})
|
||||
}
|
||||
|
||||
// BGI
|
||||
minusBgiSeries = FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
|
||||
it.isDrawBackground = false
|
||||
it.color = resourceHelper.gc(R.color.bgi)
|
||||
it.thickness = 3
|
||||
}
|
||||
minusBgiHistSeries = FixedLineGraphSeries(Array(bgiArrayPrediction.size) { i -> bgiArrayPrediction[i] }).also {
|
||||
it.setCustomPaint(Paint().also { paint ->
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.strokeWidth = 3f
|
||||
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
|
||||
paint.color = resourceHelper.gc(R.color.bgi)
|
||||
})
|
||||
}
|
||||
|
||||
// DEVIATIONS
|
||||
deviationsSeries = BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
|
||||
it.setValueDependentColor { data: OverviewPlugin.DeviationDataPoint -> data.color }
|
||||
}
|
||||
|
||||
// RATIO
|
||||
ratioSeries = LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
|
||||
it.color = resourceHelper.gc(R.color.ratio)
|
||||
it.thickness = 3
|
||||
}
|
||||
|
||||
// DEV SLOPE
|
||||
dsMaxSeries = LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
|
||||
it.color = resourceHelper.gc(R.color.devslopepos)
|
||||
it.thickness = 3
|
||||
}
|
||||
dsMinSeries = LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
|
||||
it.color = resourceHelper.gc(R.color.devslopeneg)
|
||||
it.thickness = 3
|
||||
}
|
||||
|
||||
// profiler.log(LTag.UI, "prepareIobAutosensData() $from", start)
|
||||
}
|
||||
|
||||
private fun addUpperChartMargin(maxBgValue: Double) =
|
||||
if (profileFunction.getUnits() == GlucoseUnit.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4
|
||||
|
||||
private fun getNearestBg(date: Long): Double {
|
||||
bgReadingsArray.let { bgReadingsArray ->
|
||||
for (reading in bgReadingsArray) {
|
||||
if (reading.timestamp > date) continue
|
||||
return Profile.fromMgdlToUnits(reading.value, profileFunction.getUnits())
|
||||
}
|
||||
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, profileFunction.getUnits())
|
||||
else Profile.fromMgdlToUnits(100.0, profileFunction.getUnits())
|
||||
}
|
||||
}
|
||||
|
||||
private fun <E : DataPointWithLabelInterface> List<E>.filterTimeframe(fromTime: Long, endTime: Long): List<E> =
|
||||
filter { it.x + it.duration >= fromTime && it.x <= endTime }
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import dagger.android.HasAndroidInjector
|
|||
import dagger.android.support.DaggerFragment
|
||||
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.UserEntry.Action
|
||||
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
||||
|
@ -38,6 +39,7 @@ import info.nightscout.androidaps.events.EventPreferenceChange
|
|||
import info.nightscout.androidaps.events.EventPumpStatusChanged
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview
|
||||
import info.nightscout.androidaps.extensions.directionToIcon
|
||||
import info.nightscout.androidaps.extensions.isInProgress
|
||||
import info.nightscout.androidaps.extensions.toVisibility
|
||||
import info.nightscout.androidaps.extensions.valueToUnitsString
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
|
@ -57,10 +59,13 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProv
|
|||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
||||
import info.nightscout.androidaps.plugins.source.DexcomPlugin
|
||||
import info.nightscout.androidaps.plugins.source.XdripPlugin
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
|
||||
import info.nightscout.androidaps.queue.CommandQueue
|
||||
import info.nightscout.androidaps.skins.SkinProvider
|
||||
import info.nightscout.androidaps.utils.*
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.DefaultValueHelper
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.ToastUtils
|
||||
import info.nightscout.androidaps.utils.TrendCalculator
|
||||
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.utils.protection.ProtectionCheck
|
||||
|
@ -72,6 +77,7 @@ import info.nightscout.androidaps.utils.wizard.QuickWizard
|
|||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.abs
|
||||
|
@ -92,7 +98,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
@Inject lateinit var nsDeviceStatus: NSDeviceStatus
|
||||
@Inject lateinit var loopPlugin: LoopPlugin
|
||||
@Inject lateinit var activePlugin: ActivePlugin
|
||||
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
|
||||
@Inject lateinit var iobCobCalculator: IobCobCalculator
|
||||
@Inject lateinit var dexcomPlugin: DexcomPlugin
|
||||
@Inject lateinit var dexcomMediator: DexcomPlugin.DexcomMediator
|
||||
|
@ -108,7 +113,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
@Inject lateinit var trendCalculator: TrendCalculator
|
||||
@Inject lateinit var config: Config
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
@Inject lateinit var databaseHelper: DatabaseHelperInterface
|
||||
@Inject lateinit var uel: UserEntryLogger
|
||||
@Inject lateinit var repository: AppRepository
|
||||
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
|
||||
|
@ -121,7 +125,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
private var smallHeight = false
|
||||
private lateinit var dm: DisplayMetrics
|
||||
private var axisWidth: Int = 0
|
||||
private var refreshLoop: Runnable? = null
|
||||
private lateinit var refreshLoop: Runnable
|
||||
private lateinit var handler: Handler
|
||||
|
||||
private val secondaryGraphs = ArrayList<GraphView>()
|
||||
|
@ -140,7 +144,10 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
_binding = it
|
||||
//check screen width
|
||||
dm = DisplayMetrics()
|
||||
activity?.windowManager?.defaultDisplay?.getMetrics(dm)
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
|
||||
activity?.display?.getRealMetrics(dm)
|
||||
else
|
||||
@Suppress("DEPRECATION") activity?.windowManager?.defaultDisplay?.getMetrics(dm)
|
||||
}.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -157,8 +164,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
skinProvider.activeSkin().preProcessLandscapeOverviewLayout(dm, view, landscape, resourceHelper.gb(R.bool.isTablet), smallHeight)
|
||||
binding.nsclientLayout.visibility = config.NSCLIENT.toVisibility()
|
||||
|
||||
binding.loopPumpStatusLayout.pumpStatus.setBackgroundColor(resourceHelper.gc(R.color.colorInitializingBorder))
|
||||
|
||||
binding.notifications.setHasFixedSize(false)
|
||||
binding.notifications.layoutManager = LinearLayoutManager(view.context)
|
||||
axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80
|
||||
|
@ -177,6 +182,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else overviewData.rangeToDisplay
|
||||
sp.putInt(R.string.key_rangetodisplay, overviewData.rangeToDisplay)
|
||||
overviewData.initRange()
|
||||
overviewData.prepareBucketedData("EventBucketedDataCreated")
|
||||
overviewData.prepareBgData("EventBucketedDataCreated")
|
||||
updateGUI("rangeChange", OverviewData.Property.GRAPH)
|
||||
rxBus.send(EventPreferenceChange(resourceHelper, R.string.key_rangetodisplay))
|
||||
sp.putBoolean(R.string.key_objectiveusescale, true)
|
||||
|
@ -185,10 +192,10 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
prepareGraphsIfNeeded(overviewMenus.setting.size)
|
||||
overviewMenus.setupChartMenu(binding.graphsLayout.chartMenuButton)
|
||||
|
||||
binding.loopPumpStatusLayout.activeProfile.setOnClickListener(this)
|
||||
binding.loopPumpStatusLayout.activeProfile.setOnLongClickListener(this)
|
||||
binding.loopPumpStatusLayout.tempTarget.setOnClickListener(this)
|
||||
binding.loopPumpStatusLayout.tempTarget.setOnLongClickListener(this)
|
||||
binding.activeProfile.setOnClickListener(this)
|
||||
binding.activeProfile.setOnLongClickListener(this)
|
||||
binding.tempTarget.setOnClickListener(this)
|
||||
binding.tempTarget.setOnLongClickListener(this)
|
||||
binding.buttonsLayout.acceptTempButton.setOnClickListener(this)
|
||||
binding.buttonsLayout.treatmentButton.setOnClickListener(this)
|
||||
binding.buttonsLayout.wizardButton.setOnClickListener(this)
|
||||
|
@ -200,7 +207,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
binding.buttonsLayout.quickWizardButton.setOnLongClickListener(this)
|
||||
binding.infoLayout.apsMode.setOnClickListener(this)
|
||||
binding.infoLayout.apsMode.setOnLongClickListener(this)
|
||||
binding.loopPumpStatusLayout.activeProfile.setOnLongClickListener(this)
|
||||
binding.activeProfile.setOnLongClickListener(this)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
|
@ -241,10 +248,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
.toObservable(EventNewOpenLoopNotification::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ scheduleUpdateGUI("EventNewOpenLoopNotification") }, fabricPrivacy::logException))
|
||||
disposable.add(rxBus
|
||||
disposable += rxBus
|
||||
.toObservable(EventPumpStatusChanged::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({ updatePumpStatus(it) }, fabricPrivacy::logException))
|
||||
.delay (30, TimeUnit.MILLISECONDS, aapsSchedulers.main)
|
||||
.subscribe({
|
||||
overviewData.pumpStatus = it.getStatus(resourceHelper)
|
||||
updateGUI("EventPumpStatusChanged", OverviewData.Property.PUMPSTATUS)
|
||||
}, fabricPrivacy::logException)
|
||||
|
||||
refreshLoop = Runnable {
|
||||
overviewPlugin.refreshLoop("refreshLoop")
|
||||
|
@ -374,7 +385,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
}
|
||||
|
||||
R.id.temp_target -> v.performClick()
|
||||
R.id.active_profile -> activity?.let { activity -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { ProfileSwitchDialog().show(childFragmentManager, "Overview") }) }
|
||||
R.id.active_profile -> activity?.let { activity -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { ProfileSwitchDialog().show(childFragmentManager, "ProfileSwitchDialog") }) }
|
||||
|
||||
}
|
||||
return false
|
||||
|
@ -402,18 +413,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
}
|
||||
}
|
||||
|
||||
private fun updatePumpStatus(event: EventPumpStatusChanged) {
|
||||
val status = event.getStatus(resourceHelper)
|
||||
if (status != "") {
|
||||
binding.loopPumpStatusLayout.pumpStatus.text = status
|
||||
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.VISIBLE
|
||||
binding.loopPumpStatusLayout.loopLayout.visibility = View.GONE
|
||||
} else {
|
||||
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.GONE
|
||||
binding.loopPumpStatusLayout.loopLayout.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun processButtonsVisibility() {
|
||||
val lastBG = iobCobCalculator.ads.lastBg()
|
||||
|
@ -585,9 +584,9 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
task = null
|
||||
}
|
||||
}
|
||||
handler.removeCallbacks(task)
|
||||
task?.let { handler.removeCallbacks(it) }
|
||||
task = UpdateRunnable()
|
||||
handler.postDelayed(task, 500)
|
||||
task?.let { handler.postDelayed(it, 500) }
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
|
@ -595,15 +594,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
fun updateGUI(from: String, what: OverviewData.Property) {
|
||||
// if (what != OverviewData.Property.CALC_PROGRESS)
|
||||
// aapsLogger.debug(LTag.UI, "UpdateGui $from $what")
|
||||
if (overviewData.profile == null) {
|
||||
binding.loopPumpStatusLayout.pumpStatus.setText(R.string.noprofileset)
|
||||
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.VISIBLE
|
||||
binding.loopPumpStatusLayout.loopLayout.visibility = View.GONE
|
||||
if (profileFunction.getProfile() == null) {
|
||||
binding.activeProfile.setText(R.string.noprofileset)
|
||||
binding.activeProfile.setBackgroundColor(resourceHelper.gc(R.color.errorAlertBackground))
|
||||
return
|
||||
}
|
||||
binding.notifications.let { notificationStore.updateNotifications(it) }
|
||||
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.GONE
|
||||
binding.loopPumpStatusLayout.loopLayout.visibility = View.VISIBLE
|
||||
|
||||
val units = profileFunction.getUnits()
|
||||
val pump = activePlugin.activePump
|
||||
|
@ -623,7 +619,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
binding.infoLayout.avgDelta.text = Profile.toSignedUnitsString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units)
|
||||
binding.infoLayout.longAvgDelta.text = Profile.toSignedUnitsString(glucoseStatus.longAvgDelta, glucoseStatus.longAvgDelta * Constants.MGDL_TO_MMOLL, units)
|
||||
} else {
|
||||
binding.infoLayout.deltaLarge.text = "Δ " + resourceHelper.gs(R.string.notavailable)
|
||||
binding.infoLayout.deltaLarge.text = ""
|
||||
binding.infoLayout.delta.text = "Δ " + resourceHelper.gs(R.string.notavailable)
|
||||
binding.infoLayout.avgDelta.text = ""
|
||||
binding.infoLayout.longAvgDelta.text = ""
|
||||
|
@ -638,10 +634,25 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
}
|
||||
|
||||
OverviewData.Property.PROFILE -> {
|
||||
binding.loopPumpStatusLayout.activeProfile.text = overviewData.profileNameWithRemainingTime
|
||||
?: ""
|
||||
binding.loopPumpStatusLayout.activeProfile.setBackgroundColor(overviewData.profileBackgroudColor)
|
||||
binding.loopPumpStatusLayout.activeProfile.setTextColor(overviewData.profileTextColor)
|
||||
val profileBackgroundColor =
|
||||
profileFunction.getProfile()?.let {
|
||||
val profile = (it as ProfileSealed.EPS).value
|
||||
if (profile.originalPercentage != 100 || profile.originalTimeshift != 0L || profile.originalDuration != 0L)
|
||||
resourceHelper.gc(R.color.ribbonWarning)
|
||||
else resourceHelper.gc(R.color.ribbonDefault)
|
||||
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
|
||||
|
||||
val profileTextColor =
|
||||
profileFunction.getProfile()?.let {
|
||||
val profile = (it as ProfileSealed.EPS).value
|
||||
if (profile.originalPercentage != 100 || profile.originalTimeshift != 0L || profile.originalDuration != 0L)
|
||||
resourceHelper.gc(R.color.ribbonTextWarning)
|
||||
else resourceHelper.gc(R.color.ribbonTextDefault)
|
||||
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
|
||||
|
||||
binding.activeProfile.text = profileFunction.getProfileNameWithRemainingTime()
|
||||
binding.activeProfile.setBackgroundColor(profileBackgroundColor)
|
||||
binding.activeProfile.setTextColor(profileTextColor)
|
||||
}
|
||||
|
||||
OverviewData.Property.TEMPORARY_BASAL -> {
|
||||
|
@ -655,7 +666,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
|
||||
OverviewData.Property.EXTENDED_BOLUS -> {
|
||||
binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText
|
||||
binding.infoLayout.extendedBolus.setOnClickListener {
|
||||
binding.infoLayout.extendedLayout.setOnClickListener {
|
||||
activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.extended_bolus), overviewData.extendedBolusDialogText) }
|
||||
}
|
||||
binding.infoLayout.extendedLayout.visibility = (overviewData.extendedBolus != null && !pump.isFakingTempsByExtendedBoluses).toVisibility()
|
||||
|
@ -677,14 +688,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.iob), overviewData.iobDialogText) }
|
||||
}
|
||||
// cob
|
||||
var cobText: String = resourceHelper.gs(R.string.value_unavailable_short)
|
||||
overviewData.cobInfo?.let { cobInfo ->
|
||||
if (cobInfo.displayCob != null) {
|
||||
cobText = resourceHelper.gs(R.string.format_carbs, cobInfo.displayCob!!.toInt())
|
||||
if (cobInfo.futureCarbs > 0) cobText += "(" + DecimalFormatter.to0Decimal(cobInfo.futureCarbs) + ")"
|
||||
}
|
||||
}
|
||||
binding.infoLayout.cob.text = cobText
|
||||
var cobText = overviewData.cobInfo?.displayText(resourceHelper, dateUtil, buildHelper.isDev()) ?: resourceHelper.gs(R.string.value_unavailable_short)
|
||||
|
||||
val constraintsProcessed = loopPlugin.lastRun?.constraintsProcessed
|
||||
val lastRun = loopPlugin.lastRun
|
||||
|
@ -692,7 +696,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
if (constraintsProcessed.carbsReq > 0) {
|
||||
//only display carbsreq when carbs have not been entered recently
|
||||
if (overviewData.lastCarbsTime < lastRun.lastAPSRun) {
|
||||
cobText = cobText + " | " + constraintsProcessed.carbsReq + " " + resourceHelper.gs(R.string.required)
|
||||
cobText += " | " + constraintsProcessed.carbsReq + " " + resourceHelper.gs(R.string.required)
|
||||
}
|
||||
if (carbAnimation?.isRunning == false)
|
||||
carbAnimation?.start()
|
||||
|
@ -701,36 +705,38 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
carbAnimation?.selectDrawable(0)
|
||||
}
|
||||
}
|
||||
binding.infoLayout.cob.text = cobText
|
||||
}
|
||||
|
||||
OverviewData.Property.TEMPORARY_TARGET -> {
|
||||
// temp target
|
||||
val tempTarget = overviewData.temporarytarget
|
||||
if (overviewData.temporaryTarget?.isInProgress(dateUtil) == false) overviewData.temporaryTarget = null
|
||||
val tempTarget = overviewData.temporaryTarget
|
||||
if (tempTarget != null) {
|
||||
binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning))
|
||||
binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning))
|
||||
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(tempTarget.lowTarget, tempTarget.highTarget, GlucoseUnit.MGDL, units) + " " + dateUtil.untilString(tempTarget.end, resourceHelper)
|
||||
binding.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning))
|
||||
binding.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning))
|
||||
binding.tempTarget.text = Profile.toTargetRangeString(tempTarget.lowTarget, tempTarget.highTarget, GlucoseUnit.MGDL, units) + " " + dateUtil.untilString(tempTarget.end, resourceHelper)
|
||||
} else {
|
||||
// If the target is not the same as set in the profile then oref has overridden it
|
||||
overviewData.profile?.let { profile ->
|
||||
profileFunction.getProfile()?.let { profile ->
|
||||
val targetUsed = loopPlugin.lastRun?.constraintsProcessed?.targetBG ?: 0.0
|
||||
|
||||
if (targetUsed != 0.0 && abs(profile.getTargetMgdl() - targetUsed) > 0.01) {
|
||||
aapsLogger.debug("Adjusted target. Profile: ${profile.getTargetMgdl()} APS: $targetUsed")
|
||||
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(targetUsed, targetUsed, GlucoseUnit.MGDL, units)
|
||||
binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning))
|
||||
binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.tempTargetBackground))
|
||||
binding.tempTarget.text = Profile.toTargetRangeString(targetUsed, targetUsed, GlucoseUnit.MGDL, units)
|
||||
binding.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning))
|
||||
binding.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.tempTargetBackground))
|
||||
} else {
|
||||
binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault))
|
||||
binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault))
|
||||
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units)
|
||||
binding.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault))
|
||||
binding.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault))
|
||||
binding.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OverviewData.Property.GRAPH -> {
|
||||
val graphData = GraphData(injector, binding.graphsLayout.bgGraph)
|
||||
val graphData = GraphData(injector, binding.graphsLayout.bgGraph, overviewData)
|
||||
val menuChartSettings = overviewMenus.setting
|
||||
graphData.addInRangeArea(overviewData.fromTime, overviewData.endTime, defaultValueHelper.determineLowLine(), defaultValueHelper.determineHighLine())
|
||||
graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
|
||||
|
@ -738,7 +744,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
graphData.addTreatments()
|
||||
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
|
||||
graphData.addActivity(0.8)
|
||||
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
|
||||
if ((pump.pumpDescription.isTempBasalCapable || config.NSCLIENT) && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
|
||||
graphData.addBasals()
|
||||
graphData.addTargetLine()
|
||||
graphData.addNowLine(dateUtil.now())
|
||||
|
@ -755,7 +761,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
|
||||
val now = System.currentTimeMillis()
|
||||
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
|
||||
val secondGraphData = GraphData(injector, secondaryGraphs[g])
|
||||
val secondGraphData = GraphData(injector, secondaryGraphs[g], overviewData)
|
||||
var useABSForScale = false
|
||||
var useIobForScale = false
|
||||
var useCobForScale = false
|
||||
|
@ -780,7 +786,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(useDevForScale, 1.0)
|
||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.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)
|
||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(useDSForScale, if(useDSForScale) 1.0 else 0.8, useRatioForScale)
|
||||
|
||||
// set manual x bounds to have nice steps
|
||||
secondGraphData.formatAxis(overviewData.fromTime, overviewData.endTime)
|
||||
|
@ -818,6 +824,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
|
||||
} ?: ""
|
||||
}
|
||||
|
||||
OverviewData.Property.PUMPSTATUS -> {
|
||||
val status = overviewData.pumpStatus
|
||||
binding.pumpStatus.text = status
|
||||
binding.pumpStatusLayout.visibility = (status != "").toVisibility()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,6 +113,9 @@ class OverviewMenus @Inject constructor(
|
|||
if (m == CharType.PRE) insert = predictionsAvailable
|
||||
if (m == CharType.DEVSLOPE) insert = buildHelper.isDev()
|
||||
if (used.contains(m.ordinal)) insert = false
|
||||
for (g2 in g + 1 until numOfGraphs) {
|
||||
if (settingsCopy[g2][m.ordinal]) insert = false
|
||||
}
|
||||
if (insert) {
|
||||
val item = popup.menu.add(Menu.NONE, m.ordinal + 100 * (g + 1), Menu.NONE, resourceHelper.gs(m.nameId))
|
||||
val title = item.title
|
||||
|
|
|
@ -1,50 +1,36 @@
|
|||
package info.nightscout.androidaps.plugins.general.overview
|
||||
|
||||
import android.graphics.DashPathEffect
|
||||
import android.graphics.Paint
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreference
|
||||
import com.jjoe64.graphview.series.BarGraphSeries
|
||||
import com.jjoe64.graphview.series.DataPoint
|
||||
import com.jjoe64.graphview.series.LineGraphSeries
|
||||
import dagger.android.HasAndroidInjector
|
||||
import 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.Bolus
|
||||
import info.nightscout.androidaps.events.*
|
||||
import info.nightscout.androidaps.extensions.*
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked
|
||||
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
|
||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverview
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.*
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
|
||||
import info.nightscout.androidaps.utils.*
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import org.json.JSONObject
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@Singleton
|
||||
class OverviewPlugin @Inject constructor(
|
||||
|
@ -58,15 +44,9 @@ class OverviewPlugin @Inject constructor(
|
|||
resourceHelper: ResourceHelper,
|
||||
private val config: Config,
|
||||
private val dateUtil: DateUtil,
|
||||
private val translator: Translator,
|
||||
// private val profiler: Profiler,
|
||||
private val profileFunction: ProfileFunction,
|
||||
private val iobCobCalculator: IobCobCalculator,
|
||||
private val repository: AppRepository,
|
||||
private val defaultValueHelper: DefaultValueHelper,
|
||||
private val loopPlugin: LoopPlugin,
|
||||
private val activePlugin: ActivePlugin,
|
||||
private val nsDeviceStatus: NSDeviceStatus,
|
||||
private val overviewData: OverviewData,
|
||||
private val overviewMenus: OverviewMenus
|
||||
) : PluginBase(PluginDescription()
|
||||
|
@ -110,7 +90,7 @@ class OverviewPlugin @Inject constructor(
|
|||
}, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventIobCalculationProgress::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ overviewData.calcProgress = it.progress; overviewBus.send(EventUpdateOverview("EventIobCalculationProgress", OverviewData.Property.CALC_PROGRESS)) }, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventTempBasalChange::class.java)
|
||||
|
@ -133,36 +113,44 @@ class OverviewPlugin @Inject constructor(
|
|||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
loadIobCobResults("EventTreatmentChange")
|
||||
prepareTreatmentsData("EventTreatmentChange")
|
||||
overviewData.prepareTreatmentsData("EventTreatmentChange")
|
||||
overviewBus.send(EventUpdateOverview("EventTreatmentChange", OverviewData.Property.GRAPH))
|
||||
}, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventTherapyEventChange::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
prepareTreatmentsData("EventTherapyEventChange")
|
||||
overviewData.prepareTreatmentsData("EventTherapyEventChange")
|
||||
overviewBus.send(EventUpdateOverview("EventTherapyEventChange", OverviewData.Property.GRAPH))
|
||||
}, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventBucketedDataCreated::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
prepareBucketedData("EventBucketedDataCreated")
|
||||
prepareBgData("EventBucketedDataCreated")
|
||||
overviewData.prepareBucketedData("EventBucketedDataCreated")
|
||||
overviewData.prepareBgData("EventBucketedDataCreated")
|
||||
overviewBus.send(EventUpdateOverview("EventBucketedDataCreated", OverviewData.Property.GRAPH))
|
||||
}, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventLoopInvoked::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException)
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventProfileSwitchChanged::class.java)
|
||||
.subscribe({ overviewData.preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventNewBasalProfile::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ loadProfile("EventProfileSwitchChanged") }, fabricPrivacy::logException))
|
||||
disposable.add(rxBus
|
||||
.subscribe({ loadProfile("EventNewBasalProfile") }, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventAutosensCalculationFinished::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ refreshLoop("EventAutosensCalculationFinished") }, fabricPrivacy::logException))
|
||||
.subscribe({
|
||||
if (it.cause !is EventCustomCalculationFinished) refreshLoop("EventAutosensCalculationFinished")
|
||||
}, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventPumpStatusChanged::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
overviewData.pumpStatus = it.getStatus(resourceHelper)
|
||||
}, fabricPrivacy::logException)
|
||||
|
||||
Thread { loadAll("onResume") }.start()
|
||||
}
|
||||
|
@ -188,6 +176,7 @@ class OverviewPlugin @Inject constructor(
|
|||
|
||||
override fun configuration(): JSONObject =
|
||||
JSONObject()
|
||||
.putInt(R.string.key_units, sp, resourceHelper)
|
||||
.putString(R.string.key_quickwizard, sp, resourceHelper)
|
||||
.putInt(R.string.key_eatingsoon_duration, sp, resourceHelper)
|
||||
.putDouble(R.string.key_eatingsoon_target, sp, resourceHelper)
|
||||
|
@ -214,6 +203,7 @@ class OverviewPlugin @Inject constructor(
|
|||
|
||||
override fun applyConfiguration(configuration: JSONObject) {
|
||||
configuration
|
||||
.storeInt(R.string.key_units, sp, resourceHelper)
|
||||
.storeString(R.string.key_quickwizard, sp, resourceHelper)
|
||||
.storeInt(R.string.key_eatingsoon_duration, sp, resourceHelper)
|
||||
.storeDouble(R.string.key_eatingsoon_target, sp, resourceHelper)
|
||||
|
@ -239,25 +229,27 @@ class OverviewPlugin @Inject constructor(
|
|||
.storeDouble(R.string.key_statuslights_bat_critical, sp, resourceHelper)
|
||||
}
|
||||
|
||||
@Volatile var runningRefresh = false
|
||||
@Volatile
|
||||
var runningRefresh = false
|
||||
override fun refreshLoop(from: String) {
|
||||
if (runningRefresh) return
|
||||
runningRefresh = true
|
||||
loadIobCobResults(from)
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.PROFILE))
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.BG))
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TIME))
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_BASAL))
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.EXTENDED_BOLUS))
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB))
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET))
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.SENSITIVITY))
|
||||
loadAsData(from)
|
||||
preparePredictions(from)
|
||||
prepareBasalData(from)
|
||||
prepareTemporaryTargetData(from)
|
||||
prepareTreatmentsData(from)
|
||||
prepareIobAutosensData(from)
|
||||
overviewData.preparePredictions(from)
|
||||
overviewData.prepareBasalData(from)
|
||||
overviewData.prepareTemporaryTargetData(from)
|
||||
overviewData.prepareTreatmentsData(from)
|
||||
overviewData.prepareIobAutosensData(from)
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH))
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB))
|
||||
aapsLogger.debug(LTag.UI, "refreshLoop finished")
|
||||
runningRefresh = false
|
||||
}
|
||||
|
@ -271,9 +263,9 @@ class OverviewPlugin @Inject constructor(
|
|||
loadTemporaryTarget(from)
|
||||
loadIobCobResults(from)
|
||||
loadAsData(from)
|
||||
prepareBasalData(from)
|
||||
prepareTemporaryTargetData(from)
|
||||
prepareTreatmentsData(from)
|
||||
overviewData.prepareBasalData(from)
|
||||
overviewData.prepareTemporaryTargetData(from)
|
||||
overviewData.prepareTreatmentsData(from)
|
||||
// prepareIobAutosensData(from)
|
||||
// preparePredictions(from)
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH))
|
||||
|
@ -281,9 +273,6 @@ class OverviewPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
private fun loadProfile(from: String) {
|
||||
overviewData.profile = profileFunction.getProfile()
|
||||
overviewData.profileName = profileFunction.getProfileName()
|
||||
overviewData.profileNameWithRemainingTime = profileFunction.getProfileNameWithRemainingTime()
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.PROFILE))
|
||||
}
|
||||
|
||||
|
@ -299,8 +288,8 @@ class OverviewPlugin @Inject constructor(
|
|||
|
||||
private fun loadTemporaryTarget(from: String) {
|
||||
val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
|
||||
if (tempTarget is ValueWrapper.Existing) overviewData.temporarytarget = tempTarget.value
|
||||
else overviewData.temporarytarget = null
|
||||
if (tempTarget is ValueWrapper.Existing) overviewData.temporaryTarget = tempTarget.value
|
||||
else overviewData.temporaryTarget = null
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET))
|
||||
}
|
||||
|
||||
|
@ -319,521 +308,11 @@ class OverviewPlugin @Inject constructor(
|
|||
private fun loadIobCobResults(from: String) {
|
||||
overviewData.bolusIob = iobCobCalculator.calculateIobFromBolus().round()
|
||||
overviewData.basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
|
||||
overviewData.cobInfo = iobCobCalculator.getCobInfo(false, "Overview COB")
|
||||
overviewData.cobInfo = iobCobCalculator.getCobInfo(true, "Overview COB")
|
||||
val lastCarbs = repository.getLastCarbsRecordWrapped().blockingGet()
|
||||
overviewData.lastCarbsTime = if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L
|
||||
|
||||
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
|
||||
private fun prepareBgData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
var maxBgValue = Double.MIN_VALUE
|
||||
overviewData.bgReadingsArray = repository.compatGetBgReadingsDataFromTime(overviewData.fromTime, overviewData.toTime, false).blockingGet()
|
||||
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||
for (bg in overviewData.bgReadingsArray) {
|
||||
if (bg.timestamp < overviewData.fromTime || bg.timestamp > overviewData.toTime) continue
|
||||
if (bg.value > maxBgValue) maxBgValue = bg.value
|
||||
bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper))
|
||||
}
|
||||
overviewData.bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
|
||||
overviewData.maxBgValue = Profile.fromMgdlToUnits(maxBgValue, profileFunction.getUnits())
|
||||
if (defaultValueHelper.determineHighLine() > maxBgValue) overviewData.maxBgValue = defaultValueHelper.determineHighLine()
|
||||
overviewData.maxBgValue = addUpperChartMargin(overviewData.maxBgValue)
|
||||
// profiler.log(LTag.UI, "prepareBgData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
private fun preparePredictions(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
val apsResult = if (config.APS) loopPlugin.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
|
||||
val predictionsAvailable = if (config.APS) loopPlugin.lastRun?.request?.hasPredictions == true else config.NSCLIENT
|
||||
val menuChartSettings = overviewMenus.setting
|
||||
// align to hours
|
||||
val calendar = Calendar.getInstance().also {
|
||||
it.timeInMillis = System.currentTimeMillis()
|
||||
it[Calendar.MILLISECOND] = 0
|
||||
it[Calendar.SECOND] = 0
|
||||
it[Calendar.MINUTE] = 0
|
||||
it.add(Calendar.HOUR, 1)
|
||||
}
|
||||
if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) {
|
||||
var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt()
|
||||
predictionHours = min(2, predictionHours)
|
||||
predictionHours = max(0, predictionHours)
|
||||
val hoursToFetch = overviewData.rangeToDisplay - predictionHours
|
||||
overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
|
||||
overviewData.fromTime = overviewData.toTime - T.hours(hoursToFetch.toLong()).msecs()
|
||||
overviewData.endTime = overviewData.toTime + T.hours(predictionHours.toLong()).msecs()
|
||||
} else {
|
||||
overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
|
||||
overviewData.fromTime = overviewData.toTime - T.hours(overviewData.rangeToDisplay.toLong()).msecs()
|
||||
overviewData.endTime = overviewData.toTime
|
||||
}
|
||||
|
||||
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||
val predictions: MutableList<GlucoseValueDataPoint>? = apsResult?.predictions
|
||||
?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper) }
|
||||
?.toMutableList()
|
||||
if (predictions != null) {
|
||||
predictions.sortWith { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) }
|
||||
for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction)
|
||||
}
|
||||
overviewData.predictionsGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
|
||||
// profiler.log(LTag.UI, "preparePredictions() $from", start)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
|
||||
private fun prepareBucketedData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
val bucketedData = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return
|
||||
if (bucketedData.isEmpty()) {
|
||||
aapsLogger.debug("No bucketed data.")
|
||||
return
|
||||
}
|
||||
val bucketedListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||
for (inMemoryGlucoseValue in bucketedData) {
|
||||
if (inMemoryGlucoseValue.timestamp < overviewData.fromTime || inMemoryGlucoseValue.timestamp > overviewData.toTime) continue
|
||||
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, resourceHelper))
|
||||
}
|
||||
overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })
|
||||
// profiler.log(LTag.UI, "prepareBucketedData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
private fun prepareBasalData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
overviewData.maxBasalValueFound = 0.0
|
||||
val baseBasalArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
val tempBasalArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
val basalLineArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
val absoluteBasalLineArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
var lastLineBasal = 0.0
|
||||
var lastAbsoluteLineBasal = -1.0
|
||||
var lastBaseBasal = 0.0
|
||||
var lastTempBasal = 0.0
|
||||
var time = overviewData.fromTime
|
||||
while (time < overviewData.toTime) {
|
||||
val profile = profileFunction.getProfile(time)
|
||||
if (profile == null) {
|
||||
time += 60 * 1000L
|
||||
continue
|
||||
}
|
||||
val basalData = iobCobCalculator.getBasalData(profile, time)
|
||||
val baseBasalValue = basalData.basal
|
||||
var absoluteLineValue = baseBasalValue
|
||||
var tempBasalValue = 0.0
|
||||
var basal = 0.0
|
||||
if (basalData.isTempBasalRunning) {
|
||||
tempBasalValue = basalData.tempBasalAbsolute
|
||||
absoluteLineValue = tempBasalValue
|
||||
if (tempBasalValue != lastTempBasal) {
|
||||
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, overviewData.basalScale))
|
||||
tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, overviewData.basalScale))
|
||||
}
|
||||
if (lastBaseBasal != 0.0) {
|
||||
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, overviewData.basalScale))
|
||||
baseBasalArray.add(ScaledDataPoint(time, 0.0, overviewData.basalScale))
|
||||
lastBaseBasal = 0.0
|
||||
}
|
||||
} else {
|
||||
if (baseBasalValue != lastBaseBasal) {
|
||||
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, overviewData.basalScale))
|
||||
baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, overviewData.basalScale))
|
||||
lastBaseBasal = baseBasalValue
|
||||
}
|
||||
if (lastTempBasal != 0.0) {
|
||||
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, overviewData.basalScale))
|
||||
tempBasalArray.add(ScaledDataPoint(time, 0.0, overviewData.basalScale))
|
||||
}
|
||||
}
|
||||
if (baseBasalValue != lastLineBasal) {
|
||||
basalLineArray.add(ScaledDataPoint(time, lastLineBasal, overviewData.basalScale))
|
||||
basalLineArray.add(ScaledDataPoint(time, baseBasalValue, overviewData.basalScale))
|
||||
}
|
||||
if (absoluteLineValue != lastAbsoluteLineBasal) {
|
||||
absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, overviewData.basalScale))
|
||||
absoluteBasalLineArray.add(ScaledDataPoint(time, basal, overviewData.basalScale))
|
||||
}
|
||||
lastAbsoluteLineBasal = absoluteLineValue
|
||||
lastLineBasal = baseBasalValue
|
||||
lastTempBasal = tempBasalValue
|
||||
overviewData.maxBasalValueFound = max(overviewData.maxBasalValueFound, max(tempBasalValue, baseBasalValue))
|
||||
time += 60 * 1000L
|
||||
}
|
||||
|
||||
// final points
|
||||
basalLineArray.add(ScaledDataPoint(overviewData.toTime, lastLineBasal, overviewData.basalScale))
|
||||
baseBasalArray.add(ScaledDataPoint(overviewData.toTime, lastBaseBasal, overviewData.basalScale))
|
||||
tempBasalArray.add(ScaledDataPoint(overviewData.toTime, lastTempBasal, overviewData.basalScale))
|
||||
absoluteBasalLineArray.add(ScaledDataPoint(overviewData.toTime, lastAbsoluteLineBasal, overviewData.basalScale))
|
||||
|
||||
// create series
|
||||
overviewData.baseBasalGraphSeries = LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = resourceHelper.gc(R.color.basebasal)
|
||||
it.thickness = 0
|
||||
}
|
||||
overviewData.tempBasalGraphSeries = LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = resourceHelper.gc(R.color.tempbasal)
|
||||
it.thickness = 0
|
||||
}
|
||||
overviewData.basalLineGraphSeries = LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
|
||||
it.setCustomPaint(Paint().also { paint ->
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
|
||||
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
|
||||
paint.color = resourceHelper.gc(R.color.basal)
|
||||
})
|
||||
}
|
||||
overviewData.absoluteBasalGraphSeries = LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
|
||||
it.setCustomPaint(Paint().also { absolutePaint ->
|
||||
absolutePaint.style = Paint.Style.STROKE
|
||||
absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
|
||||
absolutePaint.color = resourceHelper.gc(R.color.basal)
|
||||
})
|
||||
}
|
||||
// profiler.log(LTag.UI, "prepareBasalData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
private fun prepareTemporaryTargetData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
val profile = overviewData.profile ?: return
|
||||
val units = profileFunction.getUnits()
|
||||
var toTime = overviewData.toTime
|
||||
val targetsSeriesArray: MutableList<DataPoint> = ArrayList()
|
||||
var lastTarget = -1.0
|
||||
loopPlugin.lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) }
|
||||
var time = overviewData.fromTime
|
||||
while (time < toTime) {
|
||||
val tt = repository.getTemporaryTargetActiveAt(time).blockingGet()
|
||||
val value: Double = if (tt is ValueWrapper.Existing) {
|
||||
Profile.fromMgdlToUnits(tt.value.target(), units)
|
||||
} else {
|
||||
Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units)
|
||||
}
|
||||
if (lastTarget != value) {
|
||||
if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget))
|
||||
targetsSeriesArray.add(DataPoint(time.toDouble(), value))
|
||||
}
|
||||
lastTarget = value
|
||||
time += 5 * 60 * 1000L
|
||||
}
|
||||
// final point
|
||||
targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget))
|
||||
// create series
|
||||
overviewData.temporaryTargetSeries = LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
|
||||
it.isDrawBackground = false
|
||||
it.color = resourceHelper.gc(R.color.tempTargetBackground)
|
||||
it.thickness = 2
|
||||
}
|
||||
// profiler.log(LTag.UI, "prepareTemporaryTargetData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
private fun prepareTreatmentsData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
overviewData.maxTreatmentsValue = 0.0
|
||||
val filteredTreatments: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||
repository.getBolusesIncludingInvalidFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet()
|
||||
.map { BolusDataPoint(it, resourceHelper, activePlugin, defaultValueHelper) }
|
||||
.filter { it.data.type != Bolus.Type.SMB || it.data.isValid }
|
||||
.forEach {
|
||||
it.y = getNearestBg(it.x.toLong())
|
||||
filteredTreatments.add(it)
|
||||
}
|
||||
repository.getCarbsIncludingInvalidFromTimeToTimeExpanded(overviewData.fromTime, overviewData.endTime, true).blockingGet()
|
||||
.map { CarbsDataPoint(it, resourceHelper) }
|
||||
.forEach {
|
||||
it.y = getNearestBg(it.x.toLong())
|
||||
filteredTreatments.add(it)
|
||||
}
|
||||
|
||||
// ProfileSwitch
|
||||
repository.getEffectiveProfileSwitchDataFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet()
|
||||
.map { EffectiveProfileSwitchDataPoint(it) }
|
||||
.forEach(filteredTreatments::add)
|
||||
|
||||
// Extended bolus
|
||||
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
|
||||
repository.getExtendedBolusDataFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet()
|
||||
.map { ExtendedBolusDataPoint(it) }
|
||||
.filter { it.duration != 0L }
|
||||
.forEach {
|
||||
it.y = getNearestBg(it.x.toLong())
|
||||
filteredTreatments.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
// Careportal
|
||||
repository.compatGetTherapyEventDataFromToTime(overviewData.fromTime - T.hours(6).msecs(), overviewData.endTime).blockingGet()
|
||||
.map { TherapyEventDataPoint(it, resourceHelper, profileFunction, translator) }
|
||||
.filterTimeframe(overviewData.fromTime, overviewData.endTime)
|
||||
.forEach {
|
||||
if (it.y == 0.0) it.y = getNearestBg(it.x.toLong())
|
||||
filteredTreatments.add(it)
|
||||
}
|
||||
|
||||
// increase maxY if a treatment forces it's own height that's higher than a BG value
|
||||
filteredTreatments.map { it.y }
|
||||
.maxOrNull()
|
||||
?.let(::addUpperChartMargin)
|
||||
?.let { overviewData.maxTreatmentsValue = maxOf(overviewData.maxTreatmentsValue, it) }
|
||||
|
||||
overviewData.treatmentsSeries = PointsWithLabelGraphSeries(filteredTreatments.toTypedArray())
|
||||
// profiler.log(LTag.UI, "prepareTreatmentsData() $from", start)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Synchronized
|
||||
private fun prepareIobAutosensData(from: String) {
|
||||
// val start = dateUtil.now()
|
||||
val iobArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
val absIobArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
overviewData.maxIobValueFound = Double.MIN_VALUE
|
||||
var lastIob = 0.0
|
||||
var absLastIob = 0.0
|
||||
var time = overviewData.fromTime
|
||||
|
||||
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||
val cobArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
overviewData.maxCobValueFound = Double.MIN_VALUE
|
||||
var lastCob = 0
|
||||
|
||||
val actArrayHist: MutableList<ScaledDataPoint> = ArrayList()
|
||||
val actArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
|
||||
val now = dateUtil.now().toDouble()
|
||||
overviewData.maxIAValue = 0.0
|
||||
|
||||
val bgiArrayHist: MutableList<ScaledDataPoint> = ArrayList()
|
||||
val bgiArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
|
||||
overviewData.maxBGIValue = Double.MIN_VALUE
|
||||
|
||||
val devArray: MutableList<DeviationDataPoint> = ArrayList()
|
||||
overviewData.maxDevValueFound = Double.MIN_VALUE
|
||||
|
||||
val ratioArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
overviewData.maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
|
||||
overviewData.minRatioValueFound = -5.0
|
||||
|
||||
val dsMaxArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
val dsMinArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||
overviewData.maxFromMaxValueFound = Double.MIN_VALUE
|
||||
overviewData.maxFromMinValueFound = Double.MIN_VALUE
|
||||
|
||||
val adsData = iobCobCalculator.ads.clone()
|
||||
|
||||
while (time <= overviewData.toTime) {
|
||||
val profile = profileFunction.getProfile(time)
|
||||
if (profile == null) {
|
||||
time += 5 * 60 * 1000L
|
||||
continue
|
||||
}
|
||||
// IOB
|
||||
val iob = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
|
||||
val baseBasalIob = iobCobCalculator.calculateAbsoluteIobFromBaseBasals(time)
|
||||
val absIob = IobTotal.combine(iob, baseBasalIob)
|
||||
val autosensData = adsData.getAutosensDataAtTime(time)
|
||||
if (abs(lastIob - iob.iob) > 0.02) {
|
||||
if (abs(lastIob - iob.iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, overviewData.iobScale))
|
||||
iobArray.add(ScaledDataPoint(time, iob.iob, overviewData.iobScale))
|
||||
overviewData.maxIobValueFound = maxOf(overviewData.maxIobValueFound, abs(iob.iob))
|
||||
lastIob = iob.iob
|
||||
}
|
||||
if (abs(absLastIob - absIob.iob) > 0.02) {
|
||||
if (abs(absLastIob - absIob.iob) > 0.2) absIobArray.add(ScaledDataPoint(time, absLastIob, overviewData.iobScale))
|
||||
absIobArray.add(ScaledDataPoint(time, absIob.iob, overviewData.iobScale))
|
||||
overviewData.maxIobValueFound = maxOf(overviewData.maxIobValueFound, abs(absIob.iob))
|
||||
absLastIob = absIob.iob
|
||||
}
|
||||
|
||||
// COB
|
||||
if (autosensData != null) {
|
||||
val cob = autosensData.cob.toInt()
|
||||
if (cob != lastCob) {
|
||||
if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), overviewData.cobScale))
|
||||
cobArray.add(ScaledDataPoint(time, cob.toDouble(), overviewData.cobScale))
|
||||
overviewData.maxCobValueFound = max(overviewData.maxCobValueFound, cob.toDouble())
|
||||
lastCob = cob
|
||||
}
|
||||
if (autosensData.failoverToMinAbsorbtionRate) {
|
||||
autosensData.setScale(overviewData.cobScale)
|
||||
autosensData.setChartTime(time)
|
||||
minFailOverActiveList.add(autosensData)
|
||||
}
|
||||
}
|
||||
|
||||
// ACTIVITY
|
||||
if (time <= now) actArrayHist.add(ScaledDataPoint(time, iob.activity, overviewData.actScale))
|
||||
else actArrayPrediction.add(ScaledDataPoint(time, iob.activity, overviewData.actScale))
|
||||
overviewData.maxIAValue = max(overviewData.maxIAValue, abs(iob.activity))
|
||||
|
||||
// BGI
|
||||
val devBgiScale = overviewMenus.isEnabledIn(OverviewMenus.CharType.DEV) == overviewMenus.isEnabledIn(OverviewMenus.CharType.BGI)
|
||||
val deviation = if (devBgiScale) autosensData?.deviation ?: 0.0 else 0.0
|
||||
val bgi: Double = iob.activity * profile.getIsfMgdl(time) * 5.0
|
||||
if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, overviewData.bgiScale))
|
||||
else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, overviewData.bgiScale))
|
||||
overviewData.maxBGIValue = max(overviewData.maxBGIValue, max(abs(bgi), deviation))
|
||||
|
||||
// DEVIATIONS
|
||||
if (autosensData != null) {
|
||||
var color = resourceHelper.gc(R.color.deviationblack) // "="
|
||||
if (autosensData.type == "" || autosensData.type == "non-meal") {
|
||||
if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey)
|
||||
if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen)
|
||||
if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred)
|
||||
} else if (autosensData.type == "uam") {
|
||||
color = resourceHelper.gc(R.color.uam)
|
||||
} else if (autosensData.type == "csf") {
|
||||
color = resourceHelper.gc(R.color.deviationgrey)
|
||||
}
|
||||
devArray.add(DeviationDataPoint(time.toDouble(), autosensData.deviation, color, overviewData.devScale))
|
||||
overviewData.maxDevValueFound = maxOf(overviewData.maxDevValueFound, abs(autosensData.deviation), abs(bgi))
|
||||
}
|
||||
|
||||
// RATIO
|
||||
if (autosensData != null) {
|
||||
ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1), overviewData.ratioScale))
|
||||
overviewData.maxRatioValueFound = max(overviewData.maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
|
||||
overviewData.minRatioValueFound = min(overviewData.minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
|
||||
}
|
||||
|
||||
// DEV SLOPE
|
||||
if (autosensData != null) {
|
||||
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, overviewData.dsMaxScale))
|
||||
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, overviewData.dsMinScale))
|
||||
overviewData.maxFromMaxValueFound = max(overviewData.maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
|
||||
overviewData.maxFromMinValueFound = max(overviewData.maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation))
|
||||
}
|
||||
|
||||
time += 5 * 60 * 1000L
|
||||
}
|
||||
// IOB
|
||||
overviewData.iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
|
||||
it.color = resourceHelper.gc(R.color.iob)
|
||||
it.thickness = 3
|
||||
}
|
||||
overviewData.absIobSeries = FixedLineGraphSeries(Array(absIobArray.size) { i -> absIobArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
|
||||
it.color = resourceHelper.gc(R.color.iob)
|
||||
it.thickness = 3
|
||||
}
|
||||
|
||||
if (overviewMenus.setting[0][OverviewMenus.CharType.PRE.ordinal]) {
|
||||
val autosensData = adsData.getLastAutosensData("GraphData", aapsLogger, dateUtil)
|
||||
val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult()
|
||||
val isTempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
|
||||
val iobPrediction: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||
val iobPredictionArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
|
||||
for (i in iobPredictionArray) {
|
||||
iobPrediction.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)))
|
||||
overviewData.maxIobValueFound = max(overviewData.maxIobValueFound, abs(i.iob))
|
||||
}
|
||||
overviewData.iobPredictions1Series = PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] })
|
||||
val iobPrediction2: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||
val iobPredictionArray2 = iobCobCalculator.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
|
||||
for (i in iobPredictionArray2) {
|
||||
iobPrediction2.add(i.setColor(resourceHelper.gc(R.color.iobPred)))
|
||||
overviewData.maxIobValueFound = max(overviewData.maxIobValueFound, abs(i.iob))
|
||||
}
|
||||
overviewData.iobPredictions2Series = PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] })
|
||||
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray))
|
||||
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray2))
|
||||
} else {
|
||||
overviewData.iobPredictions1Series = PointsWithLabelGraphSeries()
|
||||
overviewData.iobPredictions2Series = PointsWithLabelGraphSeries()
|
||||
}
|
||||
|
||||
// COB
|
||||
overviewData.cobSeries = FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
|
||||
it.isDrawBackground = true
|
||||
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50%
|
||||
it.color = resourceHelper.gc(R.color.cob)
|
||||
it.thickness = 3
|
||||
}
|
||||
overviewData.cobMinFailOverSeries = PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })
|
||||
|
||||
// ACTIVITY
|
||||
overviewData.activitySeries = FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
|
||||
it.isDrawBackground = false
|
||||
it.color = resourceHelper.gc(R.color.activity)
|
||||
it.thickness = 3
|
||||
}
|
||||
overviewData.activityPredictionSeries = FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[i] }).also {
|
||||
it.setCustomPaint(Paint().also { paint ->
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.strokeWidth = 3f
|
||||
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
|
||||
paint.color = resourceHelper.gc(R.color.activity)
|
||||
})
|
||||
}
|
||||
|
||||
// BGI
|
||||
overviewData.minusBgiSeries = FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
|
||||
it.isDrawBackground = false
|
||||
it.color = resourceHelper.gc(R.color.bgi)
|
||||
it.thickness = 3
|
||||
}
|
||||
overviewData.minusBgiHistSeries = FixedLineGraphSeries(Array(bgiArrayPrediction.size) { i -> bgiArrayPrediction[i] }).also {
|
||||
it.setCustomPaint(Paint().also { paint ->
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.strokeWidth = 3f
|
||||
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
|
||||
paint.color = resourceHelper.gc(R.color.bgi)
|
||||
})
|
||||
}
|
||||
|
||||
// DEVIATIONS
|
||||
overviewData.deviationsSeries = BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
|
||||
it.setValueDependentColor { data: DeviationDataPoint -> data.color }
|
||||
}
|
||||
|
||||
// RATIO
|
||||
overviewData.ratioSeries = LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
|
||||
it.color = resourceHelper.gc(R.color.ratio)
|
||||
it.thickness = 3
|
||||
}
|
||||
|
||||
// DEV SLOPE
|
||||
overviewData.dsMaxSeries = LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
|
||||
it.color = resourceHelper.gc(R.color.devslopepos)
|
||||
it.thickness = 3
|
||||
}
|
||||
overviewData.dsMinSeries = LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
|
||||
it.color = resourceHelper.gc(R.color.devslopeneg)
|
||||
it.thickness = 3
|
||||
}
|
||||
|
||||
// profiler.log(LTag.UI, "prepareIobAutosensData() $from", start)
|
||||
}
|
||||
|
||||
private fun addUpperChartMargin(maxBgValue: Double) =
|
||||
if (profileFunction.getUnits() == GlucoseUnit.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4
|
||||
|
||||
private fun getNearestBg(date: Long): Double {
|
||||
overviewData.bgReadingsArray.let { bgReadingsArray ->
|
||||
for (reading in bgReadingsArray) {
|
||||
if (reading.timestamp > date) continue
|
||||
return Profile.fromMgdlToUnits(reading.value, profileFunction.getUnits())
|
||||
}
|
||||
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, profileFunction.getUnits())
|
||||
else Profile.fromMgdlToUnits(100.0, profileFunction.getUnits())
|
||||
}
|
||||
}
|
||||
|
||||
private fun <E : DataPointWithLabelInterface> List<E>.filterTimeframe(fromTime: Long, endTime: Long): List<E> =
|
||||
filter { it.x + it.duration >= fromTime && it.x <= endTime }
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ class StatusLightHandler @Inject constructor(
|
|||
private fun handleAge(view: TextView?, type: TherapyEvent.Type, @StringRes warnSettings: Int, defaultWarnThreshold: Double, @StringRes urgentSettings: Int, defaultUrgentThreshold: Double) {
|
||||
val warn = sp.getDouble(warnSettings, defaultWarnThreshold)
|
||||
val urgent = sp.getDouble(urgentSettings, defaultUrgentThreshold)
|
||||
val therapyEvent = repository.getLastTherapyRecord(type).blockingGet()
|
||||
val therapyEvent = repository.getLastTherapyRecordUpToNow(type).blockingGet()
|
||||
if (therapyEvent is ValueWrapper.Existing) {
|
||||
warnColors.setColorByAge(view, therapyEvent.value, warn, urgent)
|
||||
view?.text = therapyEvent.value.age(resourceHelper.shortTextMode(), resourceHelper, dateUtil)
|
||||
|
|
|
@ -64,12 +64,12 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() {
|
|||
val manager = fragmentManager
|
||||
val editQuickWizardDialog = EditQuickWizardDialog()
|
||||
val bundle = Bundle()
|
||||
bundle.putInt("position", adapterPosition)
|
||||
bundle.putInt("position", bindingAdapterPosition)
|
||||
editQuickWizardDialog.arguments = bundle
|
||||
editQuickWizardDialog.show(manager, "EditQuickWizardDialog")
|
||||
}
|
||||
removeButton.setOnClickListener {
|
||||
quickWizard.remove(adapterPosition)
|
||||
quickWizard.remove(bindingAdapterPosition)
|
||||
rxBus.send(EventQuickWizardChange())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,16 +26,16 @@ import kotlin.math.max
|
|||
|
||||
class GraphData(
|
||||
injector: HasAndroidInjector,
|
||||
private val graph: GraphView
|
||||
private val graph: GraphView,
|
||||
private val overviewData: OverviewData
|
||||
) {
|
||||
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
||||
@Inject lateinit var overviewData: OverviewData
|
||||
|
||||
var maxY = Double.MIN_VALUE
|
||||
private var maxY = Double.MIN_VALUE
|
||||
private var minY = Double.MAX_VALUE
|
||||
private val units: GlucoseUnit
|
||||
private val series: MutableList<Series<*>> = ArrayList()
|
||||
|
@ -76,7 +76,7 @@ class GraphData(
|
|||
addSeries(overviewData.tempBasalGraphSeries)
|
||||
addSeries(overviewData.basalLineGraphSeries)
|
||||
addSeries(overviewData.absoluteBasalGraphSeries)
|
||||
overviewData.basalScale.setMultiplier(maxY * scale / overviewData.maxBasalValueFound)
|
||||
overviewData.basalScale.multiplier = maxY * scale / overviewData.maxBasalValueFound
|
||||
}
|
||||
|
||||
fun addTargetLine() {
|
||||
|
@ -91,7 +91,7 @@ class GraphData(
|
|||
fun addActivity(scale: Double) {
|
||||
addSeries(overviewData.activitySeries)
|
||||
addSeries(overviewData.activityPredictionSeries)
|
||||
overviewData.actScale.setMultiplier(maxY * scale / overviewData.maxIAValue)
|
||||
overviewData.actScale.multiplier = maxY * scale / overviewData.maxIAValue
|
||||
}
|
||||
|
||||
//Function below show -BGI to be able to compare curves with deviations
|
||||
|
@ -100,7 +100,7 @@ class GraphData(
|
|||
maxY = overviewData.maxBGIValue
|
||||
minY = -overviewData.maxBGIValue
|
||||
}
|
||||
overviewData.bgiScale.setMultiplier(maxY * scale / overviewData.maxBGIValue)
|
||||
overviewData.bgiScale.multiplier = maxY * scale / overviewData.maxBGIValue
|
||||
addSeries(overviewData.minusBgiSeries)
|
||||
addSeries(overviewData.minusBgiHistSeries)
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ class GraphData(
|
|||
maxY = overviewData.maxIobValueFound
|
||||
minY = -overviewData.maxIobValueFound
|
||||
}
|
||||
overviewData.iobScale.setMultiplier(maxY * scale / overviewData.maxIobValueFound)
|
||||
overviewData.iobScale.multiplier = maxY * scale / overviewData.maxIobValueFound
|
||||
addSeries(overviewData.iobSeries)
|
||||
addSeries(overviewData.iobPredictions1Series)
|
||||
addSeries(overviewData.iobPredictions2Series)
|
||||
|
@ -123,7 +123,7 @@ class GraphData(
|
|||
maxY = overviewData.maxIobValueFound
|
||||
minY = -overviewData.maxIobValueFound
|
||||
}
|
||||
overviewData.iobScale.setMultiplier(maxY * scale / overviewData.maxIobValueFound)
|
||||
overviewData.iobScale.multiplier = maxY * scale / overviewData.maxIobValueFound
|
||||
addSeries(overviewData.absIobSeries)
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,7 @@ class GraphData(
|
|||
maxY = overviewData.maxCobValueFound
|
||||
minY = 0.0
|
||||
}
|
||||
overviewData.cobScale.setMultiplier(maxY * scale / overviewData.maxCobValueFound)
|
||||
overviewData.cobScale.multiplier = maxY * scale / overviewData.maxCobValueFound
|
||||
addSeries(overviewData.cobSeries)
|
||||
addSeries(overviewData.cobMinFailOverSeries)
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ class GraphData(
|
|||
maxY = overviewData.maxDevValueFound
|
||||
minY = -maxY
|
||||
}
|
||||
overviewData.devScale.setMultiplier(maxY * scale / overviewData.maxDevValueFound)
|
||||
overviewData.devScale.multiplier = maxY * scale / overviewData.maxDevValueFound
|
||||
addSeries(overviewData.deviationsSeries)
|
||||
}
|
||||
|
||||
|
@ -153,20 +153,32 @@ class GraphData(
|
|||
if (useForScale) {
|
||||
maxY = 100.0 + max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))
|
||||
minY = 100.0 - max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))
|
||||
overviewData.ratioScale.setMultiplier(1.0)
|
||||
} else
|
||||
overviewData.ratioScale.setMultiplier(maxY * scale / max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound)))
|
||||
overviewData.ratioScale.multiplier = 1.0
|
||||
overviewData.ratioScale.shift = 100.0
|
||||
} else {
|
||||
overviewData.ratioScale.multiplier = maxY * scale / max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))
|
||||
overviewData.ratioScale.shift = 0.0
|
||||
}
|
||||
addSeries(overviewData.ratioSeries)
|
||||
}
|
||||
|
||||
// scale in % of vertical size (like 0.3)
|
||||
fun addDeviationSlope(useForScale: Boolean, scale: Double) {
|
||||
fun addDeviationSlope(useForScale: Boolean, scale: Double, isRatioScale: Boolean = false) {
|
||||
if (useForScale) {
|
||||
maxY = max(overviewData.maxFromMaxValueFound, overviewData.maxFromMinValueFound)
|
||||
minY = -maxY
|
||||
}
|
||||
overviewData.dsMaxScale.setMultiplier(maxY * scale / overviewData.maxFromMaxValueFound)
|
||||
overviewData.dsMinScale.setMultiplier(maxY * scale / overviewData.maxFromMinValueFound)
|
||||
var graphMaxY = maxY
|
||||
if (isRatioScale) {
|
||||
graphMaxY = maxY - 100.0
|
||||
overviewData.dsMinScale.shift = 100.0
|
||||
overviewData.dsMaxScale.shift = 100.0
|
||||
} else {
|
||||
overviewData.dsMinScale.shift = 0.0
|
||||
overviewData.dsMaxScale.shift = 0.0
|
||||
}
|
||||
overviewData.dsMaxScale.multiplier = graphMaxY * scale / overviewData.maxFromMaxValueFound
|
||||
overviewData.dsMinScale.multiplier = graphMaxY * scale / overviewData.maxFromMinValueFound
|
||||
addSeries(overviewData.dsMaxSeries)
|
||||
addSeries(overviewData.dsMinSeries)
|
||||
}
|
||||
|
@ -201,7 +213,7 @@ class GraphData(
|
|||
graph.gridLabelRenderer.numHorizontalLabels = 7 // only 7 because of the space
|
||||
}
|
||||
|
||||
internal fun addSeries(s: Series<*>) = series.add(s)
|
||||
private fun addSeries(s: Series<*>) = series.add(s)
|
||||
|
||||
fun performUpdate() {
|
||||
// clear old data
|
||||
|
|
|
@ -3,10 +3,8 @@ package info.nightscout.androidaps.plugins.general.overview.graphExtensions
|
|||
import android.graphics.Color
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.core.R
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.database.entities.TherapyEvent
|
||||
import info.nightscout.androidaps.interfaces.GlucoseUnit
|
||||
import info.nightscout.androidaps.interfaces.Interval
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.utils.Translator
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
|
@ -17,7 +15,7 @@ class TherapyEventDataPoint @Inject constructor(
|
|||
private val resourceHelper: ResourceHelper,
|
||||
private val profileFunction: ProfileFunction,
|
||||
private val translator: Translator
|
||||
) : DataPointWithLabelInterface, Interval {
|
||||
) : DataPointWithLabelInterface {
|
||||
|
||||
private var yValue = 0.0
|
||||
|
||||
|
@ -49,10 +47,10 @@ class TherapyEventDataPoint @Inject constructor(
|
|||
}
|
||||
|
||||
override fun getLabel(): String? =
|
||||
if (data.note != null) data.note
|
||||
if (data.note.isNullOrBlank().not()) data.note
|
||||
else translator.translate(data.type)
|
||||
|
||||
override fun getDuration(): Long = end() - start()
|
||||
override fun getDuration(): Long = data.duration
|
||||
override fun getShape(): PointsWithLabelGraphSeries.Shape =
|
||||
when {
|
||||
data.type == TherapyEvent.Type.NS_MBG -> PointsWithLabelGraphSeries.Shape.MBG
|
||||
|
@ -74,22 +72,4 @@ class TherapyEventDataPoint @Inject constructor(
|
|||
TherapyEvent.Type.APS_OFFLINE -> Color.GRAY and -0x7f000001
|
||||
else -> Color.GRAY
|
||||
}
|
||||
|
||||
// Interval interface
|
||||
private var cutEnd: Long? = null
|
||||
|
||||
override fun durationInMsec(): Long = data.duration
|
||||
override fun start(): Long = data.timestamp
|
||||
override fun originalEnd(): Long = data.timestamp + durationInMsec()
|
||||
override fun end(): Long = cutEnd ?: originalEnd()
|
||||
override fun cutEndTo(end: Long) {
|
||||
cutEnd = end
|
||||
}
|
||||
|
||||
override fun match(time: Long): Boolean = start() <= time && end() >= time
|
||||
override fun before(time: Long): Boolean = end() < time
|
||||
override fun after(time: Long): Boolean = start() > time
|
||||
override val isInProgress: Boolean get() = match(System.currentTimeMillis())
|
||||
override val isEndingEvent: Boolean get() = durationInMsec() == 0L
|
||||
override val isValid: Boolean get() = data.type == TherapyEvent.Type.APS_OFFLINE
|
||||
}
|
|
@ -128,7 +128,7 @@ class NotificationStore @Inject constructor(
|
|||
private fun deleteIntent(id: Int): PendingIntent {
|
||||
val intent = Intent(context, DismissNotificationService::class.java)
|
||||
intent.putExtra("alertID", id)
|
||||
return PendingIntent.getService(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
return PendingIntent.getService(context, id, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
fun createNotificationChannel() {
|
||||
|
|
|
@ -171,7 +171,7 @@ class PersistentNotificationPlugin @Inject constructor(
|
|||
val msgReadPendingIntent = PendingIntent.getBroadcast(context,
|
||||
notificationHolder.notificationID,
|
||||
msgReadIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val msgReplyIntent = Intent()
|
||||
.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
|
||||
.setAction(REPLY_ACTION)
|
||||
|
@ -181,7 +181,7 @@ class PersistentNotificationPlugin @Inject constructor(
|
|||
context,
|
||||
notificationHolder.notificationID,
|
||||
msgReplyIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
// Build a RemoteInput for receiving voice input from devices
|
||||
val remoteInput = RemoteInput.Builder(EXTRA_VOICE_REPLY).build()
|
||||
// Create the UnreadConversation
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package info.nightscout.androidaps.plugins.general.smsCommunicator
|
||||
|
||||
import android.os.SystemClock
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.CommandQueueProvider
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePasswordValidationResult
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -23,6 +26,7 @@ class AuthRequest internal constructor(
|
|||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var otp: OneTimePassword
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
@Inject lateinit var commandQueue: CommandQueueProvider
|
||||
|
||||
private var date = 0L
|
||||
private var processed = false
|
||||
|
@ -49,6 +53,19 @@ class AuthRequest internal constructor(
|
|||
}
|
||||
if (dateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) {
|
||||
processed = true
|
||||
if (action.pumpCommand) {
|
||||
val start = dateUtil.now()
|
||||
//wait for empty queue
|
||||
while (start + T.mins(3).msecs() > dateUtil.now()) {
|
||||
if (commandQueue.size() == 0) break
|
||||
SystemClock.sleep(100)
|
||||
}
|
||||
if (commandQueue.size() != 0) {
|
||||
aapsLogger.debug(LTag.SMS, "Command timed out: " + requester.text)
|
||||
smsCommunicatorPlugin.sendSMS(Sms(requester.phoneNumber, resourceHelper.gs(R.string.sms_timeout_while_wating)))
|
||||
return
|
||||
}
|
||||
}
|
||||
aapsLogger.debug(LTag.SMS, "Processing confirmed SMS: " + requester.text)
|
||||
action.run()
|
||||
return
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
package info.nightscout.androidaps.plugins.general.smsCommunicator
|
||||
|
||||
abstract class SmsAction : Runnable {
|
||||
abstract class SmsAction(val pumpCommand: Boolean) : Runnable {
|
||||
|
||||
var aDouble: Double? = null
|
||||
var anInteger: Int? = null
|
||||
var secondInteger: Int? = null
|
||||
var secondLong: Long? = null
|
||||
var aString: String? = null
|
||||
|
||||
internal constructor()
|
||||
internal constructor(aDouble: Double) {
|
||||
internal constructor(pumpCommand: Boolean, aDouble: Double) : this(pumpCommand) {
|
||||
this.aDouble = aDouble
|
||||
}
|
||||
|
||||
internal constructor(aDouble: Double, secondInteger: Int) {
|
||||
internal constructor(pumpCommand: Boolean, aDouble: Double, secondInteger: Int) : this(pumpCommand) {
|
||||
this.aDouble = aDouble
|
||||
this.secondInteger = secondInteger
|
||||
}
|
||||
|
||||
internal constructor(aString: String, secondInteger: Int) {
|
||||
internal constructor(pumpCommand: Boolean, aString: String, secondInteger: Int) : this(pumpCommand) {
|
||||
this.aString = aString
|
||||
this.secondInteger = secondInteger
|
||||
}
|
||||
|
||||
internal constructor(anInteger: Int) {
|
||||
internal constructor(pumpCommand: Boolean, anInteger: Int) : this(pumpCommand) {
|
||||
this.anInteger = anInteger
|
||||
}
|
||||
|
||||
internal constructor(anInteger: Int, secondInteger: Int) {
|
||||
internal constructor(pumpCommand: Boolean, anInteger: Int, secondInteger: Int) : this(pumpCommand) {
|
||||
this.anInteger = anInteger
|
||||
this.secondInteger = secondInteger
|
||||
}
|
||||
|
||||
internal constructor(anInteger: Int, secondLong: Long) {
|
||||
internal constructor(pumpCommand: Boolean, anInteger: Int, secondLong: Long) : this(pumpCommand) {
|
||||
this.anInteger = anInteger
|
||||
this.secondLong = secondLong
|
||||
}
|
||||
|
|
|
@ -11,25 +11,27 @@ import androidx.work.Worker
|
|||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.annotations.OpenForTesting
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||
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.OfflineEvent
|
||||
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.CancelCurrentOfflineEventIfAnyTransaction
|
||||
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
|
||||
import info.nightscout.androidaps.events.EventPreferenceChange
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview
|
||||
import info.nightscout.androidaps.extensions.valueToUnitsString
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
|
||||
|
@ -41,7 +43,6 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProv
|
|||
import info.nightscout.androidaps.queue.Callback
|
||||
import info.nightscout.androidaps.receivers.DataWorker
|
||||
import info.nightscout.androidaps.utils.*
|
||||
import info.nightscout.androidaps.extensions.valueToUnitsString
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
|
@ -59,11 +60,13 @@ import javax.inject.Singleton
|
|||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@OpenForTesting
|
||||
@Singleton
|
||||
class SmsCommunicatorPlugin @Inject constructor(
|
||||
injector: HasAndroidInjector,
|
||||
aapsLogger: AAPSLogger,
|
||||
resourceHelper: ResourceHelper,
|
||||
private val smsManager: SmsManager,
|
||||
private val aapsSchedulers: AapsSchedulers,
|
||||
private val sp: SP,
|
||||
private val constraintChecker: ConstraintChecker,
|
||||
|
@ -72,7 +75,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
private val fabricPrivacy: FabricPrivacy,
|
||||
private val activePlugin: ActivePlugin,
|
||||
private val commandQueue: CommandQueueProvider,
|
||||
private val loopPlugin: LoopPlugin,
|
||||
private val loop: Loop,
|
||||
private val iobCobCalculator: IobCobCalculator,
|
||||
private val xdripCalibrations: XdripCalibrations,
|
||||
private var otp: OneTimePassword,
|
||||
|
@ -94,8 +97,8 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
|
||||
private val disposable = CompositeDisposable()
|
||||
var allowedNumbers: MutableList<String> = ArrayList()
|
||||
var messageToConfirm: AuthRequest? = null
|
||||
var lastRemoteBolusTime: Long = 0
|
||||
@Volatile var messageToConfirm: AuthRequest? = null
|
||||
@Volatile var lastRemoteBolusTime: Long = 0
|
||||
var messages = ArrayList<Sms>()
|
||||
|
||||
val commands = mapOf(
|
||||
|
@ -181,7 +184,6 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
override fun doWork(): Result {
|
||||
val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1))
|
||||
?: return Result.failure(workDataOf("Error" to "missing input data"))
|
||||
|
@ -224,7 +226,6 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
return false
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
fun processSms(receivedSms: Sms) {
|
||||
if (!isEnabled(PluginType.GENERAL)) {
|
||||
aapsLogger.debug(LTag.SMS, "Ignoring SMS. Plugin disabled.")
|
||||
|
@ -278,6 +279,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat)))
|
||||
"BOLUS" ->
|
||||
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed)))
|
||||
else if (commandQueue.bolusInQueue()) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_another_bolus_in_queue)))
|
||||
else if (divided.size == 2 && dateUtil.now() - lastRemoteBolusTime < minDistance) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotebolusnotallowed)))
|
||||
else if (divided.size == 2 && pump.isSuspended()) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.pumpsuspended)))
|
||||
else if (divided.size == 2 || divided.size == 3) processBOLUS(divided, receivedSms)
|
||||
|
@ -303,9 +305,13 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat)))
|
||||
else ->
|
||||
if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) {
|
||||
messageToConfirm?.action(divided[0])
|
||||
val execute = messageToConfirm
|
||||
messageToConfirm = null
|
||||
} else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_unknowncommand)))
|
||||
execute?.action(divided[0])
|
||||
} else {
|
||||
messageToConfirm = null
|
||||
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_unknowncommand)))
|
||||
}
|
||||
}
|
||||
}
|
||||
rxBus.send(EventSmsCommunicatorUpdateGui())
|
||||
|
@ -336,18 +342,17 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
receivedSms.processed = true
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun processLOOP(divided: Array<String>, receivedSms: Sms) {
|
||||
when (divided[1].uppercase(Locale.getDefault())) {
|
||||
"DISABLE", "STOP" -> {
|
||||
if (loopPlugin.isEnabled(PluginType.LOOP)) {
|
||||
if (loop.enabled) {
|
||||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopdisablereplywithcode), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
|
||||
override fun run() {
|
||||
uel.log(Action.LOOP_DISABLED, Sources.SMS)
|
||||
loopPlugin.setPluginEnabled(PluginType.LOOP, false)
|
||||
loop.enabled = false
|
||||
commandQueue.cancelTempBasal(true, object : Callback() {
|
||||
override fun run() {
|
||||
rxBus.send(EventRefreshOverview("SMS_LOOP_STOP"))
|
||||
|
@ -364,14 +369,14 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
"ENABLE", "START" -> {
|
||||
if (!loopPlugin.isEnabled(PluginType.LOOP)) {
|
||||
if (!loop.enabled) {
|
||||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopenablereplywithcode), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
|
||||
override fun run() {
|
||||
uel.log(Action.LOOP_ENABLED, Sources.SMS)
|
||||
loopPlugin.setPluginEnabled(PluginType.LOOP, true)
|
||||
loop.enabled = true
|
||||
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loophasbeenenabled)))
|
||||
rxBus.send(EventRefreshOverview("SMS_LOOP_START"))
|
||||
}
|
||||
|
@ -382,8 +387,8 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
"STATUS" -> {
|
||||
val reply = if (loopPlugin.isEnabled(PluginType.LOOP)) {
|
||||
if (loopPlugin.isSuspended) String.format(resourceHelper.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend())
|
||||
val reply = if (loop.enabled) {
|
||||
if (loop.isSuspended) String.format(resourceHelper.gs(R.string.loopsuspendedfor), loop.minutesToEndOfSuspend())
|
||||
else resourceHelper.gs(R.string.smscommunicator_loopisenabled)
|
||||
} else
|
||||
resourceHelper.gs(R.string.loopisdisabled)
|
||||
|
@ -395,10 +400,15 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopresumereplywithcode), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
|
||||
override fun run() {
|
||||
uel.log(Action.RESUME, Sources.SMS)
|
||||
loopPlugin.suspendTo(0L)
|
||||
disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
|
||||
.subscribe({ result ->
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
|
||||
})
|
||||
rxBus.send(EventRefreshOverview("SMS_LOOP_RESUME"))
|
||||
commandQueue.cancelTempBasal(true, object : Callback() {
|
||||
override fun run() {
|
||||
|
@ -409,7 +419,6 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
})
|
||||
loopPlugin.createOfflineEvent(0)
|
||||
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loopresumed)))
|
||||
}
|
||||
})
|
||||
|
@ -428,14 +437,19 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(duration) {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, duration) {
|
||||
override fun run() {
|
||||
uel.log(Action.SUSPEND, Sources.SMS)
|
||||
commandQueue.cancelTempBasal(true, object : Callback() {
|
||||
override fun run() {
|
||||
if (result.success) {
|
||||
loopPlugin.suspendTo(dateUtil.now() + anInteger() * 60L * 1000)
|
||||
loopPlugin.createOfflineEvent(anInteger() * 60)
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(anInteger().toLong()).msecs(), OfflineEvent.Reason.SUSPEND))
|
||||
.subscribe({ result ->
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
|
||||
})
|
||||
rxBus.send(EventRefreshOverview("SMS_LOOP_SUSPENDED"))
|
||||
val replyText = resourceHelper.gs(R.string.smscommunicator_loopsuspended) + " " +
|
||||
resourceHelper.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed)
|
||||
|
@ -456,7 +470,6 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun processNSCLIENT(divided: Array<String>, receivedSms: Sms) {
|
||||
if (divided[1].uppercase(Locale.getDefault()) == "RESTART") {
|
||||
rxBus.send(EventNSClientRestart())
|
||||
|
@ -466,7 +479,6 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat)))
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun processHELP(divided: Array<String>, receivedSms: Sms) {
|
||||
when {
|
||||
divided.size == 1 -> {
|
||||
|
@ -504,7 +516,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_pumpconnectwithcode), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
|
||||
override fun run() {
|
||||
uel.log(Action.RECONNECT, Sources.SMS)
|
||||
commandQueue.cancelTempBasal(true, object : Callback() {
|
||||
|
@ -512,10 +524,14 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
if (!result.success) {
|
||||
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpconnectfail)))
|
||||
} else {
|
||||
loopPlugin.suspendTo(0L)
|
||||
disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
|
||||
.subscribe({ result ->
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
|
||||
})
|
||||
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_reconnect)))
|
||||
rxBus.send(EventRefreshOverview("SMS_PUMP_START"))
|
||||
loopPlugin.createOfflineEvent(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -533,11 +549,11 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_pumpdisconnectwithcode), duration, passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
|
||||
override fun run() {
|
||||
uel.log(Action.DISCONNECT, Sources.SMS)
|
||||
val profile = profileFunction.getProfile()
|
||||
loopPlugin.disconnectPump(duration, profile)
|
||||
val profile = profileFunction.getProfile() ?: return
|
||||
loop.goToZeroTemp(duration, profile, OfflineEvent.Reason.DISCONNECT_PUMP)
|
||||
rxBus.send(EventRefreshOverview("SMS_PUMP_DISCONNECT"))
|
||||
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpdisconnected)))
|
||||
}
|
||||
|
@ -549,7 +565,6 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun processPROFILE(divided: Array<String>, receivedSms: Sms) { // load profiles
|
||||
val anInterface = activePlugin.activeProfileSource
|
||||
val store = anInterface.profile
|
||||
|
@ -588,7 +603,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_profilereplywithcode), list[pIndex - 1], percentage, passCode)
|
||||
receivedSms.processed = true
|
||||
val finalPercentage = percentage
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(list[pIndex - 1] as String, finalPercentage) {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, list[pIndex - 1] as String, finalPercentage) {
|
||||
override fun run() {
|
||||
profileFunction.createProfileSwitch(store, list[pIndex - 1] as String, 0, finalPercentage, 0, dateUtil.now())
|
||||
val replyText = resourceHelper.gs(R.string.profileswitchcreated)
|
||||
|
@ -603,13 +618,12 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
receivedSms.processed = true
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun processBASAL(divided: Array<String>, receivedSms: Sms) {
|
||||
if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") {
|
||||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalstopreplywithcode), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
|
||||
override fun run() {
|
||||
commandQueue.cancelTempBasal(true, object : Callback() {
|
||||
override fun run() {
|
||||
|
@ -644,7 +658,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasalPct, duration) {
|
||||
override fun run() {
|
||||
commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() {
|
||||
override fun run() {
|
||||
|
@ -688,7 +702,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasal, duration) {
|
||||
override fun run() {
|
||||
commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() {
|
||||
override fun run() {
|
||||
|
@ -720,13 +734,12 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun processEXTENDED(divided: Array<String>, receivedSms: Sms) {
|
||||
if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") {
|
||||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
|
||||
override fun run() {
|
||||
commandQueue.cancelExtended(object : Callback() {
|
||||
override fun run() {
|
||||
|
@ -756,7 +769,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(extended, duration) {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, extended, duration) {
|
||||
override fun run() {
|
||||
commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() {
|
||||
override fun run() {
|
||||
|
@ -802,7 +815,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
else
|
||||
String.format(resourceHelper.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(bolus) {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, bolus) {
|
||||
override fun run() {
|
||||
val detailedBolusInfo = DetailedBolusInfo()
|
||||
detailedBolusInfo.insulin = aDouble()
|
||||
|
@ -832,7 +845,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
currentProfile.units == GlucoseUnit.MMOL -> Constants.defaultEatingSoonTTmmol
|
||||
else -> Constants.defaultEatingSoonTTmgdl
|
||||
}
|
||||
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = dateUtil.now(),
|
||||
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
|
||||
reason = TemporaryTarget.Reason.EATING_SOON,
|
||||
|
@ -887,7 +900,6 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
return retVal
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun processCARBS(divided: Array<String>, receivedSms: Sms) {
|
||||
var grams = SafeParse.stringToInt(divided[1])
|
||||
var time = dateUtil.now()
|
||||
|
@ -904,7 +916,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_carbsreplywithcode), grams, dateUtil.timeString(time), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(grams, time) {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, grams, time) {
|
||||
override fun run() {
|
||||
val detailedBolusInfo = DetailedBolusInfo()
|
||||
detailedBolusInfo.carbs = anInteger().toDouble()
|
||||
|
@ -931,7 +943,6 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
private fun processTARGET(divided: Array<String>, receivedSms: Sms) {
|
||||
val isMeal = divided[1].equals("MEAL", ignoreCase = true)
|
||||
val isActivity = divided[1].equals("ACTIVITY", ignoreCase = true)
|
||||
|
@ -941,7 +952,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_temptargetwithcode), divided[1].uppercase(Locale.getDefault()), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
|
||||
override fun run() {
|
||||
val units = profileFunction.getUnits()
|
||||
var keyDuration = 0
|
||||
|
@ -979,7 +990,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
var tt = sp.getDouble(keyTarget, if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL)
|
||||
tt = Profile.toCurrentUnits(profileFunction, tt)
|
||||
tt = if (tt > 0) tt else if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL
|
||||
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = dateUtil.now(),
|
||||
duration = TimeUnit.MINUTES.toMillis(ttDuration.toLong()),
|
||||
reason = TemporaryTarget.Reason.EATING_SOON,
|
||||
|
@ -1003,7 +1014,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_temptargetcancel), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
|
||||
override fun run() {
|
||||
disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(dateUtil.now()))
|
||||
.subscribe({ result ->
|
||||
|
@ -1028,7 +1039,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_stopsmswithcode), passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
|
||||
override fun run() {
|
||||
sp.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)
|
||||
val replyText = String.format(resourceHelper.gs(R.string.smscommunicator_stoppedsms))
|
||||
|
@ -1046,7 +1057,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
val passCode = generatePassCode()
|
||||
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode)
|
||||
receivedSms.processed = true
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(cal) {
|
||||
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false, cal) {
|
||||
override fun run() {
|
||||
val result = xdripCalibrations.sendIntent(aDouble!!)
|
||||
val replyText =
|
||||
|
@ -1080,7 +1091,6 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
fun sendSMS(sms: Sms): Boolean {
|
||||
val smsManager = SmsManager.getDefault()
|
||||
sms.text = stripAccents(sms.text)
|
||||
try {
|
||||
aapsLogger.debug(LTag.SMS, "Sending SMS to " + sms.phoneNumber + ": " + sms.text)
|
||||
|
|
|
@ -88,7 +88,7 @@ class SmsCommunicatorOtpActivity : NoSplashAppCompatActivity() {
|
|||
Runnable {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText("OTP Secret", otp.provisioningSecret())
|
||||
clipboard.primaryClip = clip
|
||||
clipboard.setPrimaryClip(clip)
|
||||
ToastUtils.Long.infoToast(this, resourceHelper.gs(R.string.smscommunicator_otp_export_successful))
|
||||
uel.log(Action.OTP_EXPORT, Sources.SMS)
|
||||
})
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.eatthepath.otp.HmacOneTimePasswordGenerator
|
|||
import com.google.common.io.BaseEncoding
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.annotations.OpenForTesting
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
|
@ -15,6 +16,7 @@ import javax.crypto.spec.SecretKeySpec
|
|||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@OpenForTesting
|
||||
@Singleton
|
||||
class OneTimePassword @Inject constructor(
|
||||
private val sp: SP,
|
||||
|
|
|
@ -20,7 +20,7 @@ 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.CancelCurrentTemporaryTargetIfAnyTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction
|
||||
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
|
||||
import info.nightscout.androidaps.extensions.total
|
||||
import info.nightscout.androidaps.extensions.valueToUnits
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
|
@ -572,7 +572,7 @@ class ActionStringHandler @Inject constructor(
|
|||
|
||||
private fun generateTempTarget(duration: Int, low: Double, high: Double) {
|
||||
if (duration != 0) {
|
||||
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
|
||||
disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
|
||||
reason = TemporaryTarget.Reason.WEAR,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue