Merge remote-tracking branch 'upstream/dev2_dana_combo_only' into avereha/dev2-merge

This commit is contained in:
Andrei Vereha 2021-05-01 20:11:48 +02:00
commit e3f7608de7
1495 changed files with 73791 additions and 32104 deletions

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

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

1
.gitignore vendored
View file

@ -7,6 +7,7 @@
/captures
*.apk
build/
!.idea/dictionaries/project-dictionary.xml
.idea/*
!.idea/codeStyles/
full/

View file

@ -19,6 +19,7 @@
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="6" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="6" />
<option name="BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES" value="1" />

View file

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

View file

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

View file

@ -3,7 +3,8 @@
* Check the wiki: https://androidaps.readthedocs.io
* Everyone whos been looping with AndroidAPS needs to fill out the form after 3 days of looping https://docs.google.com/forms/d/14KcMjlINPMJHVt28MDRupa4sz4DDIooI4SrW0P3HSN8/viewform?c=0&w=1
[![Gitter](https://badges.gitter.im/MilosKozak/AndroidAPS.svg)](https://gitter.im/MilosKozak/AndroidAPS?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Support Server](https://img.shields.io/discord/629952586895851530.svg?label=Discord&logo=Discord&colorB=7289da&style=for-the-badge)](https://discord.gg/4fQUWHZ4Mw)
[![Build status](https://travis-ci.org/nightscout/AndroidAPS.svg?branch=master)](https://travis-ci.org/nightscout/AndroidAPS)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/androidaps/localized.svg)](https://translations.androidaps.org/project/androidaps)
[![Documentation Status](https://readthedocs.org/projects/androidaps/badge/?version=latest)](https://androidaps.readthedocs.io/en/latest/?badge=latest)

View file

@ -13,6 +13,7 @@ jacoco {
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
repositories {
@ -100,6 +101,7 @@ def allCommitted = { ->
tasks.matching { it instanceof Test }.all {
testLogging.events = ["failed", "skipped", "started"]
// testLogging.events = ["failed", "skipped", "started", "standard_out"] use to display stdout in travis
testLogging.exceptionFormat = "full"
}
@ -109,7 +111,7 @@ android {
defaultConfig {
multiDexEnabled true
versionCode 1500
version "2.8.2.1-dev-b"
version "2.8.2.1-dev-e3"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'

View file

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

View file

@ -42,7 +42,7 @@ import org.junit.runner.RunWith
@LargeTest
@RunWith(AndroidJUnit4::class)
class SetupWizardActivityTest {
/*
@Rule
@JvmField
var mActivityTestRule = ActivityTestRule(SetupWizardActivity::class.java)
@ -226,4 +226,5 @@ adb shell settings put global animator_duration_scale 0 &
}
}
}
*/
}

View file

@ -32,15 +32,20 @@ import info.nightscout.androidaps.activities.PreferencesActivity
import info.nightscout.androidaps.activities.ProfileHelperActivity
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.activities.StatsActivity
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.ActivityMainBinding
import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.IconsProvider
import info.nightscout.androidaps.interfaces.PluginType
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
@ -56,7 +61,6 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.isRunningRealPumpTest
import info.nightscout.androidaps.utils.locale.LocaleHelper
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.IconsProvider
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.tabs.TabPageAdapter
@ -80,13 +84,14 @@ class MainActivity : NoSplashAppCompatActivity() {
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var nsSettingsStatus: NSSettingsStatus
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var iconsProvider: IconsProvider
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
private var pluginPreferencesMenuItem: MenuItem? = null
@ -295,7 +300,7 @@ class MainActivity : NoSplashAppCompatActivity() {
R.id.nav_about -> {
var message = "Build: ${BuildConfig.BUILDVERSION}\n"
message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n"
message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.nightscoutVersionName}"
message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.getVersion()}"
if (buildHelper.isEngineeringMode()) message += "\n${resourceHelper.gs(R.string.engineering_mode_enabled)}"
if (!fabricPrivacy.fabricEnabled()) message += "\n${resourceHelper.gs(R.string.fabric_upload_disabled)}"
message += resourceHelper.gs(R.string.about_link_urls)
@ -316,6 +321,7 @@ class MainActivity : NoSplashAppCompatActivity() {
R.id.nav_exit -> {
aapsLogger.debug(LTag.CORE, "Exiting")
uel.log(Action.EXIT_AAPS, Sources.Aaps)
rxBus.send(EventAppExit())
finish()
System.runFinalization()
@ -375,7 +381,7 @@ class MainActivity : NoSplashAppCompatActivity() {
if (!config.NSCLIENT && !config.PUMPCONTROL)
activePlugin.activeAPS.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Aps", it::class.java.simpleName) }
activePlugin.activeBgSource.let { fabricPrivacy.firebaseAnalytics.setUserProperty("BgSource", it::class.java.simpleName) }
fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileInterface.javaClass.simpleName)
fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileSource.javaClass.simpleName)
activePlugin.activeSensitivity.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) }
activePlugin.activeInsulin.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Insulin", it::class.java.simpleName) }
// Add to crash log too

View file

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

View file

@ -0,0 +1,142 @@
package info.nightscout.androidaps
import android.bluetooth.BluetoothDevice
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.wifi.WifiManager
import android.os.Build
import com.j256.ormlite.android.apptools.OpenHelperManager
import dagger.android.AndroidInjector
import dagger.android.DaggerApplication
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction
import info.nightscout.androidaps.database.transactions.VersionChangeTransaction
import info.nightscout.androidaps.db.CompatDBHelper
import info.nightscout.androidaps.db.DatabaseHelper
import info.nightscout.androidaps.db.StaticInjector
import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent
import info.nightscout.androidaps.interfaces.ConfigBuilder
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils
import info.nightscout.androidaps.receivers.BTReceiver
import info.nightscout.androidaps.receivers.ChargingStateReceiver
import info.nightscout.androidaps.receivers.KeepAliveReceiver.KeepAliveManager
import info.nightscout.androidaps.receivers.NetworkChangeReceiver
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver
import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.locale.LocaleHelper.update
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import net.danlew.android.joda.JodaTimeAndroid
import javax.inject.Inject
class MainApp : DaggerApplication() {
private val disposable = CompositeDisposable()
@Inject lateinit var pluginStore: PluginStore
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var versionCheckersUtils: VersionCheckerUtils
@Inject lateinit var sp: SP
@Inject lateinit var config: Config
@Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var keepAliveManager: KeepAliveManager
@Inject lateinit var plugins: List<@JvmSuppressWildcards PluginBase>
@Inject lateinit var compatDBHelper: CompatDBHelper
@Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize
@Inject lateinit var uel: UserEntryLogger
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
if (gitRemote?.contains("NoGitSystemAvailable") == true) {
gitRemote = null
commitHash = null
}
disposable += repository.runTransaction(VersionChangeTransaction(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, gitRemote, commitHash)).subscribe()
disposable += repository.runTransaction(InsertIfNewByTimestampTherapyEventTransaction(timestamp = dateUtil.now(), type = TherapyEvent.Type.NOTE, note = getString(info.nightscout.androidaps.core.R.string.androidaps_start).toString() + " - " + Build.MANUFACTURER + " " + Build.MODEL, glucoseUnit = TherapyEvent.GlucoseUnit.MGDL)).subscribe()
disposable += compatDBHelper.dbChangeDisposable()
registerActivityLifecycleCallbacks(activityMonitor)
JodaTimeAndroid.init(this)
aapsLogger.debug("Version: " + BuildConfig.VERSION_NAME)
aapsLogger.debug("BuildVersion: " + BuildConfig.BUILDVERSION)
aapsLogger.debug("Remote: " + BuildConfig.REMOTE)
registerLocalBroadcastReceiver()
//trigger here to see the new version on app start after an update
versionCheckersUtils.triggerCheckVersion()
// Register all tabs in app here
pluginStore.plugins = plugins
configBuilder.initialize()
keepAliveManager.setAlarm(this)
doMigrations()
uel.log(UserEntry.Action.START_AAPS, UserEntry.Sources.Aaps)
}
private fun doMigrations() {
// set values for different builds
if (!sp.contains(R.string.key_ns_alarms)) sp.putBoolean(R.string.key_ns_alarms, config.NSCLIENT)
if (!sp.contains(R.string.key_ns_announcements)) sp.putBoolean(R.string.key_ns_announcements, config.NSCLIENT)
if (!sp.contains(R.string.key_language)) sp.putString(R.string.key_language, "default")
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent
.builder()
.application(this)
.build()
}
private fun registerLocalBroadcastReceiver() {
var filter = IntentFilter()
filter.addAction(Intent.ACTION_TIME_CHANGED)
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
registerReceiver(TimeDateOrTZChangeReceiver(), filter)
filter = IntentFilter()
@Suppress("DEPRECATION")
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION)
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION)
registerReceiver(NetworkChangeReceiver(), filter)
filter = IntentFilter()
filter.addAction(Intent.ACTION_POWER_CONNECTED)
filter.addAction(Intent.ACTION_POWER_DISCONNECTED)
filter.addAction(Intent.ACTION_BATTERY_CHANGED)
registerReceiver(ChargingStateReceiver(), filter)
filter = IntentFilter()
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
registerReceiver(BTReceiver(), filter)
}
override fun onTerminate() {
aapsLogger.debug(LTag.CORE, "onTerminate")
unregisterActivityLifecycleCallbacks(activityMonitor)
keepAliveManager.cancelAlarm(this)
super.onTerminate()
}
companion object {
lateinit var dbHelper: DatabaseHelper
}
}

View file

@ -8,13 +8,13 @@ import android.os.Bundle
import androidx.annotation.XmlRes
import androidx.preference.*
import dagger.android.support.AndroidSupportInjection
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.PluginBase

View file

@ -7,14 +7,16 @@ import android.text.TextWatcher
import android.view.Menu
import android.widget.PopupMenu
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.data.defaultProfile.DefaultProfile
import info.nightscout.androidaps.data.defaultProfile.DefaultProfileDPV
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch
import info.nightscout.androidaps.databinding.ActivityProfilehelperBinding
import info.nightscout.androidaps.db.ProfileSwitch
import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
@ -24,7 +26,6 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.stats.TddCalculator
import java.text.DecimalFormat
import javax.inject.Inject
@ -39,8 +40,8 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var repository: AppRepository
enum class ProfileType {
MOTOL_DEFAULT,
@ -61,7 +62,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
private lateinit var profileList: ArrayList<CharSequence>
private val profileUsed = arrayOf(0, 0)
private lateinit var profileSwitch: List<ProfileSwitch>
private lateinit var profileSwitch: List<EffectiveProfileSwitch>
private val profileSwitchUsed = arrayOf(0, 0)
private lateinit var binding: ActivityProfilehelperBinding
@ -85,10 +86,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
setOnMenuItemClickListener { item ->
binding.profiletype.setText(item.title)
when (item.itemId) {
R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT)
R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT)
R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT)
R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE)
R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT)
R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT)
R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT)
R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE)
R.id.menu_profileswitch -> switchTab(tabSelected, ProfileType.PROFILE_SWITCH)
}
true
@ -98,7 +99,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
}
// Active profile
profileList = activePlugin.activeProfileInterface.profile?.getProfileList() ?: ArrayList()
profileList = activePlugin.activeProfileSource.profile?.getProfileList() ?: ArrayList()
binding.availableProfileList.setOnClickListener {
PopupMenu(this, binding.availableProfileList).apply {
@ -114,12 +115,12 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
}
// Profile switch
profileSwitch = databaseHelper.getProfileSwitchData(dateUtil._now() - T.months(2).msecs(), true)
profileSwitch = repository.getEffectiveProfileSwitchDataFromTime(dateUtil.now() - T.months(2).msecs(), true).blockingGet()
binding.profileswitchList.setOnClickListener {
PopupMenu(this, binding.profileswitchList).apply {
var order = 0
for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.customizedName)
for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.originalCustomizedName)
setOnMenuItemClickListener { item ->
binding.profileswitchList.setText(item.title)
profileSwitchUsed[tabSelected] = item.itemId
@ -140,7 +141,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
else defaultProfileDPV.profile(age, tdd, pct / 100.0, profileFunction.getUnits())
profile?.let {
OKDialog.showConfirmation(this, resourceHelper.gs(R.string.careportal_profileswitch), resourceHelper.gs(R.string.copytolocalprofile), Runnable {
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(it, "DefaultProfile" + dateUtil.dateAndTimeAndSecondsString(dateUtil._now())))
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(it, "DefaultProfile " +
dateUtil.dateAndTimeAndSecondsString(dateUtil.now())
.replace(".", "/")
))
rxBus.send(EventLocalProfileChanged())
})
}
@ -207,11 +211,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
getProfile(ageUsed[1], tddUsed[1], weightUsed[1], pctUsed[1] / 100.0, 1)?.let { profile1 ->
ProfileViewerDialog().also { pvd ->
pvd.arguments = Bundle().also {
it.putLong("time", DateUtil.now())
it.putLong("time", dateUtil.now())
it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal)
it.putString("customProfile", profile0.data.toString())
it.putString("customProfile2", profile1.data.toString())
it.putString("customProfileUnits", profileFunction.getUnits())
it.putString("customProfile", profile0.jsonObject.toString())
it.putString("customProfile2", profile1.jsonObject.toString())
it.putString("customProfileName", getProfileName(ageUsed[0], tddUsed[0], weightUsed[0], pctUsed[0] / 100.0, 0) + "\n" + getProfileName(ageUsed[1], tddUsed[1], weightUsed[1], pctUsed[1] / 100.0, 1))
}
}.show(supportFragmentManager, "ProfileViewDialog")
@ -224,14 +227,14 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
switchTab(0, typeSelected[0], false)
}
private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): Profile? =
private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): PureProfile? =
try { // profile must not exist
when (typeSelected[tab]) {
ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())
ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits())
ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile()
ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileInterface.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString())
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].profileObject?.convertToNonCustomizedProfile()
ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())
ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits())
ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile(dateUtil)
ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileSource.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString())
ProfileType.PROFILE_SWITCH -> ProfileSealed.EPS(profileSwitch[profileSwitchUsed[tab]]).convertToNonCustomizedProfile(dateUtil)
}
} catch (e: Exception) {
null
@ -239,11 +242,11 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
private fun getProfileName(age: Double, tdd: Double, weight: Double, basalSumPct: Double, tab: Int): String =
when (typeSelected[tab]) {
ProfileType.MOTOL_DEFAULT -> if (tdd > 0) resourceHelper.gs(R.string.formatwithtdd, age, tdd) else resourceHelper.gs(R.string.formatwithweight, age, weight)
ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt())
ProfileType.CURRENT -> profileFunction.getProfileName()
ProfileType.MOTOL_DEFAULT -> if (tdd > 0) resourceHelper.gs(R.string.formatwithtdd, age, tdd) else resourceHelper.gs(R.string.formatwithweight, age, weight)
ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt())
ProfileType.CURRENT -> profileFunction.getProfileName()
ProfileType.AVAILABLE_PROFILE -> profileList[profileUsed[tab]].toString()
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].customizedName
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].originalCustomizedName
}
private fun storeValues() {
@ -286,7 +289,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
if (profileList.isNotEmpty())
binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString())
if (profileSwitch.isNotEmpty())
binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].customizedName)
binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].originalCustomizedName)
}
private fun setBackgroundColorOnSelected(tab: Int) {

View file

@ -2,6 +2,8 @@ package info.nightscout.androidaps.activities
import android.os.Bundle
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.ActivityStatsBinding
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.utils.ActivityMonitor
@ -31,7 +33,7 @@ class StatsActivity : NoSplashAppCompatActivity() {
binding.ok.setOnClickListener { finish() }
binding.reset.setOnClickListener {
OKDialog.showConfirmation(this, resourceHelper.gs(R.string.doyouwantresetstats)) {
uel.log("STATS RESET")
uel.log(Action.STAT_RESET, Sources.Stats)
activityMonitor.reset()
recreate()
}

View file

@ -8,7 +8,7 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.defaultProfile.DefaultProfile
import info.nightscout.androidaps.databinding.ActivitySurveyBinding
import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -24,12 +24,13 @@ import javax.inject.Inject
class SurveyActivity : NoSplashAppCompatActivity() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var tirCalculator: TirCalculator
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var defaultProfile: DefaultProfile
@Inject lateinit var dateUtil: DateUtil
private lateinit var binding: ActivitySurveyBinding
@ -40,7 +41,7 @@ class SurveyActivity : NoSplashAppCompatActivity() {
binding.id.text = InstanceId.instanceId()
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
val profileList = profileStore?.getProfileList() ?: return
binding.spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList)
@ -68,11 +69,10 @@ class SurveyActivity : NoSplashAppCompatActivity() {
defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())?.let { profile ->
ProfileViewerDialog().also { pvd ->
pvd.arguments = Bundle().also {
it.putLong("time", DateUtil.now())
it.putLong("time", dateUtil.now())
it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal)
it.putString("customProfile", runningProfile.data.toString())
it.putString("customProfile2", profile.data.toString())
it.putString("customProfileUnits", profile.units)
it.putString("customProfile", runningProfile.toPureNsJson(dateUtil).toString())
it.putString("customProfile2", profile.jsonObject.toString())
it.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight")
}
}.show(supportFragmentManager, "ProfileViewDialog")

View file

@ -1,7 +1,12 @@
package info.nightscout.androidaps.data.defaultProfile
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.data.ProfileImplOld
import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.Round
import org.json.JSONArray
import org.json.JSONObject
@ -10,14 +15,14 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class DefaultProfile @Inject constructor(val injector: HasAndroidInjector) {
class DefaultProfile @Inject constructor(val dateUtil: DateUtil) {
var oneToFive: TreeMap<Double, Array<Double>> = TreeMap()
var sixToEleven: TreeMap<Double, Array<Double>> = TreeMap()
var twelveToSeventeen: TreeMap<Double, Array<Double>> = TreeMap()
var eighteenToTwentyfor: TreeMap<Double, Array<Double>> = TreeMap()
var eighteenToTwentyFour: TreeMap<Double, Array<Double>> = TreeMap()
fun profile(age: Double, tdd: Double, weight: Double, units: String): Profile? {
fun profile(age: Double, tdd: Double, weight: Double, units: GlucoseUnit): PureProfile? {
val profile = JSONObject()
if (age >= 1 && age < 6) {
val _tdd = if (tdd == 0.0) 0.6 * weight else tdd
@ -49,8 +54,8 @@ class DefaultProfile @Inject constructor(val injector: HasAndroidInjector) {
profile.put("timezone", TimeZone.getDefault().id)
profile.put("target_high", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units))))
profile.put("target_low", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units))))
profile.put("units", units)
return Profile(injector, profile, units)
profile.put("units", units.asText)
return pureProfileFromJson(profile, dateUtil)
}
init {
@ -148,7 +153,7 @@ class DefaultProfile @Inject constructor(val injector: HasAndroidInjector) {
return array
}
private fun singleValueArrayFromMmolToUnits(value: Double, sample: Array<Double>, units: String): JSONArray {
private fun singleValueArrayFromMmolToUnits(value: Double, sample: Array<Double>, units: GlucoseUnit): JSONArray {
val array = JSONArray()
array.put(JSONObject().put("time", "00:00").put("value", Profile.fromMmolToUnits(value + sample[0],units)).put("timeAsSeconds", 0 * 3600))
array.put(JSONObject().put("time", "06:00").put("value", Profile.fromMmolToUnits(value + sample[1],units)).put("timeAsSeconds", 6 * 3600))

View file

@ -1,8 +1,12 @@
package info.nightscout.androidaps.data.defaultProfile
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.data.ProfileImplOld
import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.utils.DateUtil
import org.json.JSONArray
import org.json.JSONObject
import java.util.*
@ -10,13 +14,13 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector) {
class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector, val dateUtil: DateUtil) {
var oneToFive = arrayOf(3.97, 3.61, 3.46, 3.70, 3.76, 3.87, 4.18, 4.01, 3.76, 3.54, 3.15, 2.80, 2.86, 3.21, 3.61, 3.97, 4.43, 4.96, 5.10, 5.50, 5.81, 6.14, 5.52, 5.10)
var sixToEleven = arrayOf(4.20, 4.27, 4.41, 4.62, 4.92, 5.09, 5.01, 4.47, 3.89, 3.33, 3.10, 2.91, 2.97, 3.08, 3.36, 3.93, 4.52, 4.76, 4.69, 4.63, 4.63, 4.47, 4.47, 4.31)
var twelveToEighteen = arrayOf(3.47, 3.80, 4.31, 4.95, 5.59, 6.11, 5.89, 5.11, 4.31, 3.78, 3.55, 3.39, 3.35, 3.39, 3.64, 3.97, 4.53, 4.59, 4.50, 4.00, 3.69, 3.39, 3.35, 3.35)
fun profile(age: Double, tdd: Double, basalSumPct: Double, units: String): Profile? {
fun profile(age: Double, tdd: Double, basalSumPct: Double, units: GlucoseUnit): PureProfile? {
val basalSum = tdd * basalSumPct
val profile = JSONObject()
if (age >= 1 && age < 6) {
@ -40,8 +44,8 @@ class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector) {
profile.put("timezone", TimeZone.getDefault().id)
profile.put("target_high", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units))))
profile.put("target_low", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units))))
profile.put("units", units)
return Profile(injector, profile, units)
profile.put("units", units.asText)
return pureProfileFromJson(profile, dateUtil)
}
private fun arrayToJson(b: Array<Double>, basalSum: Double): JSONArray {
@ -59,7 +63,7 @@ class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector) {
return array
}
private fun singleValueArrayFromMmolToUnits(value: Double, units: String): JSONArray {
private fun singleValueArrayFromMmolToUnits(value: Double, units: GlucoseUnit): JSONArray {
val array = JSONArray()
array.put(JSONObject().put("time", "00:00").put("value", Profile.fromMmolToUnits(value, units)).put("timeAsSeconds", 0 * 3600))
return array

View file

@ -1,11 +1,10 @@
package info.nightscout.androidaps.db
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.interfaces.TraceableDBEntry
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.database.entities.ExtendedBolus
import info.nightscout.androidaps.database.entities.TemporaryBasal
import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
@ -27,17 +26,61 @@ class CompatDBHelper @Inject constructor(
rxBus.send(EventNewBG(null))
}
.subscribe {
it.filterIsInstance<GlucoseValue>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventNewHistoryData")
rxBus.send(EventNewHistoryData(it.timestamp))
}
it.filterIsInstance<GlucoseValue>().lastOrNull()?.let {
/**
* GlucoseValues can come in batch
* oldest one should be used for invalidation, newest one for for triggering Loop.
* Thus we need to collect both
*
*/
var newestGlucoseValue: GlucoseValue? = null
it.filterIsInstance<GlucoseValue>().lastOrNull()?.let { gv ->
aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg")
rxBus.send(EventNewBG(it))
rxBus.send(EventNewBG(gv))
newestGlucoseValue = gv
}
it.filterIsInstance<GlucoseValue>().map { gv -> gv.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventNewHistoryData")
rxBus.send(EventNewHistoryData(timestamp, true, newestGlucoseValue))
}
it.filterIsInstance<Carbs>().map { t -> t.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTreatmentChange")
rxBus.send(EventTreatmentChange())
rxBus.send(EventNewHistoryData(timestamp, false))
}
it.filterIsInstance<Bolus>().map { t -> t.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTreatmentChange")
rxBus.send(EventTreatmentChange())
rxBus.send(EventNewHistoryData(timestamp, false))
}
it.filterIsInstance<TemporaryBasal>().map { t -> t.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTempBasalChange")
rxBus.send(EventTempBasalChange())
rxBus.send(EventNewHistoryData(timestamp, false))
}
it.filterIsInstance<ExtendedBolus>().map { t -> t.timestamp }.minOrNull()?.let { timestamp ->
aapsLogger.debug(LTag.DATABASE, "Firing EventExtendedBolusChange")
rxBus.send(EventExtendedBolusChange())
rxBus.send(EventNewHistoryData(timestamp, false))
}
it.filterIsInstance<TemporaryTarget>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange")
rxBus.send(EventTempTargetChange())
}
it.filterIsInstance<TherapyEvent>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventTherapyEventChange")
rxBus.send(EventTherapyEventChange())
}
it.filterIsInstance<Food>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventFoodDatabaseChanged")
rxBus.send(EventFoodDatabaseChanged())
}
it.filterIsInstance<ProfileSwitch>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileNeedsUpdate")
rxBus.send(EventProfileSwitchChanged())
}
it.filterIsInstance<EffectiveProfileSwitch>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileNeedsUpdate")
rxBus.send(EventProfileSwitchChanged())
}
}
}

View file

@ -1,9 +1,11 @@
package info.nightscout.androidaps.db;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.j256.ormlite.dao.CloseableIterator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.json.JSONObject;
import java.sql.SQLException;
import java.util.List;
@ -14,129 +16,135 @@ 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(@NotNull CareportalEvent careportalEvent) {
MainApp.getDbHelper().createOrUpdate(careportalEvent);
@Override public void createOrUpdate(@NonNull DanaRHistoryRecord record) {
MainApp.Companion.getDbHelper().createOrUpdate(record);
}
@Override public void createOrUpdate(@NotNull DanaRHistoryRecord record) {
MainApp.getDbHelper().createOrUpdate(record);
@Override public void createOrUpdate(@NonNull OmnipodHistoryRecord record) {
MainApp.Companion.getDbHelper().createOrUpdate(record);
}
@Override public void createOrUpdate(@NotNull OmnipodHistoryRecord record) {
MainApp.getDbHelper().createOrUpdate(record);
@NonNull @Override public List<DanaRHistoryRecord> getDanaRHistoryRecordsByType(byte type) {
return MainApp.Companion.getDbHelper().getDanaRHistoryRecordsByType(type);
}
@NotNull @Override public List<DanaRHistoryRecord> getDanaRHistoryRecordsByType(byte type) {
return MainApp.getDbHelper().getDanaRHistoryRecordsByType(type);
@Override public long size(@NonNull String table) {
return MainApp.Companion.getDbHelper().size(table);
}
@NotNull @Override public List<TDD> getTDDs() {
return MainApp.getDbHelper().getTDDs();
}
@Override public long size(@NotNull String table) {
return MainApp.getDbHelper().size(table);
}
@Override public void create(@NotNull DbRequest record) {
@Override public void create(@NonNull DbRequest record) {
try {
MainApp.getDbHelper().create(record);
MainApp.Companion.getDbHelper().create(record);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override public void deleteAllDbRequests() {
MainApp.getDbHelper().deleteAllDbRequests();
MainApp.Companion.getDbHelper().deleteAllDbRequests();
}
@Override public int deleteDbRequest(@NotNull String id) {
return MainApp.getDbHelper().deleteDbRequest(id);
@Override public int deleteDbRequest(@NonNull String id) {
return MainApp.Companion.getDbHelper().deleteDbRequest(id);
}
@Override public void deleteDbRequestbyMongoId(@NotNull String action, @NotNull String _id) {
MainApp.getDbHelper().deleteDbRequestbyMongoId(action, _id);
@Override public void deleteDbRequestbyMongoId(@NonNull String action, @NonNull String _id) {
MainApp.Companion.getDbHelper().deleteDbRequestbyMongoId(action, _id);
}
@NotNull @Override public CloseableIterator<DbRequest> getDbRequestInterator() {
return MainApp.getDbHelper().getDbRequestInterator();
@NonNull @Override public CloseableIterator<DbRequest> getDbRequestIterator() {
return MainApp.Companion.getDbHelper().getDbRequestIterator();
}
@Override public long roundDateToSec(long date) {
return MainApp.getDbHelper().roundDateToSec(date);
return MainApp.Companion.getDbHelper().roundDateToSec(date);
}
@Override public void createOrUpdateTDD(@NotNull TDD record) {
MainApp.getDbHelper().createOrUpdateTDD(record);
@Override public boolean createOrUpdate(@NonNull TemporaryBasal tempBasal) {
return MainApp.Companion.getDbHelper().createOrUpdate(tempBasal);
}
@Override public void createOrUpdate(@NotNull TemporaryBasal tempBasal) {
MainApp.getDbHelper().createOrUpdate(tempBasal);
@Nullable @Override public TemporaryBasal findTempBasalByPumpId(long id) {
return MainApp.Companion.getDbHelper().findTempBasalByPumpId(id);
}
@NotNull @Override public TemporaryBasal findTempBasalByPumpId(long id) {
return MainApp.getDbHelper().findTempBasalByPumpId(id);
@Deprecated
@NonNull @Override public List<TemporaryBasal> getTemporaryBasalsDataFromTime(long mills, boolean ascending) {
return MainApp.Companion.getDbHelper().getTemporaryBasalsDataFromTime(mills, ascending);
}
@NotNull @Override public List<TemporaryBasal> getTemporaryBasalsDataFromTime(long mills, boolean ascending) {
return MainApp.getDbHelper().getTemporaryBasalsDataFromTime(mills, ascending);
}
@Override public CareportalEvent getCareportalEventFromTimestamp(long timestamp) {
return MainApp.getDbHelper().getCareportalEventFromTimestamp(timestamp);
}
@NotNull @Override public List<OmnipodHistoryRecord> getAllOmnipodHistoryRecordsFromTimestamp(long timestamp, boolean ascending) {
return MainApp.getDbHelper().getAllOmnipodHistoryRecordsFromTimeStamp(timestamp, ascending);
@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.getDbHelper().findOmnipodHistoryRecordByPumpId(pumpId);
return MainApp.Companion.getDbHelper().findOmnipodHistoryRecordByPumpId(pumpId);
}
@NotNull @Override public List<TDD> getTDDsForLastXDays(int days) {
return MainApp.getDbHelper().getTDDsForLastXDays(days);
@Override public void createOrUpdate(@NonNull InsightBolusID record) {
MainApp.Companion.getDbHelper().createOrUpdate(record);
}
@NotNull @Override public List<ProfileSwitch> getProfileSwitchData(long from, boolean ascending) {
return MainApp.getDbHelper().getProfileSwitchData(from, ascending);
@Override public void createOrUpdate(@NonNull InsightPumpID record) {
MainApp.Companion.getDbHelper().createOrUpdate(record);
}
@Override public void createOrUpdate(@NotNull InsightBolusID record) {
MainApp.getDbHelper().createOrUpdate(record);
@Override public void createOrUpdate(@NonNull InsightHistoryOffset record) {
MainApp.Companion.getDbHelper().createOrUpdate(record);
}
@Override public void createOrUpdate(@NotNull InsightPumpID record) {
MainApp.getDbHelper().createOrUpdate(record);
}
@Override public void createOrUpdate(@NotNull InsightHistoryOffset record) {
MainApp.getDbHelper().createOrUpdate(record);
}
@Override public void delete(@NotNull ExtendedBolus extendedBolus) {
MainApp.getDbHelper().delete(extendedBolus);
@Override public void delete(@NonNull ExtendedBolus extendedBolus) {
MainApp.Companion.getDbHelper().delete(extendedBolus);
}
@Nullable @Override public ExtendedBolus getExtendedBolusByPumpId(long pumpId) {
return MainApp.getDbHelper().getExtendedBolusByPumpId(pumpId);
return MainApp.Companion.getDbHelper().getExtendedBolusByPumpId(pumpId);
}
@Nullable @Override public InsightBolusID getInsightBolusID(@NotNull String pumpSerial, int bolusID, long timestamp) {
return MainApp.getDbHelper().getInsightBolusID(pumpSerial, bolusID, timestamp);
@Nullable @Override public InsightBolusID getInsightBolusID(@NonNull String pumpSerial, int bolusID, long timestamp) {
return MainApp.Companion.getDbHelper().getInsightBolusID(pumpSerial, bolusID, timestamp);
}
@Nullable @Override public InsightHistoryOffset getInsightHistoryOffset(@NotNull String pumpSerial) {
return MainApp.getDbHelper().getInsightHistoryOffset(pumpSerial);
@Nullable @Override public InsightHistoryOffset getInsightHistoryOffset(@NonNull String pumpSerial) {
return MainApp.Companion.getDbHelper().getInsightHistoryOffset(pumpSerial);
}
@Nullable @Override public InsightPumpID getPumpStoppedEvent(@NotNull String pumpSerial, long before) {
return MainApp.getDbHelper().getPumpStoppedEvent(pumpSerial, before);
@Nullable @Override public InsightPumpID getPumpStoppedEvent(@NonNull String pumpSerial, long before) {
return MainApp.Companion.getDbHelper().getPumpStoppedEvent(pumpSerial, before);
}
@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 long getCountOfAllRows() {
return MainApp.Companion.getDbHelper().getCountOfAllRows();
}
@Override public void removeAllOHQueueItemsWithIdSmallerThan(long id) {
MainApp.Companion.getDbHelper().removeAllOHQueueItemsWithIdSmallerThan(id);
}
}

View file

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

View file

@ -7,7 +7,7 @@ import dagger.android.AndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.automation.di.AutomationModule
import info.nightscout.androidaps.combo.di.ComboModule
import info.nightscout.androidaps.core.di.CoreModule
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

View file

@ -6,43 +6,49 @@ import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.MainApp
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.general.maintenance.ImportExportPrefs
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl
import info.nightscout.androidaps.plugins.general.nsclient.DataSyncSelectorImplementation
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue
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.androidNotification.NotificationHolder
import info.nightscout.androidaps.utils.resources.IconsProvider
import info.nightscout.androidaps.utils.androidNotification.NotificationHolderImpl
import info.nightscout.androidaps.utils.buildHelper.ConfigImpl
import info.nightscout.androidaps.utils.resources.IconsProviderImplementation
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.rx.DefaultAapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.storage.FileStorage
import info.nightscout.androidaps.utils.storage.Storage
import javax.inject.Singleton
@Suppress("unused")
@Module(includes = [
AppModule.AppBindings::class
])
open class AppModule {
@Provides
fun providesPlugins(configInterface: ConfigInterface,
fun providesPlugins(config: Config,
@PluginsModule.AllConfigs allConfigs: Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>,
@PluginsModule.PumpDriver pumpDrivers: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.NotNSClient notNsClient: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsModule.APS aps: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>)
: List<@JvmSuppressWildcards PluginBase> {
val plugins = allConfigs.toMutableMap()
if (configInterface.PUMPDRIVERS) plugins += pumpDrivers.get()
if (configInterface.APS) plugins += aps.get()
if (!configInterface.NSCLIENT) plugins += notNsClient.get()
if (config.PUMPDRIVERS) plugins += pumpDrivers.get()
if (config.APS) plugins += aps.get()
if (!config.NSCLIENT) plugins += notNsClient.get()
return plugins.toList().sortedBy { it.first }.map { it.second }
}
@ -56,23 +62,36 @@ open class AppModule {
@Singleton
internal fun provideSchedulers(): AapsSchedulers = DefaultAapsSchedulers()
@Provides
@Singleton
fun providesUploadQueue(
aapsLogger: AAPSLogger,
databaseHelper: DatabaseHelperInterface,
context: Context,
sp: SP,
rxBus: RxBusWrapper
): UploadQueueAdminInterface = UploadQueue(aapsLogger, databaseHelper, context, sp, rxBus)
@Module
interface AppBindings {
@Binds fun bindContext(mainApp: MainApp): Context
@Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector
@Binds fun bindActivePluginProvider(pluginStore: PluginStore): ActivePluginProvider
@Binds fun bindActivePluginProvider(pluginStore: PluginStore): ActivePlugin
@Binds fun bindCommandQueueProvider(commandQueue: CommandQueue): CommandQueueProvider
@Binds fun bindConfigInterface(config: Config): ConfigInterface
@Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilderInterface
@Binds fun bindTreatmentInterface(treatmentsPlugin: TreatmentsPlugin): TreatmentsInterface
@Binds fun bindConfigInterface(config: ConfigImpl): Config
@Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilder
@Binds fun bindTreatmentsInterface(treatmentsPlugin: TreatmentsPlugin): TreatmentsInterface
@Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface
@Binds fun bindUploadQueueInterface(uploadQueue: UploadQueue): UploadQueueInterface
@Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolder): NotificationHolderInterface
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefs): ImportExportPrefsInterface
@Binds fun bindIconsProviderInterface(iconsProvider: IconsProvider): IconsProviderInterface
@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 bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculatorInterface
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicatorInterface
@Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
@Binds fun bindUploadQueueAdminInterfaceToUploadQueue(uploadQueueAdminInterface: UploadQueueAdminInterface) : UploadQueueInterface
@Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
@Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync
}
}

View file

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

View file

@ -66,7 +66,7 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusCarbsFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment

View file

@ -3,6 +3,8 @@ package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.TherapyEventDataPoint
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction
@Module

View file

@ -17,6 +17,7 @@ abstract class ReceiversModule {
@ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver
@ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveWorker(): KeepAliveReceiver.KeepAliveWorker
@ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver
@ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver
@ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver

View file

@ -2,7 +2,12 @@ package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.nsclient.NSClientWorker
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddAckWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientRemoveWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientUpdateRemoveAckWorker
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.source.*
@ -21,5 +26,10 @@ abstract class WorkersModule {
@ContributesAndroidInjector abstract fun contributesNSClientSourceWorker(): NSClientSourcePlugin.NSClientSourceWorker
@ContributesAndroidInjector abstract fun contributesNSProfileWorker(): NSProfilePlugin.NSProfileWorker
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorWorker(): SmsCommunicatorPlugin.SmsCommunicatorWorker
@ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientWorker
@ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientAddUpdateWorker
@ContributesAndroidInjector abstract fun contributesNSClientAddAckWorker(): NSClientAddAckWorker
@ContributesAndroidInjector abstract fun contributesNSClientUpdateRemoveAckWorker(): NSClientUpdateRemoveAckWorker
@ContributesAndroidInjector abstract fun contributesNSClientRemoveWorker(): NSClientRemoveWorker
@ContributesAndroidInjector abstract fun contributesNSClientMbgWorker(): NSClientMbgWorker
@ContributesAndroidInjector abstract fun contributesFoodWorker(): FoodPlugin.FoodWorker
}

View file

@ -6,13 +6,16 @@ import android.view.View
import android.view.ViewGroup
import com.google.common.base.Joiner
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.DialogCalibrationBinding
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.XdripCalibrations
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
@ -28,6 +31,7 @@ class CalibrationDialog : DialogFragmentWithDate() {
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var xdripCalibrations: XdripCalibrations
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
private var _binding: DialogCalibrationBinding? = null
@ -51,15 +55,15 @@ class CalibrationDialog : DialogFragmentWithDate() {
super.onViewCreated(view, savedInstanceState)
val units = profileFunction.getUnits()
val bg = Profile.fromMgdlToUnits(GlucoseStatus(injector).glucoseStatusData?.glucose
val bg = Profile.fromMgdlToUnits(glucoseStatusProvider.glucoseStatusData?.glucose
?: 0.0, units)
if (units == Constants.MMOL)
if (units == GlucoseUnit.MMOL)
binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok)
else
binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
binding.units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
binding.units.text = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
}
override fun onDestroyView() {
@ -70,14 +74,14 @@ class CalibrationDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
if (_binding == null) return false
val units = profileFunction.getUnits()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
val unitLabel = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
val actions: LinkedList<String?> = LinkedList()
val bg = binding.bg.value ?: return false
actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel)
if (bg > 0) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
uel.log("CALIBRATION", d1 = bg)
uel.log(Action.CALIBRATION, Sources.CalibrationDialog, ValueWithUnit.fromGlucoseUnit(bg, units.asText))
xdripCalibrations.sendIntent(bg)
})
}

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -7,44 +8,52 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.common.base.Joiner
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction
import info.nightscout.androidaps.databinding.DialogCarbsBinding
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.IobCobCalculator
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.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.CarbsGenerator
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.math.max
class CarbsDialog : DialogFragmentWithDate() {
@Inject lateinit var mainApp: MainApp
@Inject lateinit var ctx: Context
@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 iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var carbsGenerator: CarbsGenerator
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var carbTimer: CarbTimer
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var repository: AppRepository
companion object {
@ -53,6 +62,8 @@ class CarbsDialog : DialogFragmentWithDate() {
private const val FAV3_DEFAULT = 20
}
private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
validateInputs()
@ -67,15 +78,15 @@ class CarbsDialog : DialogFragmentWithDate() {
val time = binding.time.value.toInt()
if (time > 12 * 60 || time < -12 * 60) {
binding.time.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied))
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.constraintapllied))
}
if (binding.duration.value > 10) {
binding.duration.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied))
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.constraintapllied))
}
if (binding.carbs.value.toInt() > maxCarbs) {
binding.carbs.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied))
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.carbsconstraintapplied))
}
}
@ -133,7 +144,7 @@ class CarbsDialog : DialogFragmentWithDate() {
validateInputs()
}
iobCobCalculatorPlugin.actualBg()?.let { bgReading ->
iobCobCalculator.ads.actualBg()?.let { bgReading ->
if (bgReading.value < 72)
binding.hypoTt.isChecked = true
}
@ -153,6 +164,7 @@ class CarbsDialog : DialogFragmentWithDate() {
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
_binding = null
}
@ -172,7 +184,7 @@ class CarbsDialog : DialogFragmentWithDate() {
val hypoTTDuration = defaultValueHelper.determineHypoTTDuration()
val hypoTT = defaultValueHelper.determineHypoTT()
val actions: LinkedList<String?> = LinkedList()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
val unitLabel = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
val useAlarm = binding.alarmCheckBox.isChecked
val activitySelected = binding.activityTt.isChecked
@ -212,53 +224,86 @@ class CarbsDialog : DialogFragmentWithDate() {
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
when {
activitySelected -> {
uel.log("TT ACTIVITY", d1 = activityTT, i1 = activityTTDuration)
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(activityTTDuration)
.reason(resourceHelper.gs(R.string.activity))
.source(Source.USER)
.low(Profile.toMgdl(activityTT, profileFunction.getUnits()))
.high(Profile.toMgdl(activityTT, profileFunction.getUnits()))
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
uel.log(Action.TT, Sources.CarbDialog,
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY),
ValueWithUnit.fromGlucoseUnit(activityTT, units.asText),
ValueWithUnit.Minute(activityTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(activityTTDuration.toLong()),
reason = TemporaryTarget.Reason.ACTIVITY,
lowTarget = Profile.toMgdl(activityTT, profileFunction.getUnits()),
highTarget = Profile.toMgdl(activityTT, profileFunction.getUnits())
)).subscribe({ result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
})
}
eatingSoonSelected -> {
uel.log("TT EATING SOON", d1 = eatingSoonTT, i1 = eatingSoonTTDuration)
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(eatingSoonTTDuration)
.reason(resourceHelper.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()))
.high(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()))
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
uel.log(Action.TT, Sources.CarbDialog,
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
ValueWithUnit.Minute(eatingSoonTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON,
lowTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()),
highTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits())
)).subscribe({ result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
})
}
hypoSelected -> {
uel.log("TT HYPO", d1 = hypoTT, i1 = hypoTTDuration)
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(hypoTTDuration)
.reason(resourceHelper.gs(R.string.hypo))
.source(Source.USER)
.low(Profile.toMgdl(hypoTT, profileFunction.getUnits()))
.high(Profile.toMgdl(hypoTT, profileFunction.getUnits()))
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
uel.log(Action.TT, Sources.CarbDialog,
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA),
ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText),
ValueWithUnit.Minute(hypoTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(hypoTTDuration.toLong()),
reason = TemporaryTarget.Reason.HYPOGLYCEMIA,
lowTarget = Profile.toMgdl(hypoTT, profileFunction.getUnits()),
highTarget = Profile.toMgdl(hypoTT, profileFunction.getUnits())
)).subscribe({ result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
})
}
}
if (carbsAfterConstraints > 0) {
if (duration == 0) {
uel.log("CARBS", d1 = carbsAfterConstraints.toDouble(), i1 = timeOffset)
carbsGenerator.createCarb(carbsAfterConstraints, time, CareportalEvent.CARBCORRECTION, notes)
} else {
uel.log("CARBS", d1 = carbsAfterConstraints.toDouble(), i1 = timeOffset, i2 = duration)
carbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes)
nsUpload.uploadEvent(CareportalEvent.NOTE, DateUtil.now() - 2000, resourceHelper.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset))
}
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
detailedBolusInfo.carbs = carbsAfterConstraints.toDouble()
detailedBolusInfo.context = context
detailedBolusInfo.notes = notes
detailedBolusInfo.carbsDuration = T.hours(duration.toLong()).msecs()
detailedBolusInfo.carbsTimestamp = time
uel.log(if (duration == 0) Action.CARBS else Action.EXTENDED_CARBS, Sources.CarbDialog,
notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.Gram(carbsAfterConstraints),
ValueWithUnit.Minute(timeOffset).takeIf { timeOffset != 0 },
ValueWithUnit.Hour(duration).takeIf { duration != 0 })
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
}
}
})
}
if (useAlarm && carbs > 0 && timeOffset > 0) {
carbTimer.scheduleReminder(dateUtil._now() + T.mins(timeOffset.toLong()).msecs())
carbTimer.scheduleReminder(dateUtil.now() + T.mins(timeOffset.toLong()).msecs())
}
}, null)
}

View file

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

View file

@ -8,8 +8,11 @@ import android.view.ViewGroup
import com.google.common.base.Joiner
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.DialogExtendedbolusBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.UserEntryLogger
@ -18,7 +21,7 @@ import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.text.DecimalFormat
import java.util.*
@ -31,7 +34,7 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var uel: UserEntryLogger
private var _binding: DialogExtendedbolusBinding? = null
@ -87,7 +90,9 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
uel.log("EXTENDED BOLUS", d1 = insulinAfterConstraint, i1 = durationInMinutes)
uel.log(Action.EXTENDED_BOLUS, Sources.ExtendedBolusDialog,
ValueWithUnit.Insulin(insulinAfterConstraint),
ValueWithUnit.Minute(durationInMinutes))
commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() {
override fun run() {
if (!result.success) {

View file

@ -9,22 +9,28 @@ import com.google.common.base.Joiner
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction
import info.nightscout.androidaps.databinding.DialogFillBinding
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.*
import javax.inject.Inject
import kotlin.math.abs
@ -34,10 +40,12 @@ class FillDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var ctx: Context
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private val disposable = CompositeDisposable()
private var _binding: DialogFillBinding? = null
@ -115,7 +123,7 @@ class FillDialog : DialogFragmentWithDate() {
val insulinChange = binding.fillCartridgeChange.isChecked
if (insulinChange)
actions.add(resourceHelper.gs(R.string.record_insulin_cartridge_change).formatColor(resourceHelper, R.color.actionsConfirm))
val notes = binding.notesLayout.notes.text.toString()
val notes: String = binding.notesLayout.notes.text.toString()
if (notes.isNotEmpty())
actions.add(resourceHelper.gs(R.string.notes_label) + ": " + notes)
eventTime -= eventTime % 1000
@ -127,17 +135,41 @@ class FillDialog : DialogFragmentWithDate() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
if (insulinAfterConstraints > 0) {
uel.log("PRIME BOLUS", d1 = insulinAfterConstraints)
uel.log(Action.PRIME_BOLUS, Sources.FillDialog,
notes,
ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 })
requestPrimeBolus(insulinAfterConstraints, notes)
}
if (siteChange) {
uel.log("SITE CHANGE")
nsUpload.generateCareportalEvent(CareportalEvent.SITECHANGE, eventTime, notes)
uel.log(Action.SITE_CHANGE, Sources.FillDialog,
notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.TherapyEventType(TherapyEvent.Type.CANNULA_CHANGE))
disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(
timestamp = eventTime,
type = TherapyEvent.Type.CANNULA_CHANGE,
note = notes,
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL
)).subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) }
)
}
if (insulinChange) {
// add a second for case of both checked
uel.log("INSULIN CHANGE")
nsUpload.generateCareportalEvent(CareportalEvent.INSULINCHANGE, eventTime + 1000, notes)
uel.log(Action.RESERVOIR_CHANGE, Sources.FillDialog,
notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.TherapyEventType(TherapyEvent.Type.INSULIN_CHANGE))
disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(
timestamp = eventTime + 1000,
type = TherapyEvent.Type.INSULIN_CHANGE,
note = notes,
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL
)).subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) }
)
}
}, null)
}
@ -154,8 +186,7 @@ class FillDialog : DialogFragmentWithDate() {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.insulin = insulin
detailedBolusInfo.context = context
detailedBolusInfo.source = Source.USER
detailedBolusInfo.isValid = false // do not count it in IOB (for pump history)
detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING
detailedBolusInfo.notes = notes
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {

View file

@ -8,32 +8,32 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.common.base.Joiner
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.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.databinding.DialogInsulinBinding
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.extensions.toSignedString
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.max
@ -45,7 +45,7 @@ class InsulinDialog : DialogFragmentWithDate() {
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var ctx: Context
@Inject lateinit var repository: AppRepository
@Inject lateinit var config: Config
@ -58,6 +58,8 @@ class InsulinDialog : DialogFragmentWithDate() {
private const val PLUS3_DEFAULT = 2.0
}
private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
validateInputs()
@ -139,6 +141,7 @@ class InsulinDialog : DialogFragmentWithDate() {
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
_binding = null
}
@ -149,7 +152,7 @@ class InsulinDialog : DialogFragmentWithDate() {
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
val actions: LinkedList<String?> = LinkedList()
val units = profileFunction.getUnits()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
val unitLabel = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
val recordOnlyChecked = binding.recordOnly.isChecked
val eatingSoonChecked = binding.startEatingSoonTt.isChecked
@ -166,7 +169,7 @@ class InsulinDialog : DialogFragmentWithDate() {
actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, eatingSoonTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation))
val timeOffset = binding.time.value.toInt()
val time = DateUtil.now() + T.mins(timeOffset.toLong()).msecs()
val time = dateUtil.now() + T.mins(timeOffset.toLong()).msecs()
if (timeOffset != 0)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time))
@ -178,30 +181,46 @@ class InsulinDialog : DialogFragmentWithDate() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
if (eatingSoonChecked) {
uel.log("TT EATING SOON", d1 = eatingSoonTT, i1 = eatingSoonTTDuration)
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(eatingSoonTTDuration)
.reason(resourceHelper.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()))
.high(Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()))
activePlugin.activeTreatments.addToHistoryTempTarget(tempTarget)
uel.log(Action.TT, Sources.InsulinDialog,
notes,
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
ValueWithUnit.Minute(eatingSoonTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON,
lowTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()),
highTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits())
)).subscribe({ result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
})
}
if (insulinAfterConstraints > 0) {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS
detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.context = context
detailedBolusInfo.source = Source.USER
detailedBolusInfo.notes = notes
detailedBolusInfo.timestamp = time
if (recordOnlyChecked) {
uel.log("BOLUS RECORD", d1 = insulinAfterConstraints, i1 = timeOffset)
detailedBolusInfo.date = time
activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false)
uel.log(Action.BOLUS, Sources.InsulinDialog,
resourceHelper.gs(R.string.record) + if (notes.isNotEmpty()) ": " + notes else "",
ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.record)),
ValueWithUnit.Insulin(insulinAfterConstraints),
ValueWithUnit.Minute(timeOffset).takeIf { timeOffset!= 0 })
disposable += repository.runTransactionForResult(detailedBolusInfo.insertBolusTransaction())
.subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) }
)
} else {
uel.log("BOLUS", d1 = insulinAfterConstraints)
detailedBolusInfo.date = DateUtil.now()
uel.log(Action.BOLUS, Sources.InsulinDialog,
notes,
ValueWithUnit.Insulin(insulinAfterConstraints))
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {

View file

@ -12,6 +12,9 @@ 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.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.DialogLoopBinding
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview
@ -20,13 +23,12 @@ import info.nightscout.androidaps.logging.AAPSLogger
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.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
@ -41,10 +43,10 @@ class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var uel: UserEntryLogger
private var showOkCancel: Boolean = true
@ -196,7 +198,7 @@ class LoopDialog : DaggerDialogFragment() {
}
}
val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
if (profile == null || profileStore == null) {
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofile))
@ -238,31 +240,31 @@ class LoopDialog : DaggerDialogFragment() {
val profile = profileFunction.getProfile() ?: return true
when (v.id) {
R.id.overview_closeloop -> {
uel.log("CLOSED LOOP MODE")
uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog)
sp.putString(R.string.key_aps_mode, "closed")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.closedloop)))
return true
}
R.id.overview_lgsloop -> {
uel.log("LGS LOOP MODE")
uel.log(Action.LGS_LOOP_MODE, Sources.LoopDialog)
sp.putString(R.string.key_aps_mode, "lgs")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend)))
return true
}
R.id.overview_openloop -> {
uel.log("OPEN LOOP MODE")
uel.log(Action.OPEN_LOOP_MODE, Sources.LoopDialog)
sp.putString(R.string.key_aps_mode, "open")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend)))
return true
}
R.id.overview_disable -> {
uel.log("LOOP DISABLED")
uel.log(Action.LOOP_DISABLED, Sources.LoopDialog)
loopPlugin.setPluginEnabled(PluginType.LOOP, false)
loopPlugin.setFragmentVisible(PluginType.LOOP, false)
configBuilderPlugin.storeSettings("DisablingLoop")
configBuilder.storeSettings("DisablingLoop")
rxBus.send(EventRefreshOverview("suspendmenu"))
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
@ -276,17 +278,17 @@ class LoopDialog : DaggerDialogFragment() {
}
R.id.overview_enable -> {
uel.log("LOOP ENABLED")
uel.log(Action.LOOP_ENABLED, Sources.LoopDialog)
loopPlugin.setPluginEnabled(PluginType.LOOP, true)
loopPlugin.setFragmentVisible(PluginType.LOOP, true)
configBuilderPlugin.storeSettings("EnablingLoop")
configBuilder.storeSettings("EnablingLoop")
rxBus.send(EventRefreshOverview("suspendmenu"))
loopPlugin.createOfflineEvent(0)
return true
}
R.id.overview_resume, R.id.overview_reconnect -> {
uel.log("RESUME")
uel.log(if (v.id==R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog)
loopPlugin.suspendTo(0L)
rxBus.send(EventRefreshOverview("suspendmenu"))
commandQueue.cancelTempBasal(true, object : Callback() {
@ -302,49 +304,49 @@ class LoopDialog : DaggerDialogFragment() {
}
R.id.overview_suspend_1h -> {
uel.log("SUSPEND 1h")
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1))
loopPlugin.suspendLoop(60)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_suspend_2h -> {
uel.log("SUSPEND 2h")
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2))
loopPlugin.suspendLoop(120)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_suspend_3h -> {
uel.log("SUSPEND 3h")
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3))
loopPlugin.suspendLoop(180)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_suspend_10h -> {
uel.log("SUSPEND 10h")
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10))
loopPlugin.suspendLoop(600)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_15m -> {
uel.log("DISCONNECT 15m")
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15))
loopPlugin.disconnectPump(15, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_30m -> {
uel.log("DISCONNECT 30m")
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30))
loopPlugin.disconnectPump(30, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_1h -> {
uel.log("DISCONNECT 1h")
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(1))
loopPlugin.disconnectPump(60, profile)
sp.putBoolean(R.string.key_objectiveusedisconnect, true)
rxBus.send(EventRefreshOverview("suspendmenu"))
@ -352,14 +354,14 @@ class LoopDialog : DaggerDialogFragment() {
}
R.id.overview_disconnect_2h -> {
uel.log("DISCONNECT 2h")
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2))
loopPlugin.disconnectPump(120, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_3h -> {
uel.log("DISCONNECT 3h")
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3))
loopPlugin.disconnectPump(180, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true

View file

@ -8,15 +8,18 @@ import android.widget.ArrayAdapter
import com.google.common.base.Joiner
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.databinding.DialogProfileswitchBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
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 java.text.DecimalFormat
import java.util.*
import javax.inject.Inject
@ -25,12 +28,14 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger
private var profileIndex: Int? = null
private val disposable = CompositeDisposable()
private var _binding: DialogProfileswitchBinding? = null
// This property is only valid between onCreateView and
@ -66,7 +71,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
// profile
context?.let { context ->
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
?: return
val profileList = profileStore.getProfileList()
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
@ -76,17 +81,17 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
binding.profile.setSelection(profileIndex as Int)
else
for (p in profileList.indices)
if (profileList[p] == profileFunction.getProfileName(false))
if (profileList[p] == profileFunction.getOriginalProfileName())
binding.profile.setSelection(p)
} ?: return
treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now())?.let { ps ->
if (ps.isCPP) {
profileFunction.getProfile()?.let { profile ->
if (profile.percentage != 100 || profile.timeshift != 0) {
binding.reuselayout.visibility = View.VISIBLE
binding.reusebutton.text = resourceHelper.gs(R.string.reuse_profile_pct_hours, ps.percentage, ps.timeshift)
binding.reusebutton.text = resourceHelper.gs(R.string.reuse_profile_pct_hours, profile.percentage, profile.timeshift)
binding.reusebutton.setOnClickListener {
binding.percentage.value = ps.percentage.toDouble()
binding.timeshift.value = ps.timeshift.toDouble()
binding.percentage.value = profile.percentage.toDouble()
binding.timeshift.value = profile.timeshift.toDouble()
}
} else {
binding.reuselayout.visibility = View.GONE
@ -96,20 +101,21 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
_binding = null
}
override fun submit(): Boolean {
if (_binding == null) return false
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
?: return false
val actions: LinkedList<String> = LinkedList()
val duration = binding.duration.value?.toInt() ?: return false
if (duration > 0)
if (duration > 0L)
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, duration))
val profile = binding.profile.selectedItem.toString()
actions.add(resourceHelper.gs(R.string.profile) + ": " + profile)
val profileName = binding.profile.selectedItem.toString()
actions.add(resourceHelper.gs(R.string.profile) + ": " + profileName)
val percent = binding.percentage.value.toInt()
if (percent != 100)
actions.add(resourceHelper.gs(R.string.percent) + ": " + percent + "%")
@ -124,8 +130,20 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
uel.log("PROFILE SWITCH", d1 = percent.toDouble(), i1 = timeShift, i2 = duration)
treatmentsPlugin.doProfileSwitch(profileStore, profile, duration, percent, timeShift, eventTime)
profileFunction.createProfileSwitch(profileStore,
profileName = profileName,
durationInMinutes = duration,
percentage = percent,
timeShiftInHours = timeShift,
timestamp = eventTime)
uel.log(Action.PROFILE_SWITCH,
Sources.ProfileSwitchDialog,
notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.SimpleString(profileName),
ValueWithUnit.Percent(percent),
ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 },
ValueWithUnit.Minute(duration).takeIf { duration != 0 })
})
}
return true

View file

@ -8,19 +8,18 @@ import android.view.ViewGroup
import com.google.common.base.Joiner
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.DialogTempbasalBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.PumpDescription
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.text.DecimalFormat
import java.util.*
@ -32,7 +31,7 @@ class TempBasalDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var ctx: Context
@Inject lateinit var uel: UserEntryLogger
@ -72,7 +71,7 @@ class TempBasalDialog : DialogFragmentWithDate() {
?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, binding.okcancel.ok)
binding.basalabsoluteinput.setParams(savedInstanceState?.getDouble("basalabsoluteinput")
?: profile.basal, 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok)
?: profile.getBasal(), 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok)
val tempDurationStep = pumpDescription.tempDurationStep.toDouble()
val tempMaxDuration = pumpDescription.tempMaxDuration.toDouble()
@ -125,11 +124,15 @@ class TempBasalDialog : DialogFragmentWithDate() {
}
}
if (isPercentPump) {
uel.log("TEMP BASAL", d1 = percent.toDouble(), i1 = durationInMinutes)
commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, callback)
uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog,
ValueWithUnit.Percent(percent),
ValueWithUnit.Minute(durationInMinutes))
commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
} else {
uel.log("TEMP BASAL", d1 = absolute, i1 = durationInMinutes)
commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, callback)
uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog,
ValueWithUnit.Insulin(absolute),
ValueWithUnit.Minute(durationInMinutes))
commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
}
})
}

View file

@ -9,21 +9,30 @@ import com.google.common.base.Joiner
import com.google.common.collect.Lists
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction
import info.nightscout.androidaps.databinding.DialogTemptargetBinding
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.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.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class TempTargetDialog : DialogFragmentWithDate() {
@ -32,12 +41,13 @@ class TempTargetDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private lateinit var reasonList: List<String>
private val disposable = CompositeDisposable()
private var _binding: DialogTemptargetBinding? = null
// This property is only valid between onCreateView and
@ -47,7 +57,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("duration", binding.duration.value)
savedInstanceState.putDouble("temptarget", binding.temptarget.value)
savedInstanceState.putDouble("tempTarget", binding.temptarget.value)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@ -63,23 +73,23 @@ class TempTargetDialog : DialogFragmentWithDate() {
binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok)
if (profileFunction.getUnits() == Constants.MMOL)
if (profileFunction.getUnits() == GlucoseUnit.MMOL)
binding.temptarget.setParams(
savedInstanceState?.getDouble("temptarget")
savedInstanceState?.getDouble("tempTarget")
?: 8.0,
Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok)
else
binding.temptarget.setParams(
savedInstanceState?.getDouble("temptarget")
savedInstanceState?.getDouble("tempTarget")
?: 144.0,
Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
val units = profileFunction.getUnits()
binding.units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
binding.units.text = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
// temp target
context?.let { context ->
if (activePlugin.activeTreatments.tempTargetFromHistory != null)
if (repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing)
binding.targetCancel.visibility = View.VISIBLE
else
binding.targetCancel.visibility = View.GONE
@ -126,30 +136,35 @@ class TempTargetDialog : DialogFragmentWithDate() {
binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.eatingsoon)))
}
R.id.activity -> {
R.id.activity -> {
binding.temptarget.value = defaultValueHelper.determineActivityTT()
binding.duration.value = defaultValueHelper.determineActivityTTDuration().toDouble()
binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.activity)))
}
R.id.hypo -> {
R.id.hypo -> {
binding.temptarget.value = defaultValueHelper.determineHypoTT()
binding.duration.value = defaultValueHelper.determineHypoTTDuration().toDouble()
binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.hypo)))
}
R.id.cancel -> {
binding.duration.value = 0.0
}
}
}
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
_binding = null
}
override fun submit(): Boolean {
if (_binding == null) return false
val actions: LinkedList<String> = LinkedList()
val reason = binding.reason.selectedItem?.toString() ?: return false
val unitResId = if (profileFunction.getUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol
var reason = binding.reason.selectedItem?.toString() ?: return false
val unitResId = if (profileFunction.getUnits() == GlucoseUnit.MGDL) R.string.mgdl else R.string.mmol
val target = binding.temptarget.value
val duration = binding.duration.value.toInt()
if (target != 0.0 && duration != 0) {
@ -158,30 +173,48 @@ class TempTargetDialog : DialogFragmentWithDate() {
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, duration))
} else {
actions.add(resourceHelper.gs(R.string.stoptemptarget))
reason = resourceHelper.gs(R.string.stoptemptarget)
}
if (eventTimeChanged)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
uel.log("TT", d1 = target, i1 = duration)
if (target == 0.0 || duration == 0) {
val tempTarget = TempTarget()
.date(eventTime)
.duration(0)
.low(0.0).high(0.0)
.source(Source.USER)
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
} else {
val tempTarget = TempTarget()
.date(eventTime)
.duration(duration)
.reason(reason)
.source(Source.USER)
.low(Profile.toMgdl(target, profileFunction.getUnits()))
.high(Profile.toMgdl(target, profileFunction.getUnits()))
treatmentsPlugin.addToHistoryTempTarget(tempTarget)
val units = profileFunction.getUnits()
when(reason) {
resourceHelper.gs(R.string.eatingsoon) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
resourceHelper.gs(R.string.activity) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
resourceHelper.gs(R.string.hypo) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
resourceHelper.gs(R.string.manual) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.CUSTOM), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration))
resourceHelper.gs(R.string.stoptemptarget) -> uel.log(Action.CANCEL_TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged })
}
if (target == 0.0 || duration == 0) {
disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(eventTime))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
})
} else {
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(
timestamp = eventTime,
duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
reason = when (reason) {
resourceHelper.gs(R.string.eatingsoon) -> TemporaryTarget.Reason.EATING_SOON
resourceHelper.gs(R.string.activity) -> TemporaryTarget.Reason.ACTIVITY
resourceHelper.gs(R.string.hypo) -> TemporaryTarget.Reason.HYPOGLYCEMIA
else -> TemporaryTarget.Reason.CUSTOM
},
lowTarget = Profile.toMgdl(target, profileFunction.getUnits()),
highTarget = Profile.toMgdl(target, profileFunction.getUnits())
)).subscribe({ result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
})
}
if (duration == 10) sp.putBoolean(R.string.key_objectiveusetemptarget, true)
})
}

View file

@ -8,16 +8,19 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.common.base.Joiner
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.DialogTreatmentBinding
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
@ -26,8 +29,10 @@ import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject
@ -37,11 +42,14 @@ class TreatmentDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var ctx: Context
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
@ -129,24 +137,51 @@ class TreatmentDialog : DialogFragmentWithDate() {
if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
uel.log("TREATMENT", d1 = insulin, i1 = carbs)
val action = when {
insulinAfterConstraints.equals(0.0) -> Action.CARBS
carbsAfterConstraints.equals(0) -> Action.BOLUS
else -> Action.TREATMENT
}
val detailedBolusInfo = DetailedBolusInfo()
if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION
if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS
if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CARBS_CORRECTION
if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.carbs = carbsAfterConstraints.toDouble()
detailedBolusInfo.context = context
detailedBolusInfo.source = Source.USER
if (!(recordOnlyChecked && (detailedBolusInfo.insulin > 0 || pumpDescription.storesCarbInfo))) {
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
if (recordOnlyChecked) {
uel.log(action, Sources.TreatmentDialog, if (insulinAfterConstraints != 0.0) resourceHelper.gs(R.string.record) else "",
ValueWithUnit.Timestamp(detailedBolusInfo.timestamp).takeIf { eventTimeChanged },
ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.record)).takeIf { insulinAfterConstraints != 0.0 },
ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 },
ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbsAfterConstraints != 0 })
if (detailedBolusInfo.insulin > 0)
disposable += repository.runTransactionForResult(detailedBolusInfo.insertBolusTransaction())
.subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) }
)
if (detailedBolusInfo.carbs > 0)
disposable += repository.runTransactionForResult(detailedBolusInfo.insertCarbsTransaction())
.subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) }
)
} else {
if (detailedBolusInfo.insulin > 0) {
uel.log(action, Sources.TreatmentDialog,
ValueWithUnit.Insulin(insulinAfterConstraints),
ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbsAfterConstraints != 0 })
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
}
})
} else
activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false)
})
} else
uel.log(action, Sources.TreatmentDialog,
ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbs != 0 })
}
})
}
} else

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -16,29 +17,27 @@ import androidx.fragment.app.FragmentManager
import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.databinding.DialogWizardBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.valueToUnits
import info.nightscout.androidaps.utils.wizard.BolusWizard
import io.reactivex.disposables.CompositeDisposable
import java.text.DecimalFormat
@ -52,15 +51,16 @@ class WizardDialog : DaggerDialogFragment() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var mainApp: MainApp
@Inject lateinit var ctx: Context
@Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil
private var wizard: BolusWizard? = null
@ -125,7 +125,7 @@ class WizardDialog : DaggerDialogFragment() {
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value()
val maxCorrection = constraintChecker.getMaxBolusAllowed().value()
if (profileFunction.getUnits() == Constants.MGDL)
if (profileFunction.getUnits() == GlucoseUnit.MGDL)
binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input")
?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher)
else
@ -179,7 +179,7 @@ class WizardDialog : DaggerDialogFragment() {
// profile spinner
binding.profile.onItemSelectedListener = object : OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofileselected))
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofileselected))
binding.ok.visibility = View.GONE
}
@ -207,7 +207,7 @@ class WizardDialog : DaggerDialogFragment() {
private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) {
saveCheckedStates()
binding.ttcheckbox.isEnabled = binding.bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null
binding.ttcheckbox.isEnabled = binding.bgcheckbox.isChecked && repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
if (buttonView.id == binding.cobcheckbox.id)
processCobCheckBox()
calculateInsulin()
@ -241,10 +241,10 @@ class WizardDialog : DaggerDialogFragment() {
private fun initDialog() {
val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
if (profile == null || profileStore == null) {
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofile))
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofile))
dismiss()
return
}
@ -257,27 +257,19 @@ class WizardDialog : DaggerDialogFragment() {
} ?: return
val units = profileFunction.getUnits()
binding.bgunits.text = units
if (units == Constants.MGDL)
binding.bgunits.text = units.asText
if (units == GlucoseUnit.MGDL)
binding.bgInput.setStep(1.0)
else
binding.bgInput.setStep(0.1)
// Set BG if not old
val lastBg = iobCobCalculatorPlugin.actualBg()
if (lastBg != null) {
binding.bgInput.value = lastBg.valueToUnits(units)
} else {
binding.bgInput.value = 0.0
}
binding.ttcheckbox.isEnabled = treatmentsPlugin.tempTargetFromHistory != null
binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units) ?: 0.0
binding.ttcheckbox.isEnabled = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
// IOB calculation
treatmentsPlugin.updateTotalIOBTreatments()
val bolusIob = treatmentsPlugin.lastCalculationTreatments.round()
treatmentsPlugin.updateTotalIOBTempBasals()
val basalIob = treatmentsPlugin.lastCalculationTempBasals.round()
val bolusIob = iobCobCalculator.calculateIobFromBolus().round()
val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob)
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob)
@ -288,7 +280,7 @@ class WizardDialog : DaggerDialogFragment() {
}
private fun calculateInsulin() {
val profileStore = activePlugin.activeProfileInterface.profile
val profileStore = activePlugin.activeProfileSource.profile
if (binding.profile.selectedItem == null || profileStore == null)
return // not initialized yet
var profileName = binding.profile.selectedItem.toString()
@ -297,7 +289,7 @@ class WizardDialog : DaggerDialogFragment() {
specificProfile = profileFunction.getProfile()
profileName = profileFunction.getProfileName()
} else
specificProfile = profileStore.getSpecificProfile(profileName)
specificProfile = profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) }
if (specificProfile == null) return
@ -308,24 +300,25 @@ class WizardDialog : DaggerDialogFragment() {
val carbsAfterConstraint = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
if (abs(carbs - carbsAfterConstraint) > 0.01) {
binding.carbsInput.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied))
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.carbsconstraintapplied))
return
}
bg = if (binding.bgcheckbox.isChecked) bg else 0.0
val tempTarget = if (binding.ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null
val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
val tempTarget = if (binding.ttcheckbox.isChecked && dbRecord is ValueWrapper.Existing) dbRecord.value else null
// COB
var cob = 0.0
if (binding.cobcheckbox.isChecked) {
val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard COB")
val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard COB")
cobInfo.displayCob?.let { cob = it }
}
val carbTime = SafeParse.stringToInt(binding.carbTimeInput.text)
wizard = BolusWizard(mainApp).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction,
sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(),
wizard = BolusWizard(injector).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction,
sp.getInt(R.string.key_boluswizard_percentage, 100),
binding.bgcheckbox.isChecked,
binding.cobcheckbox.isChecked,
binding.bolusiobcheckbox.isChecked,
@ -337,7 +330,7 @@ class WizardDialog : DaggerDialogFragment() {
binding.notes.text.toString(), carbTime)
wizard?.let { wizard ->
binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), valueToUnitsToString(Profile.toMgdl(bg, profileFunction.getUnits()), profileFunction.getUnits()), wizard.sens)
binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), valueToUnitsToString(Profile.toMgdl(bg, profileFunction.getUnits()), profileFunction.getUnits().asText), wizard.sens)
binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG)
binding.carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic)

View file

@ -9,12 +9,12 @@ import android.view.WindowManager
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.entities.BolusCalculatorResult
import info.nightscout.androidaps.databinding.DialogWizardinfoBinding
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONObject
import javax.inject.Inject
class WizardInfoDialog : DaggerDialogFragment() {
@ -22,10 +22,10 @@ class WizardInfoDialog : DaggerDialogFragment() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
private var json: JSONObject? = null
private lateinit var data: BolusCalculatorResult
fun setData(json: JSONObject) {
this.json = json
fun setData(bolusCalculatorResult: BolusCalculatorResult) {
this.data = bolusCalculatorResult
}
private var _binding: DialogWizardinfoBinding? = null
@ -49,44 +49,42 @@ class WizardInfoDialog : DaggerDialogFragment() {
binding.close.setOnClickListener { dismiss() }
val units = profileFunction.getUnits()
val bgString =
if (units == Constants.MGDL) DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "bg"))
else DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg"))
val bgString = Profile.toUnitsString(data.glucoseValue, data.glucoseValue * Constants.MGDL_TO_MMOLL, units)
// BG
binding.bg.text = resourceHelper.gs(R.string.format_bg_isf, bgString, JsonHelper.safeGetDouble(json, "isf"))
binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinbg"))
binding.bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused")
binding.ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused")
binding.bg.text = resourceHelper.gs(R.string.format_bg_isf, bgString, data.isf)
binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.glucoseInsulin)
binding.bgcheckbox.isChecked = data.wasGlucoseUsed
binding.ttcheckbox.isChecked = data.wasTempTargetUsed
// Trend
binding.bgtrend.text = JsonHelper.safeGetString(json, "trend")
binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulintrend"))
binding.bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused")
binding.bgtrend.text = DecimalFormatter.to1Decimal(data.glucoseTrend)
binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.trendInsulin)
binding.bgtrendcheckbox.isChecked = data.wasTrendUsed
// COB
binding.cob.text = resourceHelper.gs(R.string.format_cob_ic, JsonHelper.safeGetDouble(json, "cob"), JsonHelper.safeGetDouble(json, "ic"))
binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincob"))
binding.cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused")
binding.cob.text = resourceHelper.gs(R.string.format_cob_ic, data.cob, data.ic)
binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.cobInsulin)
binding.cobcheckbox.isChecked = data.wasCOBUsed
// Bolus IOB
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "bolusiob"))
binding.bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused")
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.bolusIOB)
binding.bolusiobcheckbox.isChecked = data.wasBolusIOBUsed
// Basal IOB
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "basaliob"))
binding.basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused")
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.basalIOB)
binding.basaliobcheckbox.isChecked = data.wasBasalIOBUsed
// Superbolus
binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinsuperbolus"))
binding.sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused")
binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.superbolusInsulin)
binding.sbcheckbox.isChecked = data.wasSuperbolusUsed
// Carbs
binding.carbs.text = resourceHelper.gs(R.string.format_carbs_ic, JsonHelper.safeGetDouble(json, "carbs"), JsonHelper.safeGetDouble(json, "ic"))
binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincarbs"))
binding.carbs.text = resourceHelper.gs(R.string.format_carbs_ic, data.carbs, data.ic)
binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.carbsInsulin)
// Correction
binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "othercorrection"))
binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.otherCorrection)
// Profile
binding.profile.text = JsonHelper.safeGetString(json, "profile")
binding.profile.text = data.profileName
// Notes
binding.notes.text = JsonHelper.safeGetString(json, "notes")
binding.notes.text = data.note
// Percentage
binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, (JsonHelper.safeGetInt(json, "percentageCorrection", 100)))
binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, data.percentageCorrection)
// Total
binding.totalinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulin"))
binding.totalinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.totalInsulin)
}
override fun onStart() {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,24 +14,23 @@ 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.interfaces.ActivePluginProvider
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.EventAutosensBgLoaded
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
@ -51,8 +50,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var iobCobCalculatorPluginHistory: IobCobCalculatorPluginHistory
@Inject lateinit var treatmentsPluginHistory: TreatmentsPluginHistory
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var overviewMenus: OverviewMenus
@ -181,16 +179,6 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
}
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventAutosensBgLoaded::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
// catch only events from iobCobCalculatorPluginHistory
if (it.cause is EventCustomCalculationFinished) {
updateGUI("EventAutosensCalculationFinished", bgOnly = true)
}
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.main)
@ -266,7 +254,6 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
private fun runCalculation(from: String) {
lifecycleScope.launch(Dispatchers.Default) {
treatmentsPluginHistory.initializeData(start - T.hours(8).msecs())
val end = start + T.hours(rangeToDisplay.toLong()).msecs()
iobCobCalculatorPluginHistory.stopCalculation(from)
iobCobCalculatorPluginHistory.clearCache()
@ -292,7 +279,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
if (destroyed) return@launch
binding.date.text = dateUtil.dateAndTimeString(start)
binding.zoom.text = rangeToDisplay.toString()
val graphData = GraphData(injector, binding.bggraph, iobCobCalculatorPluginHistory, treatmentsPluginHistory)
val graphData = GraphData(injector, binding.bggraph, iobCobCalculatorPluginHistory)
val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
// do preparation in different thread
@ -306,7 +293,8 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
graphData.addInRangeArea(fromTime, toTime, lowLine, highLine)
// **** BG ****
graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null)
graphData.addBgReadings(fromTime, toTime, highLine, null)
if (buildHelper.isDev()) graphData.addBucketedData(fromTime, toTime)
// add target line
graphData.addTargetLine(fromTime, toTime, profile, null)
@ -327,7 +315,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
// ------------------ 2nd graph
synchronized(graphLock) {
for (g in 0 until secondaryGraphs.size) {
val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPluginHistory, treatmentsPluginHistory)
val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPluginHistory)
var useIobForScale = false
var useCobForScale = false
var useDevForScale = false

View file

@ -2,7 +2,7 @@ package info.nightscout.androidaps.historyBrowser
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
@ -27,8 +27,7 @@ class IobCobCalculatorPluginHistory @Inject constructor(
sp: SP,
resourceHelper: ResourceHelper,
profileFunction: ProfileFunction,
activePlugin: ActivePluginProvider,
treatmentsPluginHistory: TreatmentsPluginHistory,
activePlugin: ActivePlugin,
sensitivityOref1Plugin: SensitivityOref1Plugin,
sensitivityAAPSPlugin: SensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
@ -36,7 +35,7 @@ class IobCobCalculatorPluginHistory @Inject constructor(
dateUtil: DateUtil,
repository: AppRepository
) : IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction,
activePlugin, treatmentsPluginHistory, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository) {
activePlugin, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository) {
override fun onStart() { // do not attach to rxbus
}

View file

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

View file

@ -98,7 +98,6 @@ class LoopFragment : DaggerFragment() {
binding.constraintsprocessed.text = it.constraintsProcessed?.toSpanned() ?: ""
binding.source.text = it.source ?: ""
binding.lastrun.text = dateUtil.dateAndTimeString(it.lastAPSRun)
?: ""
binding.smbrequestTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBRequest)
binding.smbexecutionTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBEnact)
binding.tbrrequestTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBRRequest)

View file

@ -13,31 +13,36 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.*
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction
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.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui
import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
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.notifications.Notification
import info.nightscout.androidaps.plugins.general.wear.events.EventWearDoAction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAction
import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.commands.Command
import info.nightscout.androidaps.receivers.ReceiverStatusStore
@ -45,12 +50,15 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.extensions.buildDeviceStatus
import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.convertedToPercent
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
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 org.json.JSONException
import org.json.JSONObject
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.abs
@ -68,14 +76,15 @@ open class LoopPlugin @Inject constructor(
private val profileFunction: ProfileFunction,
private val context: Context,
private val commandQueue: CommandQueueProvider,
private val activePlugin: ActivePluginProvider,
private val treatmentsPlugin: TreatmentsPlugin,
private val activePlugin: ActivePlugin,
private val virtualPumpPlugin: VirtualPumpPlugin,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
private val iobCobCalculator: IobCobCalculator,
private val receiverStatusStore: ReceiverStatusStore,
private val fabricPrivacy: FabricPrivacy,
private val nsUpload: NSUpload,
private val hardLimits: HardLimits
private val dateUtil: DateUtil,
private val uel: UserEntryLogger,
private val repository: AppRepository,
private val runningConfiguration: RunningConfiguration
) : PluginBase(PluginDescription()
.mainType(PluginType.LOOP)
.fragmentClass(LoopFragment::class.java.name)
@ -93,6 +102,7 @@ open class LoopPlugin @Inject constructor(
private var carbsSuggestionsSuspendedUntil: Long = 0
private var prevCarbsreq = 0
override var lastRun: LastRun? = null
override fun onStart() {
createNotificationChannel()
super.onStart()
@ -114,7 +124,7 @@ open class LoopPlugin @Inject constructor(
.subscribe({ event: EventAutosensCalculationFinished ->
// Autosens calculation not triggered by a new BG
if (event.cause !is EventNewBG) return@subscribe
val glucoseValue = iobCobCalculatorPlugin.actualBg() ?: return@subscribe
val glucoseValue = iobCobCalculator.ads.actualBg() ?: return@subscribe
// BG outdated
// already looped with that value
if (glucoseValue.timestamp <= lastBgTriggeredRun) return@subscribe
@ -196,7 +206,7 @@ open class LoopPlugin @Inject constructor(
val apsMode = sp.getString(R.string.key_aps_mode, "open")
val pump = activePlugin.activePump
var isLGS = false
if (!isSuspended && !pump.isSuspended()) if (closedLoopEnabled.value()) if (maxIobAllowed == hardLimits.MAXIOB_LGS || apsMode == "lgs") isLGS = true
if (!isSuspended && !pump.isSuspended()) if (closedLoopEnabled.value()) if (maxIobAllowed == HardLimits.MAX_IOB_LGS || apsMode == "lgs") isLGS = true
return isLGS
}
@ -230,7 +240,9 @@ open class LoopPlugin @Inject constructor(
private fun treatmentTimeThreshold(durationMinutes: Int): Boolean {
val threshold = System.currentTimeMillis() + durationMinutes * 60 * 1000
var bool = false
if (treatmentsPlugin.lastBolusTime > threshold || treatmentsPlugin.lastCarbTime > threshold) bool = true
val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L
val lastCarbsTime = repository.getLastCarbsRecord()?.timestamp ?: 0L
if (lastBolusTime > threshold || lastCarbsTime > threshold) bool = true
return bool
}
@ -280,7 +292,7 @@ open class LoopPlugin @Inject constructor(
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
apsResult.usePercent = true
}
apsResult.percent = (apsResult.rate / profile.basal * 100).toInt()
apsResult.percent = (apsResult.rate / profile.getBasal() * 100).toInt()
// check rate for constraints
val resultAfterConstraints = apsResult.newAndClone(injector)
@ -292,155 +304,162 @@ open class LoopPlugin @Inject constructor(
resultAfterConstraints.smb = constraintChecker.applyBolusConstraints(resultAfterConstraints.smbConstraint!!).value()
// safety check for multiple SMBs
val lastBolusTime = treatmentsPlugin.lastBolusTime
val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L
if (lastBolusTime != 0L && lastBolusTime + T.mins(3).msecs() > System.currentTimeMillis()) {
aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval")
resultAfterConstraints.smb = 0.0
}
if (lastRun != null && lastRun!!.constraintsProcessed != null) {
prevCarbsreq = lastRun!!.constraintsProcessed!!.carbsReq
}
if (lastRun == null) lastRun = LastRun()
lastRun!!.request = apsResult
lastRun!!.constraintsProcessed = resultAfterConstraints
lastRun!!.lastAPSRun = DateUtil.now()
lastRun!!.source = (usedAPS as PluginBase).name
lastRun!!.tbrSetByPump = null
lastRun!!.smbSetByPump = null
lastRun!!.lastTBREnact = 0
lastRun!!.lastTBRRequest = 0
lastRun!!.lastSMBEnact = 0
lastRun!!.lastSMBRequest = 0
nsUpload.uploadDeviceStatus(this, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
if (isSuspended) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended)))
return
}
if (pump.isSuspended()) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended)))
return
}
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
if (closedLoopEnabled.value()) {
if (allowNotification) {
if (resultAfterConstraints.isCarbsRequired
&& resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0) && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimeThreshold(-15)) {
if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL)
rxBus.send(EventNewNotification(carbReqLocal))
}
if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) {
nsUpload.uploadError(resultAfterConstraints.carbsRequiredText)
}
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 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 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 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)
.setContentTitle(resourceHelper.gs(R.string.carbssuggestion))
.setContentText(resultAfterConstraints.carbsRequiredText)
.setAutoCancel(true)
.setPriority(Notification.IMPORTANCE_HIGH)
.setCategory(Notification.CATEGORY_ALARM)
.addAction(actionIgnore5m)
.addAction(actionIgnore15m)
.addAction(actionIgnore30m)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
prevCarbsreq = lastRun?.constraintsProcessed?.carbsReq ?: prevCarbsreq
lastRun = (lastRun ?: LastRun()).also { lastRun ->
lastRun.request = apsResult
lastRun.constraintsProcessed = resultAfterConstraints
lastRun.lastAPSRun = dateUtil.now()
lastRun.source = (usedAPS as PluginBase).name
lastRun.tbrSetByPump = null
lastRun.smbSetByPump = null
lastRun.lastTBREnact = 0
lastRun.lastTBRRequest = 0
lastRun.lastSMBEnact = 0
lastRun.lastSMBRequest = 0
buildDeviceStatus(dateUtil, this, iobCobCalculator, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
repository.insert(it)
}
// mId allows you to update the notification later on.
mNotificationManager.notify(Constants.notificationID, builder.build())
rxBus.send(EventNewOpenLoopNotification())
if (isSuspended) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended)))
return
}
if (pump.isSuspended()) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended)))
return
}
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
if (closedLoopEnabled.value()) {
if (allowNotification) {
if (resultAfterConstraints.isCarbsRequired
&& resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0) && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimeThreshold(-15)) {
if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL)
rxBus.send(EventNewNotification(carbReqLocal))
}
if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) {
disposable += repository.runTransaction(InsertTherapyEventAnnouncementTransaction(resultAfterConstraints.carbsRequiredText)).subscribe()
}
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 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 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 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)
.setContentTitle(resourceHelper.gs(R.string.carbssuggestion))
.setContentText(resultAfterConstraints.carbsRequiredText)
.setAutoCancel(true)
.setPriority(Notification.IMPORTANCE_HIGH)
.setCategory(Notification.CATEGORY_ALARM)
.addAction(actionIgnore5m)
.addAction(actionIgnore15m)
.addAction(actionIgnore30m)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//only send to wear if Native notifications are turned off
if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
// Send to Wear
rxBus.send(EventWearDoAction("changeRequest"))
// mId allows you to update the notification later on.
mNotificationManager.notify(Constants.notificationID, builder.build())
uel.log(Action.CAREPORTAL, Sources.Loop, resourceHelper.gs(R.string.carbsreq, resultAfterConstraints.carbsReq, resultAfterConstraints.carbsReqWithin),
ValueWithUnit.Gram(resultAfterConstraints.carbsReq),
ValueWithUnit.Minute(resultAfterConstraints.carbsReqWithin))
rxBus.send(EventNewOpenLoopNotification())
//only send to wear if Native notifications are turned off
if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
// Send to Wear
rxBus.send(EventWearInitiateAction("changeRequest"))
}
}
} else {
//If carbs were required previously, but are no longer needed, dismiss notifications
if (prevCarbsreq > 0) {
dismissSuggestion()
rxBus.send(EventDismissNotification(Notification.CARBS_REQUIRED))
}
}
} else {
//If carbs were required previously, but are no longer needed, dismiss notifications
if (prevCarbsreq > 0) {
dismissSuggestion()
rxBus.send(EventDismissNotification(Notification.CARBS_REQUIRED))
}
}
}
if (resultAfterConstraints.isChangeRequested
&& !commandQueue.bolusInQueue()
&& !commandQueue.isRunning(Command.CommandType.BOLUS)) {
val waiting = PumpEnactResult(injector)
waiting.queued = true
if (resultAfterConstraints.tempBasalRequested) lastRun!!.tbrSetByPump = waiting
if (resultAfterConstraints.bolusRequested) lastRun!!.smbSetByPump = waiting
rxBus.send(EventLoopUpdateGui())
fabricPrivacy.logCustom("APSRequest")
applyTBRRequest(resultAfterConstraints, profile, object : Callback() {
override fun run() {
if (result.enacted || result.success) {
lastRun!!.tbrSetByPump = result
lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
lastRun!!.lastTBREnact = DateUtil.now()
rxBus.send(EventLoopUpdateGui())
applySMBRequest(resultAfterConstraints, object : Callback() {
override fun run() {
// Callback is only called if a bolus was actually requested
if (result.enacted || result.success) {
lastRun!!.smbSetByPump = result
lastRun!!.lastSMBRequest = lastRun!!.lastAPSRun
lastRun!!.lastSMBEnact = DateUtil.now()
} else {
Thread {
SystemClock.sleep(1000)
invoke("tempBasalFallback", allowNotification, true)
}.start()
if (resultAfterConstraints.isChangeRequested
&& !commandQueue.bolusInQueue()
&& !commandQueue.isRunning(Command.CommandType.BOLUS)) {
val waiting = PumpEnactResult(injector)
waiting.queued = true
if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting
if (resultAfterConstraints.bolusRequested) lastRun.smbSetByPump = waiting
rxBus.send(EventLoopUpdateGui())
fabricPrivacy.logCustom("APSRequest")
applyTBRRequest(resultAfterConstraints, profile, object : Callback() {
override fun run() {
if (result.enacted || result.success) {
lastRun.tbrSetByPump = result
lastRun.lastTBRRequest = lastRun.lastAPSRun
lastRun.lastTBREnact = dateUtil.now()
rxBus.send(EventLoopUpdateGui())
applySMBRequest(resultAfterConstraints, object : Callback() {
override fun run() {
// Callback is only called if a bolus was actually requested
if (result.enacted || result.success) {
lastRun.smbSetByPump = result
lastRun.lastSMBRequest = lastRun.lastAPSRun
lastRun.lastSMBEnact = dateUtil.now()
} else {
Thread {
SystemClock.sleep(1000)
invoke("tempBasalFallback", allowNotification, true)
}.start()
}
rxBus.send(EventLoopUpdateGui())
}
rxBus.send(EventLoopUpdateGui())
}
})
} else {
lastRun!!.tbrSetByPump = result
lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
})
} else {
lastRun.tbrSetByPump = result
lastRun.lastTBRRequest = lastRun.lastAPSRun
}
rxBus.send(EventLoopUpdateGui())
}
rxBus.send(EventLoopUpdateGui())
}
})
} else {
lastRun!!.tbrSetByPump = null
lastRun!!.smbSetByPump = null
}
} else {
if (resultAfterConstraints.isChangeRequested && allowNotification) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
builder.setSmallIcon(R.drawable.notif_icon)
.setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion))
.setContentText(resultAfterConstraints.toString())
.setAutoCancel(true)
.setPriority(Notification.IMPORTANCE_HIGH)
.setCategory(Notification.CATEGORY_ALARM)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
if (sp.getBoolean(R.string.key_wear_control, false)) {
builder.setLocalOnly(true)
})
} else {
lastRun.tbrSetByPump = null
lastRun.smbSetByPump = null
}
} else {
if (resultAfterConstraints.isChangeRequested && allowNotification) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
builder.setSmallIcon(R.drawable.notif_icon)
.setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion))
.setContentText(resultAfterConstraints.toString())
.setAutoCancel(true)
.setPriority(Notification.IMPORTANCE_HIGH)
.setCategory(Notification.CATEGORY_ALARM)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
if (sp.getBoolean(R.string.key_wear_control, false)) {
builder.setLocalOnly(true)
}
presentSuggestion(builder)
} else if (allowNotification) {
dismissSuggestion()
}
presentSuggestion(builder)
} else if (allowNotification) {
dismissSuggestion()
}
rxBus.send(EventLoopUpdateGui())
}
rxBus.send(EventLoopUpdateGui())
} finally {
aapsLogger.debug(LTag.APS, "invoke end")
}
@ -472,32 +491,39 @@ open class LoopPlugin @Inject constructor(
rxBus.send(EventNewOpenLoopNotification())
// Send to Wear
rxBus.send(EventWearDoAction("changeRequest"))
rxBus.send(EventWearInitiateAction("changeRequest"))
}
private fun dismissSuggestion() {
// dismiss notifications
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(Constants.notificationID)
rxBus.send(EventWearDoAction("cancelChangeRequest"))
rxBus.send(EventWearConfirmAction("cancelChangeRequest"))
}
fun acceptChangeRequest() {
val profile = profileFunction.getProfile()
val lp = this
applyTBRRequest(lastRun!!.constraintsProcessed, profile, object : Callback() {
override fun run() {
if (result.enacted) {
lastRun!!.tbrSetByPump = result
lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
lastRun!!.lastTBREnact = DateUtil.now()
lastRun!!.lastOpenModeAccept = DateUtil.now()
nsUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
sp.incInt(R.string.key_ObjectivesmanualEnacts)
}
rxBus.send(EventAcceptOpenLoopChange())
val profile = profileFunction.getProfile() ?: return
lastRun?.let { lastRun ->
lastRun.constraintsProcessed?.let { constraintsProcessed ->
applyTBRRequest(constraintsProcessed, profile, object : Callback() {
override fun run() {
if (result.enacted) {
lastRun.tbrSetByPump = result
lastRun.lastTBRRequest = lastRun.lastAPSRun
lastRun.lastTBREnact = dateUtil.now()
lastRun.lastOpenModeAccept = dateUtil.now()
buildDeviceStatus(dateUtil, this@LoopPlugin, iobCobCalculator, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
repository.insert(it)
}
sp.incInt(R.string.key_ObjectivesmanualEnacts)
}
rxBus.send(EventAcceptOpenLoopChange())
}
})
}
})
}
fabricPrivacy.logCustom("AcceptTemp")
}
@ -505,62 +531,70 @@ open class LoopPlugin @Inject constructor(
* expect absolute request and allow both absolute and percent response based on pump capabilities
* TODO: update pump drivers to support APS request in %
*/
private fun applyTBRRequest(request: APSResult?, profile: Profile?, callback: Callback?) {
if (!request!!.tempBasalRequested) {
callback?.result(PumpEnactResult(injector).enacted(false).success(true).comment(resourceHelper.gs(R.string.nochangerequested)))?.run()
private fun applyTBRRequest(request: APSResult, profile: Profile, callback: Callback?) {
if (!request.tempBasalRequested) {
callback?.result(PumpEnactResult(injector).enacted(false).success(true).comment(R.string.nochangerequested))?.run()
return
}
val pump = activePlugin.activePump
if (!pump.isInitialized()) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpNotInitialized))
callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false))?.run()
callback?.result(PumpEnactResult(injector).comment(R.string.pumpNotInitialized).enacted(false).success(false))?.run()
return
}
if (pump.isSuspended()) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpsuspended))
callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false))?.run()
callback?.result(PumpEnactResult(injector).comment(R.string.pumpsuspended).enacted(false).success(false))?.run()
return
}
aapsLogger.debug(LTag.APS, "applyAPSRequest: $request")
val now = System.currentTimeMillis()
val activeTemp = treatmentsPlugin.getTempBasalFromHistory(now)
val activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(now)
if (request.usePercent && allowPercentage()) {
if (request.percent == 100 && request.duration == 0) {
if (activeTemp != null) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: cancelTempBasal()")
uel.log(Action.CANCEL_TEMP_BASAL, Sources.Loop)
commandQueue.cancelTempBasal(false, callback)
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).percent(request.percent).duration(0)
.enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly)))?.run()
.enacted(false).success(true).comment(R.string.basal_set_correctly))?.run()
}
} else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && request.percent == activeTemp.percentRate) {
} else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && request.percent == activeTemp.convertedToPercent(now, profile)) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
callback?.result(PumpEnactResult(injector).percent(request.percent)
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(resourceHelper.gs(R.string.let_temp_basal_run)))?.run()
.comment(R.string.let_temp_basal_run))?.run()
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: tempBasalPercent()")
commandQueue.tempBasalPercent(request.percent, request.duration, false, profile!!, callback)
uel.log(Action.TEMP_BASAL, Sources.Loop,
ValueWithUnit.Percent(request.percent),
ValueWithUnit.Minute(request.duration))
commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
}
} else {
if (request.rate == 0.0 && request.duration == 0 || abs(request.rate - pump.baseBasalRate) < pump.pumpDescription.basalStep) {
if (activeTemp != null) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: cancelTempBasal()")
uel.log(Action.CANCEL_TEMP_BASAL, Sources.Loop)
commandQueue.cancelTempBasal(false, callback)
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).absolute(request.rate).duration(0)
.enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly)))?.run()
.enacted(false).success(true).comment(R.string.basal_set_correctly))?.run()
}
} else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && abs(request.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.pumpDescription.basalStep) {
} else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && abs(request.rate - activeTemp.convertedToAbsolute(now, profile)) < pump.pumpDescription.basalStep) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
callback?.result(PumpEnactResult(injector).absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile))
callback?.result(PumpEnactResult(injector).absolute(activeTemp.convertedToAbsolute(now, profile))
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(resourceHelper.gs(R.string.let_temp_basal_run)))?.run()
.comment(R.string.let_temp_basal_run))?.run()
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()")
commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile!!, callback)
uel.log(Action.TEMP_BASAL, Sources.Loop,
ValueWithUnit.UnitPerHour(request.rate),
ValueWithUnit.Minute(request.duration))
commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
}
}
}
@ -570,35 +604,36 @@ open class LoopPlugin @Inject constructor(
return
}
val pump = activePlugin.activePump
val lastBolusTime = treatmentsPlugin.lastBolusTime
val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L
if (lastBolusTime != 0L && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) {
aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval")
callback?.result(PumpEnactResult(injector)
.comment(resourceHelper.gs(R.string.smb_frequency_exceeded))
.comment(R.string.smb_frequency_exceeded)
.enacted(false).success(false))?.run()
return
}
if (!pump.isInitialized()) {
aapsLogger.debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpNotInitialized))
callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false))?.run()
callback?.result(PumpEnactResult(injector).comment(R.string.pumpNotInitialized).enacted(false).success(false))?.run()
return
}
if (pump.isSuspended()) {
aapsLogger.debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpsuspended))
callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false))?.run()
callback?.result(PumpEnactResult(injector).comment(R.string.pumpsuspended).enacted(false).success(false))?.run()
return
}
aapsLogger.debug(LTag.APS, "applySMBRequest: $request")
// deliver SMB
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.lastKnownBolusTime = treatmentsPlugin.lastBolusTime
detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS
detailedBolusInfo.lastKnownBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L
detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
detailedBolusInfo.insulin = request.smb
detailedBolusInfo.isSMB = true
detailedBolusInfo.source = Source.USER
detailedBolusInfo.deliverAt = request.deliverAt
detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.SMB
detailedBolusInfo.deliverAtTheLatest = request.deliverAt
aapsLogger.debug(LTag.APS, "applyAPSRequest: bolus()")
if (request.smb > 0.0)
uel.log(Action.SMB, Sources.Loop, ValueWithUnit.Insulin(detailedBolusInfo.insulin))
commandQueue.bolus(detailedBolusInfo, callback)
}
@ -610,7 +645,7 @@ open class LoopPlugin @Inject constructor(
val pump = activePlugin.activePump
disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L)
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, 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)
@ -618,7 +653,7 @@ open class LoopPlugin @Inject constructor(
}
})
} else {
commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, 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)
@ -626,7 +661,7 @@ open class LoopPlugin @Inject constructor(
}
})
}
if (pump.pumpDescription.isExtendedBolusCapable && treatmentsPlugin.isInHistoryExtendedBoluslInProgress) {
if (pump.pumpDescription.isExtendedBolusCapable && iobCobCalculator.getExtendedBolus(dateUtil.now()) != null) {
commandQueue.cancelExtended(object : Callback() {
override fun run() {
if (!result.success) {
@ -651,20 +686,16 @@ open class LoopPlugin @Inject constructor(
}
override fun createOfflineEvent(durationInMinutes: Int) {
val data = JSONObject()
try {
data.put("eventType", CareportalEvent.OPENAPSOFFLINE)
data.put("duration", durationInMinutes)
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
val event = CareportalEvent(injector)
event.date = DateUtil.now()
event.source = Source.USER
event.eventType = CareportalEvent.OPENAPSOFFLINE
event.json = data.toString()
MainApp.getDbHelper().createOrUpdate(event)
nsUpload.uploadOpenAPSOffline(event)
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 {

View file

@ -1,11 +1,15 @@
package info.nightscout.androidaps.plugins.aps.openAPSAMA
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.data.MealData
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.getPassedDurationToTimeInMinutes
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -15,8 +19,6 @@ 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.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONException
@ -37,7 +39,7 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var openHumansUploader: OpenHumansUploader
private val mScriptReader: ScriptReader
@ -160,13 +162,13 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader
this.profile.put("max_iob", maxIob)
this.profile.put("dia", min(profile.dia, 3.0))
this.profile.put("type", "current")
this.profile.put("max_daily_basal", profile.maxDailyBasal)
this.profile.put("max_daily_basal", profile.getMaxDailyBasal())
this.profile.put("max_basal", maxBasal)
this.profile.put("min_bg", minBg)
this.profile.put("max_bg", maxBg)
this.profile.put("target_bg", targetBg)
this.profile.put("carb_ratio", profile.ic)
this.profile.put("sens", profile.isfMgdl)
this.profile.put("carb_ratio", profile.getIc())
this.profile.put("sens", profile.getIsfMgdl())
this.profile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3))
this.profile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0))
this.profile.put("skip_neutral_temps", true)
@ -179,22 +181,19 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader
} else {
this.profile.put("min_5m_carbimpact", sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact))
}
if (profileFunction.getUnits() == Constants.MMOL) {
if (profileFunction.getUnits() == GlucoseUnit.MMOL) {
this.profile.put("out_units", "mmol/L")
}
val now = System.currentTimeMillis()
val tb = treatmentsPlugin.getTempBasalFromHistory(now)
val tb = iobCobCalculator.getTempBasalIncludingConvertedExtended(now)
currentTemp = JSONObject()
currentTemp.put("temp", "absolute")
currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0)
currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0)
currentTemp.put("rate", tb?.convertedToAbsolute(now, profile) ?: 0.0)
// as we have non default temps longer than 30 minutes
val tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis())
if (tempBasal != null) {
currentTemp.put("minutesrunning", tempBasal.realDuration)
}
iobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray)
if (tb != null) currentTemp.put("minutesrunning", tb.getPassedDurationToTimeInMinutes(now))
iobData = iobCobCalculator.convertToJSONArray(iobArray)
this.glucoseStatus = JSONObject()
this.glucoseStatus.put("glucose", glucoseStatus.glucose)
if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
@ -206,7 +205,6 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader
this.glucoseStatus.put("long_avgdelta", glucoseStatus.longAvgDelta)
this.mealData = JSONObject()
this.mealData.put("carbs", mealData.carbs)
this.mealData.put("boluses", mealData.boluses)
this.mealData.put("mealCOB", mealData.mealCOB)
if (constraintChecker.isAutosensModeEnabled().value()) {
autosensData.put("ratio", autosensDataRatio)

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.aps.openAPSAMA
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.loop.APSResult
import info.nightscout.androidaps.utils.DateUtil
import org.json.JSONException
import org.json.JSONObject
import org.mozilla.javascript.NativeObject
@ -14,7 +13,7 @@ class DetermineBasalResultAMA private constructor(injector: HasAndroidInjector)
private var snoozeBG = 0.0
internal constructor(injector: HasAndroidInjector, result: NativeObject, j: JSONObject) : this(injector) {
date = DateUtil.now()
date = dateUtil.now()
json = j
if (result.containsKey("error")) {
reason = result["error"].toString()

View file

@ -35,6 +35,7 @@ class OpenAPSAMAFragment : DaggerFragment() {
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var jsonFormatter: JSONFormatter
private var _binding: OpenapsamaFragmentBinding? = null
@ -92,30 +93,30 @@ class OpenAPSAMAFragment : DaggerFragment() {
private fun updateGUI() {
if (_binding == null) return
openAPSAMAPlugin.lastAPSResult?.let { lastAPSResult ->
binding.result.text = JSONFormatter.format(lastAPSResult.json)
binding.result.text = jsonFormatter.format(lastAPSResult.json)
binding.request.text = lastAPSResult.toSpanned()
}
openAPSAMAPlugin.lastDetermineBasalAdapterAMAJS?.let { determineBasalAdapterAMAJS ->
binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam)
binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterAMAJS.currentTempParam)
binding.glucosestatus.text = jsonFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam)
binding.currenttemp.text = jsonFormatter.format(determineBasalAdapterAMAJS.currentTempParam)
try {
val iobArray = JSONArray(determineBasalAdapterAMAJS.iobDataParam)
binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0)))
binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", jsonFormatter.format(iobArray.getString(0)))
} catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e)
@Suppress("SetTextI18n")
binding.iobdata.text = "JSONException see log for details"
}
binding.profile.text = JSONFormatter.format(determineBasalAdapterAMAJS.profileParam)
binding.mealdata.text = JSONFormatter.format(determineBasalAdapterAMAJS.mealDataParam)
binding.profile.text = jsonFormatter.format(determineBasalAdapterAMAJS.profileParam)
binding.mealdata.text = jsonFormatter.format(determineBasalAdapterAMAJS.mealDataParam)
binding.scriptdebugdata.text = determineBasalAdapterAMAJS.scriptDebug
}
if (openAPSAMAPlugin.lastAPSRun != 0L) {
binding.lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun)
}
openAPSAMAPlugin.lastAutosensResult.let {
binding.autosensdata.text = JSONFormatter.format(it.json())
binding.autosensdata.text = jsonFormatter.format(it.json())
}
}

View file

@ -3,7 +3,9 @@ package info.nightscout.androidaps.plugins.aps.openAPSAMA
import android.content.Context
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -13,13 +15,13 @@ import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.Profiler
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.extensions.target
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONException
import javax.inject.Inject
@ -34,12 +36,14 @@ open class OpenAPSAMAPlugin @Inject constructor(
resourceHelper: ResourceHelper,
private val profileFunction: ProfileFunction,
private val context: Context,
private val activePlugin: ActivePluginProvider,
private val treatmentsPlugin: TreatmentsInterface,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
private val activePlugin: ActivePlugin,
private val iobCobCalculator: IobCobCalculator,
private val hardLimits: HardLimits,
private val profiler: Profiler,
private val fabricPrivacy: FabricPrivacy
private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil,
private val repository: AppRepository,
private val glucoseStatusProvider: GlucoseStatusProvider
) : PluginBase(PluginDescription()
.mainType(PluginType.APS)
.fragmentClass(OpenAPSAMAFragment::class.java.name)
@ -49,7 +53,7 @@ open class OpenAPSAMAPlugin @Inject constructor(
.preferencesId(R.xml.pref_openapsama)
.description(R.string.description_ama),
aapsLogger, resourceHelper, injector
), APSInterface {
), APS {
// last values
override var lastAPSRun: Long = 0
@ -76,7 +80,7 @@ open class OpenAPSAMAPlugin @Inject constructor(
aapsLogger.debug(LTag.APS, "invoke from $initiator tempBasalFallback: $tempBasalFallback")
lastAPSResult = null
val determineBasalAdapterAMAJS = DetermineBasalAdapterAMAJS(ScriptReader(context), injector)
val glucoseStatus = GlucoseStatus(injector).glucoseStatusData
val glucoseStatus = glucoseStatusProvider.glucoseStatusData
val profile = profileFunction.getProfile()
val pump = activePlugin.activePump
if (profile == null) {
@ -100,32 +104,33 @@ open class OpenAPSAMAPlugin @Inject constructor(
}.value()
var start = System.currentTimeMillis()
var startPart = System.currentTimeMillis()
val iobArray = iobCobCalculatorPlugin.calculateIobArrayInDia(profile)
val iobArray = iobCobCalculator.calculateIobArrayInDia(profile)
profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart)
startPart = System.currentTimeMillis()
val mealData = iobCobCalculatorPlugin.mealData
val mealData = iobCobCalculator.getMealDataWithWaitingForCalculationFinish()
profiler.log(LTag.APS, "getMealData()", startPart)
val maxIob = constraintChecker.getMaxIOBAllowed().also { maxIOBAllowedConstraint ->
inputConstraints.copyReasons(maxIOBAllowedConstraint)
}.value()
var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetLowMgdl, 0.1), "minBg", hardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble())
var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetHighMgdl, 0.1), "maxBg", hardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble())
var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, "targetBg", hardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble())
var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.getTargetLowMgdl(), 0.1), R.string.profile_low_target, HardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble())
var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.getTargetHighMgdl(), 0.1), R.string.profile_high_target, HardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble())
var targetBg = hardLimits.verifyHardLimits(profile.getTargetMgdl(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble())
var isTempTarget = false
treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis())?.let { tempTarget ->
val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
if (tempTarget is ValueWrapper.Existing) {
isTempTarget = true
minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble())
maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
minBg = hardLimits.verifyHardLimits(tempTarget.value.lowTarget, R.string.temp_target_low_target, HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble())
maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
}
if (!hardLimits.checkOnlyHardLimits(profile.dia, "dia", hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, "sens", hardLimits.MINISF, hardLimits.MAXISF)) return
if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, "max_daily_basal", 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, "current_basal", 0.01, hardLimits.maxBasal())) return
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
startPart = System.currentTimeMillis()
if (constraintChecker.isAutosensModeEnabled().value()) {
val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin")
val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin")
if (autosensData == null) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)))
return
@ -155,10 +160,10 @@ open class OpenAPSAMAPlugin @Inject constructor(
lastAPSResult = null
lastAPSRun = 0
} else {
if (determineBasalResultAMA.rate == 0.0 && determineBasalResultAMA.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultAMA.tempBasalRequested = false
if (determineBasalResultAMA.rate == 0.0 && determineBasalResultAMA.duration == 0 && iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) == null) determineBasalResultAMA.tempBasalRequested = false
determineBasalResultAMA.iob = iobArray[0]
val now = System.currentTimeMillis()
determineBasalResultAMA.json?.put("timestamp", DateUtil.toISOString(now))
determineBasalResultAMA.json?.put("timestamp", dateUtil.toISOString(now))
determineBasalResultAMA.inputConstraints = inputConstraints
lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS
lastAPSResult = determineBasalResultAMA

View file

@ -1,12 +1,16 @@
package info.nightscout.androidaps.plugins.aps.openAPSSMB
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.data.MealData
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.getPassedDurationToTimeInMinutes
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -15,8 +19,6 @@ 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.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -37,8 +39,8 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePluginProvider: ActivePluginProvider
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var openHumansUploader: OpenHumansUploader
private var profile = JSONObject()
@ -173,18 +175,18 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
advancedFiltering: Boolean,
isSaveCgmSource: Boolean
) {
val pump = activePluginProvider.activePump
val pump = activePlugin.activePump
val pumpBolusStep = pump.pumpDescription.bolusStep
this.profile.put("max_iob", maxIob)
//mProfile.put("dia", profile.getDia());
this.profile.put("type", "current")
this.profile.put("max_daily_basal", profile.maxDailyBasal)
this.profile.put("max_daily_basal", profile.getMaxDailyBasal())
this.profile.put("max_basal", maxBasal)
this.profile.put("min_bg", minBg)
this.profile.put("max_bg", maxBg)
this.profile.put("target_bg", targetBg)
this.profile.put("carb_ratio", profile.ic)
this.profile.put("sens", profile.isfMgdl)
this.profile.put("carb_ratio", profile.getIc())
this.profile.put("sens", profile.getIsfMgdl())
this.profile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3))
this.profile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0))
@ -223,21 +225,18 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
this.profile.put("current_basal", basalRate)
this.profile.put("temptargetSet", tempTargetSet)
this.profile.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2")))
if (profileFunction.getUnits() == Constants.MMOL) {
if (profileFunction.getUnits() == GlucoseUnit.MMOL) {
this.profile.put("out_units", "mmol/L")
}
val now = System.currentTimeMillis()
val tb = treatmentsPlugin.getTempBasalFromHistory(now)
val tb = iobCobCalculator.getTempBasalIncludingConvertedExtended(now)
currentTemp.put("temp", "absolute")
currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0)
currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0)
currentTemp.put("rate", tb?.convertedToAbsolute(now, profile) ?: 0.0)
// as we have non default temps longer than 30 mintues
val tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis())
if (tempBasal != null) {
currentTemp.put("minutesrunning", tempBasal.realDuration)
}
iobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray)
if (tb != null) currentTemp.put("minutesrunning", tb.getPassedDurationToTimeInMinutes(now))
iobData = iobCobCalculator.convertToJSONArray(iobArray)
mGlucoseStatus.put("glucose", glucoseStatus.glucose)
mGlucoseStatus.put("noise", glucoseStatus.noise)
if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
@ -249,7 +248,6 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
mGlucoseStatus.put("long_avgdelta", glucoseStatus.longAvgDelta)
mGlucoseStatus.put("date", glucoseStatus.date)
this.mealData.put("carbs", mealData.carbs)
this.mealData.put("boluses", mealData.boluses)
this.mealData.put("mealCOB", mealData.mealCOB)
this.mealData.put("slopeFromMaxDeviation", mealData.slopeFromMaxDeviation)
this.mealData.put("slopeFromMinDeviation", mealData.slopeFromMinDeviation)

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.aps.openAPSSMB
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.loop.APSResult
import info.nightscout.androidaps.utils.DateUtil
import org.json.JSONException
import org.json.JSONObject
@ -13,7 +12,7 @@ class DetermineBasalResultSMB private constructor(injector: HasAndroidInjector)
private var snoozeBG = 0.0
internal constructor(injector: HasAndroidInjector, result: JSONObject) : this(injector) {
date = DateUtil.now()
date = dateUtil.now()
json = result
try {
if (result.has("error")) {
@ -47,7 +46,7 @@ class DetermineBasalResultSMB private constructor(injector: HasAndroidInjector)
if (result.has("deliverAt")) {
val date = result.getString("deliverAt")
try {
deliverAt = DateUtil.fromISODateString(date).time
deliverAt = dateUtil.fromISODateString(date)
} catch (e: Exception) {
aapsLogger.error(LTag.APS, "Error parsing 'deliverAt' date: $date", e)
}

View file

@ -36,6 +36,7 @@ class OpenAPSSMBFragment : DaggerFragment() {
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var jsonFormatter: JSONFormatter
private var _binding: OpenapsamaFragmentBinding? = null
@ -92,23 +93,23 @@ class OpenAPSSMBFragment : DaggerFragment() {
fun updateGUI() {
if (_binding == null) return
openAPSSMBPlugin.lastAPSResult?.let { lastAPSResult ->
binding.result.text = JSONFormatter.format(lastAPSResult.json)
binding.result.text = jsonFormatter.format(lastAPSResult.json)
binding.request.text = lastAPSResult.toSpanned()
}
openAPSSMBPlugin.lastDetermineBasalAdapterSMBJS?.let { determineBasalAdapterSMBJS ->
binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam)
binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterSMBJS.currentTempParam)
binding.glucosestatus.text = jsonFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam)
binding.currenttemp.text = jsonFormatter.format(determineBasalAdapterSMBJS.currentTempParam)
try {
val iobArray = JSONArray(determineBasalAdapterSMBJS.iobDataParam)
binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0)))
binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", jsonFormatter.format(iobArray.getString(0)))
} catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e)
@SuppressLint("SetTextI18n")
binding.iobdata.text = "JSONException see log for details"
}
binding.profile.text = JSONFormatter.format(determineBasalAdapterSMBJS.profileParam)
binding.mealdata.text = JSONFormatter.format(determineBasalAdapterSMBJS.mealDataParam)
binding.profile.text = jsonFormatter.format(determineBasalAdapterSMBJS.profileParam)
binding.mealdata.text = jsonFormatter.format(determineBasalAdapterSMBJS.mealDataParam)
binding.scriptdebugdata.text = determineBasalAdapterSMBJS.scriptDebug
openAPSSMBPlugin.lastAPSResult?.inputConstraints?.let {
binding.constraints.text = it.getReasons(aapsLogger)
@ -118,7 +119,7 @@ class OpenAPSSMBFragment : DaggerFragment() {
binding.lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun)
}
openAPSSMBPlugin.lastAutosensResult.let {
binding.autosensdata.text = JSONFormatter.format(it.json())
binding.autosensdata.text = jsonFormatter.format(it.json())
}
}

View file

@ -5,7 +5,9 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.extensions.target
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -15,8 +17,7 @@ import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.Profiler
@ -35,12 +36,14 @@ open class OpenAPSSMBPlugin @Inject constructor(
resourceHelper: ResourceHelper,
private val profileFunction: ProfileFunction,
private val context: Context,
private val activePlugin: ActivePluginProvider,
private val treatmentsPlugin: TreatmentsInterface,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
private val activePlugin: ActivePlugin,
private val iobCobCalculator: IobCobCalculator,
private val hardLimits: HardLimits,
private val profiler: Profiler,
private val sp: SP
private val sp: SP,
private val dateUtil: DateUtil,
private val repository: AppRepository,
private val glucoseStatusProvider: GlucoseStatusProvider
) : PluginBase(PluginDescription()
.mainType(PluginType.APS)
.fragmentClass(OpenAPSSMBFragment::class.java.name)
@ -51,7 +54,7 @@ open class OpenAPSSMBPlugin @Inject constructor(
.description(R.string.description_smb)
.setDefault(),
aapsLogger, resourceHelper, injector
), APSInterface, ConstraintsInterface {
), APS, Constraints {
// last values
override var lastAPSRun: Long = 0
@ -84,7 +87,7 @@ open class OpenAPSSMBPlugin @Inject constructor(
override fun invoke(initiator: String, tempBasalFallback: Boolean) {
aapsLogger.debug(LTag.APS, "invoke from $initiator tempBasalFallback: $tempBasalFallback")
lastAPSResult = null
val glucoseStatus = GlucoseStatus(injector).glucoseStatusData
val glucoseStatus = glucoseStatusProvider.glucoseStatusData
val profile = profileFunction.getProfile()
val pump = activePlugin.activePump
if (profile == null) {
@ -114,24 +117,25 @@ open class OpenAPSSMBPlugin @Inject constructor(
inputConstraints.copyReasons(maxIOBAllowedConstraint)
}.value()
var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetLowMgdl, 0.1), "minBg", hardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble())
var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetHighMgdl, 0.1), "maxBg", hardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble())
var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, "targetBg", hardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble())
var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.getTargetLowMgdl(), 0.1), R.string.profile_low_target, HardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble())
var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.getTargetHighMgdl(), 0.1), R.string.profile_high_target, HardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble())
var targetBg = hardLimits.verifyHardLimits(profile.getTargetMgdl(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble())
var isTempTarget = false
treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis())?.let { tempTarget ->
val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
if (tempTarget is ValueWrapper.Existing) {
isTempTarget = true
minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble())
maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
minBg = hardLimits.verifyHardLimits(tempTarget.value.lowTarget, R.string.temp_target_low_target, HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble())
maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
}
if (!hardLimits.checkOnlyHardLimits(profile.dia, "dia", hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, "sens", hardLimits.MINISF, hardLimits.MAXISF)) return
if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, "max_daily_basal", 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, "current_basal", 0.01, hardLimits.maxBasal())) return
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
startPart = System.currentTimeMillis()
if (constraintChecker.isAutosensModeEnabled().value()) {
val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin")
val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin")
if (autosensData == null) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)))
return
@ -140,7 +144,7 @@ open class OpenAPSSMBPlugin @Inject constructor(
} else {
lastAutosensResult.sensResult = "autosens disabled"
}
val iobArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
val iobArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart)
startPart = System.currentTimeMillis()
val smbAllowed = Constraint(!tempBasalFallback).also {
@ -164,7 +168,7 @@ open class OpenAPSSMBPlugin @Inject constructor(
activePlugin.activePump.baseBasalRate,
iobArray,
glucoseStatus,
iobCobCalculatorPlugin.mealData,
iobCobCalculator.getMealDataWithWaitingForCalculationFinish(),
lastAutosensResult.ratio,
isTempTarget,
smbAllowed.value(),
@ -182,9 +186,9 @@ open class OpenAPSSMBPlugin @Inject constructor(
} else {
// TODO still needed with oref1?
// Fix bug determine basal
if (determineBasalResultSMB.rate == 0.0 && determineBasalResultSMB.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultSMB.tempBasalRequested = false
if (determineBasalResultSMB.rate == 0.0 && determineBasalResultSMB.duration == 0 && iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) == null) determineBasalResultSMB.tempBasalRequested = false
determineBasalResultSMB.iob = iobArray[0]
determineBasalResultSMB.json?.put("timestamp", DateUtil.toISOString(now))
determineBasalResultSMB.json?.put("timestamp", dateUtil.toISOString(now))
determineBasalResultSMB.inputConstraints = inputConstraints
lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS
lastAPSResult = determineBasalResultSMB

View file

@ -9,7 +9,7 @@ import android.widget.*
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.PreferencesActivity
import info.nightscout.androidaps.databinding.ConfigbuilderFragmentBinding
@ -19,7 +19,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui
import info.nightscout.androidaps.utils.FabricPrivacy
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
@ -34,7 +34,7 @@ class ConfigBuilderFragment : DaggerFragment() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var config: Config
@ -101,18 +101,18 @@ class ConfigBuilderFragment : DaggerFragment() {
private fun updateGUI() {
binding.categories.removeAllViews()
if (!config.NSCLIENT) {
createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, activePlugin.getSpecificPluginsVisibleInListByInterface(ProfileInterface::class.java, PluginType.PROFILE))
createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, activePlugin.getSpecificPluginsVisibleInList(PluginType.PROFILE))
}
createViewsForPlugins(R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, activePlugin.getSpecificPluginsVisibleInListByInterface(InsulinInterface::class.java, PluginType.INSULIN))
createViewsForPlugins(R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, activePlugin.getSpecificPluginsVisibleInList(PluginType.INSULIN))
if (!config.NSCLIENT) {
createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, activePlugin.getSpecificPluginsVisibleInListByInterface(BgSourceInterface::class.java, PluginType.BGSOURCE))
createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, activePlugin.getSpecificPluginsVisibleInList(PluginType.BGSOURCE))
createViewsForPlugins(R.string.configbuilder_pump, R.string.configbuilder_pump_description, PluginType.PUMP, activePlugin.getSpecificPluginsVisibleInList(PluginType.PUMP))
}
createViewsForPlugins(R.string.configbuilder_sensitivity, R.string.configbuilder_sensitivity_description, PluginType.SENSITIVITY, activePlugin.getSpecificPluginsVisibleInListByInterface(SensitivityInterface::class.java, PluginType.SENSITIVITY))
createViewsForPlugins(R.string.configbuilder_sensitivity, R.string.configbuilder_sensitivity_description, PluginType.SENSITIVITY, activePlugin.getSpecificPluginsVisibleInList(PluginType.SENSITIVITY))
if (config.APS) {
createViewsForPlugins(R.string.configbuilder_aps, R.string.configbuilder_aps_description, PluginType.APS, activePlugin.getSpecificPluginsVisibleInList(PluginType.APS))
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.getSpecificPluginsVisibleInListByInterface(ConstraintsInterface::class.java, PluginType.CONSTRAINTS))
createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, activePlugin.getSpecificPluginsVisibleInList(PluginType.CONSTRAINTS))
}
createViewsForPlugins(R.string.configbuilder_treatments, R.string.configbuilder_treatments_description, PluginType.TREATMENT, activePlugin.getSpecificPluginsVisibleInList(PluginType.TREATMENT))
createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, activePlugin.getSpecificPluginsVisibleInList(PluginType.GENERAL))

View file

@ -3,6 +3,9 @@ package info.nightscout.androidaps.plugins.configBuilder
import androidx.fragment.app.FragmentActivity
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.events.EventAppInitialized
import info.nightscout.androidaps.events.EventConfigBuilderChange
import info.nightscout.androidaps.events.EventRebuildTabs
@ -26,8 +29,9 @@ class ConfigBuilderPlugin @Inject constructor(
resourceHelper: ResourceHelper,
private val sp: SP,
private val rxBus: RxBusWrapper,
private val activePlugin: ActivePluginProvider,
private val uel: UserEntryLogger
private val activePlugin: ActivePlugin,
private val uel: UserEntryLogger,
private val pumpSync: PumpSync
) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(ConfigBuilderFragment::class.java.name)
@ -39,9 +43,9 @@ class ConfigBuilderPlugin @Inject constructor(
.shortName(R.string.configbuilder_shortname)
.description(R.string.description_config_builder),
aapsLogger, resourceHelper, injector
), ConfigBuilderInterface {
), ConfigBuilder {
fun initialize() {
override fun initialize() {
(activePlugin as PluginStore).loadDefaults()
loadSettings()
setAlwaysEnabledPluginsEnabled()
@ -65,7 +69,7 @@ class ConfigBuilderPlugin @Inject constructor(
if (p.pluginDescription.alwaysEnabled && p.pluginDescription.neverVisible) continue
savePref(p, type, true)
if (type == PluginType.PUMP) {
if (p is ProfileInterface) { // Store state of optional Profile interface
if (p is ProfileSource) { // Store state of optional Profile interface
savePref(p, PluginType.PROFILE, false)
}
}
@ -89,7 +93,7 @@ class ConfigBuilderPlugin @Inject constructor(
val type = p.getType()
loadPref(p, type, true)
if (p.getType() == PluginType.PUMP) {
if (p is ProfileInterface) {
if (p is ProfileSource) {
loadPref(p, PluginType.PROFILE, false)
}
}
@ -138,13 +142,16 @@ class ConfigBuilderPlugin @Inject constructor(
val allowHardwarePump = sp.getBoolean("allow_hardware_pump", false)
if (allowHardwarePump || activity == null) {
performPluginSwitch(changedPlugin, newState, type)
pumpSync.connectNewPump()
} else {
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), Runnable {
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), {
performPluginSwitch(changedPlugin, newState, type)
pumpSync.connectNewPump()
sp.putBoolean("allow_hardware_pump", true)
uel.log("HW PUMP ALLOWED")
uel.log(Action.HW_PUMP_ALLOWED, Sources.ConfigBuilder, resourceHelper.gs(changedPlugin.pluginDescription.pluginName),
ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(changedPlugin.pluginDescription.pluginName)))
aapsLogger.debug(LTag.PUMP, "First time HW pump allowed!")
}, Runnable {
}, {
rxBus.send(EventConfigBuilderUpdateGui())
aapsLogger.debug(LTag.PUMP, "User does not allow switching to HW pump!")
})
@ -152,6 +159,14 @@ class ConfigBuilderPlugin @Inject constructor(
}
override fun performPluginSwitch(changedPlugin: PluginBase, enabled: Boolean, type: PluginType) {
if(enabled && !changedPlugin.isEnabled()) {
uel.log(Action.PLUGIN_ENABLED, Sources.ConfigBuilder, resourceHelper.gs(changedPlugin.pluginDescription.pluginName),
ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(changedPlugin.pluginDescription.pluginName)))
}
else if(!enabled) {
uel.log(Action.PLUGIN_DISABLED, Sources.ConfigBuilder, resourceHelper.gs(changedPlugin.pluginDescription.pluginName),
ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(changedPlugin.pluginDescription.pluginName)))
}
changedPlugin.setPluginEnabled(type, enabled)
changedPlugin.setFragmentVisible(type, enabled)
processOnEnabledCategoryChanged(changedPlugin, type)
@ -165,13 +180,13 @@ class ConfigBuilderPlugin @Inject constructor(
fun processOnEnabledCategoryChanged(changedPlugin: PluginBase, type: PluginType?) {
var pluginsInCategory: ArrayList<PluginBase>? = null
when (type) {
PluginType.INSULIN -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(InsulinInterface::class.java)
PluginType.SENSITIVITY -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(SensitivityInterface::class.java)
PluginType.APS -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(APSInterface::class.java)
PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileInterface::class.java)
PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSourceInterface::class.java)
PluginType.INSULIN -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Insulin::class.java)
PluginType.SENSITIVITY -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Sensitivity::class.java)
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(PumpInterface::class.java)
PluginType.PUMP -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Pump::class.java)
else -> {
}

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.configBuilder
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -11,23 +11,23 @@ import javax.inject.Singleton
class PluginStore @Inject constructor(
private val aapsLogger: AAPSLogger,
private val config: Config
) : ActivePluginProvider {
) : ActivePlugin {
lateinit var plugins: List<@JvmSuppressWildcards PluginBase>
private var activeBgSourceStore: BgSourceInterface? = null
private var activePumpStore: PumpInterface? = null
private var activeProfile: ProfileInterface? = null
private var activeAPSStore: APSInterface? = null
private var activeInsulinStore: InsulinInterface? = null
private var activeSensitivityStore: SensitivityInterface? = null
private var activeBgSourceStore: BgSource? = null
private var activePumpStore: Pump? = null
private var activeProfile: ProfileSource? = null
private var activeAPSStore: APS? = null
private var activeInsulinStore: Insulin? = null
private var activeSensitivityStore: Sensitivity? = null
private var activeTreatmentsStore: TreatmentsInterface? = null
fun loadDefaults() {
verifySelectionInCategories()
}
fun getDefaultPlugin(type: PluginType): PluginBase {
private fun getDefaultPlugin(type: PluginType): PluginBase {
for (p in plugins)
if (p.getType() == type && p.isDefault()) return p
throw IllegalStateException("Default plugin not found")
@ -41,14 +41,6 @@ class PluginStore @Inject constructor(
return newList
}
override fun getSpecificPluginsVisibleInList(type: PluginType): ArrayList<PluginBase> {
val newList = ArrayList<PluginBase>()
for (p in plugins) {
if (p.getType() == type) if (p.showInList(type)) newList.add(p)
}
return newList
}
override fun getSpecificPluginsListByInterface(interfaceClass: Class<*>): ArrayList<PluginBase> {
val newList = ArrayList<PluginBase>()
for (p in plugins) {
@ -57,10 +49,10 @@ class PluginStore @Inject constructor(
return newList
}
override fun getSpecificPluginsVisibleInListByInterface(interfaceClass: Class<*>, type: PluginType): ArrayList<PluginBase> {
override fun getSpecificPluginsVisibleInList(type: PluginType): ArrayList<PluginBase> {
val newList = ArrayList<PluginBase>()
for (p in plugins) {
if (p.javaClass != ConfigBuilderPlugin::class.java && interfaceClass.isAssignableFrom(p.javaClass)) if (p.showInList(type)) newList.add(p)
if (p.getType() == type) if (p.showInList(type)) newList.add(p)
}
return newList
}
@ -71,64 +63,64 @@ class PluginStore @Inject constructor(
// PluginType.APS
if (!config.NSCLIENT && !config.PUMPCONTROL) {
pluginsInCategory = getSpecificPluginsList(PluginType.APS)
activeAPSStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.APS) as APSInterface?
activeAPSStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.APS) as APS?
if (activeAPSStore == null) {
activeAPSStore = getDefaultPlugin(PluginType.APS) as APSInterface
activeAPSStore = getDefaultPlugin(PluginType.APS) as APS
(activeAPSStore as PluginBase).setPluginEnabled(PluginType.APS, true)
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting APSInterface")
}
setFragmentVisiblities((activeAPSStore as PluginBase).name, pluginsInCategory, PluginType.APS)
setFragmentVisibilities((activeAPSStore as PluginBase).name, pluginsInCategory, PluginType.APS)
}
// PluginType.INSULIN
pluginsInCategory = getSpecificPluginsList(PluginType.INSULIN)
activeInsulinStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.INSULIN) as InsulinInterface?
activeInsulinStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.INSULIN) as Insulin?
if (activeInsulinStore == null) {
activeInsulinStore = getDefaultPlugin(PluginType.INSULIN) as InsulinInterface
activeInsulinStore = getDefaultPlugin(PluginType.INSULIN) as Insulin
(activeInsulinStore as PluginBase).setPluginEnabled(PluginType.INSULIN, true)
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting InsulinInterface")
}
setFragmentVisiblities((activeInsulinStore as PluginBase).name, pluginsInCategory, PluginType.INSULIN)
setFragmentVisibilities((activeInsulinStore as PluginBase).name, pluginsInCategory, PluginType.INSULIN)
// PluginType.SENSITIVITY
pluginsInCategory = getSpecificPluginsList(PluginType.SENSITIVITY)
activeSensitivityStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.SENSITIVITY) as SensitivityInterface?
activeSensitivityStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.SENSITIVITY) as Sensitivity?
if (activeSensitivityStore == null) {
activeSensitivityStore = getDefaultPlugin(PluginType.SENSITIVITY) as SensitivityInterface
activeSensitivityStore = getDefaultPlugin(PluginType.SENSITIVITY) as Sensitivity
(activeSensitivityStore as PluginBase).setPluginEnabled(PluginType.SENSITIVITY, true)
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting SensitivityInterface")
}
setFragmentVisiblities((activeSensitivityStore as PluginBase).name, pluginsInCategory, PluginType.SENSITIVITY)
setFragmentVisibilities((activeSensitivityStore as PluginBase).name, pluginsInCategory, PluginType.SENSITIVITY)
// PluginType.PROFILE
pluginsInCategory = getSpecificPluginsList(PluginType.PROFILE)
activeProfile = getTheOneEnabledInArray(pluginsInCategory, PluginType.PROFILE) as ProfileInterface?
activeProfile = getTheOneEnabledInArray(pluginsInCategory, PluginType.PROFILE) as ProfileSource?
if (activeProfile == null) {
activeProfile = getDefaultPlugin(PluginType.PROFILE) as ProfileInterface
activeProfile = getDefaultPlugin(PluginType.PROFILE) as ProfileSource
(activeProfile as PluginBase).setPluginEnabled(PluginType.PROFILE, true)
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting ProfileInterface")
}
setFragmentVisiblities((activeProfile as PluginBase).name, pluginsInCategory, PluginType.PROFILE)
setFragmentVisibilities((activeProfile as PluginBase).name, pluginsInCategory, PluginType.PROFILE)
// PluginType.BGSOURCE
pluginsInCategory = getSpecificPluginsList(PluginType.BGSOURCE)
activeBgSourceStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.BGSOURCE) as BgSourceInterface?
activeBgSourceStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.BGSOURCE) as BgSource?
if (activeBgSourceStore == null) {
activeBgSourceStore = getDefaultPlugin(PluginType.BGSOURCE) as BgSourceInterface
activeBgSourceStore = getDefaultPlugin(PluginType.BGSOURCE) as BgSource
(activeBgSourceStore as PluginBase).setPluginEnabled(PluginType.BGSOURCE, true)
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting BgInterface")
}
setFragmentVisiblities((activeBgSourceStore as PluginBase).name, pluginsInCategory, PluginType.PUMP)
setFragmentVisibilities((activeBgSourceStore as PluginBase).name, pluginsInCategory, PluginType.BGSOURCE)
// PluginType.PUMP
pluginsInCategory = getSpecificPluginsList(PluginType.PUMP)
activePumpStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.PUMP) as PumpInterface?
activePumpStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.PUMP) as Pump?
if (activePumpStore == null) {
activePumpStore = getDefaultPlugin(PluginType.PUMP) as PumpInterface
activePumpStore = getDefaultPlugin(PluginType.PUMP) as Pump
(activePumpStore as PluginBase).setPluginEnabled(PluginType.PUMP, true)
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting PumpInterface")
}
setFragmentVisiblities((activePumpStore as PluginBase).name, pluginsInCategory, PluginType.PUMP)
setFragmentVisibilities((activePumpStore as PluginBase).name, pluginsInCategory, PluginType.PUMP)
// PluginType.TREATMENT
pluginsInCategory = getSpecificPluginsList(PluginType.TREATMENT)
@ -138,50 +130,11 @@ class PluginStore @Inject constructor(
(activeTreatmentsStore as PluginBase).setPluginEnabled(PluginType.TREATMENT, true)
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting PumpInterface")
}
setFragmentVisiblities((activeTreatmentsStore as PluginBase).name, pluginsInCategory, PluginType.TREATMENT)
setFragmentVisibilities((activeTreatmentsStore as PluginBase).name, pluginsInCategory, PluginType.TREATMENT)
}
/**
* disables the visibility for all fragments of Plugins with the given PluginType
* which are not equally named to the Plugin implementing the given Plugin Interface.
*
* @param pluginInterface
* @param pluginType
* @param <T>
* @return
</T> */
private fun <T> determineActivePlugin(pluginInterface: Class<T>, pluginType: PluginType): T? {
val pluginsInCategory: ArrayList<PluginBase> = getSpecificPluginsListByInterface(pluginInterface)
return determineActivePlugin(pluginsInCategory, pluginType)
}
/**
* disables the visibility for all fragments of Plugins in the given pluginsInCategory
* with the given PluginType which are not equally named to the Plugin implementing the
* given Plugin Interface.
*
*
* TODO we are casting an interface to PluginBase, which seems to be rather odd, since
* TODO the interface is not implementing PluginBase (this is just avoiding errors through
* TODO conventions.
*
* @param pluginsInCategory
* @param pluginType
* @param <T>
* @return
</T> */
private fun <T> determineActivePlugin(pluginsInCategory: ArrayList<PluginBase>,
pluginType: PluginType): T? {
@Suppress("UNCHECKED_CAST")
val activePlugin = getTheOneEnabledInArray(pluginsInCategory, pluginType) as T?
if (activePlugin != null) {
setFragmentVisiblities((activePlugin as PluginBase).name, pluginsInCategory, pluginType)
}
return activePlugin
}
private fun setFragmentVisiblities(activePluginName: String, pluginsInCategory: ArrayList<PluginBase>,
pluginType: PluginType) {
private fun setFragmentVisibilities(activePluginName: String, pluginsInCategory: ArrayList<PluginBase>,
pluginType: PluginType) {
aapsLogger.debug(LTag.CONFIGBUILDER, "Selected interface: $activePluginName")
for (p in pluginsInCategory)
if (p.name != activePluginName)
@ -203,22 +156,22 @@ class PluginStore @Inject constructor(
// ***** Interface *****
override val activeBgSource: BgSourceInterface
override val activeBgSource: BgSource
get() = activeBgSourceStore ?: checkNotNull(activeBgSourceStore) { "No bg source selected" }
override val activeProfileInterface: ProfileInterface
override val activeProfileSource: ProfileSource
get() = activeProfile ?: checkNotNull(activeProfile) { "No profile selected" }
override val activeInsulin: InsulinInterface
override val activeInsulin: Insulin
get() = activeInsulinStore ?: checkNotNull(activeInsulinStore) { "No insulin selected" }
override val activeAPS: APSInterface
override val activeAPS: APS
get() = activeAPSStore ?: checkNotNull(activeAPSStore) { "No APS selected" }
override val activePump: PumpInterface
override val activePump: Pump
get() = activePumpStore ?: checkNotNull(activePumpStore) { "No pump selected" }
override val activeSensitivity: SensitivityInterface
override val activeSensitivity: Sensitivity
get() = activeSensitivityStore
?: checkNotNull(activeSensitivityStore) { "No sensitivity selected" }
@ -226,8 +179,8 @@ class PluginStore @Inject constructor(
get() = activeTreatmentsStore
?: checkNotNull(activeTreatmentsStore) { "No treatments selected" }
override val activeOverview: OverviewInterface
get() = getSpecificPluginsListByInterface(OverviewInterface::class.java).first() as OverviewInterface
override val activeOverview: Overview
get() = getSpecificPluginsListByInterface(Overview::class.java).first() as Overview
override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins)

View file

@ -24,7 +24,7 @@ class DstHelperPlugin @Inject constructor(
private var rxBus: RxBusWrapper,
resourceHelper: ResourceHelper,
private var sp: SP,
private var activePlugin: ActivePluginProvider,
private var activePlugin: ActivePlugin,
private var loopPlugin: LoopPlugin
) : PluginBase(PluginDescription()
.mainType(PluginType.CONSTRAINTS)
@ -33,7 +33,7 @@ class DstHelperPlugin @Inject constructor(
.showInList(false)
.pluginName(R.string.dst_plugin_name),
aapsLogger, resourceHelper, injector
), ConstraintsInterface {
), Constraints {
companion object {
private const val DISABLE_TIME_FRAME_HOURS = -3

View file

@ -17,6 +17,9 @@ import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.databinding.ObjectivesFragmentBinding
import info.nightscout.androidaps.databinding.ObjectivesItemBinding
import info.nightscout.androidaps.dialogs.NtpProgressDialog
@ -234,7 +237,7 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.verify.setOnClickListener {
receiverStatusStore.updateNetworkStatus()
if (binding.fake.isChecked) {
objective.accomplishedOn = DateUtil.now()
objective.accomplishedOn = dateUtil.now()
scrollToCurrentObjective()
startUpdateTimer()
rxBus.send(EventObjectivesUpdateGui())
@ -246,7 +249,7 @@ class ObjectivesFragment : DaggerFragment() {
rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.timedetection), 0))
sntpClient.ntpTime(object : SntpClient.Callback() {
override fun run() {
aapsLogger.debug("NTP time: $time System time: ${DateUtil.now()}")
aapsLogger.debug("NTP time: $time System time: ${dateUtil.now()}")
SystemClock.sleep(300)
if (!networkConnected) {
rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.notconnected), 99))
@ -273,7 +276,7 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.start.setOnClickListener {
receiverStatusStore.updateNetworkStatus()
if (binding.fake.isChecked) {
objective.startedOn = DateUtil.now()
objective.startedOn = dateUtil.now()
scrollToCurrentObjective()
startUpdateTimer()
rxBus.send(EventObjectivesUpdateGui())
@ -285,7 +288,7 @@ class ObjectivesFragment : DaggerFragment() {
rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.timedetection), 0))
sntpClient.ntpTime(object : SntpClient.Callback() {
override fun run() {
aapsLogger.debug("NTP time: $time System time: ${DateUtil.now()}")
aapsLogger.debug("NTP time: $time System time: ${dateUtil.now()}")
SystemClock.sleep(300)
if (!networkConnected) {
rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.notconnected), 99))
@ -307,7 +310,8 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.unstart.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.doyouwantresetstart), Runnable {
uel.log("OBJECTIVE UNSTARTED", i1 = position + 1)
uel.log(Action.OBJECTIVE_UNSTARTED, Sources.Objectives,
ValueWithUnit.SimpleInt(position + 1))
objective.startedOn = 0
scrollToCurrentObjective()
rxBus.send(EventObjectivesUpdateGui())

View file

@ -5,8 +5,9 @@ import com.google.common.base.Charsets
import com.google.common.hash.Hashing
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
@ -24,9 +25,10 @@ class ObjectivesPlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
resourceHelper: ResourceHelper,
private val activePlugin: ActivePluginProvider,
private val activePlugin: ActivePlugin,
private val sp: SP,
config: Config,
private val dateUtil: DateUtil,
private val uel: UserEntryLogger
) : PluginBase(PluginDescription()
.mainType(PluginType.CONSTRAINTS)
@ -38,7 +40,7 @@ class ObjectivesPlugin @Inject constructor(
.shortName(R.string.objectives_shortname)
.description(R.string.description_objectives),
aapsLogger, resourceHelper, injector
), ConstraintsInterface {
), Constraints {
var objectives: MutableList<Objective> = ArrayList()
@ -124,25 +126,25 @@ class ObjectivesPlugin @Inject constructor(
if (!url.endsWith("/")) url = "$url/"
@Suppress("DEPRECATION") 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())
sp.putLong("Objectives_" + "maxbasal" + "_started", DateUtil.now())
sp.putLong("Objectives_" + "maxbasal" + "_accomplished", DateUtil.now())
sp.putLong("Objectives_" + "maxiobzero" + "_started", DateUtil.now())
sp.putLong("Objectives_" + "maxiobzero" + "_accomplished", DateUtil.now())
sp.putLong("Objectives_" + "maxiob" + "_started", DateUtil.now())
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())
sp.putLong("Objectives_" + "auto" + "_accomplished", DateUtil.now())
sp.putLong("Objectives_" + "openloop" + "_started", dateUtil.now())
sp.putLong("Objectives_" + "openloop" + "_accomplished", dateUtil.now())
sp.putLong("Objectives_" + "maxbasal" + "_started", dateUtil.now())
sp.putLong("Objectives_" + "maxbasal" + "_accomplished", dateUtil.now())
sp.putLong("Objectives_" + "maxiobzero" + "_started", dateUtil.now())
sp.putLong("Objectives_" + "maxiobzero" + "_accomplished", dateUtil.now())
sp.putLong("Objectives_" + "maxiob" + "_started", dateUtil.now())
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())
sp.putLong("Objectives_" + "auto" + "_accomplished", dateUtil.now())
setupObjectives()
OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeaccepted))
uel.log("OBJECTIVES SKIPPED")
uel.log(Action.OBJECTIVES_SKIPPED, Sources.Objectives)
} else {
OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeinvalid))
}

View file

@ -108,7 +108,7 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
}
task.answered = result
if (!result) {
task.disabledTo = DateUtil.now() + T.hours(1).msecs()
task.disabledTo = dateUtil.now() + T.hours(1).msecs()
ToastUtils.showToastInUiThread(context, R.string.wronganswer)
} else task.disabledTo = 0
updateGui()

View file

@ -21,6 +21,7 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var dateUtil: DateUtil
private val spName: String
@StringRes val objective: Int
@ -55,7 +56,7 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe
this.gate = gate
startedOn = sp.getLong("Objectives_" + spName + "_started", 0L)
accomplishedOn = sp.getLong("Objectives_" + spName + "_accomplished", 0L)
if (accomplishedOn - DateUtil.now() > T.hours(3).msecs() || startedOn - DateUtil.now() > T.hours(3).msecs()) { // more than 3 hours in the future
if (accomplishedOn - dateUtil.now() > T.hours(3).msecs() || startedOn - dateUtil.now() > T.hours(3).msecs()) { // more than 3 hours in the future
startedOn = 0
accomplishedOn = 0
}
@ -69,7 +70,7 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe
}
val isAccomplished: Boolean
get() = accomplishedOn != 0L && accomplishedOn < DateUtil.now()
get() = accomplishedOn != 0L && accomplishedOn < dateUtil.now()
val isStarted: Boolean
get() = startedOn != 0L
@ -144,7 +145,7 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe
override fun isCompleted(): Boolean = answered
fun isEnabledAnswer(): Boolean = disabledTo < DateUtil.now()
fun isEnabledAnswer(): Boolean = disabledTo < dateUtil.now()
fun option(option: Option): ExamTask {
options.add(option)

View file

@ -2,25 +2,25 @@ package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import javax.inject.Inject
class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R.string.objectives_0_objective, R.string.objectives_0_gate) {
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var repository: AppRepository
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var iobCobCalculator: IobCobCalculator
init {
tasks.add(object : Task(this, R.string.objectives_bgavailableinns) {
@ -49,7 +49,7 @@ class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R
})
tasks.add(object : Task(this, R.string.hasbgdata) {
override fun isCompleted(): Boolean {
return iobCobCalculatorPlugin.lastBg() != null
return iobCobCalculator.ads.lastBg() != null
}
})
tasks.add(object : Task(this, R.string.loopenabled) {
@ -65,7 +65,7 @@ class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R
})
tasks.add(object : Task(this, R.string.activate_profile) {
override fun isCompleted(): Boolean {
return treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now()) != null
return repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
}
})
}

View file

@ -5,7 +5,7 @@ import android.os.Build
import com.scottyab.rootbeer.RootBeer
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ConstraintsInterface
import info.nightscout.androidaps.interfaces.Constraints
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
@ -27,7 +27,7 @@ class PhoneCheckerPlugin @Inject constructor(
.showInList(false)
.pluginName(R.string.phonechecker),
aapsLogger, resourceHelper, injector
), ConstraintsInterface {
), Constraints {
var phoneRooted: Boolean = false
var devMode: Boolean = false

View file

@ -1,9 +1,9 @@
package info.nightscout.androidaps.plugins.constraints.safety
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin
@ -13,6 +13,7 @@ import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.Round
@ -34,11 +35,12 @@ class SafetyPlugin @Inject constructor(
private val openAPSAMAPlugin: OpenAPSAMAPlugin,
private val openAPSSMBPlugin: OpenAPSSMBPlugin,
private val sensitivityOref1Plugin: SensitivityOref1Plugin,
private val activePlugin: ActivePluginProvider,
private val activePlugin: ActivePlugin,
private val hardLimits: HardLimits,
private val buildHelper: BuildHelper,
private val treatmentsPlugin: TreatmentsInterface,
private val config: Config
private val iobCobCalculator: IobCobCalculator,
private val config: Config,
private val dateUtil: DateUtil
) : PluginBase(PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.neverVisible(true)
@ -47,7 +49,7 @@ class SafetyPlugin @Inject constructor(
.pluginName(R.string.safety)
.preferencesId(R.xml.pref_safety),
aapsLogger, resourceHelper, injector
), ConstraintsInterface {
), Constraints {
/**
* Constraints interface
@ -68,7 +70,7 @@ class SafetyPlugin @Inject constructor(
value[aapsLogger, false, resourceHelper.gs(R.string.closed_loop_disabled_on_dev_branch)] = this
}
val pump = activePlugin.activePump
if (!pump.isFakingTempsByExtendedBoluses && treatmentsPlugin.isInHistoryExtendedBoluslInProgress) {
if (!pump.isFakingTempsByExtendedBoluses && iobCobCalculator.getExtendedBolus(dateUtil.now()) != null) {
value[aapsLogger, false, resourceHelper.gs(R.string.closed_loop_disabled_with_eb)] = this
}
return value
@ -106,25 +108,25 @@ class SafetyPlugin @Inject constructor(
absoluteRate.setIfGreater(aapsLogger, 0.0, String.format(resourceHelper.gs(R.string.limitingbasalratio), 0.0, resourceHelper.gs(R.string.itmustbepositivevalue)), this)
if (config.APS) {
var maxBasal = sp.getDouble(R.string.key_openapsma_max_basal, 1.0)
if (maxBasal < profile.maxDailyBasal) {
maxBasal = profile.maxDailyBasal
if (maxBasal < profile.getMaxDailyBasal()) {
maxBasal = profile.getMaxDailyBasal()
absoluteRate.addReason(resourceHelper.gs(R.string.increasingmaxbasal), this)
}
absoluteRate.setIfSmaller(aapsLogger, maxBasal, String.format(resourceHelper.gs(R.string.limitingbasalratio), maxBasal, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
// Check percentRate but absolute rate too, because we know real current basal in pump
val maxBasalMultiplier = sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0)
val maxFromBasalMultiplier = floor(maxBasalMultiplier * profile.basal * 100) / 100
val maxFromBasalMultiplier = floor(maxBasalMultiplier * profile.getBasal() * 100) / 100
absoluteRate.setIfSmaller(aapsLogger, maxFromBasalMultiplier, String.format(resourceHelper.gs(R.string.limitingbasalratio), maxFromBasalMultiplier, resourceHelper.gs(R.string.maxbasalmultiplier)), this)
val maxBasalFromDaily = sp.getDouble(R.string.key_openapsama_max_daily_safety_multiplier, 3.0)
val maxFromDaily = floor(profile.maxDailyBasal * maxBasalFromDaily * 100) / 100
val maxFromDaily = floor(profile.getMaxDailyBasal() * maxBasalFromDaily * 100) / 100
absoluteRate.setIfSmaller(aapsLogger, maxFromDaily, String.format(resourceHelper.gs(R.string.limitingbasalratio), maxFromDaily, resourceHelper.gs(R.string.maxdailybasalmultiplier)), this)
}
absoluteRate.setIfSmaller(aapsLogger, hardLimits.maxBasal(), String.format(resourceHelper.gs(R.string.limitingbasalratio), hardLimits.maxBasal(), resourceHelper.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump
// check for pump max
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings.maxDose
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings?.maxDose ?: 0.0
absoluteRate.setIfSmaller(aapsLogger, pumpLimit, String.format(resourceHelper.gs(R.string.limitingbasalratio), pumpLimit, resourceHelper.gs(R.string.pumplimit)), this)
}
@ -136,7 +138,7 @@ class SafetyPlugin @Inject constructor(
}
override fun applyBasalPercentConstraints(percentRate: Constraint<Int>, profile: Profile): Constraint<Int> {
val currentBasal = profile.basal
val currentBasal = profile.getBasal()
val absoluteRate = currentBasal * (percentRate.originalValue().toDouble() / 100)
percentRate.addReason("Percent rate " + percentRate.originalValue() + "% recalculated to " + DecimalFormatter.to2Decimal(absoluteRate) + " U/h with current basal " + DecimalFormatter.to2Decimal(currentBasal) + " U/h", this)
val absoluteConstraint = Constraint(absoluteRate)
@ -147,7 +149,7 @@ class SafetyPlugin @Inject constructor(
percentRateAfterConst = if (percentRateAfterConst < 100) Round.ceilTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt() else Round.floorTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt()
percentRate[aapsLogger, percentRateAfterConst, String.format(resourceHelper.gs(R.string.limitingpercentrate), percentRateAfterConst, resourceHelper.gs(R.string.pumplimit))] = this
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT) {
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings.maxDose
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings?.maxDose ?: 0.0
percentRate.setIfSmaller(aapsLogger, pumpLimit.toInt(), String.format(resourceHelper.gs(R.string.limitingbasalratio), pumpLimit, resourceHelper.gs(R.string.pumplimit)), this)
}
return percentRate
@ -188,7 +190,7 @@ class SafetyPlugin @Inject constructor(
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 (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, hardLimits.MAXIOB_LGS, String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.MAXIOB_LGS, resourceHelper.gs(R.string.lowglucosesuspend)), this)
if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, HardLimits.MAX_IOB_LGS, String.format(resourceHelper.gs(R.string.limitingiob), HardLimits.MAX_IOB_LGS, resourceHelper.gs(R.string.lowglucosesuspend)), this)
return maxIob
}
}

View file

@ -5,7 +5,7 @@ import android.content.pm.PackageManager
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ConstraintsInterface
import info.nightscout.androidaps.interfaces.Constraints
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
@ -46,7 +46,7 @@ class SignatureVerifierPlugin @Inject constructor(
.showInList(false)
.pluginName(R.string.signature_verifier),
aapsLogger, resourceHelper, injector
), ConstraintsInterface {
), Constraints {
private val REVOKED_CERTS_URL = "https://raw.githubusercontent.com/nightscout/AndroidAPS/master/app/src/main/assets/revoked_certs.txt"
private val UPDATE_INTERVAL = TimeUnit.DAYS.toMillis(1)

View file

@ -6,7 +6,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ConstraintsInterface
import info.nightscout.androidaps.interfaces.Constraints
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
@ -33,17 +33,17 @@ open class StorageConstraintPlugin @Inject constructor(
.showInList(false)
.pluginName(R.string.storage),
aapsLogger, resourceHelper, injector
), ConstraintsInterface {
), Constraints {
override fun isClosedLoopAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
val diskFree = availableInternalMemorySize()
aapsLogger.debug(LTag.CONSTRAINTS, "Internal storage free (Mb):$diskFree")
if (diskFree < Constants.MINIMUM_FREE_SPACE) {
value[aapsLogger, false, resourceHelper.gs(R.string.diskfull, Constants.MINIMUM_FREE_SPACE)] = this
val notification = Notification(Notification.DISKFULL, resourceHelper.gs(R.string.diskfull, Constants.MINIMUM_FREE_SPACE), Notification.NORMAL)
val notification = Notification(Notification.DISK_FULL, resourceHelper.gs(R.string.diskfull, Constants.MINIMUM_FREE_SPACE), Notification.NORMAL)
rxBus.send(EventNewNotification(notification))
} else {
rxBus.send(EventDismissNotification(Notification.DISKFULL))
rxBus.send(EventDismissNotification(Notification.DISK_FULL))
}
return value
}

View file

@ -4,7 +4,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ConstraintsInterface
import info.nightscout.androidaps.interfaces.Constraints
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
@ -35,7 +35,7 @@ class VersionCheckerPlugin @Inject constructor(
.showInList(false)
.pluginName(R.string.versionChecker),
aapsLogger, resourceHelper, injector
), ConstraintsInterface {
), Constraints {
enum class GracePeriod(val warning: Long, val old: Long, val veryOld: Long) {
RELEASE(30, 60, 90),
@ -82,7 +82,7 @@ class VersionCheckerPlugin @Inject constructor(
gracePeriod.old,
gracePeriod.veryOld
)
val notification = Notification(Notification.OLDVERSION, message, Notification.NORMAL)
val notification = Notification(Notification.OLD_VERSION, message, Notification.NORMAL)
rxBus.send(EventNewNotification(notification))
}
}

View file

@ -11,16 +11,24 @@ import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.activities.TDDStatsActivity
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.dialogs.*
import info.nightscout.androidaps.events.*
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.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
@ -30,10 +38,10 @@ import info.nightscout.androidaps.plugins.general.overview.StatusLightHandler
import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.skins.SkinProvider
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
@ -51,18 +59,21 @@ class ActionsFragment : DaggerFragment() {
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var sp: SP
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var ctx: Context
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var statusLightHandler: StatusLightHandler
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var skinProvider: SkinProvider
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
private var disposable: CompositeDisposable = CompositeDisposable()
@ -153,8 +164,8 @@ class ActionsFragment : DaggerFragment() {
}
}
extendedBolusCancel?.setOnClickListener {
if (activePlugin.activeTreatments.isInHistoryExtendedBoluslInProgress) {
uel.log("CANCEL EXTENDED BOLUS")
if (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null) {
uel.log(Action.CANCEL_EXTENDED_BOLUS, Sources.Actions)
commandQueue.cancelExtended(object : Callback() {
override fun run() {
if (!result.success) {
@ -168,8 +179,8 @@ class ActionsFragment : DaggerFragment() {
TempBasalDialog().show(childFragmentManager, "Actions")
}
cancelTempBasal?.setOnClickListener {
if (activePlugin.activeTreatments.isTempBasalInProgress) {
uel.log("CANCEL TEMP BASAL")
if (iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) != null) {
uel.log(Action.CANCEL_TEMP_BASAL, Sources.Actions)
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (!result.success) {
@ -235,7 +246,7 @@ class ActionsFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main)
.subscribe({ updateGui() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventCareportalEventChange::class.java)
.toObservable(EventTherapyEventChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGui() }, fabricPrivacy::logException)
updateGui()
@ -254,7 +265,7 @@ class ActionsFragment : DaggerFragment() {
val pump = activePlugin.activePump
profileSwitch?.visibility = (
activePlugin.activeProfileInterface.profile != null &&
activePlugin.activeProfileSource.profile != null &&
pump.pumpDescription.isSetBasalProfileCapable &&
pump.isInitialized() &&
!pump.isSuspended()).toVisibility()
@ -263,12 +274,12 @@ class ActionsFragment : DaggerFragment() {
extendedBolus?.visibility = View.GONE
extendedBolusCancel?.visibility = View.GONE
} else {
val activeExtendedBolus = activePlugin.activeTreatments.getExtendedBolusFromHistory(System.currentTimeMillis())
if (activeExtendedBolus != null) {
val activeExtendedBolus = repository.getExtendedBolusActiveAt(dateUtil.now()).blockingGet()
if (activeExtendedBolus is ValueWrapper.Existing) {
extendedBolus?.visibility = View.GONE
extendedBolusCancel?.visibility = View.VISIBLE
@Suppress("SetTextI18n")
extendedBolusCancel?.text = resourceHelper.gs(R.string.cancel) + " " + activeExtendedBolus.toStringMedium()
extendedBolusCancel?.text = resourceHelper.gs(R.string.cancel) + " " + activeExtendedBolus.value.toStringMedium(dateUtil)
} else {
extendedBolus?.visibility = View.VISIBLE
extendedBolusCancel?.visibility = View.GONE
@ -279,7 +290,7 @@ class ActionsFragment : DaggerFragment() {
setTempBasal?.visibility = View.GONE
cancelTempBasal?.visibility = View.GONE
} else {
val activeTemp = activePlugin.activeTreatments.getTempBasalFromHistory(System.currentTimeMillis())
val activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis())
if (activeTemp != null) {
setTempBasal?.visibility = View.GONE
cancelTempBasal?.visibility = View.VISIBLE

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.plugins.general.actions
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription

View file

@ -5,14 +5,9 @@ import android.content.Intent
import android.content.pm.ResolveInfo
import android.os.Bundle
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventExtendedBolusChange
import info.nightscout.androidaps.events.EventNewBasalProfile
import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.events.EventTreatmentChange
import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -21,13 +16,14 @@ 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.events.EventOverviewBolusProgress
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.receivers.ReceiverStatusStore
import info.nightscout.androidaps.services.Intents
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.extensions.durationInMinutes
import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
@ -41,17 +37,18 @@ class DataBroadcastPlugin @Inject constructor(
resourceHelper: ResourceHelper,
private val aapsSchedulers: AapsSchedulers,
private val context: Context,
private val dateUtil: DateUtil,
private val fabricPrivacy: FabricPrivacy,
private val rxBus: RxBusWrapper,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
private val iobCobCalculator: IobCobCalculator,
private val profileFunction: ProfileFunction,
private val defaultValueHelper: DefaultValueHelper,
private val nsDeviceStatus: NSDeviceStatus,
private val loopPlugin: LoopPlugin,
private val activePlugin: ActivePluginProvider,
private val activePlugin: ActivePlugin,
private var receiverStatusStore: ReceiverStatusStore,
private val config: Config,
private val databaseHelper: DatabaseHelperInterface
private val glucoseStatusProvider: GlucoseStatusProvider
) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
@ -122,30 +119,28 @@ class DataBroadcastPlugin @Inject constructor(
}
private fun bgStatus(bundle: Bundle) {
val lastBG = iobCobCalculatorPlugin.lastBg() ?: return
val glucoseStatus = GlucoseStatus(injector).glucoseStatusData ?: return
val lastBG = iobCobCalculator.ads.lastBg() ?: return
val glucoseStatus = glucoseStatusProvider.glucoseStatusData ?: return
bundle.putDouble("glucoseMgdl", lastBG.value) // last BG in mgdl
bundle.putLong("glucoseTimeStamp", lastBG.timestamp) // timestamp
bundle.putString("units", profileFunction.getUnits()) // units used in AAPS "mg/dl" or "mmol"
bundle.putString("units", profileFunction.getUnits().asText) // units used in AAPS "mg/dl" or "mmol"
bundle.putString("slopeArrow", lastBG.trendArrow.text) // direction arrow as string
bundle.putDouble("deltaMgdl", glucoseStatus.delta) // bg delta in mgdl
bundle.putDouble("avgDeltaMgdl", glucoseStatus.avgDelta) // average bg delta
bundle.putDouble("avgDeltaMgdl", glucoseStatus.shortAvgDelta) // average bg delta
bundle.putDouble("high", defaultValueHelper.determineHighLine()) // predefined top value of in range (green area)
bundle.putDouble("low", defaultValueHelper.determineLowLine()) // predefined bottom value of in range
}
private fun iobCob(bundle: Bundle) {
profileFunction.getProfile() ?: return
activePlugin.activeTreatments.updateTotalIOBTreatments()
val bolusIob: IobTotal = activePlugin.activeTreatments.lastCalculationTreatments.round()
activePlugin.activeTreatments.updateTotalIOBTempBasals()
val basalIob: IobTotal = activePlugin.activeTreatments.lastCalculationTempBasals.round()
val bolusIob = iobCobCalculator.calculateIobFromBolus().round()
val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
bundle.putDouble("bolusIob", bolusIob.iob)
bundle.putDouble("basalIob", basalIob.basaliob)
bundle.putDouble("iob", bolusIob.iob + basalIob.basaliob) // total IOB
val cob = iobCobCalculatorPlugin.getCobInfo(false, "broadcast")
val cob = iobCobCalculator.getCobInfo(false, "broadcast")
bundle.putDouble("cob", cob.displayCob ?: -1.0) // COB [g] or -1 if N/A
bundle.putDouble("futureCarbs", cob.futureCarbs) // future scheduled carbs
}
@ -164,7 +159,7 @@ class DataBroadcastPlugin @Inject constructor(
bundle.putString("enacted", loopPlugin.lastRun?.request?.json().toString())
}
} else { //NSClient or remote
val data = NSDeviceStatus.deviceStatusOpenAPSData
val data = nsDeviceStatus.deviceStatusOpenAPSData
if (data.clockSuggested != 0L && data.suggested != null) {
bundle.putLong("suggestedTimeStamp", data.clockSuggested)
bundle.putString("suggested", data.suggested.toString())
@ -180,14 +175,14 @@ class DataBroadcastPlugin @Inject constructor(
val now = System.currentTimeMillis()
val profile = profileFunction.getProfile() ?: return
bundle.putLong("basalTimeStamp", now)
bundle.putDouble("baseBasal", profile.basal)
bundle.putDouble("baseBasal", profile.getBasal())
bundle.putString("profile", profileFunction.getProfileName())
activePlugin.activeTreatments.getTempBasalFromHistory(now)?.let {
bundle.putLong("tempBasalStart", it.date)
bundle.putInt("tempBasalDurationInMinutes", it.durationInMinutes)
if (it.isAbsolute) bundle.putDouble("tempBasalAbsolute", it.absoluteRate) // U/h for absolute TBR
else bundle.putInt("tempBasalPercent", it.percentRate) // % for percent type TBR
bundle.putString("tempBasalString", it.toStringFull()) // user friendly string
iobCobCalculator.getTempBasalIncludingConvertedExtended(now)?.let {
bundle.putLong("tempBasalStart", it.timestamp)
bundle.putLong("tempBasalDurationInMinutes", it.durationInMinutes)
if (it.isAbsolute) bundle.putDouble("tempBasalAbsolute", it.rate) // U/h for absolute TBR
else bundle.putInt("tempBasalPercent", it.rate.toInt()) // % for percent type TBR
bundle.putString("tempBasalString", it.toStringFull(profile, dateUtil)) // user friendly string
}
}

View file

@ -1,143 +0,0 @@
package info.nightscout.androidaps.plugins.general.food;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Objects;
import info.nightscout.androidaps.utils.JsonHelper;
/**
* Created by mike on 20.09.2017.
*/
@DatabaseTable(tableName = Food.TABLE_FOODS)
public class Food {
static final String TABLE_FOODS = "Foods";
@DatabaseField(id = true)
public long key;
@DatabaseField
public boolean isValid = true;
@DatabaseField
public String _id; // NS _id
@DatabaseField
public String name;
@DatabaseField
public String category;
@DatabaseField
public String subcategory;
// Example:
// name="juice" portion=250 units="ml" carbs=12
// means 250ml of juice has 12g of carbs
@DatabaseField
public double portion; // common portion in "units"
@DatabaseField
public int carbs; // in grams
@DatabaseField
public int fat = 0; // in grams
@DatabaseField
public int protein = 0; // in grams
@DatabaseField
public int energy = 0; // in kJ
@DatabaseField
public String units = "g";
@DatabaseField
public int gi; // not used yet
private Food() {
key = System.currentTimeMillis();
}
public static Food createFromJson(JSONObject json) throws JSONException {
Food food = new Food();
if ("food".equals(JsonHelper.safeGetString(json, "type"))) {
food._id = JsonHelper.safeGetString(json, "_id");
food.category = JsonHelper.safeGetString(json, "category");
food.subcategory = JsonHelper.safeGetString(json, "subcategory");
food.name = JsonHelper.safeGetString(json, "name");
food.units = JsonHelper.safeGetString(json, "unit");
food.portion = JsonHelper.safeGetDouble(json, "portion");
food.carbs = JsonHelper.safeGetInt(json, "carbs");
food.gi = JsonHelper.safeGetInt(json, "gi");
food.energy = JsonHelper.safeGetInt(json, "energy");
food.protein = JsonHelper.safeGetInt(json, "protein");
food.fat = JsonHelper.safeGetInt(json, "fat");
}
return food;
}
public boolean isEqual(Food other) {
if (portion != other.portion)
return false;
if (carbs != other.carbs)
return false;
if (fat != other.fat)
return false;
if (protein != other.protein)
return false;
if (energy != other.energy)
return false;
if (gi != other.gi)
return false;
if (!Objects.equals(_id, other._id))
return false;
if (!Objects.equals(name, other.name))
return false;
if (!Objects.equals(category, other.category))
return false;
if (!Objects.equals(subcategory, other.subcategory))
return false;
return Objects.equals(units, other.units);
}
public void copyFrom(Food other) {
isValid = other.isValid;
_id = other._id;
name = other.name;
category = other.category;
subcategory = other.subcategory;
portion = other.portion;
carbs = other.carbs;
fat = other.fat;
protein = other.protein;
energy = other.energy;
units = other.units;
gi = other.gi;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("_id=" + _id + ";");
sb.append("isValid=" + isValid + ";");
sb.append("name=" + name + ";");
sb.append("category=" + category + ";");
sb.append("subcategory=" + subcategory + ";");
sb.append("portion=" + portion + ";");
sb.append("carbs=" + carbs + ";");
sb.append("protein=" + protein + ";");
sb.append("energy=" + energy + ";");
sb.append("units=" + units + ";");
sb.append("gi=" + gi + ";");
return sb.toString();
}
}

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.plugins.general.food
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.Paint
import android.os.Bundle
import android.text.Editable
@ -15,19 +14,31 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.Food
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.InvalidateFoodTransaction
import info.nightscout.androidaps.databinding.FoodFragmentBinding
import info.nightscout.androidaps.databinding.FoodItemBinding
import info.nightscout.androidaps.events.EventFoodDatabaseChanged
import info.nightscout.androidaps.logging.AAPSLogger
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.food.FoodFragment.RecyclerViewAdapter.FoodsViewHolder
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.collections.ArrayList
@ -35,15 +46,15 @@ class FoodFragment : DaggerFragment() {
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var foodPlugin: FoodPlugin
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger
private val disposable = CompositeDisposable()
private lateinit var unfiltered: List<Food>
private lateinit var filtered: MutableList<Food>
private var unfiltered: List<Food> = arrayListOf()
private var filtered: MutableList<Food> = arrayListOf()
private var _binding: FoodFragmentBinding? = null
@ -61,8 +72,24 @@ class FoodFragment : DaggerFragment() {
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.adapter = RecyclerViewAdapter(foodPlugin.service?.foodData
?: ArrayList())
binding.refreshFromNightscout.setOnClickListener {
context?.let { context ->
OKDialog.showConfirmation(context, resourceHelper.gs(R.string.refresheventsfromnightscout) + " ?", {
uel.log(Action.FOOD, Sources.Food, resourceHelper.gs(R.string.refresheventsfromnightscout),
ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.refresheventsfromnightscout)))
disposable += Completable.fromAction { repository.deleteAllFoods() }
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribeBy(
onError = { aapsLogger.error("Error removing foods", it) },
onComplete = { rxBus.send(EventFoodDatabaseChanged()) }
)
rxBus.send(EventNSClientRestart())
})
}
}
binding.clearfilter.setOnClickListener {
binding.filter.setText("")
@ -98,10 +125,6 @@ class FoodFragment : DaggerFragment() {
override fun afterTextChanged(s: Editable) {}
})
loadData()
fillCategories()
fillSubcategories()
filterData()
}
@Synchronized
@ -110,9 +133,23 @@ class FoodFragment : DaggerFragment() {
disposable.add(rxBus
.toObservable(EventFoodDatabaseChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGui() }, fabricPrivacy::logException)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
)
updateGui()
swapAdapter()
}
private fun swapAdapter() {
disposable += repository
.getFoodData()
.observeOn(aapsSchedulers.main)
.subscribe { list ->
unfiltered = list
fillCategories()
fillSubcategories()
filterData()
binding.recyclerview.swapAdapter(RecyclerViewAdapter(filtered), true)
}
}
@Synchronized
@ -127,14 +164,11 @@ class FoodFragment : DaggerFragment() {
_binding = null
}
private fun loadData() {
unfiltered = foodPlugin.service?.foodData ?: ArrayList()
}
private fun fillCategories() {
val catSet: MutableSet<CharSequence> = HashSet()
for (f in unfiltered) {
if (f.category != null && f.category != "") catSet.add(f.category)
val category = f.category
if (!category.isNullOrBlank()) catSet.add(category)
}
// make it unique
val categories = ArrayList(catSet)
@ -150,7 +184,10 @@ class FoodFragment : DaggerFragment() {
val subCatSet: MutableSet<CharSequence> = HashSet()
if (categoryFilter != resourceHelper.gs(R.string.none)) {
for (f in unfiltered) {
if (f.category != null && f.category == categoryFilter) if (f.subcategory != null && f.subcategory != "") subCatSet.add(f.subcategory)
if (f.category != null && f.category == categoryFilter) {
val subCategory = f.subCategory
if (!subCategory.isNullOrEmpty()) subCatSet.add(subCategory)
}
}
}
// make it unique
@ -164,25 +201,25 @@ class FoodFragment : DaggerFragment() {
private fun filterData() {
val textFilter = binding.filter.text.toString()
val categoryFilter = binding.category.selectedItem.toString()
val subcategoryFilter = binding.subcategory.selectedItem.toString()
val categoryFilter = binding.category.selectedItem?.toString()
?: resourceHelper.gs(R.string.none)
val subcategoryFilter = binding.subcategory.selectedItem?.toString()
?: resourceHelper.gs(R.string.none)
val newFiltered = ArrayList<Food>()
for (f in unfiltered) {
if (f.name == null || f.category == null || f.subcategory == null) continue
if (subcategoryFilter != resourceHelper.gs(R.string.none) && f.subcategory != subcategoryFilter) continue
if (f.category == null || f.subCategory == null) continue
if (subcategoryFilter != resourceHelper.gs(R.string.none) && f.subCategory != subcategoryFilter) continue
if (categoryFilter != resourceHelper.gs(R.string.none) && f.category != categoryFilter) continue
if (textFilter != "" && !f.name.toLowerCase(Locale.getDefault()).contains(textFilter.toLowerCase(Locale.getDefault()))) continue
newFiltered.add(f)
}
filtered = newFiltered
updateGui()
}
private fun updateGui() {
binding.recyclerview.swapAdapter(RecyclerViewAdapter(filtered), true)
}
inner class RecyclerViewAdapter internal constructor(var foodList: List<Food>) : RecyclerView.Adapter<FoodsViewHolder>() {
fun Int?.isNotZero(): Boolean = this != null && this != 0
inner class RecyclerViewAdapter internal constructor(private var foodList: List<Food>) : RecyclerView.Adapter<RecyclerViewAdapter.FoodsViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): FoodsViewHolder {
val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.food_item, viewGroup, false)
@ -192,16 +229,16 @@ class FoodFragment : DaggerFragment() {
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: FoodsViewHolder, position: Int) {
val food = foodList[position]
holder.binding.nsSign.visibility = if (food._id != null) View.VISIBLE else View.GONE
holder.binding.nsSign.visibility = (food.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.name.text = food.name
holder.binding.portion.text = food.portion.toString() + food.units
holder.binding.portion.text = food.portion.toString() + food.unit
holder.binding.carbs.text = food.carbs.toString() + resourceHelper.gs(R.string.shortgramm)
holder.binding.fat.text = resourceHelper.gs(R.string.shortfat) + ": " + food.fat + resourceHelper.gs(R.string.shortgramm)
if (food.fat == 0) holder.binding.fat.visibility = View.INVISIBLE
holder.binding.fat.visibility = food.fat.isNotZero().toVisibility()
holder.binding.protein.text = resourceHelper.gs(R.string.shortprotein) + ": " + food.protein + resourceHelper.gs(R.string.shortgramm)
if (food.protein == 0) holder.binding.protein.visibility = View.INVISIBLE
holder.binding.protein.visibility = food.protein.isNotZero().toVisibility()
holder.binding.energy.text = resourceHelper.gs(R.string.shortenergy) + ": " + food.energy + resourceHelper.gs(R.string.shortkilojoul)
if (food.energy == 0) holder.binding.energy.visibility = View.INVISIBLE
holder.binding.energy.visibility = food.energy.isNotZero().toVisibility()
holder.binding.remove.tag = food
}
@ -215,12 +252,13 @@ class FoodFragment : DaggerFragment() {
binding.remove.setOnClickListener { v: View ->
val food = v.tag as Food
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.confirmation), resourceHelper.gs(R.string.removerecord) + "\n" + food.name, DialogInterface.OnClickListener { _: DialogInterface?, _: Int ->
uel.log("FOOD REMOVED", food.name)
if (food._id != null && food._id != "") {
nsUpload.removeFoodFromNS(food._id)
}
foodPlugin.service?.delete(food)
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + "\n" + food.name, {
uel.log(Action.FOOD_REMOVED, Sources.Food, food.name)
disposable += repository.runTransactionForResult(InvalidateFoodTransaction(food.id))
.subscribe(
{ aapsLogger.error(LTag.DATABASE, "Invalidated food $it") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating food", it) }
)
}, null)
}
}

View file

@ -1,12 +1,25 @@
package info.nightscout.androidaps.plugins.general.food
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.Food
import info.nightscout.androidaps.database.transactions.SyncNsFoodTransaction
import info.nightscout.androidaps.extensions.foodFromJson
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.receivers.DataWorker
import info.nightscout.androidaps.utils.JsonHelper
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
@ -25,10 +38,76 @@ class FoodPlugin @Inject constructor(
aapsLogger, resourceHelper, injector
) {
var service: FoodService? = null
// cannot be inner class because of needed injection
class FoodWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
override fun onStart() {
super.onStart()
service = FoodService(injector)
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var sp: SP
@Inject lateinit var dataWorker: DataWorker
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
override fun doWork(): Result {
val foods = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
aapsLogger.debug(LTag.DATABASE, "Received Food Data: $foods")
var ret = Result.success()
for (index in 0 until foods.length()) {
val jsonFood: JSONObject = foods.getJSONObject(index)
if (JsonHelper.safeGetString(jsonFood, "type") != "food") continue
when (JsonHelper.safeGetString(jsonFood, "action")) {
"remove" -> {
val delFood = Food(
name = "",
portion = 0.0,
carbs = 0,
isValid = false
).also { it.interfaceIDs.nightscoutId = JsonHelper.safeGetString(jsonFood, "_id") }
repository.runTransactionForResult(SyncNsFoodTransaction(delFood, true))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while removing food", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also {
it.invalidated.forEach { f -> aapsLogger.debug(LTag.DATABASE, "Invalidated food ${f.interfaceIDs.nightscoutId}") }
}
}
else -> {
val food = foodFromJson(jsonFood)
if (food != null) {
repository.runTransactionForResult(SyncNsFoodTransaction(food, false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while adding/updating food", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted food $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated food $it") }
result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated food $it") }
}
} else {
aapsLogger.error(LTag.DATABASE, "Error parsing food", jsonFood.toString())
ret = Result.failure(workDataOf("Error" to "Error parsing food"))
}
}
}
}
return ret
}
}
}

View file

@ -1,359 +0,0 @@
package info.nightscout.androidaps.plugins.general.food;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import com.j256.ormlite.android.apptools.OpenHelperManager;
import com.j256.ormlite.android.apptools.OrmLiteBaseService;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.ICallback;
import info.nightscout.androidaps.events.Event;
import info.nightscout.androidaps.events.EventFoodDatabaseChanged;
import info.nightscout.androidaps.events.EventNsFood;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.rx.AapsSchedulers;
import io.reactivex.disposables.CompositeDisposable;
/**
* Created by mike on 24.09.2017.
*/
public class FoodService extends OrmLiteBaseService<DatabaseHelper> {
@Inject AAPSLogger aapsLogger;
@Inject RxBusWrapper rxBus;
@Inject FabricPrivacy fabricPrivacy;
@Inject AapsSchedulers aapsSchedulers;
private final CompositeDisposable disposable = new CompositeDisposable();
private static final ScheduledExecutorService foodEventWorker = Executors.newSingleThreadScheduledExecutor();
private static ScheduledFuture<?> scheduledFoodEventPost = null;
public FoodService(HasAndroidInjector injector) {
injector.androidInjector().inject(this);
onCreate();
dbInitialize();
disposable.add(rxBus
.toObservable(EventNsFood.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
int mode = event.getMode();
JSONArray array = event.getFoods();
if (mode == EventNsFood.Companion.getADD() || mode == EventNsFood.Companion.getUPDATE())
this.createFoodFromJsonIfNotExists(array);
else
this.deleteNS(array);
}, fabricPrivacy::logException)
);
}
/**
* This method is a simple re-implementation of the database create and up/downgrade functionality
* in SQLiteOpenHelper#getDatabaseLocked method.
* <p>
* It is implemented to be able to late initialize separate plugins of the application.
*/
protected void dbInitialize() {
DatabaseHelper helper = OpenHelperManager.getHelper(this, DatabaseHelper.class);
int newVersion = helper.getNewVersion();
int oldVersion = helper.getOldVersion();
if (oldVersion > newVersion) {
onDowngrade(this.getConnectionSource(), oldVersion, newVersion);
} else {
onUpgrade(this.getConnectionSource(), oldVersion, newVersion);
}
}
public Dao<Food, Long> getDao() {
try {
return DaoManager.createDao(this.getConnectionSource(), Food.class);
} catch (SQLException e) {
aapsLogger.error("Cannot create Dao for Food.class");
}
return null;
}
@Override
public void onCreate() {
super.onCreate();
try {
aapsLogger.info(LTag.DATAFOOD, "onCreate");
TableUtils.createTableIfNotExists(this.getConnectionSource(), Food.class);
} catch (SQLException e) {
aapsLogger.error("Can't create database", e);
throw new RuntimeException(e);
}
}
public void onUpgrade(ConnectionSource connectionSource, int oldVersion, int newVersion) {
aapsLogger.info(LTag.DATAFOOD, "onUpgrade");
// this.resetFood();
}
public void onDowngrade(ConnectionSource connectionSource, int oldVersion, int newVersion) {
// this method is not supported right now
}
public void resetFood() {
try {
TableUtils.dropTable(this.getConnectionSource(), Food.class, true);
TableUtils.createTableIfNotExists(this.getConnectionSource(), Food.class);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
scheduleFoodChange();
}
/**
* A place to centrally register events to be posted, if any data changed.
* This should be implemented in an abstract service-class.
* <p>
* We do need to make sure, that ICallback is extended to be able to handle multiple
* events, or handle a list of events.
* <p>
* on some methods the earliestDataChange event is handled separatly, in that it is checked if it is
* set to null by another event already (eg. scheduleExtendedBolusChange).
*
* @param event
* @param eventWorker
* @param callback
*/
private void scheduleEvent(final Event event, ScheduledExecutorService eventWorker,
final ICallback callback) {
class PostRunnable implements Runnable {
public void run() {
aapsLogger.debug(LTag.DATAFOOD, "Firing EventFoodChange");
rxBus.send(event);
callback.setPost(null);
}
}
// prepare task for execution in 1 sec
// cancel waiting task to prevent sending multiple posts
if (callback.getPost() != null)
callback.getPost().cancel(false);
Runnable task = new PostRunnable();
final int sec = 1;
callback.setPost(eventWorker.schedule(task, sec, TimeUnit.SECONDS));
}
/**
* Schedule a foodChange Event.
*/
public void scheduleFoodChange() {
this.scheduleEvent(new EventFoodDatabaseChanged(), foodEventWorker, new ICallback() {
@Override
public void setPost(ScheduledFuture<?> post) {
scheduledFoodEventPost = post;
}
@Override
public ScheduledFuture<?> getPost() {
return scheduledFoodEventPost;
}
});
}
public List<Food> getFoodData() {
try {
return this.getDao().queryForAll();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<>();
}
/*
{
"_id": "551ee3ad368e06e80856e6a9",
"type": "food",
"category": "Zakladni",
"subcategory": "Napoje",
"name": "Mleko",
"portion": 250,
"carbs": 12,
"gi": 1,
"created_at": "2015-04-14T06:59:16.500Z",
"unit": "ml"
}
*/
public void createFoodFromJsonIfNotExists(JSONObject json) {
try {
Food food = Food.createFromJson(json);
this.createFoodFromJsonIfNotExists(food);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void createFoodFromJsonIfNotExists(JSONArray array) {
try {
for (int n = 0; n < array.length(); n++) {
JSONObject json = array.getJSONObject(n);
Food food = Food.createFromJson(json);
this.createFoodFromJsonIfNotExists(food);
}
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void createFoodFromJsonIfNotExists(Food food) {
this.createOrUpdateByNS(food);
}
public void deleteNS(JSONObject json) {
try {
String _id = json.getString("_id");
this.deleteByNSId(_id);
} catch (JSONException | SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void deleteNS(JSONArray array) {
try {
for (int n = 0; n < array.length(); n++) {
JSONObject json = array.getJSONObject(n);
this.deleteNS(json);
}
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
/**
* deletes an entry by its NS Id.
* <p>
* Basically a convenience method for findByNSId and delete.
*
* @param _id
*/
public void deleteByNSId(String _id) throws SQLException {
Food stored = this.findByNSId(_id);
if (stored != null) {
aapsLogger.debug(LTag.DATAFOOD, "Removing Food record from database: " + stored.toString());
this.delete(stored);
}
}
/**
* deletes the food and sends the foodChange Event
* <p>
* should be moved ot a Service
*
* @param food
*/
public void delete(Food food) {
try {
this.getDao().delete(food);
this.scheduleFoodChange();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
/**
* Create of update a food record by the NS (Nightscout) Id.
*
* @param food
* @return
*/
public boolean createOrUpdateByNS(Food food) {
// find by NS _id
if (food._id != null && !food._id.equals("")) {
Food old = this.findByNSId(food._id);
if (old != null) {
if (!old.isEqual(food)) {
this.delete(old); // need to delete/create because date may change too
old.copyFrom(food);
this.create(old);
return true;
} else {
return false;
}
} else {
this.createOrUpdate(food);
return true;
}
}
return false;
}
public void createOrUpdate(Food food) {
try {
this.getDao().createOrUpdate(food);
aapsLogger.debug(LTag.DATAFOOD, "Created or Updated: " + food.toString());
} catch (SQLException e) {
aapsLogger.error("Unable to createOrUpdate Food", e);
}
this.scheduleFoodChange();
}
public void create(Food food) {
try {
this.getDao().create(food);
aapsLogger.debug(LTag.DATAFOOD, "New record: " + food.toString());
} catch (SQLException e) {
aapsLogger.error("Unable to create Food", e);
}
this.scheduleFoodChange();
}
/**
* finds food by its NS Id.
*
* @param _id
* @return
*/
@Nullable
public Food findByNSId(String _id) {
try {
List<Food> list = this.getDao().queryForEq("_id", _id);
if (list.size() == 1) { // really? if there are more then one result, then we do not return anything...
return list.get(0);
}
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return null;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

View file

@ -15,9 +15,12 @@ import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult
import info.nightscout.androidaps.activities.PreferencesActivity
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.interfaces.ConfigInterface
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.ImportExportPrefs
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
@ -34,10 +37,10 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.protection.PasswordCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Single
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.system.exitProcess
@ -47,20 +50,21 @@ import kotlin.system.exitProcess
*/
@Singleton
class ImportExportPrefs @Inject constructor(
class ImportExportPrefsImpl @Inject constructor(
private var log: AAPSLogger,
private val resourceHelper: ResourceHelper,
private val sp: SP,
private val buildHelper: BuildHelper,
private val rxBus: RxBusWrapper,
private val passwordCheck: PasswordCheck,
private val config: ConfigInterface,
private val config: Config,
private val androidPermission: AndroidPermission,
private val classicPrefsFormat: ClassicPrefsFormat,
private val encryptedPrefsFormat: EncryptedPrefsFormat,
private val prefFileList: PrefFileListProvider,
private val uel: UserEntryLogger
) : ImportExportPrefsInterface {
private val uel: UserEntryLogger,
private val dateUtil: DateUtil
) : ImportExportPrefs {
override fun prefsFileExists(): Boolean {
return prefFileList.listPreferenceFiles().size > 0
@ -91,7 +95,7 @@ class ImportExportPrefs @Inject constructor(
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf()
metadata[PrefsMetadataKey.DEVICE_NAME] = PrefMetadata(detectUserName(context), PrefsStatus.OK)
metadata[PrefsMetadataKey.CREATED_AT] = PrefMetadata(DateUtil.toISOString(Date()), PrefsStatus.OK)
metadata[PrefsMetadataKey.CREATED_AT] = PrefMetadata(dateUtil.toISOString(dateUtil.now()), PrefsStatus.OK)
metadata[PrefsMetadataKey.AAPS_VERSION] = PrefMetadata(BuildConfig.VERSION_NAME, PrefsStatus.OK)
metadata[PrefsMetadataKey.AAPS_FLAVOUR] = PrefMetadata(BuildConfig.FLAVOR, PrefsStatus.OK)
metadata[PrefsMetadataKey.DEVICE_MODEL] = PrefMetadata(config.currentDeviceModelString, PrefsStatus.OK)
@ -105,6 +109,7 @@ class ImportExportPrefs @Inject constructor(
return metadata
}
@Suppress("SpellCheckingInspection")
private fun detectUserName(context: Context): String {
// based on https://medium.com/@pribble88/how-to-get-an-android-device-nickname-4b4700b3068c
val n1 = Settings.System.getString(context.contentResolver, "bluetooth_name")
@ -343,8 +348,8 @@ class ImportExportPrefs @Inject constructor(
private fun restartAppAfterImport(context: Context) {
sp.putBoolean(R.string.key_setupwizard_processed, true)
OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable {
uel.log("IMPORT")
OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp)) {
uel.log(Action.IMPORT_SETTINGS, Sources.Maintenance)
log.debug(LTag.CORE, "Exiting")
rxBus.send(EventAppExit())
if (context is AppCompatActivity) {
@ -352,6 +357,23 @@ class ImportExportPrefs @Inject constructor(
}
System.runFinalization()
exitProcess(0)
})
}
}
override fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single<List<UserEntry>>) {
val entries = singleEntries.blockingGet()
prefFileList.ensureExportDirExists()
val newFile = prefFileList.newExportXmlFile()
try {
classicPrefsFormat.saveCsv(newFile, entries)
ToastUtils.okToast(activity, resourceHelper.gs(R.string.ue_exported))
} catch (e: FileNotFoundException) {
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + newFile)
log.error(LTag.CORE, "Unhandled exception", e)
} catch (e: IOException) {
ToastUtils.errorToast(activity, e.message)
log.error(LTag.CORE, "Unhandled exception", e)
}
}
}

View file

@ -6,18 +6,21 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.MaintenanceFragmentBinding
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.ImportExportPrefs
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.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
@ -32,12 +35,13 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var maintenancePlugin: MaintenancePlugin
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var foodPlugin: FoodPlugin
@Inject lateinit var importExportPrefs: ImportExportPrefsInterface
@Inject lateinit var importExportPrefs: ImportExportPrefs
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var repository: AppRepository
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var pumpSync: PumpSync
private val compositeDisposable = CompositeDisposable()
@ -56,47 +60,56 @@ class MaintenanceFragment : DaggerFragment() {
super.onViewCreated(view, savedInstanceState)
binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() }
binding.logDelete.setOnClickListener {
uel.log("DELETE LOGS")
uel.log(Action.DELETE_LOGS, Sources.Maintenance)
maintenancePlugin.deleteLogs()
}
binding.navResetdb.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.maintenance), resourceHelper.gs(R.string.reset_db_confirm), Runnable {
uel.log("RESET DATABASES")
compositeDisposable.add(
fromAction {
MainApp.getDbHelper().resetDatabases()
// should be handled by Plugin-Interface and
// additional service interface and plugin registry
foodPlugin.service?.resetFood()
treatmentsPlugin.service.resetTreatments()
databaseHelper.resetDatabases()
repository.clearDatabases()
dataSyncSelector.resetToNextFullSync()
pumpSync.connectNewPump()
}
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribeBy(
onError = { aapsLogger.error("Error clearing databases", it) },
onComplete = { rxBus.send(EventNewBG(null)) }
onComplete = {
rxBus.send(EventNewBG(null))
rxBus.send(EventNewHistoryData(0, true))
}
)
)
uel.log(Action.RESET_DATABASES, Sources.Maintenance)
})
}
}
binding.navExport.setOnClickListener {
uel.log("EXPORT SETTINGS")
uel.log(Action.EXPORT_SETTINGS, Sources.Maintenance)
// start activity for checking permissions...
importExportPrefs.verifyStoragePermissions(this) {
importExportPrefs.exportSharedPreferences(this)
}
}
binding.navImport.setOnClickListener {
uel.log("IMPORT SETTINGS")
uel.log(Action.IMPORT_SETTINGS, Sources.Maintenance)
// start activity for checking permissions...
importExportPrefs.verifyStoragePermissions(this) {
importExportPrefs.importSharedPreferences(this)
}
}
binding.navLogsettings.setOnClickListener { startActivity(Intent(activity, LogSettingActivity::class.java)) }
binding.exportCsv.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.ue_export_to_csv) + "?") {
uel.log(Action.EXPORT_CSV, Sources.Maintenance)
importExportPrefs.exportUserEntriesCsv(activity, repository.getAllUserEntries())
}
}
}
}
@Synchronized

View file

@ -8,7 +8,7 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
@ -173,7 +173,7 @@ class MaintenancePlugin @Inject constructor(
builder.append("Build: " + BuildConfig.BUILDVERSION + System.lineSeparator())
builder.append("Remote: " + BuildConfig.REMOTE + System.lineSeparator())
builder.append("Flavor: " + BuildConfig.FLAVOR + BuildConfig.BUILD_TYPE + System.lineSeparator())
builder.append(resourceHelper.gs(R.string.configbuilder_nightscoutversion_label) + " " + nsSettingsStatus.nightscoutVersionName + System.lineSeparator())
builder.append(resourceHelper.gs(R.string.configbuilder_nightscoutversion_label) + " " + nsSettingsStatus.getVersion() + System.lineSeparator())
if (buildHelper.isEngineeringMode()) builder.append(resourceHelper.gs(R.string.engineering_mode_enabled))
return sendMail(attachmentUri, recipient, subject, builder.toString())
}

View file

@ -0,0 +1,448 @@
package info.nightscout.androidaps.plugins.general.nsclient
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.extensions.toJson
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
class DataSyncSelectorImplementation @Inject constructor(
private val sp: SP,
private val aapsLogger: AAPSLogger,
private val dateUtil: DateUtil,
private val profileFunction: ProfileFunction,
private val nsClientPlugin: NSClientPlugin,
private val activePlugin: ActivePlugin,
private val appRepository: AppRepository
) : DataSyncSelector {
override fun resetToNextFullSync() {
sp.remove(R.string.key_ns_temporary_target_last_synced_id)
sp.remove(R.string.key_ns_glucose_value_last_synced_id)
sp.remove(R.string.key_ns_food_last_synced_id)
sp.remove(R.string.key_ns_bolus_last_synced_id)
sp.remove(R.string.key_ns_carbs_last_synced_id)
sp.remove(R.string.key_ns_bolus_calculator_result_last_synced_id)
sp.remove(R.string.key_ns_device_status_last_synced_id)
sp.remove(R.string.key_ns_temporary_basal_last_synced_id)
sp.remove(R.string.key_ns_extended_bolus_last_synced_id)
sp.remove(R.string.key_ns_therapy_event_last_synced_id)
sp.remove(R.string.key_ns_profile_switch_last_synced_id)
}
override fun confirmLastBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting Bolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedBoluses(): List<Bolus> {
val startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
return appRepository.getModifiedBolusesDataFromId(startId)
.blockingGet()
.filter { it.type != Bolus.Type.PRIMING }
.also {
aapsLogger.debug(LTag.NSCLIENT, "Loading Bolus data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedBolusesCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
appRepository.getNextSyncElementBolus(startId).blockingGet()?.let { bolus ->
aapsLogger.info(LTag.DATABASE, "Loading Bolus data Start: $startId ID: ${bolus.first.id} HistoryID: ${bolus.second} ")
when {
// removed and not uploaded yet = ignore
!bolus.first.isValid && bolus.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!bolus.first.isValid && bolus.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("treatments", bolus.first.interfaceIDs.nightscoutId, DataSyncSelector.PairBolus(bolus.first, bolus.second))
// existing without nsId = create new
bolus.first.isValid && bolus.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", bolus.first.toJson(dateUtil), DataSyncSelector.PairBolus(bolus.first, bolus.second))
// existing with nsId = update
bolus.first.isValid && bolus.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", bolus.first.interfaceIDs.nightscoutId, bolus.first.toJson(dateUtil), DataSyncSelector.PairBolus(bolus.first, bolus.second))
}
return true
}
return false
}
override fun confirmLastCarbsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting Carbs data sync from $lastSynced")
sp.putLong(R.string.key_ns_carbs_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedCarbs(): List<Carbs> {
val startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
return appRepository.getModifiedCarbsDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading Carbs data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedCarbsCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
appRepository.getNextSyncElementCarbs(startId).blockingGet()?.let { carb ->
aapsLogger.info(LTag.DATABASE, "Loading Carbs data Start: $startId ID: ${carb.first.id} HistoryID: ${carb.second} ")
when {
// removed and not uploaded yet = ignore
!carb.first.isValid && carb.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!carb.first.isValid && carb.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("treatments", carb.first.interfaceIDs.nightscoutId, DataSyncSelector.PairCarbs(carb.first, carb.second))
// existing without nsId = create new
carb.first.isValid && carb.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", carb.first.toJson(dateUtil), DataSyncSelector.PairCarbs(carb.first, carb.second))
// existing with nsId = update
carb.first.isValid && carb.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", carb.first.interfaceIDs.nightscoutId, carb.first.toJson(dateUtil), DataSyncSelector.PairCarbs(carb.first, carb.second))
}
return true
}
return false
}
override fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting BolusCalculatorResult data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedBolusCalculatorResults(): List<BolusCalculatorResult> {
val startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
return appRepository.getModifiedBolusCalculatorResultsDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading BolusCalculatorResult data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedBolusCalculatorResultsCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
appRepository.getNextSyncElementBolusCalculatorResult(startId).blockingGet()?.let { bolusCalculatorResult ->
aapsLogger.info(LTag.DATABASE, "Loading BolusCalculatorResult data Start: $startId ID: ${bolusCalculatorResult.first.id} HistoryID: ${bolusCalculatorResult.second} ")
when {
// removed and not uploaded yet = ignore
!bolusCalculatorResult.first.isValid && bolusCalculatorResult.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!bolusCalculatorResult.first.isValid && bolusCalculatorResult.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("treatments", bolusCalculatorResult.first.interfaceIDs.nightscoutId, DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second))
// existing without nsId = create new
bolusCalculatorResult.first.isValid && bolusCalculatorResult.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", bolusCalculatorResult.first.toJson(dateUtil), DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second))
// existing with nsId = update
bolusCalculatorResult.first.isValid && bolusCalculatorResult.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", bolusCalculatorResult.first.interfaceIDs.nightscoutId, bolusCalculatorResult.first.toJson(dateUtil), DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second))
}
return true
}
return false
}
override fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryTarget data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_target_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedTempTargets(): List<TemporaryTarget> {
val startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
return appRepository.getModifiedTemporaryTargetsDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading TemporaryTarget data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedTempTargetsCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
appRepository.getNextSyncElementTemporaryTarget(startId).blockingGet()?.let { tt ->
aapsLogger.info(LTag.DATABASE, "Loading TemporaryTarget data Start: $startId ID: ${tt.first.id} HistoryID: ${tt.second} ")
when {
// removed and not uploaded yet = ignore
!tt.first.isValid && tt.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!tt.first.isValid && tt.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("treatments", tt.first.interfaceIDs.nightscoutId, DataSyncSelector.PairTemporaryTarget(tt.first, tt.second))
// existing without nsId = create new
tt.first.isValid && tt.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", tt.first.toJson(profileFunction.getUnits(), dateUtil), DataSyncSelector.PairTemporaryTarget(tt.first, tt.second))
// existing with nsId = update
tt.first.isValid && tt.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", tt.first.interfaceIDs.nightscoutId, tt.first.toJson(profileFunction.getUnits(), dateUtil), DataSyncSelector.PairTemporaryTarget(tt.first, tt.second))
}
return true
}
return false
}
override fun confirmLastFoodIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_food_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting Food data sync from $lastSynced")
sp.putLong(R.string.key_ns_food_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedFoods(): List<Food> {
val startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0)
return appRepository.getModifiedFoodDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading Food data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedFoodsCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0)
appRepository.getNextSyncElementFood(startId).blockingGet()?.let { tt ->
aapsLogger.info(LTag.DATABASE, "Loading Food data Start: $startId ID: ${tt.first.id} HistoryID: ${tt.second} ")
when {
// removed and not uploaded yet = ignore
!tt.first.isValid && tt.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!tt.first.isValid && tt.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("food", tt.first.interfaceIDs.nightscoutId, DataSyncSelector.PairFood(tt.first, tt.second))
// existing without nsId = create new
tt.first.isValid && tt.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("food", tt.first.toJson(), DataSyncSelector.PairFood(tt.first, tt.second))
// existing with nsId = update
tt.first.isValid && tt.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("food", tt.first.interfaceIDs.nightscoutId, tt.first.toJson(), DataSyncSelector.PairFood(tt.first, tt.second))
}
return true
}
return false
}
override fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting GlucoseValue data sync from $lastSynced")
sp.putLong(R.string.key_ns_glucose_value_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedGlucoseValues(): List<GlucoseValue> {
val startId = sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
return appRepository.getModifiedBgReadingsDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading GlucoseValue data for sync from $startId . Records ${it.size}")
}
}
override fun processChangedGlucoseValuesCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
appRepository.getNextSyncElementGlucoseValue(startId).blockingGet()?.let { gv ->
aapsLogger.info(LTag.DATABASE, "Loading GlucoseValue data Start: $startId ID: ${gv.first.id} HistoryID: ${gv.second} ")
if (activePlugin.activeBgSource.shouldUploadToNs(gv.first)) {
when {
// removed and not uploaded yet = ignore
!gv.first.isValid && gv.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!gv.first.isValid && gv.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("entries", gv.first.interfaceIDs.nightscoutId, DataSyncSelector.PairGlucoseValue(gv.first, gv.second))
// existing without nsId = create new
gv.first.isValid && gv.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("entries", gv.first.toJson(dateUtil), DataSyncSelector.PairGlucoseValue(gv.first, gv.second))
// existing with nsId = update
gv.first.isValid && gv.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("entries", gv.first.interfaceIDs.nightscoutId, gv.first.toJson(dateUtil), DataSyncSelector.PairGlucoseValue(gv.first, gv.second))
}
return true
}
}
return false
}
override fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting TherapyEvents data sync from $lastSynced")
sp.putLong(R.string.key_ns_therapy_event_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedTherapyEvents(): List<TherapyEvent> {
val startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
return appRepository.getModifiedTherapyEventDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading TherapyEvents data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedTherapyEventsCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
appRepository.getNextSyncElementTherapyEvent(startId).blockingGet()?.let { tt ->
aapsLogger.info(LTag.DATABASE, "Loading TherapyEvents data Start: $startId ID: ${tt.first.id} HistoryID: ${tt.second} ")
when {
// removed and not uploaded yet = ignore
!tt.first.isValid && tt.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!tt.first.isValid && tt.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("treatments", tt.first.interfaceIDs.nightscoutId, DataSyncSelector.PairTherapyEvent(tt.first, tt.second))
// existing without nsId = create new
tt.first.isValid && tt.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", tt.first.toJson(), DataSyncSelector.PairTherapyEvent(tt.first, tt.second))
// existing with nsId = update
tt.first.isValid && tt.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", tt.first.interfaceIDs.nightscoutId, tt.first.toJson(), DataSyncSelector.PairTherapyEvent(tt.first, tt.second))
}
return true
}
return false
}
override fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced")
sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced)
}
}
override fun changedDeviceStatuses(): List<DeviceStatus> {
val startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
return appRepository.getModifiedDeviceStatusDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading DeviceStatus data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedDeviceStatusesCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus ->
aapsLogger.info(LTag.DATABASE, "Loading DeviceStatus data Start: $startId ID: ${deviceStatus.id}")
when {
// without nsId = create new
deviceStatus.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("devicestatus", deviceStatus.toJson(dateUtil), deviceStatus)
// with nsId = ignore
deviceStatus.interfaceIDs.nightscoutId != null -> Any()
}
return true
}
return false
}
override fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryBasal data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedTemporaryBasals(): List<TemporaryBasal> {
val startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
return appRepository.getModifiedTemporaryBasalDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading TemporaryBasal data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedTemporaryBasalsCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
appRepository.getNextSyncElementTemporaryBasal(startId).blockingGet()?.let { tb ->
aapsLogger.info(LTag.DATABASE, "Loading TemporaryBasal data Start: $startId ID: ${tb.first.id} HistoryID: ${tb.second} ")
profileFunction.getProfile(tb.first.timestamp)?.let { profile ->
when {
// removed and not uploaded yet = ignore
!tb.first.isValid && tb.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!tb.first.isValid && tb.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("treatments", tb.first.interfaceIDs.nightscoutId, DataSyncSelector.PairTemporaryBasal(tb.first, tb.second))
// existing without nsId = create new
tb.first.isValid && tb.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", tb.first.toJson(profile, dateUtil), DataSyncSelector.PairTemporaryBasal(tb.first, tb.second))
// existing with nsId = update
tb.first.isValid && tb.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", tb.first.interfaceIDs.nightscoutId, tb.first.toJson(profile, dateUtil), DataSyncSelector.PairTemporaryBasal(tb.first, tb.second))
}
return true
}
}
return false
}
override fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting ExtendedBolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedExtendedBoluses(): List<ExtendedBolus> {
val startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
return appRepository.getModifiedExtendedBolusDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading ExtendedBolus data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedExtendedBolusesCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
appRepository.getNextSyncElementExtendedBolus(startId).blockingGet()?.let { eb ->
aapsLogger.info(LTag.DATABASE, "Loading ExtendedBolus data Start: $startId ID: ${eb.first.id} HistoryID: ${eb.second} ")
profileFunction.getProfile(eb.first.timestamp)?.let { profile ->
when {
// removed and not uploaded yet = ignore
!eb.first.isValid && eb.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!eb.first.isValid && eb.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("treatments", eb.first.interfaceIDs.nightscoutId, DataSyncSelector.PairExtendedBolus(eb.first, eb.second))
// existing without nsId = create new
eb.first.isValid && eb.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", eb.first.toJson(profile, dateUtil), DataSyncSelector.PairExtendedBolus(eb.first, eb.second))
// existing with nsId = update
eb.first.isValid && eb.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", eb.first.interfaceIDs.nightscoutId, eb.first.toJson(profile, dateUtil), DataSyncSelector.PairExtendedBolus(eb.first, eb.second))
}
return true
}
}
return false
}
override fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting ProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_profile_switch_last_synced_id, lastSynced)
}
}
override fun changedProfileSwitch(): List<ProfileSwitch> {
val startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
return appRepository.getModifiedProfileSwitchDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading ProfileSwitch data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedProfileSwitchesCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
appRepository.getNextSyncElementProfileSwitch(startId).blockingGet()?.let { eb ->
aapsLogger.info(LTag.DATABASE, "Loading ProfileSwitch data Start: $startId ID: ${eb.first.id} HistoryID: ${eb.second} ")
when {
// removed and not uploaded yet = ignore
!eb.first.isValid && eb.first.interfaceIDs.nightscoutId == null -> Any()
// removed and already uploaded = send for removal
!eb.first.isValid && eb.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbRemove("treatments", eb.first.interfaceIDs.nightscoutId, DataSyncSelector.PairProfileSwitch(eb.first, eb.second))
// existing without nsId = create new
eb.first.isValid && eb.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", eb.first.toJson(dateUtil), DataSyncSelector.PairProfileSwitch(eb.first, eb.second))
// existing with nsId = update
eb.first.isValid && eb.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", eb.first.interfaceIDs.nightscoutId, eb.first.toJson(dateUtil), DataSyncSelector.PairProfileSwitch(eb.first, eb.second))
}
return true
}
return false
}
}

View file

@ -0,0 +1,256 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.transactions.*
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.DataSyncSelector.*
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.nsclient.acks.NSAddAck
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import javax.inject.Inject
class NSClientAddAckWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var aapsSchedulers: AapsSchedulers
override fun doWork(): Result {
var ret = Result.success()
val ack = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as NSAddAck?
?: return Result.failure(workDataOf("Error" to "missing input data"))
when (ack.originalObject) {
is PairTemporaryTarget -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdTemporaryTargetTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of TemporaryTarget 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 TemporaryTarget " + pair.value)
dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryTarget " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTempTargetsCompat()
}
is PairGlucoseValue -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdGlucoseValueTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of GlucoseValue 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 GlucoseValue " + pair.value)
dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked GlucoseValue " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedGlucoseValuesCompat()
}
is PairFood -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdFoodTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of Food 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 Food " + pair.value)
dataSyncSelector.confirmLastFoodIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Food " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedFoodsCompat()
}
is PairTherapyEvent -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdTherapyEventTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of TherapyEvent 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 TherapyEvent " + pair.value)
dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TherapyEvent " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTherapyEventsCompat()
}
is PairBolus -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdBolusTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of Bolus 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 Bolus " + pair.value)
dataSyncSelector.confirmLastBolusIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Bolus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedBolusesCompat()
}
is PairCarbs -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdCarbsTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of Carbs 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 Carbs " + pair.value)
dataSyncSelector.confirmLastCarbsIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Carbs" + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedCarbsCompat()
}
is PairBolusCalculatorResult -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdBolusCalculatorResultTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of BolusCalculatorResult 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 BolusCalculatorResult " + pair.value)
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked BolusCalculatorResult" + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedBolusCalculatorResultsCompat()
}
is PairTemporaryBasal -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdTemporaryBasalTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of TemporaryBasal 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 TemporaryBasal " + pair.value)
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryBasal" + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasalsCompat()
}
is PairExtendedBolus -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdExtendedBolusTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of ExtendedBolus 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 ExtendedBolus " + pair.value)
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked ExtendedBolus" + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasalsCompat()
}
is PairProfileSwitch -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdProfileSwitchTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of ProfileSwitch 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 ProfileSwitch " + pair.value)
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileSwitch" + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasalsCompat()
}
is DeviceStatus -> {
val deviceStatus = ack.originalObject
deviceStatus.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdDeviceStatusTransaction(deviceStatus))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of DeviceStatus failed", error)
ret = Result.failure((workDataOf("Error" to error.toString())))
}
.doOnSuccess {
ret = Result.success(workDataOf("ProcessedData" to deviceStatus.toString()))
aapsLogger.debug(LTag.DATABASE, "Updated ns id of DeviceStatus $deviceStatus")
dataSyncSelector.confirmLastDeviceStatusIdIfGreater(deviceStatus.id)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked DeviceStatus" + deviceStatus.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedDeviceStatusesCompat()
}
}
return ret
}
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
}

View file

@ -0,0 +1,353 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.*
import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.logging.AAPSLogger
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.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
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.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class NSClientAddUpdateWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var sp: SP
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var config: Config
@Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var uel: UserEntryLogger
override fun doWork(): Result {
val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT
if (!acceptNSData) return Result.success()
val treatments = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
var ret = Result.success()
var latestDateInReceivedData = 0L
for (i in 0 until treatments.length()) {
var json = treatments.getJSONObject(i)
// new DB model
val insulin = JsonHelper.safeGetDouble(json, "insulin")
val carbs = JsonHelper.safeGetDouble(json, "carbs")
val eventType = JsonHelper.safeGetString(json, "eventType")
if (eventType == null) {
aapsLogger.debug(LTag.NSCLIENT, "Wrong treatment. Ignoring : $json")
continue
}
//Find latest date in treatment
val mills = safeGetLong(json, "mills")
if (mills != 0L && mills < dateUtil.now())
if (mills > latestDateInReceivedData) latestDateInReceivedData = mills
if (insulin > 0) {
bolusFromJson(json)?.let { bolus ->
repository.runTransactionForResult(SyncNsBolusTransaction(bolus, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.inserted.forEach {
uel.log(Action.BOLUS, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.Insulin(it.amount)
)
aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it")
}
result.invalidated.forEach {
uel.log(Action.BOLUS_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.Insulin(it.amount)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId bolus $it")
}
}
} ?: aapsLogger.error("Error parsing bolus json $json")
}
if (carbs > 0) {
carbsFromJson(json)?.let { carb ->
repository.runTransactionForResult(SyncNsCarbsTransaction(carb, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.inserted.forEach {
uel.log(Action.CARBS, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.Gram(it.amount.toInt())
)
aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it")
}
result.invalidated.forEach {
uel.log(Action.CARBS_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.Gram(it.amount.toInt())
)
aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId carbs $it")
}
}
} ?: aapsLogger.error("Error parsing bolus json $json")
}
// Convert back emulated TBR -> EB
if (eventType == TherapyEvent.Type.TEMPORARY_BASAL.text && json.has("extendedEmulated")) {
val ebJson = json.getJSONObject("extendedEmulated")
ebJson.put("_id", json.getString("_id"))
ebJson.put("isValid", json.getBoolean("isValid"))
json = ebJson
}
when {
insulin > 0 || carbs > 0 -> Any()
eventType == TherapyEvent.Type.TEMPORARY_TARGET.text ->
temporaryTargetFromJson(json)?.let { temporaryTarget ->
repository.runTransactionForResult(SyncNsTemporaryTargetTransaction(temporaryTarget, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.inserted.forEach { tt ->
uel.log(Action.TT, Sources.NSClient,
ValueWithUnit.TherapyEventTTReason(tt.reason),
ValueWithUnit.fromGlucoseUnit(tt.lowTarget, Constants.MGDL),
ValueWithUnit.fromGlucoseUnit(tt.highTarget, Constants.MGDL).takeIf { tt.lowTarget != tt.highTarget },
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryTarget $tt")
}
result.invalidated.forEach { tt ->
uel.log(Action.TT_REMOVED, Sources.NSClient,
ValueWithUnit.TherapyEventTTReason(tt.reason),
ValueWithUnit.Mgdl(tt.lowTarget),
ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget },
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryTarget $tt")
}
result.ended.forEach { tt ->
uel.log(Action.CANCEL_TT, Sources.NSClient,
ValueWithUnit.TherapyEventTTReason(tt.reason),
ValueWithUnit.Mgdl(tt.lowTarget),
ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget },
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Updated TemporaryTarget $tt")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryTarget $it")
}
}
} ?: aapsLogger.error("Error parsing TT json $json")
eventType == TherapyEvent.Type.CANNULA_CHANGE.text ||
eventType == TherapyEvent.Type.INSULIN_CHANGE.text ||
eventType == TherapyEvent.Type.SENSOR_CHANGE.text ||
eventType == TherapyEvent.Type.FINGER_STICK_BG_VALUE.text ||
eventType == TherapyEvent.Type.NONE.text ||
eventType == TherapyEvent.Type.ANNOUNCEMENT.text ||
eventType == TherapyEvent.Type.QUESTION.text ||
eventType == TherapyEvent.Type.EXERCISE.text ||
eventType == TherapyEvent.Type.APS_OFFLINE.text ||
eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text ->
therapyEventFromJson(json)?.let { therapyEvent ->
repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEvent, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
val action = when (eventType) {
TherapyEvent.Type.CANNULA_CHANGE.text -> Action.SITE_CHANGE
TherapyEvent.Type.INSULIN_CHANGE.text -> Action.RESERVOIR_CHANGE
else -> Action.CAREPORTAL
}
result.inserted.forEach {
uel.log(action, Sources.NSClient,
it.note ?: "",
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.TherapyEventType(it.type)
)
aapsLogger.debug(LTag.DATABASE, "Inserted TherapyEvent $it")
}
result.invalidated.forEach {
uel.log(Action.CAREPORTAL_REMOVED, Sources.NSClient,
it.note ?: "",
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.TherapyEventType(it.type)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated TherapyEvent $it")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId TherapyEvent $it")
}
}
} ?: aapsLogger.error("Error parsing TherapyEvent json $json")
eventType == TherapyEvent.Type.COMBO_BOLUS.text ->
extendedBolusFromJson(json)?.let { extendedBolus ->
repository.runTransactionForResult(SyncNsExtendedBolusTransaction(extendedBolus, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving extended bolus", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.inserted.forEach {
uel.log(Action.EXTENDED_BOLUS, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.Insulin(it.amount),
ValueWithUnit.UnitPerHour(it.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Inserted ExtendedBolus $it")
}
result.invalidated.forEach {
uel.log(Action.EXTENDED_BOLUS_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.Insulin(it.amount),
ValueWithUnit.UnitPerHour(it.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Invalidated ExtendedBolus $it")
}
result.ended.forEach {
uel.log(Action.CANCEL_EXTENDED_BOLUS, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.Insulin(it.amount),
ValueWithUnit.UnitPerHour(it.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Updated ExtendedBolus $it")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId ExtendedBolus $it")
}
}
} ?: aapsLogger.error("Error parsing ExtendedBolus json $json")
eventType == TherapyEvent.Type.TEMPORARY_BASAL.text ->
temporaryBasalFromJson(json)?.let { temporaryBasal ->
repository.runTransactionForResult(SyncNsTemporaryBasalTransaction(temporaryBasal, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary basal", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.inserted.forEach {
uel.log(Action.TEMP_BASAL, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.UnitPerHour(it.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryBasal $it")
}
result.invalidated.forEach {
uel.log(Action.TEMP_BASAL_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.UnitPerHour(it.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryBasal $it")
}
result.ended.forEach {
uel.log(Action.CANCEL_TEMP_BASAL, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.UnitPerHour(it.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Ended TemporaryBasal $it")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryBasal $it")
}
}
} ?: aapsLogger.error("Error parsing TemporaryBasal json $json")
eventType == TherapyEvent.Type.PROFILE_SWITCH.text ->
profileSwitchFromJson(json, dateUtil)?.let { profileSwitch ->
repository.runTransactionForResult(SyncNsProfileSwitchTransaction(profileSwitch, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving ProfileSwitch", 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 ProfileSwitch $it")
}
result.invalidated.forEach {
uel.log(Action.PROFILE_SWITCH_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp))
aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId ProfileSwitch $it")
}
}
} ?: aapsLogger.error("Error parsing TemporaryBasal json $json")
}
if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) {
val date = safeGetLong(json, "mills")
val now = System.currentTimeMillis()
val enteredBy = JsonHelper.safeGetString(json, "enteredBy", "")
val notes = JsonHelper.safeGetString(json, "notes", "")
if (date > now - 15 * 60 * 1000L && notes.isNotEmpty()
&& enteredBy != sp.getString("careportal_enteredby", "AndroidAPS")) {
val defaultVal = config.NSCLIENT
if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) {
val announcement = Notification(Notification.NS_ANNOUNCEMENT, notes, Notification.ANNOUNCEMENT, 60)
rxBus.send(EventNewNotification(announcement))
}
}
}
}
nsClientPlugin.updateLatestDateReceivedIfNewer(latestDateInReceivedData)
return ret
}
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
}

View file

@ -1,168 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient;
import android.graphics.Paint;
import android.os.Bundle;
import android.text.Spanned;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ScrollView;
import android.widget.TextView;
import javax.inject.Inject;
import dagger.android.support.DaggerFragment;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.UserEntryLogger;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.HtmlHelper;
import info.nightscout.androidaps.utils.alertDialogs.OKDialog;
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;
public class NSClientFragment extends DaggerFragment implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
@Inject NSClientPlugin nsClientPlugin;
@Inject SP sp;
@Inject ResourceHelper resourceHelper;
@Inject RxBusWrapper rxBus;
@Inject UploadQueue uploadQueue;
@Inject FabricPrivacy fabricPrivacy;
@Inject AapsSchedulers aapsSchedulers;
@Inject UserEntryLogger uel;
private final CompositeDisposable disposable = new CompositeDisposable();
private TextView logTextView;
private TextView queueTextView;
private TextView urlTextView;
private TextView statusTextView;
private TextView clearlog;
private TextView restart;
private TextView delivernow;
private TextView clearqueue;
private TextView showqueue;
private ScrollView logScrollview;
private CheckBox autoscrollCheckbox;
private CheckBox pausedCheckbox;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.nsclientinternal_fragment, container, false);
logScrollview = view.findViewById(R.id.nsclientinternal_logscrollview);
autoscrollCheckbox = view.findViewById(R.id.nsclientinternal_autoscroll);
autoscrollCheckbox.setChecked(nsClientPlugin.autoscroll);
autoscrollCheckbox.setOnCheckedChangeListener(this);
pausedCheckbox = view.findViewById(R.id.nsclientinternal_paused);
pausedCheckbox.setChecked(nsClientPlugin.paused);
pausedCheckbox.setOnCheckedChangeListener(this);
logTextView = view.findViewById(R.id.nsclientinternal_log);
queueTextView = view.findViewById(R.id.nsclientinternal_queue);
urlTextView = view.findViewById(R.id.nsclientinternal_url);
statusTextView = view.findViewById(R.id.nsclientinternal_status);
clearlog = view.findViewById(R.id.nsclientinternal_clearlog);
clearlog.setOnClickListener(this);
clearlog.setPaintFlags(clearlog.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
restart = view.findViewById(R.id.nsclientinternal_restart);
restart.setOnClickListener(this);
restart.setPaintFlags(restart.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
delivernow = view.findViewById(R.id.nsclientinternal_delivernow);
delivernow.setOnClickListener(this);
delivernow.setPaintFlags(delivernow.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
clearqueue = view.findViewById(R.id.nsclientinternal_clearqueue);
clearqueue.setOnClickListener(this);
clearqueue.setPaintFlags(clearqueue.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
showqueue = view.findViewById(R.id.nsclientinternal_showqueue);
showqueue.setOnClickListener(this);
showqueue.setPaintFlags(showqueue.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
return view;
}
@Override
public synchronized void onResume() {
super.onResume();
disposable.add(rxBus
.toObservable(EventNSClientUpdateGUI.class)
.observeOn(aapsSchedulers.getMain())
.subscribe(event -> updateGui(), fabricPrivacy::logException)
);
updateGui();
}
@Override
public synchronized void onPause() {
super.onPause();
disposable.clear();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.nsclientinternal_restart:
rxBus.send(new EventNSClientRestart());
fabricPrivacy.logCustom("NSClientRestart");
break;
case R.id.nsclientinternal_delivernow:
nsClientPlugin.resend("GUI");
fabricPrivacy.logCustom("NSClientDeliverNow");
break;
case R.id.nsclientinternal_clearlog:
nsClientPlugin.clearLog();
break;
case R.id.nsclientinternal_clearqueue:
OKDialog.showConfirmation(getContext(), resourceHelper.gs(R.string.nsclientinternal), resourceHelper.gs(R.string.clearqueueconfirm), () -> {
uel.log("NS QUEUE CLEARED", "", 0.0, 0.0, 0, 0);
uploadQueue.clearQueue();
updateGui();
fabricPrivacy.logCustom("NSClientClearQueue");
});
break;
case R.id.nsclientinternal_showqueue:
rxBus.send(new EventNSClientNewLog("QUEUE", uploadQueue.textList()));
break;
}
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
switch (buttonView.getId()) {
case R.id.nsclientinternal_paused:
uel.log("NS PAUSED", "", 0.0, 0.0, isChecked ? 1 : 0, 0);
nsClientPlugin.pause(isChecked);
updateGui();
fabricPrivacy.logCustom("NSClientPause");
break;
case R.id.nsclientinternal_autoscroll:
sp.putBoolean(R.string.key_nsclientinternal_autoscroll, isChecked);
nsClientPlugin.autoscroll = isChecked;
updateGui();
break;
}
}
protected void updateGui() {
nsClientPlugin.updateLog();
pausedCheckbox.setChecked(sp.getBoolean(R.string.key_nsclientinternal_paused, false));
logTextView.setText(nsClientPlugin.textLog);
if (nsClientPlugin.autoscroll) {
logScrollview.fullScroll(ScrollView.FOCUS_DOWN);
}
urlTextView.setText(nsClientPlugin.url());
Spanned queuetext = HtmlHelper.INSTANCE.fromHtml(resourceHelper.gs(R.string.queue) + " <b>" + uploadQueue.size() + "</b>");
queueTextView.setText(queuetext);
statusTextView.setText(nsClientPlugin.status);
}
}

View file

@ -0,0 +1,122 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ScrollView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.NsClientFragmentBinding
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.UploadQueueAdminInterface
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper.fromHtml
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
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 javax.inject.Inject
class NSClientFragment : DaggerFragment() {
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var uploadQueue: UploadQueueAdminInterface
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var uel: UserEntryLogger
private val disposable = CompositeDisposable()
private var _binding: NsClientFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
NsClientFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.autoscroll.isChecked = nsClientPlugin.autoscroll
binding.autoscroll.setOnCheckedChangeListener { _, isChecked ->
sp.putBoolean(R.string.key_nsclientinternal_autoscroll, isChecked)
nsClientPlugin.autoscroll = isChecked
updateGui()
}
binding.paused.isChecked = nsClientPlugin.paused
binding.paused.setOnCheckedChangeListener { _, isChecked ->
uel.log(if (isChecked) Action.NS_PAUSED else Action.NS_RESUME, Sources.NSClient)
nsClientPlugin.pause(isChecked)
updateGui()
}
binding.clearLog.setOnClickListener { nsClientPlugin.clearLog() }
binding.clearLog.paintFlags = binding.clearLog.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.restart.setOnClickListener { rxBus.send(EventNSClientRestart()) }
binding.restart.paintFlags = binding.restart.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.deliverNow.setOnClickListener { nsClientPlugin.resend("GUI") }
binding.deliverNow.paintFlags = binding.deliverNow.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.clearQueue.setOnClickListener {
context?.let { context ->
OKDialog.showConfirmation(context, resourceHelper.gs(R.string.nsclientinternal), resourceHelper.gs(R.string.clearqueueconfirm), Runnable {
uel.log(Action.NS_QUEUE_CLEARED, Sources.NSClient)
uploadQueue.clearQueue()
updateGui()
})
}
}
binding.clearQueue.paintFlags = binding.clearQueue.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.showQueue.setOnClickListener { rxBus.send(EventNSClientNewLog("QUEUE", uploadQueue.textList())) }
binding.showQueue.paintFlags = binding.showQueue.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.fullSync.setOnClickListener {
context?.let { context ->
OKDialog.showConfirmation(context, resourceHelper.gs(R.string.nsclientinternal), resourceHelper.gs(R.string.full_sync), Runnable {
dataSyncSelector.resetToNextFullSync()
})
}
}
binding.fullSync.paintFlags = binding.fullSync.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
@Synchronized override fun onResume() {
super.onResume()
disposable.add(rxBus
.toObservable(EventNSClientUpdateGUI::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGui() }, fabricPrivacy::logException)
)
updateGui()
}
@Synchronized override fun onPause() {
super.onPause()
disposable.clear()
}
private fun updateGui() {
if (_binding == null) return
nsClientPlugin.updateLog()
binding.paused.isChecked = sp.getBoolean(R.string.key_nsclientinternal_paused, false)
binding.log.text = nsClientPlugin.textLog
if (nsClientPlugin.autoscroll) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN)
binding.url.text = nsClientPlugin.url()
binding.queue.text = fromHtml(resourceHelper.gs(R.string.queue) + " <b>" + uploadQueue.size() + "</b>")
binding.status.text = nsClientPlugin.status
}
}

View file

@ -0,0 +1,60 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.transactions.SyncNsTherapyEventTransaction
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.extensions.therapyEventFromNsMbg
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
class NSClientMbgWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var repository: AppRepository
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var config: Config
override fun doWork(): Result {
var ret = Result.success()
val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT
if (!acceptNSData) return ret
val mbgArray = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
for (i in 0 until mbgArray.length()) {
val nsMbg = NSMbg(mbgArray.getJSONObject(i))
if (!nsMbg.isValid()) continue
repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEventFromNsMbg(nsMbg), false))
.doOnError {
aapsLogger.error("Error while saving therapy event", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also {
aapsLogger.debug(LTag.DATABASE, "Saved therapy event $it")
}
}
return ret
}
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
}

View file

@ -4,20 +4,16 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.text.Spanned;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
@ -25,17 +21,13 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.interfaces.Config;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.db.CareportalEvent;
import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.events.EventChargingState;
import info.nightscout.androidaps.events.EventNetworkChange;
import info.nightscout.androidaps.events.EventNsTreatment;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
@ -44,18 +36,13 @@ import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientResend;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI;
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.services.Intents;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.HtmlHelper;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.ToastUtils;
import info.nightscout.androidaps.utils.buildHelper.BuildHelper;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
@ -74,10 +61,9 @@ public class NSClientPlugin extends PluginBase {
private final AapsSchedulers aapsSchedulers;
private final FabricPrivacy fabricPrivacy;
private final SP sp;
private final NsClientReceiverDelegate nsClientReceiverDelegate;
private final Config config;
private final BuildHelper buildHelper;
private final ActivePluginProvider activePlugin;
private final NSUpload nsUpload;
public Handler handler;
@ -89,9 +75,8 @@ public class NSClientPlugin extends PluginBase {
public String status = "";
public NSClientService nsClientService = null;
public @Nullable NSClientService nsClientService = null;
private final NsClientReceiverDelegate nsClientReceiverDelegate;
@Inject
public NSClientPlugin(
@ -105,9 +90,7 @@ public class NSClientPlugin extends PluginBase {
SP sp,
NsClientReceiverDelegate nsClientReceiverDelegate,
Config config,
BuildHelper buildHelper,
ActivePluginProvider activePlugin,
NSUpload nsUpload
BuildHelper buildHelper
) {
super(new PluginDescription()
.mainType(PluginType.GENERAL)
@ -130,8 +113,6 @@ public class NSClientPlugin extends PluginBase {
this.nsClientReceiverDelegate = nsClientReceiverDelegate;
this.config = config;
this.buildHelper = buildHelper;
this.activePlugin = activePlugin;
this.nsUpload = nsUpload;
if (config.getNSCLIENT()) {
getPluginDescription().alwaysEnabled(true).visibleByDefault(true);
@ -170,12 +151,12 @@ public class NSClientPlugin extends PluginBase {
disposable.add(rxBus
.toObservable(EventNetworkChange.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> nsClientReceiverDelegate.onStatusEvent(event), fabricPrivacy::logException)
.subscribe(nsClientReceiverDelegate::onStatusEvent, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventPreferenceChange.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> nsClientReceiverDelegate.onStatusEvent(event), fabricPrivacy::logException)
.subscribe(nsClientReceiverDelegate::onStatusEvent, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventAppExit.class)
@ -197,7 +178,7 @@ public class NSClientPlugin extends PluginBase {
disposable.add(rxBus
.toObservable(EventChargingState.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> nsClientReceiverDelegate.onStatusEvent(event), fabricPrivacy::logException)
.subscribe(nsClientReceiverDelegate::onStatusEvent, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventNSClientResend.class)
@ -214,7 +195,7 @@ public class NSClientPlugin extends PluginBase {
}
@Override
public void preprocessPreferences(@NotNull PreferenceFragmentCompat preferenceFragment) {
public void preprocessPreferences(@NonNull PreferenceFragmentCompat preferenceFragment) {
super.preprocessPreferences(preferenceFragment);
if (config.getNSCLIENT()) {
@ -236,7 +217,7 @@ public class NSClientPlugin extends PluginBase {
SwitchPreference key_ns_sync_use_absolute = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_sync_use_absolute));
if (key_ns_sync_use_absolute != null) key_ns_sync_use_absolute.setVisible(false);
} else {
// APS or pumpcontrol mode
// APS or pumpControl mode
SwitchPreference key_ns_upload_only = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_upload_only));
if (key_ns_upload_only != null)
key_ns_upload_only.setVisible(buildHelper.isEngineeringMode());
@ -332,143 +313,8 @@ public class NSClientPlugin extends PluginBase {
nsClientService.sendAlarmAck(ack);
}
// Parsing input data
public void handleNewDataFromNSClient(String action, Bundle bundle) {
boolean acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.getNSCLIENT();
if (!acceptNSData) return;
aapsLogger.debug(LTag.DATASERVICE, "Got intent: " + action);
if (action.equals(Intents.ACTION_NEW_TREATMENT) || action.equals(Intents.ACTION_CHANGED_TREATMENT)) {
try {
if (bundle.containsKey("treatment")) {
JSONObject json = new JSONObject(bundle.getString("treatment"));
handleTreatmentFromNS(json, action);
}
if (bundle.containsKey("treatments")) {
String trstring = bundle.getString("treatments");
JSONArray jsonArray = new JSONArray(trstring);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject json = jsonArray.getJSONObject(i);
handleTreatmentFromNS(json, action);
}
}
} catch (JSONException e) {
aapsLogger.error(LTag.DATASERVICE, "Unhandled exception", e);
}
}
if (action.equals(Intents.ACTION_REMOVED_TREATMENT)) {
try {
if (bundle.containsKey("treatment")) {
String trstring = bundle.getString("treatment");
JSONObject json = new JSONObject(trstring);
handleRemovedTreatmentFromNS(json);
}
if (bundle.containsKey("treatments")) {
String trstring = bundle.getString("treatments");
JSONArray jsonArray = new JSONArray(trstring);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject json = jsonArray.getJSONObject(i);
handleRemovedTreatmentFromNS(json);
}
}
} catch (JSONException e) {
aapsLogger.error(LTag.DATASERVICE, "Unhandled exception", e);
}
}
if (action.equals(Intents.ACTION_NEW_MBG)) {
try {
if (bundle.containsKey("mbg")) {
String mbgstring = bundle.getString("mbg");
JSONObject mbgJson = new JSONObject(mbgstring);
storeMbg(mbgJson);
}
if (bundle.containsKey("mbgs")) {
String sgvstring = bundle.getString("mbgs");
JSONArray jsonArray = new JSONArray(sgvstring);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject mbgJson = jsonArray.getJSONObject(i);
storeMbg(mbgJson);
}
}
} catch (Exception e) {
aapsLogger.error(LTag.DATASERVICE, "Unhandled exception", e);
}
}
}
private void handleRemovedTreatmentFromNS(JSONObject json) {
// new DB model
EventNsTreatment evtTreatment = new EventNsTreatment(EventNsTreatment.Companion.getREMOVE(), json);
rxBus.send(evtTreatment);
// old DB model
String _id = JsonHelper.safeGetString(json, "_id");
MainApp.getDbHelper().deleteTempTargetById(_id);
MainApp.getDbHelper().deleteTempBasalById(_id);
MainApp.getDbHelper().deleteExtendedBolusById(_id);
MainApp.getDbHelper().deleteCareportalEventById(_id);
MainApp.getDbHelper().deleteProfileSwitchById(_id);
}
private void handleTreatmentFromNS(JSONObject json, String action) {
// new DB model
int mode = Intents.ACTION_NEW_TREATMENT.equals(action) ? EventNsTreatment.Companion.getADD() : EventNsTreatment.Companion.getUPDATE();
double insulin = JsonHelper.safeGetDouble(json, "insulin");
double carbs = JsonHelper.safeGetDouble(json, "carbs");
String eventType = JsonHelper.safeGetString(json, "eventType");
if (eventType == null) {
aapsLogger.debug(LTag.DATASERVICE, "Wrong treatment. Ignoring : " + json.toString());
return;
}
if (insulin > 0 || carbs > 0) {
EventNsTreatment evtTreatment = new EventNsTreatment(mode, json);
rxBus.send(evtTreatment);
} else if (eventType.equals(CareportalEvent.TEMPORARYTARGET)) {
MainApp.getDbHelper().createTemptargetFromJsonIfNotExists(json);
} else if (eventType.equals(CareportalEvent.TEMPBASAL)) {
MainApp.getDbHelper().createTempBasalFromJsonIfNotExists(json);
} else if (eventType.equals(CareportalEvent.COMBOBOLUS)) {
MainApp.getDbHelper().createExtendedBolusFromJsonIfNotExists(json);
} else if (eventType.equals(CareportalEvent.PROFILESWITCH)) {
MainApp.getDbHelper().createProfileSwitchFromJsonIfNotExists(activePlugin, nsUpload, json);
} else if (eventType.equals(CareportalEvent.SITECHANGE) ||
eventType.equals(CareportalEvent.INSULINCHANGE) ||
eventType.equals(CareportalEvent.SENSORCHANGE) ||
eventType.equals(CareportalEvent.BGCHECK) ||
eventType.equals(CareportalEvent.NOTE) ||
eventType.equals(CareportalEvent.NONE) ||
eventType.equals(CareportalEvent.ANNOUNCEMENT) ||
eventType.equals(CareportalEvent.QUESTION) ||
eventType.equals(CareportalEvent.EXERCISE) ||
eventType.equals(CareportalEvent.OPENAPSOFFLINE) ||
eventType.equals(CareportalEvent.PUMPBATTERYCHANGE)) {
MainApp.getDbHelper().createCareportalEventFromJsonIfNotExists(json);
}
if (eventType.equals(CareportalEvent.ANNOUNCEMENT)) {
long date = JsonHelper.safeGetLong(json, "mills");
long now = System.currentTimeMillis();
String enteredBy = JsonHelper.safeGetString(json, "enteredBy", "");
String notes = JsonHelper.safeGetString(json, "notes", "");
if (date > now - 15 * 60 * 1000L && !notes.isEmpty()
&& !enteredBy.equals(sp.getString("careportal_enteredby", "AndroidAPS"))) {
boolean defaultVal = config.getNSCLIENT();
if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) {
Notification announcement = new Notification(Notification.NSANNOUNCEMENT, notes, Notification.ANNOUNCEMENT, 60);
rxBus.send(new EventNewNotification(announcement));
}
}
}
}
private void storeMbg(JSONObject mbgJson) {
NSMbg nsMbg = new NSMbg(mbgJson);
CareportalEvent careportalEvent = new CareportalEvent(nsMbg);
MainApp.getDbHelper().createOrUpdate(careportalEvent);
aapsLogger.debug(LTag.DATASERVICE, "Adding/Updating new MBG: " + careportalEvent.toString());
public void updateLatestDateReceivedIfNewer(long latestReceived) {
if (nsClientService != null && latestReceived > nsClientService.latestDateInReceivedData)
nsClientService.latestDateInReceivedData = latestReceived;
}
}

View file

@ -0,0 +1,181 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.*
import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.concurrent.TimeUnit
import javax.inject.Inject
// This will not be needed fpr NS v3
// Now NS provides on _id of removed records
class NSClientRemoveWorker(
context: Context,
params: WorkerParameters) : Worker(context, params) {
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var sp: SP
@Inject lateinit var config: Config
@Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var uel: UserEntryLogger
override fun doWork(): Result {
val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT
if (!acceptNSData) return Result.success()
var ret = Result.success()
val treatments = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
for (i in 0 until treatments.length()) {
val json = treatments.getJSONObject(i)
val nsId = JsonHelper.safeGetString(json, "_id") ?: continue
// room Temporary target
val temporaryTarget = temporaryTargetFromNsIdForInvalidating(nsId)
repository.runTransactionForResult(SyncNsTemporaryTargetTransaction(temporaryTarget, invalidateByNsOnly = true))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.invalidated.forEach { tt ->
uel.log(
Action.TT_REMOVED, Sources.NSClient,
ValueWithUnit.TherapyEventTTReason(tt.reason),
ValueWithUnit.Mgdl(tt.lowTarget),
ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget },
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()).takeIf { tt.duration != 0L }
)
}
}
// room Therapy Event
val therapyEvent = therapyEventFromNsIdForInvalidating(nsId)
repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEvent, invalidateByNsOnly = true))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.invalidated.forEach {
uel.log(Action.CAREPORTAL_REMOVED, Sources.NSClient,
(it.note ?: ""),
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.TherapyEventType(it.type))
}
}
// room Bolus
val bolus = bolusFromNsIdForInvalidating(nsId)
repository.runTransactionForResult(SyncNsBolusTransaction(bolus, invalidateByNsOnly = true))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.invalidated.forEach {
uel.log(Action.CAREPORTAL_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.Insulin(it.amount))
}
}
// room Carbs
val carbs = carbsFromNsIdForInvalidating(nsId)
repository.runTransactionForResult(SyncNsCarbsTransaction(carbs, invalidateByNsOnly = true))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.invalidated.forEach {
uel.log(Action.CAREPORTAL_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.Gram(it.amount.toInt()))
}
}
// room TemporaryBasal
val temporaryBasal = temporaryBasalFromNsIdForInvalidating(nsId)
repository.runTransactionForResult(SyncNsTemporaryBasalTransaction(temporaryBasal, invalidateByNsOnly = true))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.invalidated.forEach {
uel.log(
Action.CAREPORTAL_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.UnitPerHour(it.rate))
}
}
// room ExtendedBolus
val extendedBolus = extendedBolusFromNsIdForInvalidating(nsId)
repository.runTransactionForResult(SyncNsExtendedBolusTransaction(extendedBolus, invalidateByNsOnly = true))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.invalidated.forEach {
uel.log(
Action.CAREPORTAL_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.UnitPerHour(it.rate))
}
}
// room ProfileSwitch
repository.runTransactionForResult(InvalidateNsIdProfileSwitchTransaction(nsId))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.invalidated.forEach {
uel.log(
Action.CAREPORTAL_REMOVED, Sources.NSClient,
ValueWithUnit.Timestamp(it.timestamp))
}
}
}
return ret
}
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
}

View file

@ -0,0 +1,135 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.DataSyncSelector.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import javax.inject.Inject
class NSClientUpdateRemoveAckWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var aapsSchedulers: AapsSchedulers
override fun doWork(): Result {
var ret = Result.success()
val ack = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as NSUpdateAck?
?: return Result.failure(workDataOf("Error" to "missing input data"))
// new room way
when (ack.originalObject) {
is PairTemporaryTarget -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked TemporaryTarget" + ack._id))
// Send new if waiting
dataSyncSelector.processChangedTempTargetsCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairGlucoseValue -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked GlucoseValue " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedGlucoseValuesCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairFood -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastFoodIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked Food " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedFoodsCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairTherapyEvent -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked TherapyEvent " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedTherapyEventsCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairBolus -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastBolusIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked Bolus " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedBolusesCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairCarbs -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastCarbsIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked Carbs " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedCarbsCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairBolusCalculatorResult -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked BolusCalculatorResult " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedBolusCalculatorResultsCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairTemporaryBasal -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked TemporaryBasal " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasalsCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairExtendedBolus -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked ExtendedBolus " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedExtendedBolusesCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairProfileSwitch -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked ProfileSwitch " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedProfileSwitchesCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
}
return ret
}
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
}

View file

@ -1,41 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import org.jetbrains.annotations.NotNull;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.receivers.BundleStore;
import info.nightscout.androidaps.receivers.DataReceiver;
// cannot be inner class because of needed injection
public class NSClientWorker extends Worker {
public NSClientWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
((HasAndroidInjector) context.getApplicationContext()).androidInjector().inject(this);
}
@Inject NSClientPlugin nsClientPlugin;
@Inject BundleStore bundleStore;
@NotNull
@Override
public Result doWork() {
Bundle bundle = bundleStore.pickup(getInputData().getLong(DataReceiver.STORE_KEY, -1));
if (bundle == null) return Result.failure();
String action = getInputData().getString(DataReceiver.ACTION_KEY);
nsClientPlugin.handleNewDataFromNSClient(action, bundle);
return Result.success();
}
}

View file

@ -11,12 +11,11 @@ import org.json.JSONObject;
import java.sql.SQLException;
import javax.inject.Inject;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.DbRequest;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.interfaces.UploadQueueAdminInterface;
import info.nightscout.androidaps.interfaces.UploadQueueInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
@ -28,14 +27,13 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP;
/**
* Created by mike on 21.02.2016.
*/
public class UploadQueue implements UploadQueueInterface {
public class UploadQueue implements UploadQueueAdminInterface {
private final AAPSLogger aapsLogger;
private final DatabaseHelperInterface databaseHelper;
private final Context context;
private final SP sp;
private final RxBusWrapper rxBus;
@Inject
public UploadQueue(
AAPSLogger aapsLogger,
DatabaseHelperInterface databaseHelper,
@ -54,6 +52,7 @@ public class UploadQueue implements UploadQueueInterface {
return "QUEUE: " + databaseHelper.size(DatabaseHelper.DATABASE_DBREQUESTS);
}
@Override
public long size() {
return databaseHelper.size(DatabaseHelper.DATABASE_DBREQUESTS);
}
@ -76,7 +75,7 @@ public class UploadQueue implements UploadQueueInterface {
rxBus.send(new EventNSClientResend("newdata"));
}
void clearQueue() {
@Override public void clearQueue() {
startService();
if (NSClientService.handler != null) {
NSClientService.handler.post(() -> {
@ -87,28 +86,8 @@ public class UploadQueue implements UploadQueueInterface {
}
}
public void removeID(final JSONObject record) {
startService();
if (NSClientService.handler != null) {
NSClientService.handler.post(() -> {
try {
String id;
if (record.has("NSCLIENT_ID")) {
id = record.getString("NSCLIENT_ID");
} else {
return;
}
if (databaseHelper.deleteDbRequest(id) == 1) {
aapsLogger.debug(LTag.NSCLIENT, "Removed item from UploadQueue. " + status());
}
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
});
}
}
public void removeID(final String action, final String _id) {
@Override
public void removeByMongoId(final String action, final String _id) {
if (_id == null || _id.equals(""))
return;
startService();
@ -120,11 +99,11 @@ public class UploadQueue implements UploadQueueInterface {
}
}
String textList() {
@Override public String textList() {
String result = "";
CloseableIterator<DbRequest> iterator;
try {
iterator = databaseHelper.getDbRequestInterator();
iterator = databaseHelper.getDbRequestIterator();
try {
while (iterator.hasNext()) {
DbRequest dbr = iterator.next();

View file

@ -1,62 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient.acks;
import org.json.JSONArray;
import org.json.JSONObject;
import info.nightscout.androidaps.events.Event;
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.nsclient.events.EventNSClientRestart;
import io.socket.client.Ack;
/**
* Created by mike on 29.12.2015.
*/
public class NSAddAck extends Event implements Ack {
private final AAPSLogger aapsLogger;
private final RxBusWrapper rxBus;
public String _id = null;
public String nsClientID = null;
public JSONObject json = null;
public NSAddAck(AAPSLogger aapsLogger, RxBusWrapper rxBus) {
this.aapsLogger = aapsLogger;
this.rxBus = rxBus;
}
public void call(Object... args) {
// Regular response
try {
JSONArray responsearray = (JSONArray) (args[0]);
JSONObject response;
if (responsearray.length() > 0) {
response = responsearray.getJSONObject(0);
_id = response.getString("_id");
json = response;
if (response.has("NSCLIENT_ID")) {
nsClientID = response.getString("NSCLIENT_ID");
}
}
rxBus.send(this);
return;
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
// Check for not authorized
try {
JSONObject response = (JSONObject) (args[0]);
if (response.has("result")) {
_id = null;
if (response.getString("result").contains("Not")) {
rxBus.send(new EventNSClientRestart());
return;
}
aapsLogger.debug(LTag.NSCLIENT, "DBACCESS " + response.getString("result"));
}
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
}
}

View file

@ -0,0 +1,54 @@
package info.nightscout.androidaps.plugins.general.nsclient.acks
import info.nightscout.androidaps.events.Event
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.nsclient.events.EventNSClientRestart
import io.socket.client.Ack
import org.json.JSONArray
import org.json.JSONObject
class NSAddAck(
private val aapsLogger: AAPSLogger,
private val rxBus: RxBusWrapper,
val originalObject: Any? = null
) : Event(), Ack {
var id: String? = null
@JvmField var nsClientID: String? = null
@JvmField var json: JSONObject? = null
override fun call(vararg args: Any) {
// Regular response
try {
val responseArray = args[0] as JSONArray
val response: JSONObject
if (responseArray.length() > 0) {
response = responseArray.getJSONObject(0)
id = response.getString("_id")
json = response
if (response.has("NSCLIENT_ID")) {
nsClientID = response.getString("NSCLIENT_ID")
}
}
rxBus.send(this)
return
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
// Check for not authorized
try {
val response = args[0] as JSONObject
if (response.has("result")) {
id = null
if (response.getString("result").contains("Not")) {
rxBus.send(EventNSClientRestart())
return
}
aapsLogger.debug(LTag.NSCLIENT, "DBACCESS " + response.getString("result"))
}
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
}

View file

@ -1,46 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient.acks;
import org.json.JSONException;
import org.json.JSONObject;
import info.nightscout.androidaps.events.Event;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import io.socket.client.Ack;
/**
* Created by mike on 21.02.2016.
*/
public class NSUpdateAck extends Event implements Ack {
private final AAPSLogger aapsLogger;
private final RxBusWrapper rxBus;
public boolean result = false;
public String _id;
public String action;
public void call(Object... args) {
JSONObject response = (JSONObject) args[0];
if (response.has("result"))
try {
if (response.getString("result").equals("success"))
result = true;
else if (response.getString("result").equals("Missing _id")) {
result = true;
aapsLogger.debug(LTag.NSCLIENT, "Internal error: Missing _id returned on dbUpdate ack");
}
rxBus.send(this);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public NSUpdateAck(String action, String _id, AAPSLogger aapsLogger, RxBusWrapper rxBus) {
super();
this.action = action;
this._id = _id;
this.aapsLogger = aapsLogger;
this.rxBus = rxBus;
}
}

View file

@ -0,0 +1,37 @@
package info.nightscout.androidaps.plugins.general.nsclient.acks
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import io.socket.client.Ack
import org.json.JSONException
import org.json.JSONObject
/**
* Created by mike on 21.02.2016.
*/
class NSUpdateAck(
val action : String,
var _id: String,
private val aapsLogger: AAPSLogger,
private val rxBus: RxBusWrapper,
val originalObject: Any? = null
) : Event(), Ack {
var result = false
override fun call(vararg args: Any) {
val response = args[0] as JSONObject
if (response.has("result")) try {
if (response.getString("result") == "success") {
result = true
} else if (response.getString("result") == "Missing _id") {
result = true
aapsLogger.debug(LTag.NSCLIENT, "Internal error: Missing _id returned on dbUpdate ack")
}
rxBus.send(this)
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
}
}

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